Pular para o conteúdo principal

Pré-requisitos

  • Um contrato implantado na Plasma testnet. Consulte implantar um contrato se ainda não implantou um.
  • O código-fonte do seu contrato e as configurações de compilação.
  • Argumentos do construtor usados durante a implantação (se houver).
O block explorer da Plasma (plasmascan.to) oferece uma API de verificação compatível com o Etherscan. Os exemplos abaixo usam essa API diretamente — nenhuma conta de terceiros é necessária para submeter uma verificação.

Contratos de Exemplo

Usaremos dois contratos de exemplo para demonstrar argumentos de construtor simples e complexos:

Contrato Simples

Um contrato básico de armazenamento de dados que demonstra conceitos fundamentais de smart contracts.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract SimpleStorage {
    string private storedData;
    address public owner;
    
    constructor(string memory initialData) {
        storedData = initialData;
        owner = msg.sender;
    }
    
    function setData(string memory data) public {
        require(msg.sender == owner, "Only owner can set data");
        storedData = data;
    }
    
    function getData() public view returns (string memory) {
        return storedData;
    }
}

Contrato Complexo

Um sistema de vault mais sofisticado para gerenciar depósitos de tokens com parâmetros configuráveis.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract TokenVault {
    struct VaultConfig {
        uint256 minDeposit;
        uint256 maxDeposit;
        bool isActive;
    }
    
    address[] public authorisedTokens;
    VaultConfig public config;
    mapping(address => uint256) public balances;
    
    constructor(
        address[] memory _tokens,
        uint256 _minDeposit,
        uint256 _maxDeposit,
        bool _isActive
    ) {
        authorisedTokens = _tokens;
        config = VaultConfig(_minDeposit, _maxDeposit, _isActive);
    }
    
    function deposit(address token, uint256 amount) external {
        require(config.isActive, "Vault is not active");
        require(amount >= config.minDeposit, "Amount below minimum");
        require(amount <= config.maxDeposit, "Amount above maximum");
        balances[msg.sender] += amount;
    }
}

Verificar com Foundry

Foundry fornece o comando forge verify-contract para verificação de contratos.
  1. Certifique-se de que seu foundry.toml inclua as configurações de verificação:
    [profile.default]
    src = "src"
    out = "out"
    libs = ["lib"]
    solc_version = "0.8.28"
    optimizer = true
    optimizer_runs = 200
    
    [rpc_endpoints]
    plasma_testnet = "https://testnet-rpc.plasma.to"
    
  2. Verifique o contrato. Para o contrato SimpleStorage com um argumento de construtor do tipo string:
    forge verify-contract \
      --chain-id 9746 \
      --num-of-optimizations 200 \
      --watch \
      --constructor-args $(cast abi-encode "constructor(string)" "Hello, Plasma!") \
      --compiler-version v0.8.28+commit.7893614a \
      0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
      src/SimpleStorage.sol:SimpleStorage
    
    Para o contrato TokenVault com vários argumentos de construtor:
    # First, encode the constructor arguments.
    CONSTRUCTOR_ARGS=$(cast abi-encode \
        "constructor(address[],uint256,uint256,bool)" \
        "[0x1234567890123456789012345678901234567890,0x0987654321098765432109876543210987654321]" \
        1000000000000000000 \
        10000000000000000000 \
        true)
    
    forge verify-contract \
        --chain-id 9746 \
        --num-of-optimizations 200 \
        --watch \
        --constructor-args $CONSTRUCTOR_ARGS \
        --compiler-version v0.8.28+commit.7893614a \
        0x9876543210987654321098765432109876543210 \
        src/TokenVault.sol:TokenVault
    
  3. Verifique o status da verificação. O Foundry exibirá o status da verificação em tempo real com a flag --watch. Procure por:
    Submitting verification for [src/SimpleStorage.sol:SimpleStorage] 0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F.
    Submitted contract for verification:
            Response: `OK`
            GUID: `abc123def456ghi789`
            URL: https://testnet.plasmascan.to/address/0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F
    Contract verification status:
    Response: `NOTOK`
    Details: `Pending in queue`
    Contract verification status:
    Response: `OK`
    Details: `Pass - Verified`
    Contract successfully verified
    

Verificar com Hardhat

Hardhat oferece verificação de contratos por meio do plugin @nomicfoundation/hardhat-verify.
  1. Atualize seu hardhat.config.js para incluir configurações de verificação:
    require("@nomicfoundation/hardhat-toolbox");
    require("@nomicfoundation/hardhat-verify");
    require("dotenv").config();
    
    module.exports = {
      solidity: {
        version: "0.8.28",
        settings: {
          optimizer: {
            enabled: true,
            runs: 200
          }
        }
      },
      networks: {
        plasmaTestnet: {
          url: process.env.RPC_URL,
          chainId: 9746,
          accounts: [process.env.PRIVATE_KEY]
        }
      },
      etherscan: {
        customChains: [
          {
            network: "plasmaTestnet",
            chainId: 9746,
            urls: {
              apiURL: "https://testnet.plasmascan.to/api",
              browserURL: "https://testnet.plasmascan.to/"
            }
          }
        ]
      },
      sourcify: {
        enabled: false
      }
    };
    
  2. Verifique o contrato. Para o contrato simples:
    npx hardhat verify \
        --network plasmaTestnet \
        0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
        "Hello, Plasma!"
    
    Para contratos com vários argumentos de construtor, crie um script de verificação scripts/verify.js:
    const hre = require("hardhat");
    
    async function main() {
      const contractAddress = "0x9876543210987654321098765432109876543210";
      
      // Constructor arguments for TokenVault.
      const constructorArgs = [
        [
          "0x1234567890123456789012345678901234567890",
          "0x0987654321098765432109876543210987654321"
        ], // address[] _tokens
        "1000000000000000000", // uint256 _minDeposit (1e18, assuming 18 decimals)
        "10000000000000000000", // uint256 _maxDeposit (1e19, assuming 18 decimals)
        true // bool _isActive
      ];
    
      try {
        await hre.run("verify:verify", {
          address: contractAddress,
          constructorArguments: constructorArgs,
        });
        console.log("Contract verified successfully!");
      } catch (error) {
        console.error("Verification failed:", error);
      }
    }
    
    main()
      .then(() => process.exit(0))
      .catch((error) => {
        console.error(error);
        process.exit(1);
      });
    
  3. Execute o script de verificação:
    npx hardhat run scripts/verify.js --network plasmaTestnet
    

Verificação Automatizada Durante a Implantação

Você também pode verificar contratos automaticamente durante a implantação adicionando a verificação ao seu script de implantação:
const { ethers } = require("hardhat");

async function main() {
  const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
  const simpleStorage = await SimpleStorage.deploy("Hello, Plasma!");
  
  await simpleStorage.waitForDeployment();
  const contractAddress = await simpleStorage.getAddress();
  
  console.log("SimpleStorage deployed to:", contractAddress);
  
  // Wait for a few block confirmations before verifying.
  console.log("Waiting for block confirmations...");
  await simpleStorage.deploymentTransaction().wait(5);
  
  // Verify the contract.
  try {
    await hre.run("verify:verify", {
      address: contractAddress,
      constructorArguments: ["Hello, Plasma!"],
    });
    console.log("Contract verified successfully!");
  } catch (error) {
    console.log("Verification failed:", error.message);
  }
}

main();

Verificar com Ethers.js

Ethers.js requer chamadas de API manuais ao block explorer para verificação. Essa abordagem oferece controle total sobre o processo de verificação.
  1. Instale as dependências necessárias:
    npm install ethers dotenv axios form-data
    
  2. Crie o script de verificação verify-ethers.js:
    const axios = require('axios');
    const fs = require('fs');
    require('dotenv').config();
    
    async function verifyContract(contractAddress, sourceCode, contractName, constructorArgs = "") {
      const apiUrl = "https://testnet.plasmascan.to/api";
      
      const data = {
        module: 'contract',
        action: 'verifysourcecode',
        contractaddress: contractAddress,
        sourceCode: sourceCode,
        codeformat: 'solidity-single-file',
        contractname: contractName,
        compilerversion: 'v0.8.28+commit.7893614a',
        optimizationUsed: '1',
        runs: '200',
        constructorArguements: constructorArgs, // Note: API uses this spelling.
        evmversion: 'default',
        licenseType: '3' // MIT License
      };
    
      try {
        console.log('Submitting contract for verification...');
        const response = await axios.post(apiUrl, new URLSearchParams(data));
        
        if (response.data.status === '1') {
          const guid = response.data.result;
          console.log('Verification submitted successfully!');
          console.log('GUID:', guid);
          
          // Check verification status.
          await checkVerificationStatus(guid);
        } else {
          console.error('Verification submission failed:', response.data.result);
        }
      } catch (error) {
        console.error('Error submitting verification:', error.message);
      }
    }
    
    async function checkVerificationStatus(guid) {
      const apiUrl = "https://testnet.plasmascan.to/api";
      const maxAttempts = 30;
      let attempts = 0;
    
      while (attempts < maxAttempts) {
        try {
          const response = await axios.get(apiUrl, {
            params: {
              module: 'contract',
              action: 'checkverifystatus',
              guid: guid
            }
          });
    
          const status = response.data.status;
          const result = response.data.result;
    
          if (status === '1') {
            console.log('✅ Contract verified successfully!');
            console.log('Result:', result);
            break;
          } else if (result.includes('Fail')) {
            console.error('❌ Verification failed:', result);
            break;
          } else {
            console.log('⏳ Verification pending...');
            attempts++;
            
            if (attempts < maxAttempts) {
              await new Promise(resolve => setTimeout(resolve, 10000)); // Wait 10 seconds.
            }
          }
        } catch (error) {
          console.error('Error checking status:', error.message);
          break;
        }
      }
    
      if (attempts >= maxAttempts) {
        console.log('⚠️ Verification status check timed out. Please check manually.');
      }
    }
    
    // Verify SimpleStorage contract.
    async function verifySimpleStorage() {
      const contractAddress = "0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F";
      const sourceCode = fs.readFileSync('SimpleStorage.sol', 'utf8');
      const constructorArgs = "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d48656c6c6f2c20506c61736d612100000000000000000000000000000000000000";
      
      await verifyContract(contractAddress, sourceCode, "SimpleStorage", constructorArgs);
    }
    
    // Verify TokenVault contract.
    async function verifyTokenVault() {
      const contractAddress = "0x9876543210987654321098765432109876543210";
      const sourceCode = fs.readFileSync('TokenVault.sol', 'utf8');
      
      // Complex constructor arguments (ABI encoded).
      const constructorArgs = "0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000de0b6b3a76400000000000000000000000000000000000000000000000000008ac7230489e8000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000123456789012345678901234567890123456789000000000000000000000000098765432109876543210987654321098765432100";
      
      await verifyContract(contractAddress, sourceCode, "TokenVault", constructorArgs);
    }
    
    // Run verification (uncomment the one you need).
    verifySimpleStorage();
    // verifyTokenVault();
    
  3. Codifique os argumentos do construtor. Para argumentos de construtor complexos, você precisa codificá-los em ABI. Você pode usar a ferramenta cast do Foundry:
    # For SimpleStorage with string argument.
    cast abi-encode "constructor(string)" "Hello, Plasma!"
    
    # For TokenVault with complex arguments.
    cast abi-encode \
        "constructor(address[],uint256,uint256,bool)" \
        "[0x1234567890123456789012345678901234567890,0x0987654321098765432109876543210987654321]" \
        1000000000000000000 \
        10000000000000000000 \
        true
    
  4. Finalmente, execute a verificação:
    node verify-ethers.js
    

Verifique o Status da Verificação

Após a verificação, você pode verificar se foi bem-sucedida:

No Block Explorer

  1. Visite o Plasma testnet explorer.
  2. Pesquise pelo endereço do seu contrato.
  3. Procure por uma marca de seleção verde ao lado da aba “Contract”.
  4. Clique na aba “Contract” para visualizar o código-fonte verificado.

Programaticamente

  1. Crie um script de verificação de status check-verification.js:
    const axios = require('axios');
    require('dotenv').config();
    
    async function checkIfVerified(contractAddress) {
      const apiUrl = "https://testnet.plasmascan.to/api";
      
      try {
        const response = await axios.get(apiUrl, {
          params: {
            module: 'contract',
            action: 'getsourcecode',
            address: contractAddress,
          }
        });
    
        const result = response.data.result[0];
        
        if (result.SourceCode && result.SourceCode !== '') {
          console.log('✅ Contract is verified!');
          console.log('Contract Name:', result.ContractName);
          console.log('Compiler Version:', result.CompilerVersion);
          console.log('Optimisation Used:', result.OptimizationUsed);
          return true;
        } else {
          console.log('❌ Contract is not verified');
          return false;
        }
      } catch (error) {
        console.error('Error checking verification status:', error.message);
        return false;
      }
    }
    
    // Check your contract.
    checkIfVerified("0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F");
    

Métodos Alternativos

Embora os block explorers sejam o método de verificação mais comum, você também pode usar:

Sourcify

Sourcify fornece verificação descentralizada de contratos:
# Install Sourcify CLI.
npm install -g @ethereum-sourcify/cli

# Verify contract.
sourcify verify \
    --network 9746 \
    --address 0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
    --files contracts/SimpleStorage.sol

Tenderly

Tenderly oferece verificação como parte de sua plataforma de depuração. Consulte a documentação para detalhes.

Achatamento de Contratos

Para contratos com imports ou bibliotecas, você pode precisar achatar seu contrato em um único arquivo antes da verificação. Ferramentas populares incluem: Para projetos complexos com bibliotecas externas, consulte a documentação de verificação do Hardhat para opções avançadas de configuração.

Solução de Problemas

Incompatibilidade dos Argumentos do Construtor

Error: Invalid constructor arguments provided
Verifique novamente se os argumentos do construtor correspondem exatamente ao que foi usado durante a implantação. Use cast abi-encode para verificar a codificação.

Incompatibilidade da Versão do Compilador

Error: Compilation failed
Garanta que a versão do compilador em sua solicitação de verificação corresponda à versão usada durante a compilação.

Incompatibilidade das Configurações de Otimização

Error: Bytecode doesn't match
Verifique se as configurações de otimização (habilitado/desabilitado e contagem de runs) correspondem às suas configurações de compilação.

Já Verificado

Error: Contract source code already verified
Isso significa que a verificação já foi bem-sucedida anteriormente. Verifique o block explorer para confirmar.

Limite de Taxa

Error: Rate limit exceeded
Aguarde alguns minutos antes de tentar novamente. Considere atualizar seu plano de API se precisar de limites maiores.