Links
🛠

Symbiosis Mobile SDK

Symbiosis Mobile SDK Quick Start

What is Mobile Symbiosis SDK?

Symbiosis Mobile SDK is a Kotlin MultiPlatform library designed to create applications that work with the Symbiosis protocol and Web3.
In the first place, the SDK and the documentation presented in this section are for mobile application developers. However, thanks to Kotlin versatility, it also can be used for desktop and back-end applications.
Checklist to do before deploying to production (Mainnet)
If any of these checks fail, you put the assets of your users at risk. Thus, you must not go to Mainnet with real assets and real users. This can result in the loss of your users' assets.
  1. 1.
    You approve users’ ERC20 tokens only for one contract and this contract is metaRouterGateway on each blockchain. You can check contracts’ addresses for all blockchains in this configuration.
  2. 2.
    All contracts you use in your software exist on the corresponding blockchains and their addresses on each blockchain correspond to the addresses in this configuration.
  3. 3.
    You do not modify, reuse, or cash the calldata you get by calling methods of Symbiosis SDKs and API.
  4. 4.
    After deployment to Mainnet, run at least one transaction.
Please do these checks for the initial deployment AND every software update.
Symbiosis Mobile SDK provides support for iOS and Android OS.
Looking for API? It's here Symbiosis API

Quick Start

The Mobile SDK addresses the following cases:
  1. 1.
    Searching and calculating on-chain & cross-chain swaps.
  2. 2.
    Generating calldata and sending transactions.
  3. 3.
    Waiting for transaction execution. In the case of cross-chain swaps, we are waiting for a special event on the second (destination) network and transactions' receipts.
Let's take a look at these cases one by one.
The tutorial below applies to both types of swaps: on-chain and cross-chain. In the case of on-chain swaps, the UniSwap and 1Inch are used.

Before we start

First of all, we should create a Symbiosis client to maintain a persistent connection with a blockchain over Web3-Rpc. There are predefined clients for this:
  • SymbiosisSdk: This client supports all networks supported by the Symbiosis protocol (Mainnet and Testnet).
  • SymbiosisSdkMainnet: This client supports Mainnet networks only, hence the name.
  • SymbiosisSdkTestnet: This client supports Testnet networks, hence the name.
Let's use a test client on Testnet to see how the Mobile SDK works:
val testSdk = SymbiosisSdkTestnet(...)
In the class constructor, all endpoints must be specified to connect to networks (if a network has a public endpoint, it is most likely already a parameter with a default value).
It is not necessary to connect all networks at once.

Searching for an exchange

Limitations

The Symbiosis protocol provides a very convenient way to swap tokens. On the other hand, the protocol applies some restrictions, specifically:
  1. 1.
    Cross-chain swaps can be done only on the networks supported by the Symbiosis protocol. The list of supported blockchains is growing and can be found here: Supported Blockchains
  2. 2.
    There are restrictions on the minimum and maximum amount of swaps that should be taken into consideration.
Let's set the configuration a swap. In the example below, we exchange 0.01 BNB for MATIC tokens:
val inputToken = testSdk.bscTestnet.token.BNB
val outputToken = testSdk.polygonMumbai.token.MATIC
val amountIn = inputToken.amount(0.01.bn)
Here we use the kbignum library to avoid problems with fractional numbers (where the .bn appears).
BNB and MATIC are presets in this case, but you can also create your own instance of the DecimalsErc20Token class and use it. There is no restriction on tokens in the Symbiosis protocol.

Check if Symbiosis supports the networks

There is a method for checking the compatibility of a pair of networks and the Symbiosis protocol. To check if it is possible to do a cross-chain swap from the inputToken network to the outputToken network, please use the following code:
testSdk.swap.isChainsSupported(inputToken.network, outputToken.network)
If the inputToken network and the outputToken network are the same, the function always returns true
This method is also helpful if you want to filter networks to show supported combinations only.
Check if amount fits the allowed range
There is a method that allows you to calculate limits for a swap. It takes inputToken and the destination network as input:
when (
val range = testSdk.swap
.getAllowedRangeForInputToken(inputToken, outputToken.network)
) {
is UnifiedSwapRepository.AllowedRangeResult.Limited ->
println("You can trade from ${range.min.amount} to ${range.max.amount}")
is UnifiedSwapRepository.AllowedRangeResult.NoLimit ->
println("No limits for current trade set")
}
Some swaps (1inch or UniSwap) do not provide limits, so the method can return two results: Limited, NotLimit.
At the time of writing, we can do the swap from the example. However, you may need to adjust amountIn to a higher value, depending on the available liquidity.

And finally, swap

To calculate a swap, simply call the following method:
val tradeResult = testSdk.swap.findBestTrade(
amountIn = amountIn.raw,
tokens = TokenPair(inputToken, outputToken),
from = WalletAddress(/* sender wallet address */)
)
The swap calculation is an asynchronous operation. The Mobile SDK connects to the blockchains via the Internet a couple of times during the calculation. So, the estimated calculation time is approximately 8-15 seconds, depending on the speed of the Internet and the response time of the blockchains. Once the swap has been calculated, the result must be processed.

Processing a calculated swap

The state of the blockchains may change during a calculation. In this case, the Mobile SDK will not throw an error and will require processing the results explicitly and checking possible new circumstances.
Even if you have checked before searching for a swap that the amount of tokens is in the range, the liquidity may change, so there is no way to swap the tokens. In this case, two errors are possible:
  • StableTokensGreaterThanMax
  • StableTokensLessThanMin
The errors contain information about the actual amount of stable coins you can get with the current amountIn and the limit.
In addition, it may happen that the amount of stable tokens is less than the bridgingFee (the fee that should be paid on the destination network for the transaction processing).
The StableTokensLessThanBridgingFee error returns the actual bridgingFee.
And the last error is TradeNotFound. It occurs when the liquidity in the UniSwap or 1Inch liquidity pools is not enough for the swap.
The following code handles all possible errors and returns a successful swap (if any):
val trade = when (tradeResult) {
is UnifiedSwapRepository.SwapResult.StableTokensGreaterThanMax ->
return println("Can't swap ${amountIn.amount} ($${tradeResult.actualInDollars}), max allowed is $${tradeResult.maxInDollars}")
is UnifiedSwapRepository.SwapResult.StableTokensLessThanBridgingFee ->
return println("Can't swap ${amountIn.amount} ($${tradeResult.actualInDollars}), because bridging fee is higher ($${tradeResult.bridgingFee.amount})")
is UnifiedSwapRepository.SwapResult.StableTokensLessThanMin ->
return println("Can't swap ${amountIn.amount} ($${tradeResult.actualInDollars}), min allowed is $${tradeResult.minInDollars} ")
is UnifiedSwapRepository.SwapResult.TradeNotFound ->
return println("Trade was not found for selected tokens ($inputToken -> $outputToken)")
is UnifiedSwapRepository.SwapResult.Success ->
tradeResult.trade
}

Sending the transaction

To send a transaction, call the execute() method and provide the transaction signer there:
val transaction = when (val result = trade.execute(/* Credentials instance here */)) {
// we don't want to throw errors, so the explicit check for this is required
is UnifiedSwapTrade.ExecuteResult.ExecutionRevertedWithoutSending ->
return@runBlocking println("Transaction reverted while estimating gas")
is UnifiedSwapTrade.ExecuteResult.Success ->
result.transaction
}

Waiting for transaction

Waiting for a transaction completion is done with a single function:
when (val result = transaction.waitForCompletion()) {
is UnifiedSwapTransaction.CompletionResult.Success ->
println("Trade successfully executed! Tx hash on input network: ${result.receipt.txHash}")
is UnifiedSwapTransaction.CompletionResult.TransactionFailed ->
println("Unknown exception occurred during execution. Tx hash on input network: ${result.receipt.txHash}")
}

Play around

You can see how it works using the code from here. There is a result:
Results of snippet execution on Testnet.

Dependencies

Symbiosis Mobile SDK uses the following third-party libraries:

GitHub Repository