> ## 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 и публичных сетях (mainnet и testnet). Один и тот же плагин работает и там, и там; укажите нужную сеть в `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>
  ### Установите флаг автоматического заполнения

  Под капотом плагин управляет `@nomicfoundation/hardhat-verify` с расширением `@openzeppelin/hardhat-upgrades`. Установка `TENDERLY_AUTOMATIC_POPULATE_HARDHAT_VERIFY_CONFIG=true` позволяет плагину автоматически заполнить URL верификации `@nomicfoundation/hardhat-verify`.

  ```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="Автоматически">
      Разверните прокси как обычно, используя `deployProxy` из расширения **`@openzeppelin/hardhat-upgrades`**.

      <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="Вручную">
      Верификация прокси вручную выполняется после того, как прокси-контракт развёрнут, — путём вызова функции **`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="Публичная сеть">
      При деплое в публичный mainnet или testnet укажите, нужна ли публичная или приватная верификация.

      ```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>

## Обходное решение для более ранних версий

При использовании `@tenderly/hardhat-tenderly` версий **`< 1.10.0`** и **`< 2.1.0`** это обходное решение включает автоматическую верификацию.

Необходимо верифицировать следующее:

* Прокси-контракт (например, прокси от 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** и создайте новую среду. Выберите базовую сеть, от которой сделать форк, и Chain ID, затем скопируйте **Admin RPC URL** со страницы деталей Virtual Environment.

  Добавьте 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, которая использовалась для компиляции контрактов
  * Настройки оптимизации, использованные плагином upgrades от Openzepplin при деплое/апгрейде прокси

  <Warning>
    Плагин **`hardhat-tenderly`** использует для верификации как исходный код смарт-контрактов, так и настройки компилятора. Если что-то из этого указано неверно, верификация не пройдёт.
  </Warning>

  Добавьте следующую карту **`overrides`** в раздел **`config.solidity`** объекта Hardhat User config.

  ```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. Примеры используют проксированный контракт `Vault`, ссылающийся на ERC-20 токен (`TToken`).

### UUPS-прокси

Чтобы верифицировать UUPS-прокси и связанную информацию, вызовите плагин **`hardhat-tenderly`** дважды:

1. Для верификации реализации необходимо указать:

* **`name`** вашего проксированного контракта (в нашем случае **`Vault`**)
* Адрес, по которому был развёрнут контракт, полученный методом **`getImplementationAddress`** из **`@openzeppelin/upgrades-core`**.

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`**
* Верифицирует экземпляр прокси (**`ERC1967Proxy`**), развёрнутый по адресу **`await proxy.getAddress()`**
* Верифицирует экземпляр реализации **`Vault`**, развёрнутый по адресу **`getImplementationAddress(ethers.provider, await proxy.getAddress())`**
* Апгрейдит прокси до **`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`**)
* Адрес, по которому был развёрнут контракт, полученный методом **`getImplementationAddress`** из **`@openzeppelin/upgrades-core`**.

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`** (реализация) и **`UpgradableBeacon`** от OpenZepplin:

```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),
    });
  });
});
```
