前置条件
- Node.js 16+
- 熟悉 Ethereum 开发
- 拥有测试网代币的钱包(参见测试网链上详情)
合约准备
本指南将使用以下示例合约:// 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 是一个基于 Rust 的快速 Ethereum 开发工具包。-
安装 Foundry:
你可能需要 source 你的
curl -L https://foundry.paradigm.xyz | bash.bashrc或.zshrc文件 -
设置新的 Foundry 项目:
foundryup forge init my-plasma-project cd my-plasma-project -
使用 Plasma 测试网设置更新
foundry.toml:[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 -
加载环境变量:
source .env -
将合约代码保存为
src/SimpleStorage.sol,然后部署:Foundry 会输出部署交易哈希和合约地址:forge create src/SimpleStorage.sol:SimpleStorage \ --rpc-url $RPC_URL \ --private-key $PRIVATE_KEY \ --constructor-args "Hello, Plasma!"[⠊] 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", [...] -
使用 Foundry 的 cast 工具测试已部署的合约。先读取已存储的数据:
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_URLblockHash 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 开发
- 拥有测试网代币的钱包(参见测试网链上详情)
Hardhat 不再支持 Node.js 18 及以下版本,并可能导致 ES module 兼容性问题。
-
验证 Node.js 版本:
如果使用的是 Node.js 18 或更低版本,请升级至 Node.js 20+:
node --version# 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在提示时选择 Create a JavaScript project。如果遇到兼容性问题,请查阅 Hardhat 文档了解最新支持的版本。 -
更新
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 目录和部署脚本:
mkdir 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,然后编译:输出如下:node compile.jsContract 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); }); -
运行部署脚本:
输出类似如下:
node deploy.jsDeploying 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 Module 兼容性问题
如果遇到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
资金不足
Error: insufficient funds for intrinsic transaction cost
网络配置错误
Error: network with chainId "1" doesn't match the configured chainId "9746"
Gas 估算失败
Error: cannot estimate gas
// In your deploy script
const simpleStorage = await SimpleStorage.deploy("Hello, Plasma from Hardhat!", {
gasLimit: 500000
});