Skip to main content

Swap

To perform a swap, a swap transaction needs to be generated by calling the Swap endpoint in the Rest API and then broadcast to a chain.

The swap parameters are the following:

asset_in
amount_in
slippage
timeout
swaps
cross_chain_addresses
partner_fee

We will go through all the steps needed to get each of the parameters.

Steps

The following steps describe the workflow to perform a swap using the Euclid Layer:

1. Get all available tokens

The first step will be getting all the available tokens and selecting the input and output tokens. This ensures that the tokens involved in the swap are supported by the Euclid Layer. This can be done using the All Tokens query:

query Router {
router {
all_tokens {
tokens
}
}
}

This will return a response similar to the following:

{
"data": {
"router": {
"all_tokens": {
"tokens": [
"axs",
"bera",
"bnb",
"chog",
"coin50",
"const",
"eth",
"euclid",
"inj",
"mon",
"nibi",
"ntrn",
"orai",
"osmo",
"pol",
"ron",
"slp",
"sp500",
"stars",
"testcore",
"usdc",
"usdt",
"xau"
]
}
}
}
}

Once fetched, they can be displayed for the user who can select the desired tokens for the swap.

2. Get the Chains + Denoms of Token In

Next, we get the chains that have escrows for token_in and then select the one to use. This can be done using the Token Denoms query:

tip
  • If a wallet is connected, make sure it matches the selected chain.
  • For the $token parameter, use the token_in selected in the previous step.
  • Use the returned denom to construct the asset_in parameter for the trade message.
query Router($token: String!) {
router {
token_denoms(token: $token) {
denoms {
chain_uid
token_type {
... on NativeTokenType {
native {
denom
}
}
... on SmartTokenType {
smart {
contract_address
}
}
... on VoucherTokenType {
voucher
}
}
}
}
}
}

Here is a response for a "euclid" token in:

{
"data": {
"router": {
"token_denoms": {
"denoms": [
{
"chain_uid": "neuron",
"token_type": {
"native": {
"denom": "ueuclid"
}
}
},
{
"chain_uid": "injective",
"token_type": {
"smart": {
"contract_address": "inj1c9s44gr4jqzt9q44xq5as8smsspc8u6qu8ct8w"
}
}
},
{
"chain_uid": "nibiru",
"token_type": {
"smart": {
"contract_address": "nibi1nxw729ht6dt3tllhtsmfepdx3yz4v4ry6m55p0jl8tzjqe4jejsscadex3"
}
}
},
{
"chain_uid": "andromeda",
"token_type": {
"smart": {
"contract_address": "andr18mv7knjgwr0s662ztexvpvwau03nmfkcr3dy4yy75v6j3zzff5vs4mypv3"
}
}
},
{
"chain_uid": "archway",
"token_type": {
"smart": {
"contract_address": "archway1gsj5dj864xea7eglfn6uudxflhfkd27nlg7l88qje9f5m3kqm70qceskg7"
}
}
},
{
"chain_uid": "coreum",
"token_type": {
"smart": {
"contract_address": "testcore1cgpa0mmqls80h7a7v68qtvjx742eak4f0pnlzcp9rn2jsgnrk6cstqd6yt"
}
}
},
{
"chain_uid": "neutron",
"token_type": {
"smart": {
"contract_address": "neutron17w83ykjsk92l2uekg6gq6qwdvqgzsydzyd2uzdezwgzy4zk2ck8s0a03fm"
}
}
},
{
"chain_uid": "stargaze",
"token_type": {
"smart": {
"contract_address": "stars1r2cj4ap3e8felsfnyfm7p7r0j0t579wlkk0mn087s5y5ujcvuvyq6vcqhv"
}
}
},
{
"chain_uid": "osmosis",
"token_type": {
"smart": {
"contract_address": "osmo16x06cjm7wzw4d89ya9drpru2dc99szeg75yy3t493ujthems2eysz0qpfg"
}
}
},
{
"chain_uid": "oraichain",
"token_type": {
"smart": {
"contract_address": "orai1z6fjtpsa9fhnkuxvef5lkcaafuw40zc3hr0wml0tngqahtl0s58sduy0e3"
}
}
},
{
"chain_uid": "monad",
"token_type": {
"smart": {
"contract_address": "0xf6447bc85ff1669a46c849b09cad86b7ca669522"
}
}
},
{
"chain_uid": "0g",
"token_type": {
"smart": {
"contract_address": "0x72975d80179c6a6e1428108f426c8f14eee46226"
}
}
},
{
"chain_uid": "base",
"token_type": {
"smart": {
"contract_address": "0x31c25d98e97d7275be6962a200dae6f3838dc709"
}
}
},
{
"chain_uid": "sepolia",
"token_type": {
"smart": {
"contract_address": "0xaa6a3a711b54b390824d4e12bd73c0cd84a4ea88"
}
}
},
{
"chain_uid": "manta",
"token_type": {
"smart": {
"contract_address": "0xd0786f3a7e5b0dc05fe8b648d757d684eb84b0ed"
}
}
},
{
"chain_uid": "unichain",
"token_type": {
"smart": {
"contract_address": "0xd0786f3a7e5b0dc05fe8b648d757d684eb84b0ed"
}
}
},

...
]
}
}
}

You can then prompt the user to select the chain of their choice or automatically use the connected chain, if the wallet is already connected. This ensures that the escrow is selected on a chain where the user is ready to sign and broadcast transactions.

3. Specify token in amount

Next, we need to select the amount of token_in to swap. This would be specified by the user.

4. Get swap routes

In many cases, multiple routes can be taken to perform the desired swap. In this step, we will fetch these routes and select the one we want to use. This can be done using the Get Routes query:

curl -X 'POST' \
'https://testnet.api.euclidprotocol.com/api/v1/routes' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"amount_in": "1000000",
"token_in": "euclid",
"token_out": "eth"
}'

Here is a result for euclid in and eth out:

{
"paths": [
{
"path": [
{
"route": [
"euclid",
"eth"
],
"dex": "euclid",
"amount_in": "1000000",
"amount_out": "143104841102727696",
"chain_uid": "vsl",
"amount_out_for_hops": [
"eth: 143104841102727696"
]
}
],
"total_price_impact": "NaN"
}
]
}
note

In addition to specifying the swap route, you can optionally include detailed execution parameters for each hop:

  • dex: The decentralized exchange to use for this segment of the swap (e.g., "euclid", "osmosis", "astroport").
  • amount_in: The input amount for this hop.
  • amount_out: The expected output amount after this hop.
  • chain_uid: The chain where this hop should be executed.

Providing these fields can offer more precise control over the routing behavior when constructing the swap message.

Example:

"swap_path": {
"path": [
{
"route": ["nibi", "euclid"],
"dex": "euclid",
"amount_in": "1000000",
"amount_out": "950000",
"chain_uid": "nibiru"
}
]
}

5. Simulate the swap

Now that we have all the parameters required, we can simulate the swap. This can be done using the Simulate Swap query:

tip

For simulate swap, specify the min_amount_out as 1. We are only interested in getting the expected amount out and do not care about slippage here.

query Simulate_swap($assetIn: String!, $amountIn: String!, $assetOut: String!, $minAmountOut: String!, $swaps: [String!]) {
router {
simulate_swap(asset_in: $assetIn, amount_in: $amountIn, asset_out: $assetOut, min_amount_out: $minAmountOut, swaps: $swaps) {
amount_out
asset_out
}
}
}

The following parameters are used in the above example:

  "assetIn": "euclid",
"amountIn": "1000000",
"assetOut": "eth",
"minAmountOut": "1",
"swaps": ["euclid","eth"]

The response will return the expected amount_out for the swap:

{
"data": {
"router": {
"simulate_swap": {
"amount_out": "142493829878315471",
"asset_out": "eth"
}
}
}
}

6. Set slippage Value

The slippage field specifies the maximum allowed slippage for the swap, expressed in basis points (bps):

  • 100 = 1%
  • 500 = 5%
  • 1000 = 10%

This value is used to protect the swap from executing at a worse rate than expected.

Example

To allow up to 5% slippage, set:

"slippage": "500"

7. Select Output Chain

Token out might exist on multiple chains. We query the available escrows for the selected token and then prompt the user to choose which chain they want to receive the tokens on. This can be done using the Escrows query:

note
  • The returned balance for each chain specifies the amount of tokens available in the escrow. Make sure the selected chain has enough balance to cover the token_out amount.
  • In case the balance is less than the expected amount out, remaining tokens will be minted as vouchers.
query Escrows($token: String!) {
router {
escrows(token: $token) {
chain_uid
balance
chain_id
}
}
}

Here is an example for eth token:

{
"data": {
"router": {
"escrows": [
{
"chain_uid": "base",
"balance": "0",
"chain_id": "84532"
},
{
"chain_uid": "linea",
"balance": "0",
"chain_id": "59141"
},
{
"chain_uid": "manta",
"balance": "0",
"chain_id": "3441006"
},
{
"chain_uid": "megaeth",
"balance": "20314545986679486197214",
"chain_id": "6342"
},
{
"chain_uid": "scroll",
"balance": "0",
"chain_id": "534351"
},
{
"chain_uid": "sepolia",
"balance": "0",
"chain_id": "11155111"
},
{
"chain_uid": "soneium",
"balance": "0",
"chain_id": "1946"
},
{
"chain_uid": "unichain",
"balance": "0",
"chain_id": "1301"
}
]
}
}
}

8. Generate swap transaction

note
  • Use the responses we got in all the previous steps for the swap fields.
  • For sender address and chain_uid use the ones from the connected chain.
  • You can include a specific timeout. Excluding it will take the default of 60 seconds.
  • You can include a partner_fee if you wish to include a fee for your application.
  • The cross_chain_addresses are taken as an input from the user. The addresses for different chains can be fetched from the wallet using the chain Id.

We now have everything needed to generate the swap transaction message:

const msg = await axios.post("https://testnet.api.euclidprotocol.com/api/v1/execute/swap", {
  amount_in: data.amountIn, // amount of asset in being swapped 
  asset_in: data.assetIn, // the type of asset in
  asset_out: data.assetOut, // the type of asset out
  cross_chain_addresses: data.crossChainAddresses, // the chains and addresses to release asset out
  slippage: data.slippage, // Used to specify max slippage tolerated.
  sender: {
    address: wallet!.bech32Address,
    chain_uid: chain!.chain_uid,
  },
  swaps: data.swaps,
}).then((res) => res.data as TxResult);
const msg = await axios.post("https://testnet.api.euclidprotocol.com/api/v1/execute/swap", {
  amount_in: data.amountIn,
  asset_in: data.assetIn,
  asset_out: data.assetOut,
  cross_chain_addresses: data.crossChainAddresses,
  slippage: data.slippage,
  sender: {
    address: walletAddress, // EVM address
    chain_uid: chainUid
  },
  swaps: data.swaps
}).then((res) => res.data);

9. Broadcast the transaction to chain

The final step will be broadcasting this transaction to the chain and signing it with the connected wallet:

const tx = await client!.executeMultiple(
  wallet!.bech32Address,
  msg.msgs,
  "auto",
  "Swap"
);
return tx;
const provider = new ethers.providers.Web3Provider(window.ethereum);
const signer = provider.getSigner();

// Construct the transaction data
const tx = {
  to: msg.msgs[0].to, // Contract address
  data: msg.msgs[0].data, // Encoded calldata
  value: msg.msgs[0].value || "0x0" 
};

// Send transaction using signer (e.g. MetaMask)
const receipt = await signer.sendTransaction(tx);

// Optional: Wait for confirmation
const confirmed = await receipt.wait();
return confirmed;