Предварительные требования
Подготовка контракта
Следующий контракт-пример используется во всём руководстве:
// 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.
-
Установите Foundry:
curl -L https://foundry.paradigm.xyz | bash
Возможно, вам потребуется выполнить source файла .bashrc или .zshrc
-
Настройте новый проект Foundry:
foundryup
forge init my-plasma-project
cd my-plasma-project
-
Обновите
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"
-
Создайте файл
.env в корне проекта:
PRIVATE_KEY=your_private_key_here
RPC_URL=https://testnet-rpc.plasma.to
-
Загрузите переменные окружения:
-
Сохраните код контракта как
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",
[...]
-
Протестируйте развёрнутый контракт, используя инструмент cast от Foundry. Сначала прочитайте сохранённые данные:
cast call 0x742d35Cc6610C7532C8582d4C371Acb1D5f44D7F \
"getData()" \
--rpc-url $RPC_URL
-
Затем обновите данные и снова прочитайте их:
# 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 — это полнофункциональный фреймворк разработки с богатой поддержкой плагинов.
- Убедитесь, что у вас есть следующее:
- Node.js 20+ (требуется для совместимости с современным Hardhat)
- Знакомство с разработкой на Ethereum
- Кошелёк с testnet-токенами (см. подробности testnet-цепочки)
Node.js 18 и ниже больше не поддерживаются Hardhat и могут вызывать проблемы совместимости ES-модулей.
-
Проверьте версию Node.js:
Если вы используете 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
-
Инициализируйте новый проект 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 при появлении запроса.
-
Обновите
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/"
}
}
]
}
};
-
Создайте файл
.env в корне проекта:
PRIVATE_KEY=your_private_key_here
RPC_URL=https://testnet-rpc.plasma.to
ETHERSCAN_API_KEY=your_api_key_for_verification
-
Создайте каталог scripts и скрипт развёртывания:
-
Создайте
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);
});
-
Сохраните код контракта как
contracts/SimpleStorage.sol, затем разверните:
npx hardhat run scripts/deploy.js --network plasmaTestnet
-
Готово!
Развёртывание с Ethers.js
Ethers.js предоставляет минимальный программный поток развёртывания.
-
Создайте новый проект:
mkdir my-plasma-ethers-project
cd my-plasma-ethers-project
npm init -y
npm install ethers dotenv solc
-
Создайте файл
.env в корне проекта:
PRIVATE_KEY=<your_private_key_here>
RPC_URL=https://testnet-rpc.plasma.to
ETHERSCAN_API_KEY=your_api_key_for_verification
-
Создайте
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!');
-
Сохраните контракт как
SimpleStorage.sol, затем скомпилируйте:
Должен получиться вывод:
Contract compiled successfully!
-
Создайте
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);
});
-
Запустите скрипт развёртывания:
Вывод должен быть примерно таким:
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, это обычно означает:
-
Слишком старая версия Node.js: обновитесь до Node.js 20+, как указано в предварительных требованиях
-
Несоответствие версий зависимостей: если обновить 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
});