Passer au contenu principal

Prérequis

  • Un contrat déployé sur le testnet Plasma. Voir déployer un contrat si vous n’en avez pas encore déployé un.
  • Le code source de votre contrat et les paramètres de compilation.
  • Les arguments du constructeur utilisés lors du déploiement (le cas échéant).
L’explorateur de blocs Plasma (plasmascan.to) propose une API de vérification compatible Etherscan. Les exemples ci-dessous utilisent cette API directement — aucun compte tiers n’est requis pour soumettre une vérification.

Contrats d’exemple

Nous utiliserons deux contrats d’exemple pour démontrer à la fois des arguments de constructeur simples et complexes :

Contrat simple

Un contrat basique de stockage de données qui illustre les concepts fondamentaux des 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;
    }
}

Contrat complexe

Un système de coffre plus sophistiqué pour gérer les dépôts de tokens avec des paramètres configurables.
// 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;
    }
}

Vérifier avec Foundry

Foundry fournit la commande forge verify-contract pour la vérification de contrats.
  1. Assurez-vous que votre foundry.toml inclut les paramètres de vérification :
    [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. Vérifiez le contrat. Pour le contrat SimpleStorage avec un argument de constructeur de type 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
    
    Pour le contrat TokenVault avec plusieurs arguments de constructeur :
    # 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. Vérifiez le statut de la vérification. Foundry affichera l’état de vérification en temps réel avec le drapeau --watch. Recherchez :
    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
    

Vérifier avec Hardhat

Hardhat propose la vérification de contrats via le plugin @nomicfoundation/hardhat-verify.
  1. Mettez à jour votre hardhat.config.js pour inclure les paramètres de vérification :
    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. Vérifiez le contrat. Pour le contrat simple :
    npx hardhat verify \
        --network plasmaTestnet \
        0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
        "Hello, Plasma!"
    
    Pour les contrats avec plusieurs arguments de constructeur, créez un script de vérification 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. Exécutez le script de vérification :
    npx hardhat run scripts/verify.js --network plasmaTestnet
    

Vérification automatisée pendant le déploiement

Vous pouvez aussi vérifier des contrats automatiquement pendant le déploiement en ajoutant la vérification à votre script de déploiement :
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();

Vérifier avec Ethers.js

Ethers.js nécessite des appels API manuels à l’explorateur de blocs pour la vérification. Cette approche vous donne un contrôle total sur le processus de vérification.
  1. Installez les dépendances requises :
    npm install ethers dotenv axios form-data
    
  2. Créez le script de vérification 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. Encodez les arguments du constructeur. Pour des arguments de constructeur complexes, vous devez les encoder en ABI. Vous pouvez utiliser l’outil cast de 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. Enfin, lancez la vérification :
    node verify-ethers.js
    

Vérifier le statut de vérification

Après la vérification, vous pouvez vérifier si elle a réussi :

Sur l’explorateur de blocs

  1. Visitez l’explorateur du testnet Plasma.
  2. Recherchez l’adresse de votre contrat.
  3. Recherchez une coche verte à côté de l’onglet « Contract ».
  4. Cliquez sur l’onglet « Contract » pour voir le code source vérifié.

Par programmation

  1. Créez un script de vérification de statut 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éthodes alternatives

Bien que les explorateurs de blocs soient la méthode de vérification la plus courante, vous pouvez également utiliser :

Sourcify

Sourcify propose une vérification décentralisée des contrats :
# Install Sourcify CLI.
npm install -g @ethereum-sourcify/cli

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

Tenderly

Tenderly propose la vérification dans le cadre de sa plateforme de débogage. Consultez leur documentation pour plus de détails.

Aplatissement de contrat

Pour les contrats avec des imports ou des bibliothèques, vous devrez peut-être aplatir votre contrat en un seul fichier avant la vérification. Les outils populaires incluent : Pour les projets complexes avec des bibliothèques externes, consultez la documentation de vérification Hardhat pour les options de configuration avancées.

Dépannage

Décalage d’arguments de constructeur

Error: Invalid constructor arguments provided
Vérifiez à deux reprises que vos arguments de constructeur correspondent exactement à ceux utilisés lors du déploiement. Utilisez cast abi-encode pour vérifier l’encodage.

Décalage de version de compilateur

Error: Compilation failed
Assurez-vous que la version du compilateur dans votre requête de vérification correspond à la version utilisée lors de la compilation.

Décalage de paramètres d’optimisation

Error: Bytecode doesn't match
Vérifiez que les paramètres d’optimisation (activé/désactivé et nombre d’exécutions) correspondent à vos paramètres de compilation.

Déjà vérifié

Error: Contract source code already verified
Cela signifie que la vérification a déjà réussi. Vérifiez sur l’explorateur de blocs pour confirmer.

Limitation de débit

Error: Rate limit exceeded
Attendez quelques minutes avant de réessayer. Envisagez de mettre à niveau votre plan API si vous avez besoin de limites plus élevées.