title |
---|
Quickstart Guide |
This will walkthrough shows you how to get started with the API by performing a swap of USDC on Noble to TIA on Celestia.
**Get in touch if you're having trouble**Please contact us if you believe the API doesn't conveniently support your desired user experience, or if you're confused about how to use it.
You can reach us easily by joining our discord and grabbing the "Skip API developer" role.
For detailed information on all the Skip API endpoints and functionality, please see our reference docs.
Running this example requires a minimum of NodeJS 16.
In your command line terminal, install all the required libraries:
npm i @skip-router/core
npm i @cosmjs/crypto
npm i @cosmjs/proto-signing
This example constructs a cross-chain swap between 2 Cosmos chains, but Skip API supports all major EVM chains & Solana. (See Supported Ecosystems for more info about which chains, routes, and tokens we support)
If you'd like to perform a cross-chain swap that starts or ends on Solana or an EVM chain, you'll need to import additional dependencies and configure additional tx signers. After going through this tutorial, check out EVM Transactions Intro and SVM Transactions Intro if you need.
Next, you can copy the following code snippets to a javascript file (e.g. skip-api-example.js
).
We'll build on this file throughout the example:
import * as chainRegistry from "chain-registry";
import { DirectSecp256k1HdWallet } from "@cosmjs/proto-signing";
import { stringToPath } from "cosmjs/crypto";
import { SkipRouter } from "@skip-router/core";
// async main function
(async () => {
// empty for now
}
)()
You'll also need to initialize your package.json:
{
"type": "module"
}
Now you should be able to run your script, but it won't do anything yet:
node skip-api-example.js
Since there are many chains in Cosmos (more than 50!), we will write a getCosmosSigner
function that can generate your Cosmos signer for any chain, given a chain ID.
We'll pass this function into the SkipRouter
when we initialize it, so we can sign any cross-chain Cosmos route it generates.
We'll also create another helper function called getCosmosAddress
that uses getCosmosSigner
to retrieve user addresses for individual chains later.
Copy the following code into skip-api-example.js
:
// Generates a Cosmos signer for a given chainID,
// loading the mnemonic from the environment
async function getCosmosSigner(chainID) {
// load mnemonic from environment
const mnemonic = process.env.MNEMONIC;
if (!mnemonic) {
throw new Error("Mnemonic not set")
}
// find chain in chain registry to get bech32 prefix and cointype
const chain = chainRegistry.chains.find(chain => chain.chain_id==chainID)
if (!chain.bech32_prefix) {
throw new Error(`Chain Registry missing prefix for {chainID}`)
}
if (!chain.slip44) {
throw new Error(`Chain Registry missing cointype for {chainID}`)
}
// create wallet
const hdPath = stringToPath(`m/44'/${chain.slip44}'/0'/0/0`)
const wallet = await DirectSecp256k1HdWallet.fromMnemonic(mnemonic, {
prefix: chain.bech32_prefix,
hdPath: hdPath
})
return wallet
}
async function getCosmosAddress(chainID) {
const signer = await getCosmosSigner(chainID)
return signer.getAccounts()[0].address
}
(async () => {
const skipClient = new SkipRouter({
getCosmosSigner,
})
})()
Make sure you set up your mnemonic in your local environment:
export MNEMONIC="12 word mnemonic here"
(If you don't already have one, you can generate one by installing a popular Cosmos wallet, like Leap or Keplr.)
Now, we can use the SkipRouter.route
function to request a quote & route to swap USDC on Noble to TIA on Celestia:
Just add the following line after the line where you initialized the skipClient:
const route = await skipClient.route({
sourceAssetDenom: "uusdc",
sourceAssetChainID: "noble-1",
destAssetDenom: "utia",
destAssetChainID: "celestia",
amountIn: "1000000", // 1 uusdc
smartSwapOptions: {
splitRoutes: true,
}
})
The route response contains a ton of information about how the user can move from their source chain & token to their destination. For more information, see /v2/fungible/route reference docs.
The most basic outputs you will need are:
amountOut
: The estimated amount your user will receive after their swap / transfer takes place -- net of fees (e.g. bridge fees, swap fees, etc..) and price impact.requiredChainAddresses
: These are the chainIDs for which you will need to pass a valid user address when generating the transaction.operations
: The set of steps that take place to get the user from their source chain & token to their destination chain & token
You can your set your own swap fee into the quote by setting cumulativeAffiliateFeeBPS
to the total fee amount you want to collect from the user, measured in hundredths of a percent (aka "bips").
You can find more info about setting fees in Setting Affiliate Fees
Now that we have the route, we can use our signer to get the list of required user addresses for executing the route.
We'll use these addresses in the next step to populate userAddresses
when executing the route with SkipRouter.executeRoute
method.
The requiredChainAddresses
field in the output of route
gives the chainIDs we need to use.
Funds could end up in any of the addresses you provide -- including intermediate chains in certain failure conditions (e.g. a relayer timeout after a swap). So you must be sure your user can sign for each address you provide.
See Cross-chain Failure Cases for more details on when funds might end up in an intermediate address.
(Rest assured that SkipRouter.transactionStatus
will tell you where your users funds end up even if they don't make it to their final destination.)
Add the following snippet to your script after getting the route to retrieve the address associated with each requiredChainAddresses
entry:
// get user addresses for each requiredChainAddress
const userAddresses = await Promise.all(route.requiredChainAddresses.map(async (chainID) => {
return {
chainID: chainID,
address: await getCosmosAddress(chainID)
}
}))
Two things you should notice about this code:
- The
userAddresses
array contains objects with{chainID, address}
- The
userAddresses
array needs to have the same order and length as therequiredChainAddresses
array had
Whenever you need a user address, please request it from the corresponding wallet / signer. Do not attempt to use bech32 cross-chain derivation.
If you attempt to derive an address on one chain from an address on another chain, you may derive an address that the user cannot actually sign for if the two chains have different address-derivation processes. For example, if you derive a Cosmos address from an Ethereum address, you will get an address that the user cannot sign for and cannot use.
Finally, just use the SkipRouter.executeRoute
method to create, sign, submit, and track the transaction for you.
Simply add the following snippet below your retrieval code to execute the route:
await skipClient.executeRoute({
route,
userAddresses,
onTransactionCompleted: (chainID, txHash, status) => {
console.log(`Route completed with tx hash: ${txHash} & status: ${status.state}`)
}
})
After the transaction completes, you'll have additional TIA in your Celestia address!
**`executeRoute` callbacks**executeRoute
takes care of all the complexity between getting a quote & executing a transaction: creating the tx, signing the tx, submitting it, and tracking it across all the chains.
You can supply a variety of callbacks to hook into the process and update your UI or retrieve information while it's executing.
These callbacks include:
onTransactionBroadcast(txInfo {txHash, chainID})
: Executes after the transaction that the user signs gets broadcast on chain.onTransactionTracked(txInfo, {txHash, chainID})
: Executes after the transaction that the user signs is successfully registered for tracking.onTransactionComplete(chainID, txHash, status)
: Executes after all of the operations triggered by a user's signature complete. For example, in this case, it executed after the transfer to Celestia completed. (For multi-tx routes that require multiple user signatures, this will be called once for each tx)
You can reach us easily by joining our Discord and grabbing the "Skip API Developer" role.