For a more in-depth guide, check out our detailed walkthrough, which covers both Solana and EVM transactions. You can also explore more about EVM Transactions or dive into the specifics of SVM Transactions.

Prerequisites

  • Browser environment setup with Keplr installed (create-next-app is recommended)
  • Node.js and npm
1

Install Library

Install the library using npm or yarn:

2

Initialize Client

To start integrating with the Skip Go API, initialize a SkipClient instance. This skipClient provides helper methods for common actions such as querying assets and chains, constructing routes, and executing transactions.

import { SkipClient } from "@skip-go/client";
const skipClient = new SkipClient({
  getCosmosSigner: async (chainID) => {
    const offlineSigner = window.keplr?.getOfflineSigner(chainID);
    if (!offlineSigner) throw new Error("Keplr not installed");
    return offlineSigner;
  }
});
3

Get a Route

Now, we can use the SkipClient.route function to request a quote and route to swap USDC on Noble to TIA on Celestia.

const route = await skipClient.route({
  sourceAssetDenom: 'uusdc',
  sourceAssetChainID: 'noble-1',
  destAssetDenom: 'utia',
  destAssetChainID: 'celestia',
  amountIn: '1000000', // 1 uusdc
  smartRelay: true
});

Understanding the Route Response

The route response contains important information about the swap process:

  • amountOut: The estimated amount the user will receive after the swap, net of all fees and price impact
  • requiredChainAddresses: Chain IDs where you need to provide user addresses when generating the transaction
  • operations: Steps involved in moving from the source to the destination token

For more details, see the /route endpoint reference.

4

Get Required Addresses

After generating a route, you need to provide user addresses for the required chains. The route.requiredChainAddresses array lists the chain IDs for which addresses are needed.

Only use addresses your user can sign for. Funds could get stuck in any address you provide, including intermediate chains in certain failure conditions. Ensure your user can sign for each address you provide. See Cross-chain Failure Cases for more details.

We recommend storing the user’s addresses and creating a function like getAddress that retrieves the address based on the chain ID (see an example here).

// get user addresses for each requiredChainAddress to execute the route
  const userAddresses = await Promise.all(
  route.requiredChainAddresses.map(async (chainID) => ({
    chainID,
    address: await getAddress(chainID),
  }))
);

Never attempt to derive an address on one chain from an address on another chain

Whenever you need a user address, please request it from the corresponding wallet or 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 thus risk lost tokens.

5

Execute the Route

Once you have a route, you can execute it in a single function call by passing in the route, the user addresses for at least the chains the route includes, and optional callback functions. This also registers the transaction for tracking.

await skipClient.executeRoute({
  route,
  userAddresses,
  // Executes after all of the operations triggered by a user's signature complete.
  onTransactionCompleted: async (chainID, txHash, status) => {
    console.log(
      `Route completed with tx hash: ${txHash} & status: ${status.state}`
    );
  },
  onTransactionBroadcast: async ({ txHash, chainID }) => {
    console.log(`Transaction broadcasted with tx hash: ${txHash}`);
  },
  onTransactionTracked: async ({ txHash, chainID }) => {
    console.log(`Transaction tracked with tx hash: ${txHash}`);
  },
});

Once the transaction is complete, you’ll have new TIA in your Celestia address!