JellyButler

What is Butler?

Butler is automated market making software that is used for liquidity provision to JellySwap protocol. Everyone can run Butler instance on his/her own machine and start earning interest from market spreads. Butler supports automatic order matching, withdraws, refunds, portfolio rebalancing, email and slack notifications. Supported coins are BTC, ETH, DAI, AE, WBTC.

How to install Butler?

There are two main approaches to install Butler:

  1. Using Docker

  2. Manual Setup

You can find and freely download the software from this link:

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

How to setup Butler via Docker?

The first requirement is to setup Docker on your system.

To install Docker on Mac follow this quick guide: https://docs.docker.com/docker-for-mac/install/

Once, you have successfully installed docker on your system you can clone/download the Butler official distribution from here:

Open Butler download folder - it should have the following structure:

Update .env file with the desired value for mongodb password.

MONGO_PASSWORD=adminpass

Now you should edit config.ts file which is in the root folder of the project.

export default {
    // ================== Your Cherry Node name ==================
    NAME: 'ADD_YOUR_NODE_NAME',

    AGGREGATOR_URL: 'https://network.jelly.market/api/v1/info/update',

    // ================== Server configuration ==================
    SERVER: {
        PORT: 9000,
    },

    // ================== Database configuration ==================
    //options: mongodb or sqlite
    ACTIVE_DB: 'mongodb',
    MONGODB: {
        //Docker Setup
        URL: 'mongodb://db:27017/butler',
        AUTH: 'admin',
        MONGO_PASSWORD: process.env.MONGO_PASSWORD,

        //Manual setup
        //URL: 'mongodb://localhost:27017/butler',
    },
    SQLITE: {
        //Database name
        database: 'butler.sqlite',
    },

    // Specify the networks you want to support
    // Available options: ETH, BTC, DAI, WBTC, AE
    NETWORKS: {
        ETH: true,
        DAI: true,
        USDC: false,
        WBTC: false,
        BTC: false,
        AE: false,
    },

    // For every enabled network, you should specify an address and a secret
    BLOCKCHAIN: {
        //ETH provider wallet
        //Should be different than your ERC20 provider wallet
        ETH: {
            ADDRESS: '',
            // Ethereum Private Key
            SECRET: '',
        },

        BTC: {
            ADDRESS: '',
            // BIP39 mnemonic
            SECRET: '',
        },

        AE: {
            ADDRESS: '',
            // Aeternity Key Pair
            SECRET: {
                publicKey: '',
                secretKey: '',
            },
        },

        AES: {
            ADDRESS: '',
            // Aeternity Key Pair
            SECRET: {
                publicKey: '',
                secretKey: '',
            },
        },

        //Use one common ETH address for all ERC20 tokens
        //Should be different than your ETH provider wallet
        DAI: {
            ADDRESS: '',
            // Ethereum Private Key
            SECRET: '',
        },

        USDC: {
            ADDRESS: '',
            // Ethereum Private Key
            SECRET: '',
        },

        WBTC: {
            ADDRESS: '',
            // Ethereum Private Key
            SECRET: '',
        },
    },

    // The amount of fee that is charged by JellySwap protocol.
    // Currently the usage of JellySwap protocol is free
    FEE: 0.0,

    // Choose wheter you will cover the fees for the user on their first withdraw or not.
    // If a user wants to swap BTC for ETH, but his ETH wallet is empty, he won't be able to make the withdraw.
    // By enabling this option, you will perform the withraw for the user, only if his address has 0 ETH balance
    COVER_FEES: true,

    // Specify the pairs you want to support as a liquidity provider.
    PAIRS: {
        'ETH-BTC': true,
        'ETH-DAI': true,
        'ETH-AE': true,
        'ETH-USDC': true,

        'BTC-ETH': true,
        'BTC-DAI': true,
        'BTC-AE': true,
        'BTC-USDC': true,

        'DAI-BTC': true,
        'DAI-ETH': true,

        'AE-BTC': true,
        'AE-ETH': true,

        'USDC-BTC': true,
        'USDC-ETH': true,
    },

    // Specify the exchange you want to use as a risk management tool.
    // Available options: binance or leave it empty
    // Spreads are measured in percentage  0.08 = 8% spread on top of the market price
    // Tolerance option - minimum spread a t which you will take the order
    // If you enable this option, whenever you execute a swap, a mirror order will be placed on Binance.
    // Example: You swap 1 BTC for 10 ETH. With the 10 ETH you receive you place an order to buy BTC from Binance.
    // If you have gathered enough fees, you will receive more than 1 BTC for the 10 ETH leaving you with profit.
    EXCHANGE: '',

    // ================== Price configuration ==================
    PRICE: {
        COINS: ['USDT', 'BTC', 'ETH', 'DAI', 'AE', 'USDC'],
        SPREAD: {
            DEFAULT: 0.0091,
            'AE-ETH': 0.08,
            'ETH-AE': 0.08,
            'ETH-BTC': 0.5,
            'BTC-ETH': 0.5,
        },
        TOLERANCE: {
            DEFAULT: 0.001,
            'AE-ETH': 0.1,
            'ETH-AE': 0.1,
            'ETH-BTC': 0.3,
            'BTC-ETH': 0.3,
        },
        PROVIDER: 'cryptocompare',
        UPDATE_INTERVAL: 30,
        USE_FALLBACK: false,
        FALLBACK: 'cryptocompare',
    },
    // ================== Balance Snapshot ==================
    // Saves you current balance on every x seconds.
    // You can track your portfolio P&L with this option
    BALANCE_SNAPSHOT_INTERVAL: 3600,

    // ================== Email configuration ==================
    // If you enable this option, you will receive an email whenever you process a new swap/withdraw/refund.
    EMAIL: {
        ENABLED: false,
        SERVICE: 'gmail',
        USERNAME: '',
        PASSWORD: '',
        FROM: '',
        TO: '',
        SUBJECT: 'JELLY',
    },

    // If you specify a Slack Webhook, you will receive real time messages with your swap statuses.
    SLACK_WEBHOOK_URL: '', //https://hooks.slack.com/services/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

    CRYPTOCOMPARE_API: '',

    // ================== Binance configuration ==================
    BINANCE: {
        API_KEY: '', // place binance API KEY here
        SECRET_KEY: '', // place binance SECRET KEY here

        PAIRS: {
            ETHBTC: true,
            BTCUSDT: true,
            AEETH: true,
            AEBTC: true,
            ETHUSDC: true,
            BTCUSDC: true,
        },
        PRECISION: {
            ETH: 3,
            BTC: 4,
            USDT: 4,
            AE: 1,
            USDC: 4,
        },
        DUPLICATE_PRICE: { DAI: 'USDC' },
    },
};

You can generate Binance API key from your Binance exchange account

For Cryptocompare API Key you can use this link:

In order to use email notifications you should have gmail account and to fill your credentials. All provided private keys, API keys, and gmail credentials are securely stored only on your local machine!

After .env and config.ts are filled you can run the following command:

$ chmod 777 wait-for.sh && mkdir logs && docker-compose up -d

The above mentioned command will start the application in background mode.

Type in the next command to check the log and verify that everything is working fine

$ docker-compose logs -f

You should see something like this if the application is running and configured correctly

3/19/2020, 16:14:00 info:       Starting task: Price Task
3/19/2020, 16:14:01 info:       Starting task: Balance Task
3/19/2020, 16:14:04 info:       Starting task: Info Task
3/19/2020, 16:14:04 info:       Server started on port 9003
3/19/2020, 16:14:04 info:       Starting BTC Events
3/19/2020, 16:14:04 info:       Starting ETH Events
3/19/2020, 16:14:04 info:       Starting ERC20 Events

To confirm that everything works, you can open your browser at http://localhost:9003/api/v1/info and the response should be similar to this one:

{
   "name":"BUTLER_DOCKER",
   "pairs":{
      "ETH-BTC":true,
      "BTC-ETH":true,
      "BTC-DAI":true,
      "DAI-BTC":true,
      "TRX-ETH":false,
      "AE-ETH":false,
      "ETH-AE":false,
      "AE-BTC":false,
      "BTC-AE":false,
      "ETH-DAI":true,
      "DAI-ETH":true
   },
   "prices":{
      "USDT-BTC":"0.000170747278924407125867",
      "BTC-USDT":"5797.023626",
      "USDT-ETH":"0.007864200458461781679038",
      "ETH-USDT":"125.864799",
      "BTC-ETH":"45.833141382964020822771163",
      "ETH-BTC":"0.0215962943",
      "USDT-TRX":"91.275229357798165137615044",
      "TRX-USDT":"0.01084441",
      "BTC-TRX":"532032.085561497326203208557267",
      "TRX-BTC":"0.000001860463",
      "ETH-TRX":"11535.072463768115942028986084",
      "TRX-ETH":"0.000085810125",
      "BTC-AE":"58317.702227432590855803044275",
      "AE-BTC":"0.000016972994",
      "ETH-AE":"1262.563451776649746192893098",
      "AE-ETH":"0.0007839812",
      "DAI-BTC":"0.000170747278924407125867",
      "BTC-DAI":"5797.023626",
      "DAI-ETH":"0.007864200458461781679038",
      "ETH-DAI":"125.864799",
      "DAI-TRX":"91.275229357798165137615044",
      "TRX-DAI":"0.01084441"
   },
   "balances":{
      "ETH":{
         "address":"0x426dcFB43293C2ef6Cd457bDaDbEF932ceE1d1a2",
         "raw":"1554467049803775060",
         "balance":"1.55446704980377506"
      },
      "DAI":{
         "address":"0x0efffB0e375F4bd895FD90360FD03CE1D5BF64FA",
         "raw":"9644643000000000000",
         "balance":"9.644643"
      },
      "WBTC":{
         "address":"0x0efffB0e375F4bd895FD90360FD03CE1D5BF64FA",
         "raw":"0",
         "balance":"0.0"
      },
      "BTC":{
         "address":"bc1qttmes87g3w5k8ewswkwprj7znpe2pmuumcjz5v",
         "raw":"14752",
         "balance":"0.00014752"
      }
   },
   "updated":1584631135775
}

Last step is to open this link https://network.jelly.market/api/v1/info/get and confirm that you are connected to the network. If your info is presented there then the process is successful and Butler is up and running.

Stop or Reconfigure Butler

If you want to stop Butler or you jast want to update the configuration use this command:

$ docker-compose stop

Then you can open again config.ts and update your configurations. When you are ready you can start Butler:

$ docker-compose up

How to setup Butler manually?

Manual Butler setup supports two options for database. Sqlite and mongoDB. Installation with sqlite is much more simpler and the preferred way for the non-technical users. Setup with sqlite requires less resources on your machine and the only thing that you must change is config file ACTIVE_DB directive and write 'sqlite'. If you decide to use sqlite, you can skip the mongoDB setup step.

This step is optional - mongoDB setup

Now you have to install nodejs from this link - https://nodejs.org/en/download/

npm - node package manager is coming with the nodejs installation

Next step is to install yarn

$ npm install yarn -g

We will need one more package to start Butler as background process. For this purpose we will use pm2

$ npm install pm2 -g

Once, you have successfully installed mongoDB, nodejs, npm, yarn and pm2 on your system you can clone/download the Butler official distribution from here:

Open Butler download folder - it should have the following structure:

Next step is to install all node packages

$ yarn install

Now you should edit config.ts file which is in the root folder of the project.

export default {
    // ================== Your Cherry Node name ==================
    NAME: 'ADD_YOUR_NODE_NAME',

    AGGREGATOR_URL: 'https://network.jelly.market/api/v1/info/update',

    // ================== Server configuration ==================
    SERVER: {
        PORT: 9000,
    },

    // ================== Database configuration ==================
    //options: mongodb or sqlite
    ACTIVE_DB: 'mongodb',
    MONGODB: {
        //Docker Setup
        URL: 'mongodb://localhost:27017/butler',
        AUTH: 'admin',
        MONGO_PASSWORD: process.env.MONGO_PASSWORD,

        //Manual setup
        //URL: 'mongodb://localhost:27017/butler',
    },
    SQLITE: {
        //Database name
        database: 'butler.sqlite',
    },

    // Specify the networks you want to support
    // Available options: ETH, BTC, DAI, WBTC, AE
    NETWORKS: {
        ETH: true,
        DAI: true,
        USDC: false,
        WBTC: false,
        BTC: false,
        AE: false,
    },

    // For every enabled network, you should specify an address and a secret
    BLOCKCHAIN: {
        //ETH provider wallet
        //Should be different than your ERC20 provider wallet
        ETH: {
            ADDRESS: '',
            // Ethereum Private Key
            SECRET: '',
        },

        BTC: {
            ADDRESS: '',
            // BIP39 mnemonic
            SECRET: '',
        },

        AE: {
            ADDRESS: '',
            // Aeternity Key Pair
            SECRET: {
                publicKey: '',
                secretKey: '',
            },
        },

        AES: {
            ADDRESS: '',
            // Aeternity Key Pair
            SECRET: {
                publicKey: '',
                secretKey: '',
            },
        },

        //Use one common ETH address for all ERC20 tokens
        //Should be different than your ETH provider wallet
        DAI: {
            ADDRESS: '',
            // Ethereum Private Key
            SECRET: '',
        },

        USDC: {
            ADDRESS: '',
            // Ethereum Private Key
            SECRET: '',
        },

        WBTC: {
            ADDRESS: '',
            // Ethereum Private Key
            SECRET: '',
        },
    },

    // The amount of fee that is charged by JellySwap protocol.
    // Currently the usage of JellySwap protocol is free
    FEE: 0.0,

    // Choose wheter you will cover the fees for the user on their first withdraw or not.
    // If a user wants to swap BTC for ETH, but his ETH wallet is empty, he won't be able to make the withdraw.
    // By enabling this option, you will perform the withraw for the user, only if his address has 0 ETH balance
    COVER_FEES: true,

    // Specify the pairs you want to support as a liquidity provider.
    PAIRS: {
        'ETH-BTC': true,
        'ETH-DAI': true,
        'ETH-AE': true,
        'ETH-USDC': true,

        'BTC-ETH': true,
        'BTC-DAI': true,
        'BTC-AE': true,
        'BTC-USDC': true,

        'DAI-BTC': true,
        'DAI-ETH': true,

        'AE-BTC': true,
        'AE-ETH': true,

        'USDC-BTC': true,
        'USDC-ETH': true,
    },

    // Specify the exchange you want to use as a risk management tool.
    // Available options: binance or leave it empty
    // Spreads are measured in percentage  0.08 = 8% spread on top of the market price
    // Tolerance option - minimum spread a t which you will take the order
    // If you enable this option, whenever you execute a swap, a mirror order will be placed on Binance.
    // Example: You swap 1 BTC for 10 ETH. With the 10 ETH you receive you place an order to buy BTC from Binance.
    // If you have gathered enough fees, you will receive more than 1 BTC for the 10 ETH leaving you with profit.
    EXCHANGE: '',

    // ================== Price configuration ==================
    PRICE: {
        COINS: ['USDT', 'BTC', 'ETH', 'DAI', 'AE', 'USDC'],
        SPREAD: {
            DEFAULT: 0.0091,
            'AE-ETH': 0.08,
            'ETH-AE': 0.08,
            'ETH-BTC': 0.5,
            'BTC-ETH': 0.5,
        },
        TOLERANCE: {
            DEFAULT: 0.001,
            'AE-ETH': 0.1,
            'ETH-AE': 0.1,
            'ETH-BTC': 0.3,
            'BTC-ETH': 0.3,
        },
        PROVIDER: 'cryptocompare',
        UPDATE_INTERVAL: 30,
        USE_FALLBACK: false,
        FALLBACK: 'cryptocompare',
    },
    // ================== Balance Snapshot ==================
    // Saves you current balance on every x seconds.
    // You can track your portfolio P&L with this option
    BALANCE_SNAPSHOT_INTERVAL: 3600,

    // ================== Email configuration ==================
    // If you enable this option, you will receive an email whenever you process a new swap/withdraw/refund.
    EMAIL: {
        ENABLED: false,
        SERVICE: 'gmail',
        USERNAME: '',
        PASSWORD: '',
        FROM: '',
        TO: '',
        SUBJECT: 'JELLY',
    },

    // If you specify a Slack Webhook, you will receive real time messages with your swap statuses.
    SLACK_WEBHOOK_URL: '', //https://hooks.slack.com/services/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

    CRYPTOCOMPARE_API: '',

    // ================== Binance configuration ==================
    BINANCE: {
        API_KEY: '', // place binance API KEY here
        SECRET_KEY: '', // place binance SECRET KEY here

        PAIRS: {
            ETHBTC: true,
            BTCUSDT: true,
            AEETH: true,
            AEBTC: true,
            ETHUSDC: true,
            BTCUSDC: true,
        },
        PRECISION: {
            ETH: 3,
            BTC: 4,
            USDT: 4,
            AE: 1,
            USDC: 4,
        },
        DUPLICATE_PRICE: { DAI: 'USDC' },
    },
};

You can generate Binance API key from your Binance exchange account:

For Cryptocompare API Key you can use this link:

In order to use email notifications you should have gmail account and to fill your credentials. All provided private keys, API keys, and gmail credentials are securely stored only on your local machine!

After config.ts is filled you can run the following command from the project root folder:

$ pm2 start yarn --name "butler" -- start

This will start the butler software as a background process.

Run the following command to check if Butler is running

$ pm2 logs

You should see some similar output:

3/19/2020, 16:14:00 info:       Starting task: Price Task
3/19/2020, 16:14:01 info:       Starting task: Balance Task
3/19/2020, 16:14:04 info:       Starting task: Info Task
3/19/2020, 16:14:04 info:       Server started on port 9003
3/19/2020, 16:14:04 info:       Starting BTC Events
3/19/2020, 16:14:04 info:       Starting ETH Events
3/19/2020, 16:14:04 info:       Starting ERC20 Events

If you want to stop Butler background service you can execute

$ pm2 stop butler

To confirm that Butler is up and running you can open this link on your browser http://localhost:9003/api/v1/info

{
   "name":"BUTLER_MANUAL",
   "pairs":{
      "ETH-BTC":true,
      "BTC-ETH":true,
      "BTC-DAI":true,
      "DAI-BTC":true,
      "TRX-ETH":false,
      "AE-ETH":false,
      "ETH-AE":false,
      "AE-BTC":false,
      "BTC-AE":false,
      "ETH-DAI":true,
      "DAI-ETH":true
   },
   "prices":{
      "USDT-BTC":"0.000170747278924407125867",
      "BTC-USDT":"5797.023626",
      "USDT-ETH":"0.007864200458461781679038",
      "ETH-USDT":"125.864799",
      "BTC-ETH":"45.833141382964020822771163",
      "ETH-BTC":"0.0215962943",
      "USDT-TRX":"91.275229357798165137615044",
      "TRX-USDT":"0.01084441",
      "BTC-TRX":"532032.085561497326203208557267",
      "TRX-BTC":"0.000001860463",
      "ETH-TRX":"11535.072463768115942028986084",
      "TRX-ETH":"0.000085810125",
      "BTC-AE":"58317.702227432590855803044275",
      "AE-BTC":"0.000016972994",
      "ETH-AE":"1262.563451776649746192893098",
      "AE-ETH":"0.0007839812",
      "DAI-BTC":"0.000170747278924407125867",
      "BTC-DAI":"5797.023626",
      "DAI-ETH":"0.007864200458461781679038",
      "ETH-DAI":"125.864799",
      "DAI-TRX":"91.275229357798165137615044",
      "TRX-DAI":"0.01084441"
   },
   "balances":{
      "ETH":{
         "address":"0x426dcFB43293C2ef6Cd457bDaDbEF932ceE1d1a2",
         "raw":"1554467049803775060",
         "balance":"1.55446704980377506"
      },
      "DAI":{
         "address":"0x0efffB0e375F4bd895FD90360FD03CE1D5BF64FA",
         "raw":"9644643000000000000",
         "balance":"9.644643"
      },
      "WBTC":{
         "address":"0x0efffB0e375F4bd895FD90360FD03CE1D5BF64FA",
         "raw":"0",
         "balance":"0.0"
      },
      "BTC":{
         "address":"bc1qttmes87g3w5k8ewswkwprj7znpe2pmuumcjz5v",
         "raw":"14752",
         "balance":"0.00014752"
      }
   },
   "updated":1584631135775
}

Last step is to open this link https://network.jelly.market/api/v1/info/get and confirm that you are connected to the network. If your info is presented there then the process is successful and Butler is up and running.

What are the risks?

Butler is automated software and it should be up and running 24/7 as long as you want to participate in the protocol. Risks related to connectivity and hardware:

  • Power outage

  • Internet outage

  • Hardware problem

Next group of problems is related to software and service dependencies:

  • Bug in the Butler software or smart contracts

  • Price provider service outage

  • Lack of connectivity to blockchain

  • Database problem

  • Problem with connecting to rebalancing exchange

  • Hacker attack that can get access to Butler operator node

Market related risks:

  • Too volatile market and big price movement can bring some losses

  • Ghost attack - fake swaps are started, but never finalized - no losses for Butler operator, but funds will be locked for 4 hours in HTLC

  • Slow blockchain transaction speed due to network overload, usually combined with massive price movement

All swaps have a lock period of 24 hours. If Butler is down for some of the above mentioned reasons or some new unexpected bug, every Butler node operator have 24 hours period to recover his/her Butler and no loses will be encountered.

JellySwap team have paid for 3rd party security audit and all of the code for Butler is open source, therefore everyone can check it and run it on his/her own risk.

Last updated