Truffle 开发套件示例
Truffle 以太坊开发框架示例,包括智能合约、测试、部署、迁移、调试和开发工作流
💻 Truffle Hello World - 基础设置 javascript
🟢 simple
⭐⭐
完整的 Truffle 项目设置,包含智能合约、测试、迁移和以太坊开发配置
⏱️ 20 min
🏷️ truffle, ethereum, smart contracts, development
Prerequisites:
Node.js, Truffle Suite installed, Ganache or other Ethereum client
// Truffle Hello World - Complete Project Setup
// Initialize: truffle init
// ===== truffle-config.js =====
module.exports = {
// Networks configuration
networks: {
development: {
host: "127.0.0.1",
port: 7545,
network_id: "*",
},
ganache: {
host: "127.0.0.1",
port: 7545,
network_id: "*",
},
// Testnet configurations
goerli: {
provider: () => {
const wallet = require('ethereumjs-wallet').default;
const HDWalletProvider = require('@truffle/hdwallet-provider');
const mnemonic = process.env.MNEMONIC;
const infuraKey = process.env.INFURA_KEY;
return new HDWalletProvider({
mnemonic: {
phrase: mnemonic
},
providerOrUrl: `https://goerli.infura.io/v3/${infuraKey}`
});
},
network_id: 5,
gas: 4465030,
gasPrice: 10000000000,
},
sepolia: {
provider: () => {
const HDWalletProvider = require('@truffle/hdwallet-provider');
const privateKey = process.env.PRIVATE_KEY;
const infuraKey = process.env.INFURA_KEY;
return new HDWalletProvider(privateKey, `https://sepolia.infura.io/v3/${infuraKey}`);
},
network_id: 11155111,
gas: 4465030,
gasPrice: 10000000000,
},
},
// Solidity compiler configuration
compilers: {
solc: {
version: "0.8.19",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
},
// Mocha test configuration
mocha: {
timeout: 100000,
},
// Plugins configuration
plugins: [
'truffle-plugin-verify',
'truffle-contract-size',
'solidity-coverage',
],
// API keys for plugins
api_keys: {
etherscan: process.env.ETHERSCAN_API_KEY,
},
// Verify contracts after deployment
verify: {
etherscan: {
apiKey: process.env.ETHERSCAN_API_KEY,
},
},
};
// ===== contracts/Storage.sol =====
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
contract Storage {
uint256 private number;
string private message;
address public owner;
event DataStored(uint256 indexed newValue, string indexed newMessage, address indexed storedBy);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event MessageUpdated(string indexed oldMessage, string indexed newMessage);
constructor(uint256 _initialNumber, string memory _initialMessage) {
number = _initialNumber;
message = _initialMessage;
owner = msg.sender;
emit DataStored(_initialNumber, _initialMessage, msg.sender);
}
modifier onlyOwner() {
require(msg.sender == owner, "Storage: caller is not the owner");
_;
}
function store(uint256 _num, string memory _msg) public onlyOwner {
uint256 oldValue = number;
string memory oldMessage = message;
number = _num;
message = _msg;
emit DataStored(_num, _msg, msg.sender);
emit MessageUpdated(oldMessage, _msg);
}
function retrieve() public view returns (uint256, string memory) {
return (number, message);
}
function getNumber() public view returns (uint256) {
return number;
}
function getMessage() public view returns (string memory) {
return message;
}
function transferOwnership(address _newOwner) public onlyOwner {
require(_newOwner != address(0), "Storage: new owner is the zero address");
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}
}
// ===== test/storage.test.js =====
const { expect } = require('chai');
const { ethers } = require('hardhat');
describe('Storage Contract', function () {
let storage;
let owner;
let addr1;
let addr2;
beforeEach(async function () {
[owner, addr1, addr2] = await ethers.getSigners();
const Storage = await ethers.getContractFactory('Storage');
storage = await Storage.deploy(42, "Hello, Truffle!");
await storage.deployed();
});
describe('Deployment', function () {
it('Should set the right owner', async function () {
expect(await storage.owner()).to.equal(owner.address);
});
it('Should set the initial number', async function () {
const [number] = await storage.retrieve();
expect(number).to.equal(42);
});
it('Should set the initial message', async function () {
const [, message] = await storage.retrieve();
expect(message).to.equal("Hello, Truffle!");
});
it('Should emit DataStored event on deployment', async function () {
await expect(storage.deployTransaction)
.to.emit(storage, 'DataStored')
.withArgs(42, "Hello, Truffle!", owner.address);
});
});
describe('Store function', function () {
it('Should allow owner to store new values', async function () {
await storage.store(100, "New message");
const [number, message] = await storage.retrieve();
expect(number).to.equal(100);
expect(message).to.equal("New message");
});
it('Should emit DataStored event when storing', async function () {
await expect(storage.store(100, "New message"))
.to.emit(storage, 'DataStored')
.withArgs(100, "New message", owner.address);
});
it('Should emit MessageUpdated event when storing', async function () {
await expect(storage.store(100, "New message"))
.to.emit(storage, 'MessageUpdated')
.withArgs("Hello, Truffle!", "New message");
});
it('Should not allow non-owner to store values', async function () {
await expect(
storage.connect(addr1).store(100, "Unauthorized")
).to.be.revertedWith('Storage: caller is not the owner');
});
});
describe('Get functions', function () {
it('Should return correct number', async function () {
expect(await storage.getNumber()).to.equal(42);
});
it('Should return correct message', async function () {
expect(await storage.getMessage()).to.equal("Hello, Truffle!");
});
});
describe('Ownership transfer', function () {
it('Should allow owner to transfer ownership', async function () {
await storage.transferOwnership(addr1.address);
expect(await storage.owner()).to.equal(addr1.address);
});
it('Should emit OwnershipTransferred event', async function () {
await expect(storage.transferOwnership(addr1.address))
.to.emit(storage, 'OwnershipTransferred')
.withArgs(owner.address, addr1.address);
});
it('Should not allow transfer to zero address', async function () {
await expect(
storage.transferOwnership(ethers.constants.AddressZero)
).to.be.revertedWith('Storage: new owner is the zero address');
});
it('Should allow new owner to store values', async function () {
await storage.transferOwnership(addr1.address);
await storage.connect(addr1).store(200, "New owner message");
const [number, message] = await storage.retrieve();
expect(number).to.equal(200);
expect(message).to.equal("New owner message");
});
it('Should not allow old owner to store after transfer', async function () {
await storage.transferOwnership(addr1.address);
await expect(
storage.store(300, "Old owner message")
).to.be.revertedWith('Storage: caller is not the owner');
});
});
describe('Edge cases', function () {
it('Should handle zero values', async function () {
await storage.store(0, "");
const [number, message] = await storage.retrieve();
expect(number).to.equal(0);
expect(message).to.equal("");
});
it('Should handle large values', async function () {
const largeNumber = ethers.constants.MaxUint256;
await storage.store(largeNumber, "Maximum value");
const [number] = await storage.retrieve();
expect(number).to.equal(largeNumber);
});
});
});
// ===== migrations/1_initial_migration.js =====
const Migrations = artifacts.require("Migrations");
module.exports = function (deployer) {
deployer.deploy(Migrations);
};
// ===== migrations/2_deploy_storage.js =====
const Storage = artifacts.require("Storage");
module.exports = function (deployer) {
// Deploy with initial values
deployer.deploy(Storage, 42, "Hello, Truffle!");
};
// ===== scripts/deploy.js =====
const Storage = artifacts.require("Storage");
async function main() {
console.log("Starting deployment...");
// Get deployer account
const accounts = await web3.eth.getAccounts();
const deployer = accounts[0];
console.log("Deploying contracts with account:", deployer);
// Get account balance
const balance = await web3.eth.getBalance(deployer);
console.log("Account balance:", web3.utils.fromWei(balance, "ether"), "ETH");
// Deploy Storage contract
console.log("Deploying Storage contract...");
const storage = await Storage.new(100, "Initial deployment message", {
from: deployer,
gas: 2000000,
gasPrice: web3.utils.toWei("20", "gwei"),
});
console.log("Storage contract deployed to:", storage.address);
// Verify deployment
const [number, message] = await storage.retrieve();
console.log("Initial values:");
console.log(" Number:", number.toString());
console.log(" Message:", message);
console.log("Deployment completed successfully!");
return storage;
}
// For use with truffle exec
if (typeof module !== 'undefined' && module.exports) {
module.exports = function(callback) {
main().then(() => callback()).catch(err => callback(err));
};
}
// ===== scripts/interact.js =====
const Storage = artifacts.require("Storage");
async function main() {
console.log("Contract Interaction Example");
console.log("==========================");
// Get deployed contract
const storage = await Storage.deployed();
console.log("Storage contract address:", storage.address);
// Get current values
const [number, message] = await storage.retrieve();
console.log("\nCurrent values:");
console.log(" Number:", number.toString());
console.log(" Message:", message);
// Store new values
console.log("\nStoring new values...");
const tx = await storage.store(200, "Updated via script");
console.log("Transaction hash:", tx.tx);
console.log("Gas used:", tx.receipt.gasUsed.toString());
// Verify updated values
const [newNumber, newMessage] = await storage.retrieve();
console.log("\nUpdated values:");
console.log(" Number:", newNumber.toString());
console.log(" Message:", newMessage);
// Get contract owner
const owner = await storage.owner();
console.log("\nContract owner:", owner);
console.log("\nInteraction completed!");
}
// For use with truffle exec
if (typeof module !== 'undefined' && module.exports) {
module.exports = function(callback) {
main().then(() => callback()).catch(err => callback(err));
};
}
// ===== package.json =====
{
"name": "truffle-hello-world",
"version": "1.0.0",
"description": "Truffle Hello World example project",
"main": "truffle-config.js",
"scripts": {
"compile": "truffle compile",
"migrate": "truffle migrate",
"test": "truffle test",
"dev": "truffle migrate --network development",
"goerli": "truffle migrate --network goerli",
"sepolia": "truffle migrate --network sepolia",
"verify": "truffle run verify Storage --network sepolia",
"console": "truffle console",
"coverage": "truffle run coverage"
},
"dependencies": {
"@truffle/hdwallet-provider": "^2.1.15",
"@openzeppelin/contracts": "^4.9.0",
"ethereumjs-wallet": "^1.0.2"
},
"devDependencies": {
"chai": "^4.3.8",
"solidity-coverage": "^0.8.4",
"truffle-plugin-verify": "^0.6.4",
"truffle-contract-size": "^2.0.1"
}
}
💻 Truffle 高级测试 javascript
🟡 intermediate
⭐⭐⭐⭐
全面的测试策略,包括单元测试、集成测试、Gas 优化测试和自定义测试工具
⏱️ 35 min
🏷️ truffle, testing, smart contracts, gas
Prerequisites:
Truffle basics, Chai assertion library, Understanding of smart contract testing
// Truffle Advanced Testing Suite
// Comprehensive testing patterns for smart contracts
const { expect } = require('chai');
const { ethers } = require('hardhat');
const { time } = require('@openzeppelin/test-helpers');
// ===== Test Utilities =====
class ContractTestHelper {
constructor(contract) {
this.contract = contract;
}
// Revert expectation helper
async expectRevert(promise, expectedError) {
await expect(promise).to.be.revertedWith(expectedError);
}
// Event emission helper
async expectEvent(promise, eventName, ...args) {
const tx = await promise;
await expect(tx).to.emit(this.contract, eventName);
if (args.length > 0) {
await expect(tx).to.emit(this.contract, eventName).withArgs(...args);
}
return tx;
}
// Balance change helper
async expectBalanceChange(account, change) {
const before = await ethers.provider.getBalance(account);
const result = await this.contract.deployTransaction;
const receipt = await result.wait();
const after = await ethers.provider.getBalance(account);
const actualChange = after.sub(before);
expect(actualChange).to.equal(change);
}
// Gas usage helper
async measureGas(transactionFunction) {
const tx = await transactionFunction();
const receipt = await tx.wait();
return receipt.gasUsed.toString();
}
// Time helper for time-dependent contracts
async increaseTime(seconds) {
await ethers.provider.send('evm_increaseTime', [seconds]);
await ethers.provider.send('evm_mine');
}
// Mine blocks helper
async mineBlocks(count) {
for (let i = 0; i < count; i++) {
await ethers.provider.send('evm_mine');
}
}
}
// ===== test/advanced/Storage.advanced.test.js =====
describe('Storage Contract - Advanced Testing', function () {
let storage;
let owner, addr1, addr2, addrs;
let helper;
beforeEach(async function () {
[owner, addr1, addr2, ...addrs] = await ethers.getSigners();
const Storage = await ethers.getContractFactory('Storage');
storage = await Storage.deploy(42, "Initial message");
await storage.deployed();
helper = new ContractTestHelper(storage);
});
describe('State Testing', function () {
it('Should maintain consistent state across operations', async function () {
// Initial state
const initialNumber = await storage.getNumber();
const initialMessage = await storage.getMessage();
expect(initialNumber).to.equal(42);
expect(initialMessage).to.equal("Initial message");
// State change
await storage.store(100, "Updated message");
// Verify new state
const newNumber = await storage.getNumber();
const newMessage = await storage.getMessage();
expect(newNumber).to.equal(100);
expect(newMessage).to.equal("Updated message");
// State should persist
const [number, message] = await storage.retrieve();
expect(number).to.equal(100);
expect(message).to.equal("Updated message");
});
it('Should handle boundary values correctly', async function () {
// Test with zero
await storage.store(0, "");
let [number, message] = await storage.retrieve();
expect(number).to.equal(0);
expect(message).to.equal("");
// Test with maximum uint256
const maxUint256 = ethers.constants.MaxUint256;
await storage.store(maxUint256, "Max value");
[number, message] = await storage.retrieve();
expect(number).to.equal(maxUint256);
expect(message).to.equal("Max value");
});
});
describe('Access Control Testing', function () {
it('Should enforce ownership restrictions', async function () {
// Owner can call restricted functions
await expect(storage.store(1, "Owner test")).to.not.be.reverted;
// Non-owners cannot call restricted functions
await expect(
storage.connect(addr1).store(1, "Non-owner test")
).to.be.revertedWith('Storage: caller is not the owner');
// After ownership transfer, new owner can call
await storage.transferOwnership(addr1.address);
await expect(
storage.connect(addr1).store(2, "New owner test")
).to.not.be.reverted;
// Old owner cannot call after transfer
await expect(
storage.store(3, "Old owner test")
).to.be.revertedWith('Storage: caller is not the owner');
});
});
describe('Event Testing', function () {
it('Should emit correct events with proper parameters', async function () {
// Test DataStored event
await expect(storage.store(100, "Test message"))
.to.emit(storage, 'DataStored')
.withArgs(100, "Test message", owner.address);
// Test MessageUpdated event
await expect(storage.store(200, "New test"))
.to.emit(storage, 'MessageUpdated')
.withArgs("Test message", "New test");
// Test OwnershipTransferred event
await expect(storage.transferOwnership(addr1.address))
.to.emit(storage, 'OwnershipTransferred')
.withArgs(owner.address, addr1.address);
});
});
describe('Gas Optimization Testing', function () {
it('Should measure gas consumption for different operations', async function () {
// Measure deployment gas
const deployGas = await helper.measureGas(async () => {
const Storage = await ethers.getContractFactory('Storage');
return await Storage.deploy(42, "Test");
});
console.log('Deployment gas:', deployGas);
// Measure store operation gas
const storeGas = await helper.measureGas(async () => {
return await storage.store(100, "Gas test message");
});
console.log('Store operation gas:', storeGas);
// Measure retrieve operation gas
const retrieveGas = await helper.measureGas(async () => {
return await storage.retrieve();
});
console.log('Retrieve operation gas:', retrieveGas);
// Measure transfer ownership gas
const transferGas = await helper.measureGas(async () => {
return await storage.transferOwnership(addr1.address);
});
console.log('Transfer ownership gas:', transferGas);
// Assertions for gas efficiency
expect(parseInt(storeGas)).to.be.lessThan(100000);
expect(parseInt(retrieveGas)).to.be.lessThan(50000);
});
});
describe('Integration Testing', function () {
it('Should work correctly with multiple accounts', async function () {
// Deploy with one account
const deployer = owner;
expect(await storage.owner()).to.equal(deployer.address);
// Transfer to another account
await storage.transferOwnership(addr1.address);
expect(await storage.owner()).to.equal(addr1.address);
// New owner performs operations
await storage.connect(addr1).store(300, "Multi-account test");
const [number, message] = await storage.retrieve();
expect(number).to.equal(300);
expect(message).to.equal("Multi-account test");
// Original account should no longer have access
await expect(
storage.store(400, "Should fail")
).to.be.revertedWith('Storage: caller is not the owner');
});
});
describe('Error Handling Testing', function () {
it('Should handle error conditions gracefully', async function () {
// Test unauthorized access
await expect(
storage.connect(addr1).store(1, "Unauthorized")
).to.be.revertedWith('Storage: caller is not the owner');
// Test invalid ownership transfer
await expect(
storage.transferOwnership(ethers.constants.AddressZero)
).to.be.revertedWith('Storage: new owner is the zero address');
});
});
describe('Fuzz Testing', function () {
it('Should handle random inputs correctly', async function () {
const testCases = [
{ number: 0, message: "" },
{ number: 1, message: "1" },
{ number: ethers.constants.MaxUint256, message: "A".repeat(1000) },
{ number: 12345, message: "Random message 🚀" },
{ number: 0, message: "Zero number test" },
];
for (const testCase of testCases) {
await storage.store(testCase.number, testCase.message);
const [number, message] = await storage.retrieve();
expect(number).to.equal(testCase.number);
expect(message).to.equal(testCase.message);
}
});
});
});
// ===== test/advanced/GasProfiler.js =====
class GasProfiler {
constructor() {
this.measurements = [];
}
async measureFunction(contract, functionName, args = [], description = '') {
const tx = await contract[functionName](...args);
const receipt = await tx.wait();
const measurement = {
description: description || functionName,
functionName,
gasUsed: receipt.gasUsed.toString(),
transactionHash: tx.hash,
timestamp: new Date().toISOString(),
};
this.measurements.push(measurement);
console.log(`⛽ ${measurement.description}: ${measurement.gasUsed} gas`);
return measurement;
}
getMeasurements() {
return this.measurements;
}
exportMeasurements() {
return {
timestamp: new Date().toISOString(),
measurements: this.measurements,
totalGasUsed: this.measurements.reduce((sum, m) => sum + parseInt(m.gasUsed), 0),
averageGasUsed: Math.round(
this.measurements.reduce((sum, m) => sum + parseInt(m.gasUsed), 0) /
this.measurements.length
),
};
}
generateReport() {
const report = this.exportMeasurements();
console.log('\n=== Gas Profiling Report ===');
console.log(`Total measurements: ${this.measurements.length}`);
console.log(`Total gas used: ${report.totalGasUsed.toLocaleString()}`);
console.log(`Average gas per operation: ${report.averageGasUsed.toLocaleString()}`);
console.log('\nDetailed measurements:');
this.measurements.forEach((m, i) => {
console.log(`${i + 1}. ${m.description}: ${parseInt(m.gasUsed).toLocaleString()} gas`);
});
return report;
}
}
// Usage in tests
describe('Gas Profiling', function () {
let storage, profiler;
beforeEach(async function () {
const Storage = await ethers.getContractFactory('Storage');
storage = await Storage.deploy(42, "Test");
await storage.deployed();
profiler = new GasProfiler();
});
it('Should profile gas usage', async function () {
await profiler.measureFunction(storage, 'store', [100, "Test 1"], "Store operation 1");
await profiler.measureFunction(storage, 'retrieve', [], "Retrieve operation");
await profiler.measureFunction(storage, 'store', [200, "Test 2"], "Store operation 2");
await profiler.measureFunction(storage, 'transferOwnership', [addr1.address], "Transfer ownership");
const report = profiler.generateReport();
// Assertions for gas efficiency
expect(report.averageGasUsed).to.be.lessThan(100000);
});
});
// ===== test/hooks/beforeEach.js =====
// Global test setup and teardown
const { expect } = require('chai');
// Test timeout helper
const testTimeout = (ms) => {
return new Promise(resolve => setTimeout(resolve, ms));
};
// Clean revert helper
const cleanRevert = async (promise) => {
try {
await promise;
throw new Error('Expected revert but transaction succeeded');
} catch (error) {
expect(error.message).to.include('revert');
}
};
// Snapshot helper for EVM state
const takeSnapshot = async () => {
return await ethers.provider.send('evm_snapshot', []);
};
const revertToSnapshot = async (snapshotId) => {
await ethers.provider.send('evm_revert', [snapshotId]);
};
// Global setup
before(async function () {
this.timeout(60000); // Increase timeout for network tests
// Log test environment
const network = await ethers.provider.getNetwork();
console.log(`Testing on network: ${network.name} (Chain ID: ${network.chainId})`);
// Get accounts
const accounts = await ethers.provider.listAccounts();
console.log(`Testing with ${accounts.length} accounts`);
});
// Global teardown
after(async function () {
console.log('All tests completed');
});
💻 Truffle 部署脚本和策略 javascript
🟡 intermediate
⭐⭐⭐⭐
生产就绪的部署脚本,包括验证、Gas 优化、可升级合约和多网络部署
⏱️ 40 min
🏷️ truffle, deployment, production, optimization
Prerequisites:
Truffle experience, Understanding of contract deployment, Gas optimization knowledge
// Truffle Deployment Scripts and Strategies
// Production-ready deployment automation
// ===== scripts/deployWithVerification.js =====
const Storage = artifacts.require("Storage");
const { expect } = require('chai');
async function main() {
console.log("🚀 Starting Deployment with Verification");
console.log("=====================================");
// Get network information
const network = await web3.eth.net.getId();
console.log(`Deploying to network: ${network}`);
// Get deployer account
const accounts = await web3.eth.getAccounts();
const deployer = accounts[0];
console.log(`Deployer address: ${deployer}`);
// Check account balance
const balance = await web3.eth.getBalance(deployer);
const balanceEth = web3.utils.fromWei(balance, 'ether');
console.log(`Account balance: ${balanceEth} ETH`);
if (parseFloat(balanceEth) < 0.1) {
console.warn("⚠️ Warning: Low account balance for deployment");
}
// Gas price estimation
const gasPrice = await web3.eth.getGasPrice();
const gasPriceGwei = web3.utils.fromWei(gasPrice, 'gwei');
console.log(`Current gas price: ${gasPriceGwei} gwei`);
try {
// Deploy contract
console.log("\n📦 Deploying Storage contract...");
const estimatedGas = await Storage.deploy.estimateGas(42, "Hello, Production!");
console.log(`Estimated gas: ${estimatedGas}`);
const storage = await Storage.new(42, "Hello, Production!", {
from: deployer,
gas: Math.floor(estimatedGas * 1.2), // 20% buffer
gasPrice: gasPrice,
});
console.log(`✅ Storage contract deployed to: ${storage.address}`);
// Wait for a few confirmations
console.log("⏳ Waiting for confirmations...");
await new Promise(resolve => setTimeout(resolve, 3000));
// Verify deployment
const [number, message] = await storage.retrieve();
console.log(`📋 Contract initialized with:`);
console.log(` Number: ${number}`);
console.log(` Message: ${message}`);
// Contract verification (if supported)
if (network !== 1337 && network !== 31337) { // Not development networks
console.log("\n🔍 Verifying contract on Etherscan...");
try {
await run('verify:verify', {
address: storage.address,
constructorArguments: [42, "Hello, Production!"],
});
console.log("✅ Contract verified on Etherscan");
} catch (error) {
console.log("❌ Contract verification failed:", error.message);
}
}
// Save deployment info
const deploymentInfo = {
network: network,
contract: "Storage",
address: storage.address,
deployer: deployer,
gasUsed: storage.deployTransaction.gasUsed,
gasPrice: gasPrice,
transactionHash: storage.deployTransaction.transactionHash,
blockNumber: storage.deployTransaction.blockNumber,
timestamp: new Date().toISOString(),
};
console.log("\n📄 Deployment Summary:");
console.log(JSON.stringify(deploymentInfo, null, 2));
return deploymentInfo;
} catch (error) {
console.error("❌ Deployment failed:", error);
throw error;
}
}
// Export for truffle exec
module.exports = function(callback) {
main()
.then(() => callback())
.catch(err => callback(err));
};
// ===== scripts/upgradeableDeployment.js =====
// OpenZeppelin upgradeable contracts deployment
const { deployProxy, upgradeProxy } = require('@openzeppelin/truffle-upgrades');
const StorageV1 = artifacts.require('StorageV1');
const StorageV2 = artifacts.require('StorageV2');
async function deployV1() {
console.log("🚀 Deploying Storage V1 (Upgradeable)");
const storage = await deployProxy(StorageV1, [42, "Hello, V1!"], {
initializer: 'initialize',
});
console.log(`Storage V1 deployed to: ${storage.address}`);
// Test initial functionality
const [number, message] = await storage.retrieve();
console.log(`Initial values - Number: ${number}, Message: ${message}`);
return storage;
}
async function upgradeToV2() {
console.log("🔄 Upgrading to Storage V2");
const existing = await StorageV1.deployed();
const upgraded = await upgradeProxy(existing.address, StorageV2);
console.log(`Storage upgraded to V2 at: ${upgraded.address}`);
// Test new functionality
await upgraded.setAdditionalData("Additional V2 feature");
const additionalData = await upgraded.getAdditionalData();
console.log(`Additional data: ${additionalData}`);
return upgraded;
}
// ===== scripts/multiNetworkDeployment.js =====
const Storage = artifacts.require("Storage");
// Network configurations
const NETWORKS = {
development: {
name: "Development",
gasLimit: 8000000,
gasPrice: 20000000000, // 20 gwei
},
goerli: {
name: "Goerli Testnet",
gasLimit: 6000000,
gasPrice: 20000000000, // 20 gwei
confirmations: 2,
},
sepolia: {
name: "Sepolia Testnet",
gasLimit: 6000000,
gasPrice: 20000000000, // 20 gwei
confirmations: 2,
},
mainnet: {
name: "Ethereum Mainnet",
gasLimit: 8000000,
gasPrice: 30000000000, // 30 gwei
confirmations: 5,
},
};
async function deployToNetwork(networkName) {
const networkConfig = NETWORKS[networkName];
if (!networkConfig) {
throw new Error(`Network ${networkName} not configured`);
}
console.log(`🌐 Deploying to ${networkConfig.name}`);
const network = await web3.eth.net.getId();
const accounts = await web3.eth.getAccounts();
const deployer = accounts[0];
console.log(`Deployer: ${deployer}`);
const balance = await web3.eth.getBalance(deployer);
console.log(`Balance: ${web3.utils.fromWei(balance, 'ether')} ETH`);
// Get current gas price for mainnet
let gasPrice = networkConfig.gasPrice;
if (networkName === 'mainnet') {
gasPrice = await web3.eth.getGasPrice();
console.log(`Current gas price: ${web3.utils.fromWei(gasPrice, 'gwei')} gwei`);
}
try {
const storage = await Storage.new(42, "Multi-network deployment", {
from: deployer,
gas: networkConfig.gasLimit,
gasPrice: gasPrice,
});
console.log(`✅ Deployed to: ${storage.address}`);
// Wait for confirmations on testnets/mainnet
if (networkConfig.confirmations > 0) {
console.log(`⏳ Waiting for ${networkConfig.confirmations} confirmations...`);
for (let i = 0; i < networkConfig.confirmations; i++) {
await new Promise(resolve => setTimeout(resolve, 15000)); // 15 seconds per block
console.log(`Confirmation ${i + 1}/${networkConfig.confirmations}`);
}
}
// Verify deployment
const [number, message] = await storage.retrieve();
console.log(`✅ Verification successful - Number: ${number}, Message: ${message}`);
return {
network: networkName,
address: storage.address,
transactionHash: storage.deployTransaction.transactionHash,
};
} catch (error) {
console.error(`❌ Deployment to ${networkName} failed:`, error);
throw error;
}
}
async function deployToAllNetworks() {
const deployments = {};
const currentNetwork = await web3.eth.net.getId();
for (const networkName of Object.keys(NETWORKS)) {
try {
// Skip if we're not on the right network
if (currentNetwork !== 1337 && networkName === 'development') continue;
const deployment = await deployToNetwork(networkName);
deployments[networkName] = deployment;
console.log(`✅ ${networkName}: ${deployment.address}`);
} catch (error) {
console.error(`❌ ${networkName}: Deployment failed`);
deployments[networkName] = { error: error.message };
}
}
console.log("\n📊 Deployment Summary:");
console.log(JSON.stringify(deployments, null, 2));
return deployments;
}
// ===== scripts/gasOptimization.js =====
const Storage = artifacts.require("Storage");
class GasOptimizer {
constructor() {
this.measurements = [];
}
async optimizeGasPrice() {
const currentGasPrice = await web3.eth.getGasPrice();
const recommendedGasPrice = Math.floor(currentGasPrice * 0.9); // 10% less
console.log(`Current gas price: ${web3.utils.fromWei(currentGasPrice, 'gwei')} gwei`);
console.log(`Recommended: ${web3.utils.fromWei(recommendedGasPrice, 'gwei')} gwei`);
return recommendedGasPrice;
}
async estimateDeploymentGas(contractName, constructorArgs) {
const Contract = artifacts.require(contractName);
try {
const estimatedGas = await Contract.deploy.estimateGas(...constructorArgs);
const bufferedGas = Math.floor(estimatedGas * 1.1); // 10% buffer
console.log(`Estimated gas for ${contractName}: ${estimatedGas}`);
console.log(`With 10% buffer: ${bufferedGas}`);
return { estimated: estimatedGas, buffered: bufferedGas };
} catch (error) {
console.error(`Gas estimation failed for ${contractName}:`, error);
return null;
}
}
async deployWithGasOptimization(contractName, constructorArgs = [], options = {}) {
console.log(`⛡️ Optimizing deployment for ${contractName}`);
// Get optimized gas price
const optimizedGasPrice = await this.optimizeGasPrice();
// Estimate gas usage
const gasEstimate = await this.estimateDeploymentGas(contractName, constructorArgs);
if (!gasEstimate) {
throw new Error(`Failed to estimate gas for ${contractName}`);
}
const Contract = artifacts.require(contractName);
// Deploy with optimized parameters
const deployOptions = {
gas: gasEstimate.buffered,
gasPrice: optimizedGasPrice,
...options,
};
console.log(`📦 Deploying ${contractName} with optimized gas settings...`);
const startTime = Date.now();
const contract = await Contract.new(...constructorArgs, deployOptions);
const endTime = Date.now();
const deploymentTime = endTime - startTime;
const deploymentCost = web3.utils.fromWei(
(gasEstimate.buffered * optimizedGasPrice).toString(),
'ether'
);
console.log(`✅ ${contractName} deployed to: ${contract.address}`);
console.log(`⏱️ Deployment time: ${deploymentTime}ms`);
console.log(`💰 Deployment cost: ${deploymentCost} ETH`);
// Store measurement
this.measurements.push({
contract: contractName,
address: contract.address,
gasUsed: contract.deployTransaction.gasUsed.toString(),
gasPrice: optimizedGasPrice.toString(),
cost: deploymentCost,
time: deploymentTime,
timestamp: new Date().toISOString(),
});
return contract;
}
getOptimizationReport() {
const totalCost = this.measurements.reduce((sum, m) => sum + parseFloat(m.cost), 0);
const avgGasPrice = this.measurements.reduce((sum, m) =>
sum + parseInt(m.gasPrice), 0) / this.measurements.length;
return {
deployments: this.measurements.length,
totalCost: totalCost.toFixed(6),
averageGasPrice: web3.utils.fromWei(avgGasPrice.toFixed(0), 'gwei'),
deployments: this.measurements,
};
}
}
// Usage example
async function deployWithOptimization() {
const optimizer = new GasOptimizer();
// Deploy multiple contracts with optimization
await optimizer.deployWithGasOptimization('Storage', [100, "Gas optimized"]);
const report = optimizer.getOptimizationReport();
console.log("\n📊 Gas Optimization Report:");
console.log(JSON.stringify(report, null, 2));
}
// Export for truffle exec
module.exports = function(callback) {
deployWithOptimization()
.then(() => callback())
.catch(err => callback(err));
};
// ===== scripts/deploymentManager.js =====
class DeploymentManager {
constructor() {
this.deployments = new Map();
this.dependencies = new Map();
}
addDependency(contractName, dependencyNames = []) {
this.dependencies.set(contractName, dependencyNames);
}
async deploySequential() {
console.log("🔗 Starting sequential deployment");
const deployedContracts = {};
// Simple dependency resolution (would need more complex algorithm for real use)
for (const [contractName, dependencies] of this.dependencies) {
console.log(`\n📦 Deploying ${contractName}`);
// Check if dependencies are deployed
for (const dep of dependencies) {
if (!deployedContracts[dep]) {
throw new Error(`Dependency ${dep} not deployed for ${contractName}`);
}
}
// Deploy contract (simplified example)
const Contract = artifacts.require(contractName);
const contract = await Contract.new();
deployedContracts[contractName] = contract;
this.deployments.set(contractName, {
address: contract.address,
transactionHash: contract.deployTransaction.transactionHash,
});
console.log(`✅ ${contractName} deployed to: ${contract.address}`);
}
return deployedContracts;
}
async verifyDeployments() {
console.log("\n🔍 Verifying all deployments");
for (const [contractName, deployment] of this.deployments) {
try {
const Contract = artifacts.require(contractName);
const contract = await Contract.at(deployment.address);
// Basic verification - would need contract-specific tests
console.log(`✅ ${contractName} at ${deployment.address} is verified`);
} catch (error) {
console.error(`❌ ${contractName} verification failed:`, error.message);
}
}
}
exportDeploymentManifest() {
const manifest = {
version: "1.0.0",
timestamp: new Date().toISOString(),
network: await web3.eth.net.getId(),
deployments: Object.fromEntries(this.deployments),
dependencies: Object.fromEntries(this.dependencies),
};
console.log("\n📄 Deployment Manifest:");
console.log(JSON.stringify(manifest, null, 2));
return manifest;
}
}
// Usage example
async function manageDeployment() {
const manager = new DeploymentManager();
// Define deployment dependencies
manager.addDependency('Storage', []);
manager.addDependency('StorageManager', ['Storage']);
manager.addDependency('StorageFactory', ['Storage', 'StorageManager']);
// Execute deployment
await manager.deploySequential();
// Verify deployments
await manager.verifyDeployments();
// Export manifest
manager.exportDeploymentManifest();
}
// Export for truffle exec
module.exports = function(callback) {
manageDeployment()
.then(() => callback())
.catch(err => callback(err));
};