Overview

As previously highlighted, the DeDust Protocol is composed of multiple components.

The swapping operation primarily involves two: the Pool and the Vault.

To swap 100 tokens A for a respective amount of tokens B, the procedure involves the following steps:

  1. The user deposits token A into Vault (A).
  2. Vault (A) communicates to Pool (A, B) that it has received tokens A from a user.
  3. Pool (A, B) computes the quantity of token B the user is due and instructs Vault (B) to release the appropriate amount to the user.
  4. Vault (B) then distributes the determined amount of token B to the user.

This scenario is universal for all asset types. The only variation is in how assets are deposited into the Vault, as each asset type has a unique transfer method. For instance, for the native coin, we send a swap message to the Vault, attaching the corresponding amount of TON. However, for jettons, we transfer them to the Vault using transfer message and attach a specific forward_payload.

Let's dive into both scenarios below.

Swapping native coin

Let's examine an example where we swap TON to SCALE.

To achieve this, follow these steps:

  1. Find the Vault (TON)
  2. Find the Pool (TON, SCALE)
  3. Check if Vault (TON) and Pool (TON, SCALE) are deployed
  4. Send a message to a Vault (TON)

Step 1. Find the Vault (TON)

The Factory is not only useful for creating contracts but also for locating any contract within the DeDust Protocol.
Let's locate the Vault (TON).

import { Asset, VaultNative } from '@dedust/sdk';

// ...

// NOTE: We will use tonVault to send a message.
const tonVault = tonClient.open(await factory.getNativeVault());

Step 2. Find the Pool (TON, SCALE)

To construct the message body correctly, we must identify the address of the Pool (TON, SCALE) where we plan to swap our assets.

import { Asset, PoolType } from '@dedust/sdk';
import { Address } from '@ton/core';

const SCALE_ADDRESS = Address.parse('EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE');

const TON = Asset.native();
const SCALE = Asset.jetton(SCALE_ADDRESS);

const pool = tonClient.open(await factory.getPool(PoolType.VOLATILE, [TON, SCALE]));

Step 3. Check if Vault (TON) and Pool (TON, SCALE) are deployed

❗️

It's important to ensure that contracts are deployed.

Sending funds to an inactive contract could result in irretrievable loss.

import { ReadinessStatus } from '@dedust/sdk';

// ...

// Check if pool exists:
if ((await pool.getReadinessStatus()) !== ReadinessStatus.READY) {
  throw new Error('Pool (TON, SCALE) does not exist.');
}

// Check if vault exits:
if ((await tonVault.getReadinessStatus()) !== ReadinessStatus.READY) {
  throw new Error('Vault (TON) does not exist.');
}

Step 4. Send a message to a Vault (TON)

The final step is to send a message with the appropriate amount of TON to Vault (TON).

import { toNano } from '@ton/core';

// ...

const amountIn = toNano('5'); // 5 TON

await tonVault.sendSwap(sender, {
  poolAddress: pool.address,
  amount: amountIn,
  gasAmount: toNano("0.25"),
});

👍

Success!

We've swapped 5 TON to SCALE using the DeDust Protocol.

Swapping jettons

Swapping jettons to any other asset is mostly the same as swapping TON.

  1. Find the Vault (SCALE)
  2. Find the jetton wallet for the user
  3. Send jettons to the Vault (SCALE) with corresponding payload.

Step 1. Find the Vault (SCALE)

const scaleVault = tonClient.open(await factory.getJettonVault(SCALE_ADDRESS));

Step 2. Find the jetton wallet for the user

import { JettonRoot, JettonWallet } from '@dedust/sdk';

// ...

const scaleRoot = tonClient.open(JettonRoot.createFromAddress(SCALE_ADDRESS));
const scaleWallet = tonClient.open(await scaleRoot.getWallet(sender.address);

Step 3. Transfer jettons to the Vault (SCALE) with corresponding payload

// ...

const amountIn = toNano('50'); // 50 SCALE

await scaleWallet.sendTransfer(sender, toNano("0.3"), {
  amount: amountIn,
  destination: scaleVault.address,
  responseAddress: sender.address, // return gas to user
  forwardAmount: toNano("0.25"),
  forwardPayload: VaultJetton.createSwapPayload({ poolAddress }),
});

👍

Success!

We've swapped 50 SCALE to TON.

Multi-hop swaps

Sometimes, a direct pool for our swap doesn't exist.

For instance, if we want to swap asset A for C, but Pool (A, C) lacks liquidity or doesn't exist. However, we have pools - Pool (A, B) and Pool (B, C).

So, instead of a direct swap from A to C, we first swap A to B, then B to C.

DeDust lets you do these consecutive swaps in one step, ensuring a smooth user experience.

Here is an example how to swap SCALE to BOLT through TON using multi-hop swaps with DeDust SDK.

First of all, we should find necessary pools.

/* ... */

const SCALE = Asset.jetton(SCALE_ADDRESS);
const TON = Asset.native();
const BOLT = Asset.jetton(BOLT_ADDRESS);

const TON_SCALE = tonClient.open(await factory.getPool(PoolType.VOLATILE, [TON, SCALE]));
const TON_BOLT = tonClient.open(await factory.getPool(PoolType.VOLATILE, [TON, BOLT]));

After that we should send a message that enables multi-hop swaps:

await scaleWallet.sendTransfer(
    sender,
    toNano("0.3"), // 0.3 TON
    {
      amount: amountIn,
      destination: scaleVault.address,
      responseAddress: USER_ADDR, // return gas to user
      forwardAmount: toNano("0.25"),
      forwardPayload: VaultJetton.createSwapPayload({
        poolAddress: TON_SCALE.address, // first step: SCALE -> TON
        limit: minimalAmountOut,
        next: {
          poolAddress: TON_BOLT.address, // next step: TON -> BOLT
        },
      }),
    },
  );

That's it.