Skip to content

SDK

swap.coffee logo

SDK for swap.coffee DEX aggregator

npm License

Installation

For JavaScript/TypeScript please use the SDK as stated below:

$ npm install @swap-coffee/sdk

For any other languages you can integrate with our Rest API.

Concepts

Here are some basic concepts you need to know to work with our SDK:

  • Asset: an asset is a token on a blockchain. It can be a native token (like TON on TON blockchain) or a token on a smart contract, named jetton (like USDT on TON blockchain). Each asset has a unique address on the blockchain.
  • Route: a route is a set of paths.
  • Path: a path is a list of tokens (or, more specifically, of liquidity pools) that can be swapped one to another. For example, if you want to swap TON to USDT, you can do it directly (TON -> USDT) or you can do it in two steps (TON -> CES -> USDT). The first path has one swap, the second path has two swaps.
  • Price impact: when you perform a swap, the price of the token can change. The price impact depends on the amount you want to swap and the liquidity of the token. If you swap a small amount, the price impact will be low. If you swap a large amount, the price impact will be high.
  • Price slippage: the price of a token can change over time. When you swap tokens, you can set a slippage tolerance. If the price of the token changes more than the slippage tolerance, the transaction will fail.
  • Transaction: a transaction is a set of messages that are sent to the blockchain. Each message can be a token transfer, a contract call, etc. When you swap tokens, you need to build a transaction that will swap the tokens for you.

Our DEX aggregator works in the following way:

  1. You provide the input token, the output token and the amount you want to swap.
  2. We build the optimal route for you. This route can be direct (input token -> output token) or indirect (input token -> token1 -> token2 -> ... -> output token). By splitting the amount of the input token between several liquidity pools on several decentralized exchanges (DeDust, STONfi), we can reduce the price impact and get a better price for you.
  3. We build the transactions that will swap the tokens for you. Each transaction is a set of messages that are sent to the blockchain.
  4. You send the transactions to the blockchain. If the price slippage is too high and higher than the slippage tolerance you set, the transaction will be reverted, and you receive your input token back.
  5. You get the output token in your wallet or another address that you provide.

Basic usage

Swapping assets using our SDK and TonConnect SDK

import {ApiTokenAddress, RoutingApi} from "@swap-coffee/sdk";
import TonConnect, {WalletInfo, WalletInfoRemote} from "@tonconnect/sdk";

const connector = await setupTonConnect()
const routingApi = new RoutingApi()

const assetIn: ApiTokenAddress = {
    blockchain: "ton",
    address: "native" // stands for TON
}
const assetOut: ApiTokenAddress = {
    blockchain: "ton",
    address: "EQCl0S4xvoeGeFGijTzicSA8j6GiiugmJW5zxQbZTUntre-1" // CES
}

const input_amount = 5 // 5 TON

// let's build an optimal route
const route = await routingApi.buildRoute({
    input_token: assetIn,
    output_token: assetOut,
    input_amount: input_amount,
})

// then we can build transactions payload
const transactions = await routingApi.buildTransactionsV2({
    sender_address: connector.account?.address!!, // address of user's wallet
    slippage: 0.1, // 10% slippage
    paths: route.data.paths,
})

let messages = []

for (const transaction of transactions.data.transactions) {
    // swap.coffee takes care of all the boring stuff here :)
    messages.push({
        address: transaction.address,
        amount: transaction.value,
        payload: transaction.cell,
    })
}

// just send the transaction to the user
await connector.sendTransaction({
    validUntil: Date.now() + 5 * 60 * 1000,
    messages: messages,
})

Advanced usage

There are extra settings that you're able to modify in case you want more control over how routes are built.

The following parameters can be configured:

  • max_length - maximum path length in tokens, default is 3. If specified, it must be an integer from [2; 5] range. Value of 2 indicates that route can contain only direct-swaps paths (input token -> output token). Value of 3, however, states that route may also contain paths with one intermediate token (input token -> token1 -> output token).
  const route = await routingApi.buildRoute({
    input_token: assetIn,
    output_token: assetOut,
    input_amount: input_amount,
    max_length: 4 // allow up to 2 intermediate tokens
});
  • max_splits - maximum number of splits, default is 4. If specified, it must be an integer from [1; 20] range. Each split is a distinct path, therefore a distinct blockchain-message that will need to be sent. Currently, wallet contract implementations up to version 4 support not more than 4 messages in a single transaction. Starting from version 5, the number of message in a single transaction has been raise to 255 (but ours upper limit is 20).
  const route = await routingApi.buildRoute({
    input_token: assetIn,
    output_token: assetOut,
    input_amount: input_amount,
    max_splits: 2
});
  • pool_selector is an optional property that allows you to control which liquidity pools can be used whilst building the route.
    • blockchains - allows you to limit liquidity pools to given blockchains only. Currently swap.coffee does not support cross-chain swaps, but who knows what will happen tomorrow 🙂 If you're certain that you won't like performing cross-chain swaps in the future, set this property to ["ton"] value. By default, all supported blockchains are used.
    • dexes - allows you to limit liquidity pools to given dexes only. Supported values: ["stonfi"] and ["dedust"]. By default, all supported DEXes are used.
    • max_volatility - allows you to filter out those liquidity pools, whose price volatility over the last 15 minutes exceeded the given value. The higher the value, the more volatile liquidity pools may be used.
      const route = await routingApi.buildRoute({
          input_token: assetIn,
          output_token: assetOut,
          input_amount: input_amount,
          pool_selector: {
              max_volatility: 2.2, // allowing max price volatility of 220%
          },
      });
      

Difference between direct and indirect routes

Direct routes without intermediate tokens are usually more safe, but less profitable. Indirect routes with intermediate tokens are usually more profitable, but less safe, because the price of intermediate tokens can change during the transaction and chance that your may receive intermediate tokens instead of the desired output token is higher.

It's called "slippage" - the difference between the expected price and the actual price. For example, when you set slippage to 0.1, it means that transaction within blockchain must be aborted if any of the swaps lead to obtaining less than 1 - 0.1 = 0.9 = 90% of amount predicted by swap.coffee backend in returned route. Due to async nature of TON blockchain, such slippage abortion can also lead to a situation where the sender is left with neither the input nor the output tokens, but an intermediate one.

There are also tokens that can't be swapped directly, because they don't share a liquidity pool. Therefore, if you setup parameters so that only direct swaps allowed (i.e. if max_length is equal to 2), an empty route will be built for you.

ExactOut swaps

If you want to receive an exact amount of output tokens, you can specify the output_amount parameter. Those swaps, in comparison to direct swaps (when you specify input_amount instead), may be significantly less profitable. They also do not support splitting, which means that resulting route (if exists) will always contain a single path.

const route = await routingApi.buildRoute({
    input_token: assetIn,
    output_token: assetOut,
    output_amount: 200 // desired amount of output token
});

Check result of the transaction

Along with the transaction built, you receive the route_id field. It can be used to check the result of the route's transactions on the blockchain.

Basic usage

SDK contains inbuilt method for waiting execution results of route's transactions. Promise will be resolved when all transactions are in terminal status (one of succeeded, failed or timed_out).

import {ApiTokenAddress, RoutingApi, waitForTransactionResults} from "@swap-coffee/sdk";

const connector = await setupTonConnect();
const routingApi = new RoutingApi();

const assetIn: ApiTokenAddress = {
  blockchain: 'ton',
  address: 'native', // stands for TON
};
const assetOut: ApiTokenAddress = {
  blockchain: 'ton',
  address: 'EQCl0S4xvoeGeFGijTzicSA8j6GiiugmJW5zxQbZTUntre-1', // CES
};

const route = await routingApi.buildRoute({
  input_token: assetIn,
  output_token: assetOut,
  input_amount: 5, // 5 TON
});

const transactions = await routingApi.buildTransactionsV2({
  sender_address: connector.account?.address!!,
  slippage: 0.1,
  paths: route.data.paths, // note: use route.data here
});


let messages = [];

for (const transaction of transactions.data.transactions) {
  messages.push({
    address: transaction.address,
    amount: transaction.value,
    payload: transaction.cell,
  });
}

await connector.sendTransaction({
  validUntil: Date.now() + 5 * 60 * 1000,
  messages: messages,
});

const results = await waitForTransactionResults(transactions.data.route_id, routingApi);

for (const result of results) {
  // handle result for each route's paths
}

Advanced usage

By passing route_id into getTransactionsResult() you can obtain current state of the route's transactions execution status. Response is as follows:

[
  {
    "status": "TX_STATUS",
    "steps": [
      {
        "status": "SWAP_STATUS",
        "input": {
          "token_blockchain": "ton",
          "token_address": "EQ...1",
          "amount": 1.2345
        },
        "output": {
          "token_blockchain": "ton",
          "token_address": "EQ...2",
          "amount": 2.3456
        }
      }
    ],
    "input": {
      "token_blockchain": "ton",
      "token_address": "EQ...1",
      "amount": 1.2345
    },
    "output": {
      "token_blockchain": "ton",
      "token_address": "EQ...2",
      "amount": 2.3456
    }
  }
]

Each element of the returned array corresponds to a path at the same index that you provided buildTransactionsV2 method with.

Each element of the steps corresponds to a single swap within transaction. For example, if transaction is A -> B -> C, then first entry in steps describes A -> B swap, and the second one describes B -> C.

  • TX_STATUS is a status of a single transaction (related to a single path):
    • pending indicates that none of transaction's swaps have occurred in the blockchain yet.
    • partially_complete - some (but not all) of transaction's swaps have occurred in the blockchain.
    • succeeded - all of transaction's swaps have successfully executed in blockchain.
    • failed - one of transaction's swaps has been aborted due to slippage tolerance.
    • timed_out - our service could not wait enough for at least one of transaction's swaps to be present in the blockchain. This happens if more than 5 minutes have passed since the transaction was generated by our service until the user sent it, or if more than 3 minutes have passed since the previous swap for this transaction appeared in the blockchain.
  • SWAP_STATUS is a status of a single swap within a transaction:
    • pending indicates that this swap didn't happen yet.
    • cancelled - swap has been cancelled due to one of the previous swaps within same transaction failure or timeout.
    • succeeded - swap successfully executed.
    • failed - swap has been aborted due to slippage tolerance.
    • timed_out - our service could not wait enough for this swap to be present in the blockchain.
  • Transaction's input and output fields are presented only if at least one swap has already moved to status succeeded or failed.
  • Swap's input and output fields are presented only if swap's status is succeeded or failed. On failure (abortion due to slippage tolerance) output is equal to input.

The route status can be requested within 2 hours from the moment route_id (and transactions) is generated via buildTransactionsV2.

If all transactions are in terminal status (one of succeeded, failed or timed_out), further receipt of this route's status will be possible only for 1 minute, after which it will be invalidated from our service's cache.

API Limitations

Our API is public, which means it allows unauthorized access. However, this access has its limitations:

  • input_token and output_token must be different, so you are not allowed to find cycles. Moreover, none of the returned routes will contain cycles within them (so no paths like token1 -> token2 -> token1 -> token3 will be returned).
  • We believe that the average load of 1 request per second per IP is more than acceptable. If you generate more, the API will return a 429 Too Many Requests error.
  • The entire commission received through the execution of transactions built by swap.coffee goes to swap.coffee.

If you are considering integration with our product, building an arbitrage service or generating a higher level of load, please contact us via Telegram @pandemoniuz.