跳转到主要内容

概览

了解如何在您的 dapp 中使用 Simulation API,让用户在将交易发送到链上之前预览交易结果。这样,您可以帮助他们避免因失败或错误的交易而造成的经济损失,并增强他们使用您的 dapp 时的信心。 要将 Tenderly Simulations API 集成到您的 dapp 中,以便在交易发送到链上之前检测其是否会失败,您需要:
  1. 从 ethers.js 库填充原始交易。
  2. 在发送交易之前先对其进行模拟。
  3. (可选)将每一次区块链交互包装到 Tenderly 模拟中。
有关单次调用的请求和响应详情,请参阅单笔模拟

从 ethers.js 填充原始交易

第一步是创建将发送到我们模拟端点的交易对象。我们将直接从 ethers.js 库生成它。在下面的示例中,我们将演示如何针对 DAI 合约上的 transfer() 函数完成此操作:
example.tsx
const dai = new ethers.Contract(DAI_ADDRESS, DAI_ABI, tenderlyForkProvider);

const unsignedTx = await dai.populateTransaction.transfer(
  ZERO_ADDRESS,
  YOUR_ADDRESS,
  util.ether(1),
);

在发送之前模拟交易

现在我们已经提取了未签名的原始交易,让我们在将其发送到链上(发送到 Ethereum Mainnet)之前对其进行模拟:
example.tsx
...

const body = {
    "network_id": "1",
    "from": senderAddr,
    "to": contract.address,
    "input": unsignedTx.data,
    "gas": 21204,
    "gas_price": "0",
    "value": 0,
    "save_if_fails": true
}

const headers = {
    headers: {
        'content-type': 'application/JSON',
        'X-Access-Key': TENDERLY_ACCESS_KEY,
  }
}
const resp = await axios.post(apiURL, body, headers);

if (resp.data.simulation.status === false) {
	// it failed, do as you please
}

将每一次区块链交互包装到 Tenderly 模拟中(可选)

为了确保每一次区块链交互都会先被模拟,请围绕 ethers.js 的 signer 对象编写一个简单的封装,让它始终对交易进行模拟。 此外,您还可以在后续引入类型化错误,供您的逻辑与之交互:
example.tsx
export class TenderlySimulationSigner {
  public _provider: ethers.Provider;

  constructor(provider: ethers.Provider) {
    this._provider = provider;
  }

  public async sendTransaction(
    transaction: Deferrable<TransactionRequest>,
  ): Promise<TransactionResponse> {
    await this._simulateTx(transaction);

    return this._signer.sendTransaction(transaction);
  }

  public async getAddress(): Promise<string> {
    return this._signer.getAddress();
  }

  public async signTransaction(transaction: Deferrable<TransactionRequest>): Promise<string> {
    return this._signer.signTransaction(transaction);
  }

  _simulateTx(transaction: Deferrable<TransactionRequest>): Promise<void> {
    const unsignedTx = await contract.populateTransaction[funcName](...args);

    const apiURL = `https://api.tenderly.co/api/v1/account/me/project/project/simulate`;
    const body = {
      network_id: '1',
      from: senderAddr,
      to: contract.address,
      input: unsignedTx.data,
      gas: 21204,
      gas_price: '0',
      value: 0,
      save_if_fails: true,
    };

    const headers = {
      headers: {
        'content-type': 'application/JSON',
        'X-Access-Key': REACT_APP_TENDERLY_ACCESS_KEY as string,
      },
    };
    const resp = await axios.post(apiURL, body, headers);
    if (resp.data.simulation.status == false) {
      throw new Error('Transaction is going to fail');
    }

    return;
  }
}
这里有一个 GitHub 仓库,您可以在其中 找到此实现的示例。