> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tenderly.co/llms.txt
> Use this file to discover all available pages before exploring further.

# 验证代理合约

> 按照本分步指南了解如何使用 Tenderly-Hardhat 插件验证代理智能合约。

<Info>
  **适用于：** Virtual Environments 和公共网络（主网和测试网）。同一插件同时处理两者；请在 `hardhat.config.ts` 中将其指向正确的网络。
</Info>

本指南将引导您使用 `@tenderly/hardhat-tenderly` 完成代理合约验证。

## 要求

自动代理验证在以下版本的 `@tenderly/hardhat-tenderly` 中开箱即用：

* **`>= 1.10.0`**（Ethers 5 系列）
* **`>= 2.1.0`**（Ethers 6 系列）

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
npm update @tenderly/hardhat-tenderly
```

它仅在您使用 [`@openzeppelin/hardhat-upgrades`](https://docs.openzeppelin.com/upgrades-plugins/api-hardhat-upgrades) 部署代理时有效。[Hardhat Ignition](https://hardhat.org/ignition/docs/getting-started#overview) 目前**不支持**代理验证。

如果您使用的是较低版本的插件且无法升级，请使用下面的[手动变通方法](#workaround-for-lower-versions)。

## 自动验证

该插件可验证三种代理模式：`TransparentUpgradeableProxy`、`UUPSUpgradeableProxy` 和 `BeaconProxy`。

<Steps>
  ### 设置自动填充标志

  在底层，该插件通过 `@openzeppelin/hardhat-upgrades` 扩展驱动 `@nomicfoundation/hardhat-verify`。设置 `TENDERLY_AUTOMATIC_POPULATE_HARDHAT_VERIFY_CONFIG=true` 让插件为您填充 `@nomicfoundation/hardhat-verify` 的验证 URL。

  ```bash title=".env" theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  TENDERLY_AUTOMATIC_POPULATE_HARDHAT_VERIFY_CONFIG=true
  ```

  ### 编写部署脚本

  如需更清晰的视图，您可以查看[此 GitHub 仓库](https://github.com/Tenderly/hardhat-tenderly/tree/master/examples/contract-verification/ethers-v6)并转到 **`scripts/proxy/`** 查看完整示例。

  <Tabs>
    <Tab title="Automatic">
      使用来自 **`@openzeppelin/hardhat-upgrades`** 扩展的 `deployProxy` 像往常一样部署代理。

      <Warning>
        您必须捕获 **`waitForDeployment`** 函数返回的对象，才能进一步与之交互。
      </Warning>

      在底层，自动验证是通过包装 **`deployProxy`** 并等待部署完成来实现的。

      ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}

      async function main() {
          console.log(
              "🖖🏽[ethers] Deploying TransparentUpgradeableProxy with VotingLogic as implementation on Tenderly.",
          );

          const VotingLogic = await ethers.getContractFactory("VotingLogic");
          let proxyContract = await upgrades.deployProxy(VotingLogic);
          proxyContract = await proxyContract.waitForDeployment();

          const proxyAddress = await proxyContract.getAddress();

          console.log("VotingLogic proxy deployed to:", proxyAddress);
          console.log(
              "VotingLogic impl deployed to:",
              await getImplementationAddress(ethers.provider, proxyAddress),
          );
      }

      main().catch((error) => {
          console.error(error);
          process.exitCode = 1;
      });
      ```
    </Tab>

    <Tab title="Manual">
      使用手动验证方法验证代理是在代理合约部署完成后，通过调用 **`tenderly.verify()`** 函数完成的。

      ```typescript theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}

      async function main() {
          console.log(
              "🖖🏽[ethers] Deploying TransparentUpgradeableProxy with VotingLogic as implementation on Tenderly.",
          );

          const VotingLogic = await ethers.getContractFactory("VotingLogic");
          let proxyContract = await upgrades.deployProxy(VotingLogic);
          proxyContract = await proxyContract.waitForDeployment();

          const proxyAddress = await proxyContract.getAddress();

          console.log("VotingLogic proxy deployed to:", proxyAddress);
          console.log(
              "VotingLogic impl deployed to:",
              await getImplementationAddress(ethers.provider, proxyAddress),
          );

          await tenderly.verify({
              name: ProxyPlaceholderName,
              address: proxyAddress,
          });
      }

      main().catch((error) => {
          console.error(error);
          process.exitCode = 1;
      });
      ```
    </Tab>
  </Tabs>

  ### 运行脚本

  <Tabs>
    <Tab title="Virtual Environment">
      ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      TENDERLY_AUTOMATIC_VERIFICATION=true \
      TENDERLY_AUTOMATIC_POPULATE_HARDHAT_VERIFY_CONFIG=true \
      npx hardhat run scripts/deploy.ts
      ```
    </Tab>

    <Tab title="Public network">
      部署到公共主网或测试网时，请指定您要进行公开还是私密验证。

      ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      TENDERLY_AUTOMATIC_VERIFICATION=true \
      TENDERLY_AUTOMATIC_POPULATE_HARDHAT_VERIFY_CONFIG=true \
      TENDERLY_PRIVATE_VERIFICATION=true \
      npx hardhat run scripts/deploy.ts --network mainnet_base
      ```
    </Tab>
  </Tabs>
</Steps>

## 较低版本的变通方法

在使用版本 **`< 1.10.0`** 和 **`< 2.1.0`** 的 `@tenderly/hardhat-tenderly` 时，此变通方法将启用自动验证。

您需要验证以下内容：

* 代理合约（例如 OpenZeppelin 的代理）
* 代理背后的实现
* 实现的任何依赖项
* 通过升级部署的新实现实例

验证过程根据代理合约类型和实现而有所不同。

<figure>
  <img src="https://mintcdn.com/tenderly/XsEZlaGXYskrtN68/images/Verifying%20Proxy%20Contracts%20Doc%20Chart.webp?fit=max&auto=format&n=XsEZlaGXYskrtN68&q=85&s=e9f97aa47100831216c10cc3ed7e7c58" alt="Tenderly Docs" width="2200" height="1219" data-path="images/Verifying Proxy Contracts Doc Chart.webp" />

  <figcaption />
</figure>

### 概述

在本指南中，我们将使用[一个示例 Hardhat 项目](https://github.com/Tenderly/tenderly-examples)和 **`@tenderly/hardhat-tenderly`** 插件来演示 OpenZeppelin 的 [UUPSUpgradeable](https://docs.openzeppelin.com/contracts/4.x/api/proxy#UUPSUpgradeable)、[TransparentUpgradeableProxy](https://docs.openzeppelin.com/contracts/4.x/api/proxy#TransparentUpgradeableProxy) 和 [BeaconProxy](https://docs.openzeppelin.com/contracts/4.x/api/proxy#BeaconProxy) 替代方案的验证。

<Warning>
  代理合约需要[手动验证](/contract-verification/hardhat#manual-verification)。像这样关闭自动验证：

  ```typescript title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  // use manual verification
  tenderly.setup({ automaticVerifications: false });
  ```
</Warning>

要获取已部署实现的地址，请使用 **`@openzeppelin/upgrades-core`** 包和 **`getImplementationAddress`** 函数。

验证代理实现通常很简单；就像验证任何其他合约一样验证它。

```typescript title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
await tenderly.verify({
  // the new implementation contract
  name: 'VaultV2',
  // the address where implementation is deployed
  address: await getImplementationAddress(ethers.provider, await proxy.getAddress()),
});
```

要验证代理实例，您需要完成以下两个准备步骤：

1. [根据您使用的代理类型加载代理的确切智能合约](#loading-proxy-contracts)，以便对其进行编译。您需要通过创建一个虚拟 .sol 文件将代理合约通过编译器导入。
2. [修改 **`hardhat.config.ts`**](#configuring-solidity-compiler-overrides) 以指定 OpenZepplin 合约编译时使用的设置。

完成这些步骤后，您可以像验证任何其他合约一样继续验证代理。

```ts title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
await tenderly.verify({
  name: 'ERC1967Proxy', // or TransparentUpgradeableProxy or BeaconProxy
  address: await proxy.getAddress(),
});
```

<Steps>
  ### 克隆示例仓库

  ```sh theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  git clone git@github.com:Tenderly/tenderly-examples.git
  cd contract-verifications
  npm i
  ```

  ### 设置 Tenderly CLI

  ```sh theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  brew tap tenderly/tenderly && brew install tenderly
  tenderly login
  ```

  有关非 Homebrew 安装选项，请参阅 [Tenderly CLI 仓库](https://github.com/Tenderly/tenderly-cli)。

  ### 配置 Hardhat

  在 `hardhat.config.ts` 中，将 `tenderly.username` 和 `tenderly.project` 设置为您的[项目和用户名 slug](/platform/account/projects/slug)。

  ### 创建 Virtual Environment

  部署和验证合约最快的方式是在 [Virtual Environment](/virtual-environments/overview) 上进行。

  在 [Tenderly Dashboard](https://dashboard.tenderly.co) 中，打开 **Virtual Environments** 并创建一个新的。选择要 fork 的基础网络和一个 Chain ID，然后从 Virtual Environment 的详情页面复制 **Admin RPC URL**。

  将 RPC URL 添加到您的 `.env` 文件：

  ```bash title=".env" theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  TENDERLY_VIRTUAL_TESTNET_RPC=https://virtual.<network>.rpc.tenderly.co/<your-uuid>
  ```

  然后在 `hardhat.config.ts` 中引用它：

  ```typescript title="hardhat.config.ts" theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  networks: {
    virtualMainnet: {
      url: process.env.TENDERLY_VIRTUAL_TESTNET_RPC!,
      chainId: 73571, // the Chain ID you set when creating the Virtual Environment
    },
  },
  ```

  有关完整演练，请参阅 [Virtual Environment 快速入门](/virtual-environments/quickstart)。

  ### 运行测试

  ```sh theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  rm -rf .openzeppelin && npx hardhat test --network virtualMainnet
  ```

  <Note>重新部署合约时，先删除 **`.openzeppelin`** 文件夹。它缓存有关代理及其实现的信息。</Note>

  ### 加载代理合约

  要验证代理合约，请创建一个 **`DummyProxy.sol`** 文件并导入您正在使用的 OpenZepplin 代理合约。这样一来，这些合约被加载并已通过编译器，使您能够在验证期间引用代理的确切合约源代码。

  ```sol theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  // SPDX-License-Identifier: MIT
  pragma solidity ^0.8.17;


  abstract contract ERC1967ProxyAccess is ERC1967Proxy {}
  abstract contract UpgradableBeaconAccess is UpgradeableBeacon {}
  abstract contract BeaconProxyAccess is BeaconProxy {}
  abstract contract TransparentUpgradeableProxyAccess is TransparentUpgradeableProxy {}
  ```

  ### 配置 Solidity 编译器覆盖

  <Note>
    下面的覆盖映射是针对 **`@openzeppelin/contracts-upgradeable`** 版本 **`4.9.1`** 得出的。
    它们可能与该包的其他版本有所不同。
  </Note>

  编译 Openzepplin 的代理合约后，您还需要指定以下内容：

  * 编译合约时使用的 Solidity 编译器版本
  * Openzepplin 的升级插件在执行代理部署/升级时使用的优化设置

  <Warning>
    **`hardhat-tenderly`** 插件同时使用智能合约的源代码和编译器设置进行验证。如果其中任何设置不正确，验证将失败。
  </Warning>

  在 Hardhat User 配置对象的 **`config.solidity`** 部分添加以下 **`overrides`** 映射。

  ```ts title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  const config: HardhatUserConfig = {
    solidity: {
      compilers: [{ version: '0.8.18' } /* OTHER COMPILER VERSIONS*/],
      overrides: {
        '@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol': {
          version: '0.8.9',
          settings: {
            optimizer: {
              enabled: true,
              runs: 200,
            },
          },
        },
        '@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol': {
          version: '0.8.9',
          settings: {
            optimizer: {
              enabled: true,
              runs: 200,
            },
          },
        },

        '@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol': {
          version: '0.8.9',
          settings: {
            optimizer: {
              enabled: true,
              runs: 200,
            },
          },
        },
        '@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol': {
          version: '0.8.9',
          settings: {
            optimizer: {
              enabled: true,
              runs: 200,
            },
          },
        },
        'contracts/proxy.sol': {
          version: '0.8.9',
          settings: {
            optimizer: {
              enabled: true,
              runs: 200,
            },
          },
        },
      },
    },
    /* OTHER CONFIG */
  };
  ```
</Steps>

## 按代理类型验证

以下代码示例展示了如何验证 OpenZeppelin 三种代理模式的实现和代理。这些示例使用一个引用 ERC-20 代币（`TToken`）的代理化 `Vault` 合约。

### UUPS 代理

要验证 UUPS 代理及其底层信息，请调用 **`hardhat-tenderly`** 插件两次：

1. 要验证实现，您需要提供以下内容：

* 代理化合约的 **`name`**（在我们的例子中是 **`Vault`**）
* 使用来自 **`@openzeppelin/upgrades-core`** 的 **`getImplementationAddress`** 方法获取的合约部署地址。

2. 要验证代理，请提供以下内容：

* **`ERC1967Proxy`** 作为代理合约的 **`name`**
* 代理地址 **`proxy.address`**

```ts title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
await tenderly.verify(
  {
    name: 'Vault',
    address: await getImplementationAddress(ethers.provider, await proxy.getAddress()),
  },
  {
    name: 'ERC1967Proxy',
    address: await proxy.getAddress(),
  },
);
```

#### 完整代码示例

以下是一个完整的 Hardhat 测试，执行以下操作：

* 部署 **`TToken`**（Vault 所需）
* 部署 **`Vault`** 作为代理，使用 **`TToken`** 合约初始化
* 验证部署在 **`await proxy.getAddress()`** 的代理（**`ERC1967Proxy`**）实例
* 验证部署在 **`getImplementationAddress(ethers.provider, await proxy.getAddress())`** 的实现实例 **`Vault`**
* 将代理升级到 **`VaultV2`**

```ts title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}

describe('Vault', () => {
  it('uups proxy deployment and verification', async () => {
    const VaultFactory = await ethers.getContractFactory('Vault');
    const TokenFactory = await ethers.getContractFactory('TToken');

    let token = await ethers.deployContract('TToken');
    token = await token.waitForDeployment();
    const tokenAddress = await token.getAddress();

    await tenderly.verify({
      name: 'TToken',
      address: tokenAddress,
    });

    let proxy = await upgrades.deployProxy(VaultFactory, [tokenAddress], {
      kind: 'uups',
    });
    await proxy.waitForDeployment();
    const proxyAddress = await proxy.getAddress();

    console.log('Deployed UUPS ', {
      proxy: proxyAddress,
      implementation: await getImplementationAddress(ethers.provider, proxyAddress),
    });

    await tenderly.verify(
      {
        name: 'Vault',
        address: await getImplementationAddress(ethers.provider, proxyAddress),
      },
      {
        name: 'ERC1967Proxy',
        address: proxyAddress,
      },
    );

    // upgrade
    const vaultV2Factory = await ethers.getContractFactory('VaultV2');
    proxy = (await upgrades.upgradeProxy(proxy, vaultV2Factory, {
      kind: 'uups',
    })) as Vault;

    await proxy.waitForDeployment();

    console.log('Upgraded UUPS ', {
      proxy: proxyAddress,
      implementation: await getImplementationAddress(ethers.provider, proxyAddress),
    });

    await tenderly.verify({
      name: 'VaultV2',
      address: await getImplementationAddress(ethers.provider, proxyAddress),
    });
  });
});
```

### Transparent 代理

要验证 UUPS 代理及其底层信息，请调用 **`hardhat-tenderly`** 并传递两个合约：**`Vault`** 用于实现，**`TransparentUpgradeableProxy`** 用于代理本身。

```ts title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
await tenderly.verify(
  {
    name: 'Vault',
    address: await getImplementationAddress(ethers.provider, await proxy.getAddress()),
  },
  {
    name: 'TransparentUpgradeableProxy',
    address: await proxy.getAddress(),
  },
);
```

1. 要验证实现，请提供以下内容：

* 代理化合约的 **`name`**（在我们的例子中是 **`Vault`**）
* 使用来自 **`@openzeppelin/upgrades-core`** 的 **`getImplementationAddress`** 方法获取的合约部署地址。

2. 要验证代理，请提供以下内容：

* **`TransparentUpgradeableProxy`** 作为代理合约的 **`name`**
* 代理地址 **`await proxy.getAddress()`**

#### 完整代码示例

```ts title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}

describe('Vault', () => {
  it('transparent upgradable proxy deployment and verification', async () => {
    const VaultFactory = await ethers.getContractFactory('Vault');
    const TokenFactory = await ethers.getContractFactory('TToken');

    let token = await ethers.deployContract('TToken');
    token = await token.waitForDeployment();
    const tokenAddress = await token.getAddress();

    await tenderly.verify({
      name: 'TToken',
      address: tokenAddress,
    });

    let proxy = await upgrades.deployProxy(VaultFactory, [tokenAddress], {
      kind: 'transparent',
    });
    await proxy.waitForDeployment();
    const proxyAddress = await proxy.getAddress();

    console.log('Deployed transparent', {
      proxy: proxyAddress,
      implementation: await getImplementationAddress(ethers.provider, proxyAddress),
    });

    await tenderly.verify(
      {
        name: 'Vault',
        address: await getImplementationAddress(ethers.provider, proxyAddress),
      },
      {
        name: 'TransparentUpgradeableProxy',
        address: proxyAddress,
      },
    );

    // upgrade
    const vaultV2Factory = await ethers.getContractFactory('VaultV2');

    proxy = (await upgrades.upgradeProxy(proxy, vaultV2Factory, {
      kind: 'transparent',
    })) as Vault;

    await proxy.waitForDeployment();

    console.log('Upgraded transparent ', {
      proxy: proxyAddress,
      implementation: await getImplementationAddress(ethers.provider, proxyAddress),
    });

    await tenderly.verify({
      name: 'VaultV2',
      address: await getImplementationAddress(ethers.provider, proxyAddress),
    });
  });
});
```

### Beacon 代理

要验证 Beacon 代理及其底层信息，您必须验证两个合约：**`Vault`**（实现）和 OpenZepplin 的 **`UpgradableBeacon`**：

```ts title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
await tenderly.verify(
  {
    name: 'Vault',
    address: await getImplementationAddressFromBeacon(ethers.provider, await beacon.getAddress()),
  },
  {
    name: 'UpgradeableBeacon',
    address: await beacon.getAddress(),
  },
);
```

#### 完整代码示例

```ts title="example.ts" showLineNumbers theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}

describe('Vault', () => {
  it('beacon proxy deployment and verification', async () => {
    const VaultFactory = await ethers.getContractFactory('Vault');
    const TokenFactory = await ethers.getContractFactory('TToken');

    let token = await ethers.deployContract('TToken');
    token = await token.waitForDeployment();
    const tokenAddress = await token.getAddress();

    await tenderly.verify({
      name: 'TToken',
      address: tokenAddress,
    });

    let beacon = (await upgrades.deployBeacon(VaultFactory)) as UpgradeableBeacon;

    await beacon.waitForDeployment();
    const beaconAddress = await beacon.getAddress();

    let vault = await upgrades.deployBeaconProxy(beacon, VaultFactory, [tokenAddress], {
      initializer: 'initialize',
    });
    await vault.waitForDeployment();

    console.log('Deployed beacon ', {
      proxy: beaconAddress,
      implementation: await getImplementationAddressFromBeacon(ethers.provider, beaconAddress),
      beacon: beaconAddress,
    });

    await tenderly.verify(
      {
        name: 'Vault',
        address: await getImplementationAddressFromBeacon(ethers.provider, beaconAddress),
      },
      {
        name: 'UpgradeableBeacon',
        address: beaconAddress,
      },
    );

    const vaultV2Factory = await ethers.getContractFactory('VaultV2');

    // upgrade
    vault = await upgrades.deployBeaconProxy(beacon, vaultV2Factory, [tokenAddress]);

    await upgrades.upgradeBeacon(beaconAddress, vaultV2Factory, {});

    console.log('Upgraded beacon ', {
      proxy: beaconAddress,
      implementation: await getImplementationAddressFromBeacon(ethers.provider, beaconAddress),
      beacon: beaconAddress,
    });

    await tenderly.verify({
      name: 'VaultV2',
      address: await getImplementationAddressFromBeacon(ethers.provider, beaconAddress),
    });
  });
});
```
