Soroswap.Finance Docs
  • Welcome 👋🏼
    • What is Soroswap Finance?
    • Getting Started
      • Wallet Setup and Connection
      • How to Swap
      • Provide Liquidity
      • How the Aggregator Works
  • Concepts
    • AMM
    • Liquidity Pools
    • Swap
    • Fees
    • Slippage
    • Router
    • SDEX
    • Aggregator
    • Trustlines
    • Advanced Topics
      • Pricing
      • Understanding Returns
      • Security
      • Research
  • Soroswap AMM (DEX)
    • How Soroswap AMM works
    • Ecosystem Participants
    • Audits
    • Technical Reference
      • Smart contracts
        • SoroswapPair
        • SoroswapFactory
        • SoroswapRouter
        • SoroswapLibrary
      • Deployed Addresses
      • Error Codes
      • Using Soroswap with TypeScript
      • Smart Contract Integration
      • Deploy Soroswap Yourself
        • Setup your environment.
        • Experiment the Pair contract
        • Experiment the Factory Contract.
        • Deployments.
        • Using the Soroswap Testnet
    • Glossary
  • Soroswap Aggregator
    • Supported AMMs
    • Audits
    • Technical Reference
      • How Soroswap Aggregator works
      • Design
      • Technical Overview
      • Aggregator Operation
      • Smart Contracts
        • SoroswapAggregator
        • Adapter Trait
        • SoroswapAdapter
      • Inspirations
        • 1inch
      • Other AMMs in Soroban
        • Phoenix
    • Disclaimer
  • Soroswap API
  • Soroswap Info
  • Tutorials
    • Installing Freighter
    • Soroswap sections
    • Adding Liquidity
    • Doing Swap
    • Remove Liquidity
    • Using Stellar Classic Assets
      • Wrapping Stellar Classic Assets
      • Swap Stellar Classic Assets
      • Test Stellar Classic Assets
    • Bridge using Pendulum
    • Conclusions
  • Partnerships
    • Collaboration with Mercury and SubQuery
    • Business Partnerships
  • Support & Resources
    • About Us
    • General FAQ
    • Additional Resources
Powered by GitBook
On this page
  • Prerequisites:
  • Build, sign & send:
  • Building the transaction:
  • Simulate, sign & send the transaction:
  • Methods:
  • Add liquidity to a pool:
  • Remove liquidity from a pool:
  • Swap:
  • Finding the Most Optimal Path:
  • Putting it All Together:
Edit on GitHub
  1. Soroswap AMM (DEX)
  2. Technical Reference

Using Soroswap with TypeScript

PreviousError CodesNextSmart Contract Integration

Last updated 1 year ago

The Soroswap protocol allows you to interact with Stellar's smart contract platform: Soroban. In this section, we will explore how to write TypeScript scripts to use the contracts in our own automations or applications:

Prerequisites:

Before starting, it is necessary to clarify that to understand what we are doing here, you need to have a good understanding of TypeScript, smart contracts, and how a blockchain works. In addition, you need to know how to use since we will use its class to create operations, simulate, sign, and send transactions. Additionally, some types and functions for transforming values.

[!Tip] If you need practical examples of how to create a transaction builder or how to use the SDK in general, you can guide yourself from our projects and

Build, sign & send:

[!Warning] For educational purposes, we will use an adaptation of the TransactionBuilder used in the repository. The code will need adjustments depending on your project and work methodology, so we recommend always working hand-in-hand with the official Stellar SDK to be able to build one tailored to your needs.

Installing Stellar SDK

In this guide, we will be using the ^11.2.2 version of Stellar SDK, available through npm or yarn as. To do this, we will install it as follows:

npm i soroswap-router-sdk@11.2.2

or

yarn add soroswap-router-sdk@11.2.2

Building the transaction:

In order to execute our operations on the blockchain, we will first need to create a transaction to send ( in this guide you will find the available methods and their predefined parameters):

First, we must create an instance of the router contract using the Contract class, giving as an argument the Address of the contract and using its call method, we create the operation delivering as arguments the method of the operation (for example: "swap_exact_assets_for_assets") and the parameters defined to then create the transaction with our TransactionBuilder:

[!Tip] If you need to thoroughly review all the methods available in the router contract or simply want to know how the contract works, you can review it directly in the official repository of Soroswap.

[!Tip] To obtain the routerAddress respective to the network on which you want to operate, you can make a direct call to the Soroswap api in the following way:

curl -XGET -H "Content-type: application/json" 'https://api.soroswap.finance/api/${network}/router'
const horizonServer = stellarSDK.Horizon.Server("horizon-rpc url");
const createTx = async (account: Keypair, routerAddress: Address, method: String) => {
  const createTxBuilder = async (account: Keypair): Promise<TransactionBuilder> => {
      try {
        const account: Account = await horizonServer.getAccount(account.publicKey());
        return new TransactionBuilder(account, {
          fee: stellarSdk.BASE_FEE,
          timebounds: { minTime: 0, maxTime: 0 },
          networkPassphrase: NETWORK.PASSPHRASE,
        });
      } catch (e: any) {
        console.error(e);
        throw Error("unable to create txBuilder");
      }
    }
    const contractInstance = new Contract(routerAddress);
    const contractOperation = contractInstance.call( method, ...params );
    const txBuilder = await createTxBuilder(account);
    txBuilder.addOperation(contractOperation);
    const tx = txBuilder.build();
  return tx;
}

Simulate, sign & send the transaction:

Once you have created the transaction, we must deliver it as an argument to our function to invoke transactions together with the keypair of the account with which we are going to operate. This function will be responsible for simulating the transaction (to verify the validity of this same one) and if everything is correct, we will proceed to assemble, sign and send the transaction:

const horizonServer = stellarSDK.Horizon.Server("horizon-rpc url");
const invokeTransaction = async (tx: Transaction, source: Keypair) => {
  const simulatedTx = await server.simulateTransaction(tx);
  //If you only want to review the transaction, you can return the simulatedTx object to explore it in detail.
  // return simulatedTx;
  const txResources = simulatedTx.transactionData.build().resources();
  simulatedTx.minResourceFee = (Number(simulatedTx.minResourceFee) + 10000000).toString();
  const sim_tx_data = simulatedTx.transactionData
    .setResources(
      txResources.instructions() == 0 ? 0 : txResources.instructions() + 500000,
      txResources.readBytes(),
      txResources.writeBytes()
    )
    .build();
  const assemble_tx = SorobanRpc.assembleTransaction(tx, simulatedTx);
  sim_tx_data.resourceFee(
    xdr.Int64.fromString((Number(sim_tx_data.resourceFee().toString()) + 100000).toString())
  );
  const prepped_tx = assemble_tx.setSorobanData(sim_tx_data).build();
  prepped_tx.sign(source);
  const tx_hash = prepped_tx.hash().toString("hex");

  console.log("submitting tx...");
  let response: txResponse = await horizonServer.sendTransaction(prepped_tx);
  let status: txStatus = response.status;
  console.log(`Hash: ${tx_hash}`);
  // Poll this until the status is not "NOT_FOUND"
  while (status === "PENDING" || status === "NOT_FOUND") {
    // See if the transaction is complete
    await new Promise((resolve) => setTimeout(resolve, 2000));
    console.log("checking tx...");
    response = await horizonServer.getTransaction(tx_hash);
    status = response.status;
  }
  return response;
}

After calling this function, we can inspect the response object to verify that everything went as expected.

Methods:

[!Note] The operations available in the router contract that we will review in this documentation are:

Add liquidity to a pool:

To add liquidity to a Soroswap pool (or deposit funds), we will need to define the following parameters:

asset_a: Address;
asset_b: Address;
amount_a_desired: Number | BigNumber;
amount_b_desired: Number | BigNumber;
amount_a_min: Number | BigNumber;
amount_b_min: Number | BigNumber;
account: Address;
getCurrentTimePlusOneHour: Number;
  • [asset_a, asset_b]: These are the respective addresses of the asset pair to which we want to add liquidity.

  • [amount_a_desired, amount_b_desired]: These are the liquidity amounts you want to add to the respective assets.

  • [amount_a_min, amount_b_min]: These are the minimum amounts required to add to each asset, respectively.

  • account: This is the address where the tokens will be sent.

  • getCurrentTimePlusOneHour: This is the maximum date by which the transaction can be executed.


const addLiquidityParams: xdr.ScVal[] = [
  new Address(asset_a.contract).toScVal(),
  new Address(asset_a.contract).toScVal(),
  nativeToScVal(amount_a_desired, { type: "i128" }),
  nativeToScVal(amount_b_desired, { type: "i128" }),
  nativeToScVal(amount_a_min, { type: "i128" }),
  nativeToScVal(amount_b_min, { type: "i128" }),
  new Address(account.publicKey()).toScVal(),
  nativeToScVal(getCurrentTimePlusOneHour(), { type: "u64" }),
];

Remove liquidity from a pool:

To remove liquidity from a Soroswap pool (or withdraw funds), we will need to define the following parameters:

asset_a: Address;
asset_b: Address;
liquidity: Number | BigNumber;
amount_a_min: Number | BigNumber;
amount_b_min: Number | BigNumber;
account: Address;
getCurrentTimePlusOneHour: Number;
  • [asset_a, asset_b]: These are the respective addresses of the asset pair from which we want to remove liquidity.

  • Liquidity: This represents the desired amount of assets to remove from the liquidity pool.

  • [amount_a_min, amount_b_min]: These are the minimum amounts required to receive from each asset, respectively.

  • account: This is the address where the tokens will be sent.

  • getCurrentTimePlusOneHour: This is the maximum date by which the transaction can be executed.

const removeLiquidityParams: xdr.ScVal[] = [
  new Address(token0.contract).toScVal(),
  new Address(token1.contract).toScVal(),
  nativeToScVal(lpBalance, { type: "i128" }),
  nativeToScVal(0, { type: "i128" }),
  nativeToScVal(0, { type: "i128" }),
  new Address(testAccount.publicKey()).toScVal(),
  nativeToScVal(getCurrentTimePlusOneHour(), { type: "u64" }),
];

Swap:

To create a Swap operation on Soroswap, we will need to define the following parameters:

amount_in: Number | BigNumber;
amount_out_min: Number | BigNumber;
path: Address[];
account: KeyPair;
getCurrentTimePlusOneHour: Number;
  • amount_in: Represents the desired amount to be exchanged.

  • amount_out_min: Represents the minimum acceptable amount to receive for this operation.

  • path: Represents the exchange path to follow to obtain the requested asset.

  • account: Represents the account where the transaction will be executed.

  • getCurrentTimePlusOneHour: Represents the maximum date by which this transaction can be executed.

const swapParams: xdr.ScVal[] = [
    nativeToScVal(amount_in, { type: "i128" }),
    nativeToScVal(amount_out_min, { type: "i128" }),
    nativeToScVal(path, { type: "Vec" }),
    new Address(account.publicKey()).toScVal(),
    nativeToScVal(getCurrentTimePlusOneHour(), { type: "u64" }),
];

Finding the Most Optimal Path:

It is important to note that: the swap methods in the router will iterate through the path array step by step, performing the indicated exchanges between assets (0 <-> 1, 1 <-> 2, ... n <-> n+1) until the entire route is completed. This is why it is crucial to find the most optimal route to avoid wasting resources on unnecessary transactions.

npm i soroswap-router-sdk

or

yarn add soroswap-router-sdk

Then, we import it into our project and use it to calculate the optimal path.

import {
  Router,
  Token,
  CurrencyAmount,
  TradeType,
  Networks,
} from "soroswap-router-sdk";

const asset0_address = "address0_address";
const asset1_address = "address1_address";

const ASSEET0_TOKEN = new Token(
  Networks.TESTNET,
  asset0_address,
  7, //Number of decimals
  "asset0_symbol",
  "asset0_name"
);

const USDC_TOKEN = new Token(
  Networks.TESTNET,
  asset1_address,
  7, //Number of decimals
  "asset1_symbol",
  "asset1_name"
);

const amount = 10000000; //In stellar Stroops

const router = new Router({
  backendUrl: "https://my-backend.com/", //soroswap backend
  backendApiKey: "my-api-key", // soroswap backend api key
  pairsCacheInSeconds: 20, // pairs cache duration in seconds
  protocols: [Protocols.SOROSWAP], // protocols to be used
  network: Networks.TESTNET, // network to be used
});

const currencyAmount = CurrencyAmount.fromRawAmount(USDC_TOKEN, amount);
const quoteCurrency = ASSEET0_TOKEN;

const route = await router.route(
  currencyAmount,
  quoteCurrency,
  TradeType.EXACT_INPUT
);

console.log(route.trade.path);

//Output: ["0x...", "0x...", "0x..."]

Putting it All Together:

Once we have created our methods for interacting with the blockchain and defined the type of operation to be performed along with its parameters, we only need to call the functions to execute our transaction:

for this example we will perform a swap operation on testnet with a random account:

const executeSwap = async () => {
  const account = stellarSdk.Keypair.random();
  const routerAddress = axios.get("https://api.soroswap.finance/api/testnet/router");
  const method = "swap_exact_assets_for_assets";
  const amount_in = 2500000; //In stellar stroops
  const amount_out_min = 0; //In stellar stroops
  const path = route.trade.path;
  const swapParams: xdr.ScVal[] = [
      nativeToScVal(amount_in, { type: "i128" }),
      nativeToScVal(amount_out_min, { type: "i128" }),
      nativeToScVal(path, { type: "Vec" }),
      new Address(account.publicKey()).toScVal(),
      nativeToScVal(getCurrentTimePlusOneHour(), { type: "u64" })
  ];
  const tx = await createTx(account, routerAddress, method);
  const res = await invokeTransaction(tx, account);
  console.log(res);
}

executeSwap();

: "add_liquidity"

: "remove_liquidity"

: "swap_exact_assets_for_assets"

[!Note] method: "add_liquidity"

[!Note] All of these values must be converted to as shown below.

[!Note] method: "remove_liquidity"

[!Note] All of these values must be converted to as shown below.

[!Note] method: "swap_exact_assets_for_assets"

[!Note] All of these values must be converted to as shown below.

This is why we at Soroswap have developed , a tool that helps you find the most efficient route for exchanging assets, taking into account the available reserves in Soroswap's liquidity pools.

To utilize this tool, we'll install the 1.2.4 version of Soroswap Router SDK into our project. It's available through npm or yarn as .

This will give us the route object, which contains an ordered array of addresses representing the most optimal route for the exchange within the trade.path property. If you need more information on how to use the Router-sdk or how it works, you can do it directly in the repository of

ScVal
ScVal
ScVal
soroswap-router-sdk
"soroswap-router-sdk"
soroswap/soroswap-router-sdk
stellar-sdk
TransactionBuilder
soroswap/core
paltalabs/mercury-client.
soroswap/core
Documentation
"@stellar/stellar-sdk"
here
Forward
Add_liquidity
Remove_liquidity
Swap
Reference
Reference
Reference