Перейти к основному содержанию

Предварительные требования

Подготовка контракта

Следующий контракт-пример используется во всём руководстве:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

contract SimpleStorage {
    string private storedData;
    address public owner;
    
    event DataStored(string data, address indexed by);
    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    
    constructor(string memory initialData) {
        storedData = initialData;
        owner = msg.sender;
        emit DataStored(initialData, msg.sender);
    }
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Only owner can perform this action");
        _;
    }
    
    function setData(string memory data) public onlyOwner {
        storedData = data;
        emit DataStored(data, msg.sender);
    }
    
    function getData() public view returns (string memory) {
        return storedData;
    }
    
    function transferOwnership(address newOwner) public onlyOwner {
        require(newOwner != address(0), "New owner cannot be zero address");
        emit OwnershipTransferred(owner, newOwner);
        owner = newOwner;
    }
}

Развёртывание с Foundry

Foundry — это быстрый набор инструментов для разработки на Ethereum, написанный на Rust.
  1. Установите Foundry:
    curl -L https://foundry.paradigm.xyz | bash
    
    Возможно, вам потребуется выполнить source файла .bashrc или .zshrc
  2. Настройте новый проект Foundry:
    foundryup
    forge init my-plasma-project
    cd my-plasma-project
    
  3. Обновите foundry.toml с настройками Plasma testnet:
    [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"
    
  4. Создайте файл .env в корне проекта:
    PRIVATE_KEY=your_private_key_here
    RPC_URL=https://testnet-rpc.plasma.to
    
  5. Загрузите переменные окружения:
    source .env
    
  6. Сохраните код контракта как src/SimpleStorage.sol, затем разверните:
    forge create src/SimpleStorage.sol:SimpleStorage \
        --rpc-url $RPC_URL \
        --private-key $PRIVATE_KEY \
        --constructor-args "Hello, Plasma!"
    
    Foundry выведет хеш транзакции развёртывания и адрес контракта:
    [⠊] Compiling...
    [⠢] Compiling 1 files with Solc 0.8.28
    [⠆] Solc 0.8.28 finished in 124.81ms
    Compiler run successful!
    Warning: Dry run enabled, not broadcasting transaction
    
    Contract: SimpleStorage
    Transaction: {
      "from": "0xbd828f7679656f8f830b89611c933017442f2ebf",
      "to": null,
      "maxFeePerGas": "0xf",
      "maxPriorityFeePerGas": "0x1",
      "gas": "0x69f18",
    
    [...]
    
  7. Протестируйте развёрнутый контракт, используя инструмент cast от Foundry. Сначала прочитайте сохранённые данные:
    cast call 0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
        "getData()" \
        --rpc-url $RPC_URL
    
  8. Затем обновите данные и снова прочитайте их:
    # Update the stored data.
    cast send 0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
        "setData(string)" "Updated from Foundry" \
        --private-key $PRIVATE_KEY \
        --rpc-url $RPC_URL
    
    # Read the stored data again.
    cast call 0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
        "getData()" \
        --rpc-url $RPC_URL
    
    Вывод должен быть примерно таким:
    blockHash            0x68fbd2aaf1de9c577869056ca634f2103fa1695673a94d8c049d0b78d3733aac
    blockNumber          1200423
    contractAddress
    cumulativeGasUsed    21712
    effectiveGasPrice    8
    from                 0xBd828F7679656F8f830b89611C933017442F2EbF
    gasUsed              21712
    logs                 []
    logsBloom            0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    root
    status               1 (success)
    transactionHash      0xcf5b1fcc8d19f7cf7992f6e6a8b3ade74e07afbd0da0b9fce26bda4c9503a12b
    transactionIndex     0
    type                 2
    blobGasPrice
    blobGasUsed
    to                   0x742D35Cc6610c7532C8582D4C371aCb1D5F44D7F
    0x
    

Развёртывание с Hardhat

Hardhat — это полнофункциональный фреймворк разработки с богатой поддержкой плагинов.
  1. Убедитесь, что у вас есть следующее:
  • Node.js 20+ (требуется для совместимости с современным Hardhat)
  • Знакомство с разработкой на Ethereum
  • Кошелёк с testnet-токенами (см. подробности testnet-цепочки)
Node.js 18 и ниже больше не поддерживаются Hardhat и могут вызывать проблемы совместимости ES-модулей.
  1. Проверьте версию Node.js:
    node --version
    
    Если вы используете Node.js 18 или ниже, обновитесь до Node.js 20+:
    # Using nvm (recommended)
    nvm install 20
    nvm use 20
    
    # Or using NodeSource repository (Ubuntu/Debian)
    curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
    sudo apt-get install -y nodejs
    
  2. Инициализируйте новый проект Hardhat:
    mkdir my-plasma-hardhat-project
    cd my-plasma-hardhat-project
    npm init -y
    npm install --save-dev hardhat@latest @nomicfoundation/hardhat-toolbox@latest
    npm install dotenv@latest ethers@latest
    npx hardhat init
    
    Если вы столкнётесь с проблемами совместимости, ознакомьтесь с документацией Hardhat для последних поддерживаемых версий.
    Выберите Create a JavaScript project при появлении запроса.
  3. Обновите hardhat.config.js:
    require("@nomicfoundation/hardhat-toolbox");
    require("dotenv").config();
    
    /** @type import('hardhat/config').HardhatUserConfig */
    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],
          gasPrice: 1000000000, // 1 gwei
        }
      },
      etherscan: {
        apiKey: {
          plasmaTestnet: process.env.ETHERSCAN_API_KEY
        },
        customChains: [
          {
            network: "plasmaTestnet",
            chainId: 9746,
            urls: {
              apiURL: "https://testnet.plasmascan.to/api",
              browserURL: "https://testnet.plasmascan.to/"
            }
          }
        ]
      }
    };
    
  4. Создайте файл .env в корне проекта:
    PRIVATE_KEY=your_private_key_here
    RPC_URL=https://testnet-rpc.plasma.to
    ETHERSCAN_API_KEY=your_api_key_for_verification
    
  5. Создайте каталог scripts и скрипт развёртывания:
    mkdir scripts
    
  6. Создайте scripts/deploy.js:
    const { ethers } = require("hardhat");
    
    async function main() {
      // Get the contract factory
      const SimpleStorage = await ethers.getContractFactory("SimpleStorage");
    
      console.log("Deploying SimpleStorage contract...");
    
      // Deploy the contract with constructor arguments
      const simpleStorage = await SimpleStorage.deploy("Hello, Plasma from Hardhat!");
    
      // Wait for deployment to complete
      await simpleStorage.waitForDeployment();
    
      const contractAddress = await simpleStorage.getAddress();
      console.log("SimpleStorage deployed to:", contractAddress);
      console.log("Transaction hash:", simpleStorage.deploymentTransaction().hash);
    
      // Wait a few blocks for the transaction to be included
      console.log("Waiting for block confirmations...");
      await simpleStorage.deploymentTransaction().wait(5);
    
      console.log("✅ Deployment completed successfully!");
    }
    
    main()
      .then(() => process.exit(0))
      .catch((error) => {
        console.error(error);
        process.exit(1);
      });
    
  7. Сохраните код контракта как contracts/SimpleStorage.sol, затем разверните:
    npx hardhat run scripts/deploy.js --network plasmaTestnet
    
  8. Готово!

Развёртывание с Ethers.js

Ethers.js предоставляет минимальный программный поток развёртывания.
  1. Создайте новый проект:
    mkdir my-plasma-ethers-project
    cd my-plasma-ethers-project
    npm init -y
    npm install ethers dotenv solc
    
  2. Создайте файл .env в корне проекта:
    PRIVATE_KEY=<your_private_key_here>
    RPC_URL=https://testnet-rpc.plasma.to
    ETHERSCAN_API_KEY=your_api_key_for_verification
    
  3. Создайте compile.js для компиляции вашего Solidity-контракта:
    const fs = require('fs');
    const solc = require('solc');
    require('dotenv').config();
    
    // Read the contract source code.
    const contractSource = fs.readFileSync('SimpleStorage.sol', 'utf8');
    
    // Prepare the input for the Solidity compiler.
    const input = {
      language: 'Solidity',
      sources: {
        'SimpleStorage.sol': {
          content: contractSource,
        },
      },
      settings: {
        outputSelection: {
          '*': {
            '*': ['*'],
          },
        },
        optimizer: {
          enabled: true,
          runs: 200,
        },
      },
    };
    
    // Compile the contract.
    const output = JSON.parse(solc.compile(JSON.stringify(input)));
    
    // Check for compilation errors.
    if (output.errors) {
      output.errors.forEach((error) => {
        console.error(error.formattedMessage);
      });
    }
    
    // Extract the contract data.
    const contract = output.contracts['SimpleStorage.sol']['SimpleStorage'];
    const abi = contract.abi;
    const bytecode = contract.evm.bytecode.object;
    
    // Save compilation artifacts.
    fs.writeFileSync('SimpleStorage.json', JSON.stringify({
      abi: abi,
      bytecode: bytecode
    }, null, 2));
    
    console.log('Contract compiled successfully!');
    
  4. Сохраните контракт как SimpleStorage.sol, затем скомпилируйте:
    node compile.js
    
    Должен получиться вывод:
    Contract compiled successfully!
    
  5. Создайте deploy.js:
    const { ethers } = require('ethers');
    const fs = require('fs');
    require('dotenv').config();
    
    async function deploy() {
      // Set up provider and wallet.
      const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
      const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
      
      console.log('Deploying from account:', wallet.address);
      
      // Check account balance.
      const balance = await provider.getBalance(wallet.address);
      console.log('Account balance:', ethers.formatEther(balance), 'XPL');
      
      // Load compiled contract.
      const contractData = JSON.parse(fs.readFileSync('SimpleStorage.json', 'utf8'));
      
      // Create contract factory.
      const factory = new ethers.ContractFactory(
        contractData.abi,
        contractData.bytecode,
        wallet
      );
      
      // Deploy the contract.
      console.log('Deploying contract...');
      const contract = await factory.deploy("Hello, Plasma from Ethers.js!", {
        gasLimit: 500000, // Set a reasonable gas limit.
        gasPrice: ethers.parseUnits('1', 'gwei'), // 1 gwei gas price.
      });
      
      // Wait for deployment.
      await contract.waitForDeployment();
      
      const contractAddress = await contract.getAddress();
      console.log('Contract deployed to:', contractAddress);
      console.log('Transaction hash:', contract.deploymentTransaction().hash);
      
      // Save deployment info.
      const deploymentInfo = {
        contractAddress: contractAddress,
        transactionHash: contract.deploymentTransaction().hash,
        deployer: wallet.address,
        network: 'plasmaTestnet',
        timestamp: new Date().toISOString()
      };
      
      fs.writeFileSync('deployment.json', JSON.stringify(deploymentInfo, null, 2));
      
      return contract;
    }
    
    async function interact(contract) {
      console.log('\nInteracting with deployed contract...');
      
      // Read initial data.
      const initialData = await contract.getData();
      console.log('Initial data:', initialData);
      
      // Update data.
      const updateTx = await contract.setData("Updated via Ethers.js");
      await updateTx.wait();
      console.log('Data updated. Transaction hash:', updateTx.hash);
      
      // Read updated data.
      const updatedData = await contract.getData();
      console.log('Updated data:', updatedData);
      
      // Get owner.
      const owner = await contract.owner();
      console.log('Contract owner:', owner);
    }
    
    // Main execution.
    deploy()
      .then(async (contract) => {
        await interact(contract);
        console.log('\nDeployment and interaction completed successfully!');
      })
      .catch((error) => {
        console.error('Error:', error);
        process.exit(1);
      });
    
  6. Запустите скрипт развёртывания:
    node deploy.js
    
    Вывод должен быть примерно таким:
    Deploying from account: 0xBd828F7679656F8f830b89611C933017442F2EbF
    Account balance: 98539.897261933991888304 XPL
    Deploying contract...
    Contract deployed to: 0xf298A2A7BC526F9228B8C422D38f3c2E0D15449F
    Transaction hash: 0x3d16cc55b9148bac9ac20981d9748a0fc89861c6beb92a2f869bb09b75f685b2
    
    Interacting with deployed contract...
    Initial data: Hello, Plasma from Ethers.js!
    Data updated. Transaction hash: 0xe3328862ece5d0ca4ca84d25c4130d6749ec872c24bf76eea23dfa8c50c22505
    Updated data: Updated via Ethers.js
    Contract owner: 0xBd828F7679656F8f830b89611C933017442F2EbF
    
    Deployment and interaction completed successfully!
    

Проверка развёртывания

После развёртывания любым из приведённых способов проверьте развёртывание контракта.

Устранение неполадок

Проблемы совместимости с ES-модулями

Если вы видите ошибки вроде Error [ERR_REQUIRE_ESM]: require() of ES Module, это обычно означает:
  1. Слишком старая версия Node.js: обновитесь до Node.js 20+, как указано в предварительных требованиях
  2. Несоответствие версий зависимостей: если обновить Node.js невозможно, можно понизить проблемную зависимость:
    npm install micro-eth-signer@0.10.0
    

Предупреждения о версии Node.js

Если вы видите предупреждения о неподдерживаемых версиях Node.js:
WARNING: You are currently using Node.js v18.19.1, which is not supported by Hardhat
Обновитесь до Node.js 20+, следуя шагам в разделе предварительных требований.

Недостаточно средств

Error: insufficient funds for intrinsic transaction cost
Убедитесь, что в кошельке достаточно testnet-токенов. Посетите testnet faucet.

Неправильная конфигурация сети

Error: network with chainId "1" doesn't match the configured chainId "9746"
Проверьте, что конфигурация сети соответствует подробностям testnet-цепочки.

Сбой оценки газа

Error: cannot estimate gas
Установите явные лимиты газа в конфигурации развёртывания:
// In your deploy script
const simpleStorage = await SimpleStorage.deploy("Hello, Plasma from Hardhat!", {
  gasLimit: 500000
});