简介

  • 当用户需要从 EVM 链(例如 Ethereum mainnet、Arbitrum、Optimism 等)进行转账或兑换时,Skip Go API 会返回一个 EvmTx 类型,供开发者交给用户签名。
  • 与 CosmosSDK 交易不同,EVM 交易没有消息(message)的概念,因此这个对象与“消息”并不是一一对应的;对 Cosmos 开发者来说,“消息”可能是更熟悉的概念。
  • 本文面向尚不熟悉 EVM 中交易构造概念、但需要使用 EvmTx 帮助用户在 EVM 链之间转入或转出的 CosmosSDK 开发者。

EvmTx 数据结构

EvmTx 有 4 个开发者需要理解的字段:
  • to:该交易交互的智能合约或外部拥有账户(EOA)地址,格式为带 0x 前缀的十六进制字符串(例如 0xfc05aD74C6FE2e7046E091D6Ad4F660D2A159762)
  • value:该交易发送给其所交互合约的 wei 数量(1 ETH = 1^18 WEI)
  • data:该交易用于调用所交互智能合约的 calldata,格式为十六进制字符串。数据字节会按照被调用合约的应用二进制接口(ABI)进行解析。如果该字段为空,表示这笔交易是在向某个地址转账,而不是调用合约。
  • required_erc20_approvals:必须授予特定智能合约的权限,使其能够代表最终用户花费或转移一定数量的 ERC-20 代币。这使智能合约能够执行更复杂的流程,其中可能涉及转移用户的一部分 ERC-20 代币。
    • 如果路由需要任何 ERC-20 授权,Skip Go 总会返回该字段。客户端有责任检查用户当前授权额度是否已经达到或超过返回所需的授权额度(例如,集成方允许最大授权的情况)。如果该字段非空且用户没有所需授权,则必须先完成授权、签名并提交,然后才能将响应中由其他字段填充的 EvmTx 提交到网络。否则,执行时会因权限错误而失败。
    • Skip 的 ERC20Approval 对象有 3 个定义授权的字段:
      • token_contract:被授予授权的 ERC-20 代币合约地址
      • spender:将被授予支出权限的合约地址
      • amount:授权 spender 可支出的 token_contract 代币数量
    • 关于 ERC-20 授权的更多信息,请参阅 EIP-2612。
  • chain_id:这一点与 Cosmos 场景中的含义相同(只是链的标识符),但它是 int 而不是字符串。
关于交易的更多信息,请参阅以太坊基金会的文档。

构造并签名 EVM 交易示例

1. 安装签名库和 Skip 库

要在你的应用中启用 EVM 交易,首先安装一个 EVM 开发库。最常见的选项包括: 下面的代码示例使用 viem。
Shell
npm i viem
npm i @skip-go/client

1. 使用 EVM WalletClient 对象初始化 SkipClient 客户端

上面提到的 3 个库都允许你创建 WalletClient “signer” 对象,这些对象会:
  • 在底层使用 RPC provider 查询链上构造交易所需的数据(例如 nonce、gas price 等)
  • 暴露可用于构造、签名和广播交易的 API
你需要在 SkipClient 构造函数中设置 getEVMSigner 函数,用于为指定的 EVM 链初始化这个 signer 对象。 例如,使用 Viem 时,我们这样做:
TypeScript
import { createWalletClient, custom} from 'viem';
import * as chains from 'viem/chains';
import { SkipClient } from '@skip-go/client';

const
const skipClient = new SkipClient({
  getEVMSigner: async (chainID) => {
    const chain = extractChain({
  		chains: Object.values(chains),
  		id: parseInt(chainID)
    });
    const evmWalletClient = createWalletClient({
  		chain: chain,
  		transport: custom(window.ethereum!)
  	});
    return evmWalletClient;
  }
});

2. 使用 SkipClient 请求路由并获取所需链

接下来,像平常一样请求你的路由:
TypeScript
const route = await skipClient.route({
  amountIn: "1000",
  sourceAssetDenom: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
  sourceAssetChainID: "1",
  destAssetDenom: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
  destAssetChainID: "42161",
  smartRelay: true,
  smartSwapOptions: {
    splitRoutes: true
  }
};

3. 获取所有所需链的用户地址

使用路由来确定你需要为哪些链提供用户地址(源链、目标链,以及所有在失败时需要恢复地址来接收代币的中间链)。
TypeScript
let userAddresses = []
const requiredAddresses = route.requiredChainAddresses;
// iterate over chain IDs for chains that require addresses
for (const chainID of requiredAddresses) {
  	// Check that the chain is an EVM chain
    if (parseInt(chainID)) {
      // use signer library to get address from wallet
      const chain = extractChain({
        chains: Object.values(chains),
        id: parseInt(chainID)
      });
      const evmWalletClient = createWalletClient({
        chain: chain,
        transport: custom(window.ethereum!)
      });
      const [address] = await client.requestAddresses();
      // add to map
      userAddresses.append({address: address, chainID: chainID})
    } else {
      // handle cosmos and SVM wallets -- not shown
    }

});
return evmWalletClient;
}

4. 使用 SkipClient 执行路由

最后,你可以使用 SkipClient.executeRoute 提示用户签署授权和交易,并将交易提交到链上。
TypeScript
await skipClient.executeRoute({
  route:route,
  userAddresses: userAddresses
});
有问题或反馈?帮助我们做得更好!加入我们的 Discord,并选择 “Skip Go Developer” 角色,分享你的问题和反馈。

Intro

  • When a user needs to transfer or swap from an EVM chain (e.g. Ethereum mainnet, Arbitrum, Optimism, etc…), the Skip Go API will return an EvmTx type for the developer to pass to the user for signing
  • Unlike CosmosSDK transactions, EVM transactions do not have a notion of messages, so this object doesn’t correspond 1-to-1 to a “message”, which might be a more familiar notion to Cosmos developers
  • This doc is intended for CosmosSDK developers who aren’t already familiar with the concepts of transaction construction in the EVM and need to use EvmTx to help their users move from/to EVM chains.

EvmTx Data Structure

The EvmTx has 4 fields that the developer needs to understand:
  • to: The address of the smart contract or externally owned account (EOA) with which this transaction interacts, as a hex-string prefixed with 0x (e.g. 0xfc05aD74C6FE2e7046E091D6Ad4F660D2A159762)
  • value: The amount of wei this transaction sends to the contract its interacting with (1 ETH = 1^18 WEI)
  • data: The calldata this transaction uses to call the smart contract it interacts with, as a hex string. The data bytes will be interpreted according to the application-binary-interface (ABI) of the contract that’s being interacted with. If this field is empty, it means the transaction is sending funds to an address, rather than calling a contract.
  • required_erc20_approvals: The permissions that must be granted to a specific smart contract to spend or transfer a certain amount of their ERC-20 tokens on behalf of the end user. This allows smart contracts to execute expressive flows that may involve moving some amount of the user’s ERC-20 tokens
    • Skip Go will always return this field if there are any erc20 approvals needed for the route. It is the client’s responsibility to check if the user’s approval is already at or above the returned approval needed (for example, if the integrator allows for max approvals). If this field is non-empty and the user does not have the approvals necessary, the approval must be granted, signed, and submitted before the EvmTx populated by the other fields in the response can be submitted to the network. Otherwise, it will fail to execute with a permission error.
    • Skip’s ERC20Approval object has 3 fields that define approval: _ token_contract: The address of the ERC-20 token on which the approval is granted _ spender: The address of the contract to which the approval will grant spend authority * amount: The amount of token_contract tokens the approval will grant the spender to spend
    • Check out EIP-2612 for more information on ERC-20 approvals.
  • chain_id: This is the same as in the Cosmos context (simply an identifier for the chain), but it’s an int instead of a string
For more information on transactions, check out the Ethereum foundation’s docs

Example constructing & signing an EVM Transaction

1. Install Signing Library and Skip Library

To enable EVM transactions in your application, first install an EVM developer library. The most popular options are: The code snippets below use viem.
Shell
npm i viem
npm i @skip-go/client

1. Initialize the SkipClient client with the EVM WalletClient object

All 3 libraries mentioned above allow you to create WalletClient “signer” objects that:
  • Use an RPC provider under the hood to query the chain for necessary data to create transactions (e.g. nonce, gas price, etc…)
  • Expose an API that allows constructing, signing, and broadcasting transactions
You need to set up the getEVMSigner function in the SkipClient constructor to initialize this signer object for the a given EVM chain. For example, with Viem, we do the following:
TypeScript
import { createWalletClient, custom} from 'viem';
import * as chains from 'viem/chains';
import { SkipClient } from '@skip-go/client';

const
const skipClient = new SkipClient({
  getEVMSigner: async (chainID) => {
    const chain = extractChain({
  		chains: Object.values(chains),
  		id: parseInt(chainID)
    });
    const evmWalletClient = createWalletClient({
  		chain: chain,
  		transport: custom(window.ethereum!)
  	});
    return evmWalletClient;
  }
});

2. Request Route using SkipClient and get required chain

Next, request your route as normal:
TypeScript
const route = await skipClient.route({
  amountIn: "1000",
  sourceAssetDenom: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
  sourceAssetChainID: "1",
  destAssetDenom: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
  destAssetChainID: "42161",
  smartRelay: true,
  smartSwapOptions: {
    splitRoutes: true
  }
};

3. Get User Addresses for all Required Chains

Use the route to determine the chains for which you need to supply a user address (the source, destination, and all intermediate chains that require a recovery address for receiving tokens in case of a failure)
TypeScript
let userAddresses = []
const requiredAddresses = route.requiredChainAddresses;
// iterate over chain IDs for chains that require addresses
for (const chainID of requiredAddresses) {
  	// Check that the chain is an EVM chain
    if (parseInt(chainID)) {
      // use signer library to get address from wallet
      const chain = extractChain({
        chains: Object.values(chains),
        id: parseInt(chainID)
      });
      const evmWalletClient = createWalletClient({
        chain: chain,
        transport: custom(window.ethereum!)
      });
      const [address] = await client.requestAddresses();
      // add to map
      userAddresses.append({address: address, chainID: chainID})
    } else {
      // handle cosmos and SVM wallets -- not shown
    }

});
return evmWalletClient;
}

4. Execute the Route using SkipClient

Finally, you can use SkipClient.executeRoute to prompt the user to sign the approval(s) and transaction, and submit the transaction on chain.
TypeScript
await skipClient.executeRoute({
  route:route,
  userAddresses: userAddresses
});
Have questions or feedback? Help us get better!Join our Discord and select the “Skip Go Developer” role to share your questions and feedback.