跳到主要内容

Deploying Upgradeable Contracts on Conflux eSpace using Foundry

This tutorial will guide you through the process of deploying and upgrading smart contracts on Conflux eSpace using Foundry.

1. 项目设置​

First, ensure you have Foundry installed. If not, follow the official Foundry installation guide.

Create a new Foundry project:

forge init upgradeable-contract-foundry-demo
cd upgradeable-contract-foundry-demo

2. Configure Foundry​

Update the foundry.toml file to include the Conflux eSpace testnet:

[profile.default]
src = "src"
out = "out"
libs = ["lib"]

[rpc_endpoints]
espaceTestnet = "https://evmtestnet.confluxrpc.com"

3. 编写智能合约​

Create the following contracts in the src directory:

// src/SimpleUpgrade.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract SimpleUpgrade {
address public implementation;
address public admin;
string public words;

constructor(address _implementation) {
admin = msg.sender;
implementation = _implementation;
}

fallback() external payable {
(bool success, ) = implementation.delegatecall(msg.data);
require(success, "Delegatecall failed");
}

receive() external payable {}

function upgrade(address newImplementation) external {
require(msg.sender == admin, "Only admin can upgrade");
implementation = newImplementation;
}
}
// src/Logic1.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Logic1 {
address public implementation;
address public admin;
string public words;

function foo() public {
words = "old";
}
}
// src/Logic2.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract Logic2 {
address public implementation;
address public admin;
string public words;

function foo() public {
words = "new";
}
}

4. 部署脚本​

Create a deployment script in the script directory:

// script/Deploy.s.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "forge-std/Script.sol";
import "../src/SimpleUpgrade.sol";
import "../src/Logic1.sol";

contract DeployScript is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
vm.startBroadcast(deployerPrivateKey);

Logic1 logic1 = new Logic1();
SimpleUpgrade proxy = new SimpleUpgrade(address(logic1));

console.log("Logic1 deployed to:", address(logic1));
console.log("Proxy deployed to:", address(proxy));

vm.stopBroadcast();
}
}

5. 升级脚本​

Create an upgrade script:

// script/Upgrade.s.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "forge-std/Script.sol";
import "../src/SimpleUpgrade.sol";
import "../src/Logic2.sol";

contract UpgradeScript is Script {
function run() external {
uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY");
address proxyAddress = vm.envAddress("PROXY_ADDRESS");

vm.startBroadcast(deployerPrivateKey);

Logic2 logic2 = new Logic2();
SimpleUpgrade proxy = SimpleUpgrade(payable(proxyAddress));

console.log("Logic2 deployed to:", address(logic2));
console.log("Current implementation:", proxy.implementation());
console.log("New implementation address:", address(logic2));

proxy.upgrade(address(logic2));
console.log("Proxy upgraded to Logic2");

vm.stopBroadcast();
}
}

6. 升级前后的测试​

Create test scripts:

// script/TestBeforeUpgrade.s.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "forge-std/Script.sol";
import "../src/Logic1.sol";

contract TestBeforeUpgradeScript is Script {
function run() external {
address proxyAddress = vm.envAddress("PROXY_ADDRESS");
Logic1 proxy = Logic1(proxyAddress);

vm.startBroadcast();

proxy.foo();
console.log("Words after calling Logic1's foo():", proxy.words());
console.log("Current implementation address:", proxy.implementation());

vm.stopBroadcast();
}
}
// script/TestAfterUpgrade.s.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "forge-std/Script.sol";
import "../src/Logic2.sol";

contract TestAfterUpgradeScript is Script {
function run() external {
address proxyAddress = vm.envAddress("PROXY_ADDRESS");
Logic2 proxy = Logic2(proxyAddress);

vm.startBroadcast();

proxy.foo();
console.log("Words after calling Logic2's foo():", proxy.words());
console.log("Current implementation address:", proxy.implementation());

vm.stopBroadcast();
}
}

7. 部署和升级过程​

  1. Set up environment variables:

Create a .env file in the project root:

PRIVATE_KEY=your_private_key_here
PROXY_ADDRESS=
  1. Deploy the initial contract:
forge script script/Deploy.s.sol:DeployScript --rpc-url espaceTestnet --broadcast -g 200

Note: The -g flag sets the gas price multiplier (in percentage). Using -g 200 means the gas price will be 200% of the estimated price, which helps prevent "insufficient gas fee" errors during deployment.

预期输出:

Logic1 deployed to: 0x...(Logic1's address)
Proxy deployed to: 0x...(Proxy's address)
  1. Update the PROXY_ADDRESS in the .env file with the deployed proxy address.

  2. Run the pre-upgrade test:

forge script script/TestBeforeUpgrade.s.sol:TestBeforeUpgradeScript --rpc-url espaceTestnet 

预期输出:

Words after calling Logic1's foo(): old
Current implementation address: 0x...(Logic1's address)
  1. Upgrade the contract:
forge script script/Upgrade.s.sol:UpgradeScript --rpc-url espaceTestnet --broadcast -g 200
  1. Run the post-upgrade test:
forge script script/TestAfterUpgrade.s.sol:TestAfterUpgradeScript --rpc-url espaceTestnet

预期输出:

Words after calling Logic2's foo(): new
Current implementation address: 0x...(Logic2's address)

By following this process, you can successfully deploy, test, and upgrade smart contracts on Conflux eSpace using Foundry. This example demonstrates how to use a proxy contract to achieve upgradeability, allowing you to update contract logic without changing the contract address.

Remember to replace your_private_key_here with your actual private key and update the PROXY_ADDRESS after deployment. Always keep your private keys secure and never share them publicly.