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:
- If a wallet is connected, make sure it matches the selected chain.
- For the
$token
parameter, use thetoken_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"
}
]
}
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:
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:
- 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
- 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;