From 5d4596d38a79e06cfa0fdc727b32512dab1a2bda Mon Sep 17 00:00:00 2001 From: Jack Lee <280147597@qq.com> Date: Fri, 25 Oct 2024 16:12:46 +0800 Subject: [PATCH 1/7] add using transparent proxy with foundry --- .../transparent-proxy-foundry.md | 309 ++++++++++++++++++ 1 file changed, 309 insertions(+) create mode 100644 docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md diff --git a/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md b/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md new file mode 100644 index 0000000000..d5cce5070a --- /dev/null +++ b/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md @@ -0,0 +1,309 @@ +--- +displayed_sidebar: eSpaceSidebar +keywords: + - tutorial + - smart contracts + - upgradeable contracts + - transparent proxy + - Foundry + - eSpace +tags: [Tutorial, Upgradeable Contracts, Transparent Proxy] +--- + +# Deploying Upgradeable Contracts using Transparent Proxy with Foundry + +## Introduction + +This tutorial demonstrates how to deploy and upgrade smart contracts using the transparent proxy pattern with Foundry. We'll use the same Box contract example but implement it using Foundry's tools. + +## Project Setup + +1. Create a new Foundry project: + +```bash +forge init transparent-proxy-demo +cd transparent-proxy-demo +``` + +2. Install OpenZeppelin contracts: + +```bash +forge install OpenZeppelin/openzeppelin-contracts +``` + +3. Add the following to `remappings.txt`: + +``` +@openzeppelin/=lib/openzeppelin-contracts/ +``` + +4. Update `foundry.toml`: + +```toml +[profile.default] +src = "src" +out = "out" +libs = ["lib"] +solc = "0.8.24" +``` + +## Writing Smart Contracts + +1. Create the initial Box contract in `src/Box.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; + +contract Box is Initializable { + uint256 private _value; + + event ValueChanged(uint256 value); + + function initialize(uint256 initialValue) public initializer { + _value = initialValue; + emit ValueChanged(initialValue); + } + + function store(uint256 value) public { + _value = value; + emit ValueChanged(value); + } + + function retrieve() public view returns (uint256) { + return _value; + } +} +``` + +2. Create BoxV2 contract in `src/BoxV2.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; + +contract BoxV2 is Initializable { + uint256 private _value; + + event ValueChanged(uint256 value); + + function initialize(uint256 initialValue) public initializer { + _value = initialValue; + emit ValueChanged(initialValue); + } + + function store(uint256 value) public { + _value = value; + emit ValueChanged(value); + } + + function retrieve() public view returns (uint256) { + return _value; + } + + function increment() public { + _value = _value + 1; + emit ValueChanged(_value); + } +} +``` + +## Deployment Scripts + +1. Create the deployment script in `script/DeployBox.s.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "forge-std/Script.sol"; +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "../src/Box.sol"; + +contract DeployBox is Script { + function run() external { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + + vm.startBroadcast(deployerPrivateKey); + + // Deploy implementation + Box box = new Box(); + + // Deploy ProxyAdmin + ProxyAdmin admin = new ProxyAdmin(); + + // Encode initialization data + bytes memory data = abi.encodeWithSelector(Box.initialize.selector, 42); + + // Deploy proxy + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(box), + address(admin), + data + ); + + vm.stopBroadcast(); + + console.log("Box implementation deployed to:", address(box)); + console.log("ProxyAdmin deployed to:", address(admin)); + console.log("Proxy deployed to:", address(proxy)); + } +} +``` + +2. Create the upgrade script in `script/UpgradeBox.s.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "forge-std/Script.sol"; +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "../src/BoxV2.sol"; + +contract UpgradeBox is Script { + function run() external { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + address proxyAddress = vm.envAddress("PROXY_ADDRESS"); + address adminAddress = vm.envAddress("ADMIN_ADDRESS"); + + vm.startBroadcast(deployerPrivateKey); + + // Deploy new implementation + BoxV2 boxV2 = new BoxV2(); + + // Upgrade proxy + ProxyAdmin admin = ProxyAdmin(adminAddress); + admin.upgrade( + TransparentUpgradeableProxy(payable(proxyAddress)), + address(boxV2) + ); + + vm.stopBroadcast(); + + console.log("BoxV2 implementation deployed to:", address(boxV2)); + console.log("Proxy upgraded"); + } +} +``` + +## Testing Scripts + +Create a test file in `test/Box.t.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "forge-std/Test.sol"; +import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "../src/Box.sol"; +import "../src/BoxV2.sol"; + +contract BoxTest is Test { + Box box; + BoxV2 boxV2; + ProxyAdmin admin; + TransparentUpgradeableProxy proxy; + + function setUp() public { + // Deploy implementation + box = new Box(); + + // Deploy ProxyAdmin + admin = new ProxyAdmin(); + + // Encode initialization data + bytes memory data = abi.encodeWithSelector(Box.initialize.selector, 42); + + // Deploy proxy + proxy = new TransparentUpgradeableProxy( + address(box), + address(admin), + data + ); + } + + function testBoxV1() public { + Box proxiedBox = Box(address(proxy)); + assertEq(proxiedBox.retrieve(), 42); + + proxiedBox.store(100); + assertEq(proxiedBox.retrieve(), 100); + } + + function testUpgrade() public { + // Deploy new implementation + boxV2 = new BoxV2(); + + // Upgrade + admin.upgrade(proxy, address(boxV2)); + + BoxV2 proxiedBoxV2 = BoxV2(address(proxy)); + + // Test existing functionality + assertEq(proxiedBoxV2.retrieve(), 100); + + // Test new functionality + proxiedBoxV2.increment(); + assertEq(proxiedBoxV2.retrieve(), 101); + } +} +``` + +## Deployment and Upgrade Process + +1. Create a `.env` file: + +``` +PRIVATE_KEY=your_private_key_here +RPC_URL=https://evmtestnet.confluxrpc.com +``` + +2. Deploy the initial contract: + +```bash +forge script script/DeployBox.s.sol --rpc-url $RPC_URL --broadcast +``` + +3. Set the proxy and admin addresses in `.env`: + +``` +PROXY_ADDRESS=deployed_proxy_address +ADMIN_ADDRESS=deployed_admin_address +``` + +4. Upgrade the contract: + +```bash +forge script script/UpgradeBox.s.sol --rpc-url $RPC_URL --broadcast +``` + +5. Run the tests: + +```bash +forge test +``` + +## Verification + +To verify the contracts on the block explorer: + +```bash +forge verify-contract src/Box.sol:Box --chain-id 71 --verifier-url https://evmapi-testnet.confluxscan.io/api +forge verify-contract @openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy --chain-id 71 --verifier-url https://evmapi-testnet.confluxscan.io/api +``` + +This tutorial demonstrates how to use Foundry to deploy and upgrade transparent proxy contracts. The main differences from the Hardhat version are: +- Use of Solidity for deployment scripts instead of JavaScript +- Built-in testing framework with Solidity +- Different command-line interface and tooling +- More direct interaction with the proxy contracts + +The core concepts of transparent proxy and upgradeability remain the same regardless of the development framework used. \ No newline at end of file From d34cbe5e8a768356dc4da51c0b080a19dfcda3d9 Mon Sep 17 00:00:00 2001 From: Jack Lee <280147597@qq.com> Date: Wed, 30 Oct 2024 19:12:42 +0800 Subject: [PATCH 2/7] update content --- .../transparent-proxy-foundry.md | 217 +++++++++--------- 1 file changed, 105 insertions(+), 112 deletions(-) diff --git a/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md b/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md index d5cce5070a..f370c40e0b 100644 --- a/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md +++ b/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md @@ -14,27 +14,29 @@ tags: [Tutorial, Upgradeable Contracts, Transparent Proxy] ## Introduction -This tutorial demonstrates how to deploy and upgrade smart contracts using the transparent proxy pattern with Foundry. We'll use the same Box contract example but implement it using Foundry's tools. +This tutorial demonstrates how to deploy and upgrade smart contracts using the transparent proxy pattern with Foundry. The transparent proxy pattern allows you to upgrade your smart contracts while maintaining the same address and state. ## Project Setup 1. Create a new Foundry project: ```bash -forge init transparent-proxy-demo -cd transparent-proxy-demo +forge init transparent-proxy-foundry-demo +cd transparent-proxy-foundry-demo ``` 2. Install OpenZeppelin contracts: ```bash forge install OpenZeppelin/openzeppelin-contracts +forge install OpenZeppelin/openzeppelin-contracts-upgradeable ``` 3. Add the following to `remappings.txt`: ``` @openzeppelin/=lib/openzeppelin-contracts/ +@openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/ ``` 4. Update `foundry.toml`: @@ -45,6 +47,11 @@ src = "src" out = "out" libs = ["lib"] solc = "0.8.24" + +remappings = [ + "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", + "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/" +] ``` ## Writing Smart Contracts @@ -55,13 +62,18 @@ solc = "0.8.24" // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; -import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; contract Box is Initializable { uint256 private _value; event ValueChanged(uint256 value); + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + function initialize(uint256 initialValue) public initializer { _value = initialValue; emit ValueChanged(initialValue); @@ -84,16 +96,20 @@ contract Box is Initializable { // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; -import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; contract BoxV2 is Initializable { uint256 private _value; event ValueChanged(uint256 value); + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + function initialize(uint256 initialValue) public initializer { _value = initialValue; - emit ValueChanged(initialValue); } function store(uint256 value) public { @@ -105,6 +121,7 @@ contract BoxV2 is Initializable { return _value; } + // New added function function increment() public { _value = _value + 1; emit ValueChanged(_value); @@ -116,6 +133,7 @@ contract BoxV2 is Initializable { 1. Create the deployment script in `script/DeployBox.s.sol`: + ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; @@ -128,42 +146,43 @@ import "../src/Box.sol"; contract DeployBox is Script { function run() external { uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + address deployer = vm.addr(deployerPrivateKey); vm.startBroadcast(deployerPrivateKey); - // Deploy implementation + // Deploy implementation contract Box box = new Box(); - // Deploy ProxyAdmin - ProxyAdmin admin = new ProxyAdmin(); - // Encode initialization data bytes memory data = abi.encodeWithSelector(Box.initialize.selector, 42); - // Deploy proxy + // Deploy proxy contract TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( address(box), - address(admin), + deployer, data ); - vm.stopBroadcast(); + // Get actual ProxyAdmin address + address proxyAdminAddress = address(uint160(uint256(vm.load( + address(proxy), + bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1) + )))); - console.log("Box implementation deployed to:", address(box)); - console.log("ProxyAdmin deployed to:", address(admin)); - console.log("Proxy deployed to:", address(proxy)); - } -} + vm.stopBroadcast(); ``` + 2. Create the upgrade script in `script/UpgradeBox.s.sol`: + ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import "forge-std/Script.sol"; import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "../src/Box.sol"; import "../src/BoxV2.sol"; contract UpgradeBox is Script { @@ -172,138 +191,112 @@ contract UpgradeBox is Script { address proxyAddress = vm.envAddress("PROXY_ADDRESS"); address adminAddress = vm.envAddress("ADMIN_ADDRESS"); + // Test before upgrade + console.log("============ Before Upgrade ============"); + Box box = Box(proxyAddress); + uint256 valueBefore = box.retrieve(); + console.log("Current value:", valueBefore); + vm.startBroadcast(deployerPrivateKey); // Deploy new implementation BoxV2 boxV2 = new BoxV2(); - - // Upgrade proxy - ProxyAdmin admin = ProxyAdmin(adminAddress); - admin.upgrade( - TransparentUpgradeableProxy(payable(proxyAddress)), - address(boxV2) - ); - - vm.stopBroadcast(); - - console.log("BoxV2 implementation deployed to:", address(boxV2)); - console.log("Proxy upgraded"); - } -} -``` - -## Testing Scripts - -Create a test file in `test/Box.t.sol`: - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol"; -import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; -import "../src/Box.sol"; -import "../src/BoxV2.sol"; - -contract BoxTest is Test { - Box box; - BoxV2 boxV2; - ProxyAdmin admin; - TransparentUpgradeableProxy proxy; - - function setUp() public { - // Deploy implementation - box = new Box(); - - // Deploy ProxyAdmin - admin = new ProxyAdmin(); - - // Encode initialization data - bytes memory data = abi.encodeWithSelector(Box.initialize.selector, 42); - - // Deploy proxy - proxy = new TransparentUpgradeableProxy( - address(box), - address(admin), - data + console.log("\n============ Deploying New Implementation ============"); + console.log("New implementation:", address(boxV2)); + + // Upgrade using ProxyAdmin + ProxyAdmin proxyAdmin = ProxyAdmin(adminAddress); + proxyAdmin.upgradeAndCall( + ITransparentUpgradeableProxy(proxyAddress), + address(boxV2), + "" ); - } - - function testBoxV1() public { - Box proxiedBox = Box(address(proxy)); - assertEq(proxiedBox.retrieve(), 42); - proxiedBox.store(100); - assertEq(proxiedBox.retrieve(), 100); - } + vm.stopBroadcast(); - function testUpgrade() public { - // Deploy new implementation - boxV2 = new BoxV2(); + // Test after upgrade + console.log("\n============ After Upgrade ============"); + BoxV2 upgradedBox = BoxV2(proxyAddress); + uint256 valueAfter = upgradedBox.retrieve(); + console.log("Value after upgrade:", valueAfter); + console.log("Testing new increment function..."); - // Upgrade - admin.upgrade(proxy, address(boxV2)); + vm.startBroadcast(deployerPrivateKey); + upgradedBox.increment(); + vm.stopBroadcast(); - BoxV2 proxiedBoxV2 = BoxV2(address(proxy)); + uint256 valueAfterIncrement = upgradedBox.retrieve(); + console.log("Value after increment:", valueAfterIncrement); - // Test existing functionality - assertEq(proxiedBoxV2.retrieve(), 100); + // Verify upgrade results + require(valueAfter == valueBefore, "State verification failed: Value changed during upgrade"); + require(valueAfterIncrement == valueAfter + 1, "Function verification failed: Increment not working"); - // Test new functionality - proxiedBoxV2.increment(); - assertEq(proxiedBoxV2.retrieve(), 101); + console.log("\n============ Upgrade Successful ============"); + console.log("1. State preserved: Initial value maintained after upgrade"); + console.log("2. New function working: Increment successfully added"); } } ``` -## Deployment and Upgrade Process + +## Environment Setup 1. Create a `.env` file: -``` +```bash PRIVATE_KEY=your_private_key_here RPC_URL=https://evmtestnet.confluxrpc.com ``` -2. Deploy the initial contract: +## Deployment Process + +1. Deploy the initial implementation and proxy: ```bash -forge script script/DeployBox.s.sol --rpc-url $RPC_URL --broadcast +source .env +forge script script/DeployBox.s.sol --rpc-url $RPC_URL --broadcast ``` -3. Set the proxy and admin addresses in `.env`: - +Expected output: ``` -PROXY_ADDRESS=deployed_proxy_address -ADMIN_ADDRESS=deployed_admin_address +== Return == +Box implementation deployed to: +Proxy deployed to: +ProxyAdmin deployed to: ``` -4. Upgrade the contract: +2. After deployment, save the addresses in `.env`: ```bash -forge script script/UpgradeBox.s.sol --rpc-url $RPC_URL --broadcast +PROXY_ADDRESS= +ADMIN_ADDRESS= ``` -5. Run the tests: +3. Upgrade to BoxV2: ```bash -forge test +forge script script/UpgradeBox.s.sol --rpc-url $RPC_URL --broadcast ``` -## Verification +Expected output: +``` -To verify the contracts on the block explorer: +============ Before Upgrade ============ +Current value: 42 -```bash -forge verify-contract src/Box.sol:Box --chain-id 71 --verifier-url https://evmapi-testnet.confluxscan.io/api -forge verify-contract @openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol:TransparentUpgradeableProxy --chain-id 71 --verifier-url https://evmapi-testnet.confluxscan.io/api -``` +============ Deploying New Implementation ============ +New implementation: -This tutorial demonstrates how to use Foundry to deploy and upgrade transparent proxy contracts. The main differences from the Hardhat version are: -- Use of Solidity for deployment scripts instead of JavaScript -- Built-in testing framework with Solidity -- Different command-line interface and tooling -- More direct interaction with the proxy contracts +============ After Upgrade ============ +Value after upgrade: 42 +Testing new increment function... +Value after increment: 43 + +============ Upgrade Successful ============ +1. State preserved: Initial value maintained after upgrade +2. New function working: Increment successfully added + +``` -The core concepts of transparent proxy and upgradeability remain the same regardless of the development framework used. \ No newline at end of file +This tutorial provides a complete workflow for deploying and upgrading smart contracts using the transparent proxy pattern with Foundry, including all expected outputs at each step. \ No newline at end of file From 621ce95a439317fd169e661daac1c5cb2a3e3af2 Mon Sep 17 00:00:00 2001 From: Jack Lee <280147597@qq.com> Date: Wed, 30 Oct 2024 19:23:49 +0800 Subject: [PATCH 3/7] update content --- .../transparent-proxy-foundry.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md b/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md index f370c40e0b..27dd58f00c 100644 --- a/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md +++ b/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md @@ -7,7 +7,7 @@ keywords: - transparent proxy - Foundry - eSpace -tags: [Tutorial, Upgradeable Contracts, Transparent Proxy] +tags: [Tutorial, Upgradeable Contracts, Transparent Proxy, Foundry] --- # Deploying Upgradeable Contracts using Transparent Proxy with Foundry @@ -69,11 +69,6 @@ contract Box is Initializable { event ValueChanged(uint256 value); - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { - _disableInitializers(); - } - function initialize(uint256 initialValue) public initializer { _value = initialValue; emit ValueChanged(initialValue); @@ -103,11 +98,6 @@ contract BoxV2 is Initializable { event ValueChanged(uint256 value); - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { - _disableInitializers(); - } - function initialize(uint256 initialValue) public initializer { _value = initialValue; } @@ -299,4 +289,6 @@ Value after increment: 43 ``` -This tutorial provides a complete workflow for deploying and upgrading smart contracts using the transparent proxy pattern with Foundry, including all expected outputs at each step. \ No newline at end of file +By following these steps, you can deploy and upgrade smart contracts using transparent proxy on Conflux eSpace with Foundry. This pattern not only allows you to update contract logic without changing the contract address but also effectively addresses selector conflicts by separating management functions from user functions. + +The tutorial provides a complete workflow with all expected outputs at each step to help you successfully implement upgradeable contracts. \ No newline at end of file From 4faa913871694b18a9ba3c89bcf75b67542ba28d Mon Sep 17 00:00:00 2001 From: Jack Lee <280147597@qq.com> Date: Thu, 31 Oct 2024 00:12:55 +0800 Subject: [PATCH 4/7] add -g note --- .../upgradableContract/transparent-proxy-foundry.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md b/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md index 27dd58f00c..2ff2f2849e 100644 --- a/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md +++ b/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md @@ -245,8 +245,9 @@ RPC_URL=https://evmtestnet.confluxrpc.com ```bash source .env -forge script script/DeployBox.s.sol --rpc-url $RPC_URL --broadcast +forge script script/DeployBox.s.sol --rpc-url $RPC_URL --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. Expected output: ``` @@ -266,9 +267,10 @@ ADMIN_ADDRESS= 3. Upgrade to BoxV2: ```bash -forge script script/UpgradeBox.s.sol --rpc-url $RPC_URL --broadcast +forge script script/UpgradeBox.s.sol --rpc-url $RPC_URL --broadcast -g 200 ``` + Expected output: ``` From 2ae562e05a4b32adc984cf55b7c948ea74f5b38c Mon Sep 17 00:00:00 2001 From: Jack Lee <280147597@qq.com> Date: Fri, 1 Nov 2024 23:27:17 +0800 Subject: [PATCH 5/7] update content --- .../upgradableContract/uups-foundry.md | 365 ++++++++++++++++++ 1 file changed, 365 insertions(+) create mode 100644 docs/espace/tutorials/upgradableContract/uups-foundry.md diff --git a/docs/espace/tutorials/upgradableContract/uups-foundry.md b/docs/espace/tutorials/upgradableContract/uups-foundry.md new file mode 100644 index 0000000000..bd100d2bf0 --- /dev/null +++ b/docs/espace/tutorials/upgradableContract/uups-foundry.md @@ -0,0 +1,365 @@ +--- +displayed_sidebar: eSpaceSidebar +keywords: + - tutorial + - smart contracts + - upgradeable contracts + - UUPS + - Foundry + - eSpace +tags: [Tutorial, Upgradeable Contracts] +--- + +# Deploying Upgradeable Contracts using UUPS with Foundry + +### UUPS (Universal Upgradeable Proxy Standard) + +[Keep the same UUPS explanation section as in the original tutorial...] + +## Project Setup + +1. Create a new Foundry project: + +```bash +forge init uups-proxy-foundry-demo +cd uups-proxy-foundry-demo +``` + +2. Install OpenZeppelin contracts: + +```bash +forge install OpenZeppelin/openzeppelin-contracts +forge install OpenZeppelin/openzeppelin-contracts-upgradeable +``` + +3. Configure remappings by creating a `remappings.txt` file: + +```txt +@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ +@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ +``` + +4. Update your `foundry.toml`: + +```toml +[profile.default] +src = "src" +out = "out" +libs = ["lib"] +solc = "0.8.24" +remappings = [ + "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", + "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/" +] +``` + +5. Create a `.env` file: + +```env +PRIVATE_KEY=your_private_key_here +RPC_URL=https://evmtestnet.confluxrpc.com +``` + +6. Load environment variables (for Unix-based systems): + +```bash +source .env +``` + +For Windows PowerShell: +```powershell +$env:PRIVATE_KEY="your_private_key_here" +$env:RPC_URL="https://evmtestnet.confluxrpc.com" +``` + +## Writing Smart Contracts + +1. Create the initial Counter contract in `src/Counter.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract Counter is UUPSUpgradeable, OwnableUpgradeable { + uint256 private count; + + event CountChanged(uint256 count); + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize() public initializer { + __Ownable_init(msg.sender); + __UUPSUpgradeable_init(); + } + + function increment() public { + count += 1; + emit CountChanged(count); + } + + function getCount() public view returns (uint256) { + return count; + } + + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} +} +``` + +2. Create CounterV2 in `src/CounterV2.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; + +contract CounterV2 is UUPSUpgradeable, OwnableUpgradeable { + uint256 private count; + + event CountChanged(uint256 count); + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize() public initializer { + __Ownable_init(msg.sender); + __UUPSUpgradeable_init(); + } + + function increment() public { + count += 1; + emit CountChanged(count); + } + + function getCount() public view returns (uint256) { + return count; + } + + function reset() public { + count = 0; + emit CountChanged(count); + } + + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} +} +``` + +## Deployment Scripts + +1. Create a deployment script in `script/DeployCounter.s.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "forge-std/Script.sol"; +import "../src/Counter.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +contract DeployCounter is Script { + function run() external { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + address deployer = vm.addr(deployerPrivateKey); + + vm.startBroadcast(deployerPrivateKey); + + // Deploy implementation + Counter counter = new Counter(); + console.log("Implementation deployed to:", address(counter)); + + // Encode initialize function call + bytes memory data = abi.encodeWithSelector(Counter.initialize.selector); + + // Deploy proxy + ERC1967Proxy proxy = new ERC1967Proxy( + address(counter), + data + ); + console.log("Proxy deployed to:", address(proxy)); + + // Verify deployment + Counter proxiedCounter = Counter(address(proxy)); + console.log("Initial count:", proxiedCounter.getCount()); + + vm.stopBroadcast(); + } +} +``` + +2. Create an upgrade script in `script/UpgradeCounter.s.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "forge-std/Script.sol"; +import "../src/Counter.sol"; +import "../src/CounterV2.sol"; + +contract UpgradeCounter is Script { + function run() external { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + address proxyAddress = vm.envAddress("PROXY_ADDRESS"); + + // Test before upgrade + console.log("============ Before Upgrade ============"); + Counter counter = Counter(proxyAddress); + uint256 valueBefore = counter.getCount(); + console.log("Current count:", valueBefore); + + vm.startBroadcast(deployerPrivateKey); + + // Deploy new implementation + CounterV2 counterV2 = new CounterV2(); + console.log("\n============ Deploying New Implementation ============"); + console.log("New implementation:", address(counterV2)); + + // Upgrade proxy to new implementation + Counter(proxyAddress).upgradeTo(address(counterV2)); + + vm.stopBroadcast(); + + // Test after upgrade + console.log("\n============ After Upgrade ============"); + CounterV2 upgradedCounter = CounterV2(proxyAddress); + uint256 valueAfter = upgradedCounter.getCount(); + console.log("Count after upgrade:", valueAfter); + + vm.startBroadcast(deployerPrivateKey); + upgradedCounter.increment(); + vm.stopBroadcast(); + + uint256 valueAfterIncrement = upgradedCounter.getCount(); + console.log("Count after increment:", valueAfterIncrement); + + vm.startBroadcast(deployerPrivateKey); + upgradedCounter.reset(); + vm.stopBroadcast(); + + uint256 valueAfterReset = upgradedCounter.getCount(); + console.log("Count after reset:", valueAfterReset); + + // Verify upgrade results + require(valueAfter == valueBefore, "State verification failed: Value changed during upgrade"); + require(valueAfterIncrement == valueAfter + 1, "Function verification failed: Increment not working"); + require(valueAfterReset == 0, "Function verification failed: Reset not working"); + + console.log("\n============ Upgrade Successful ============"); + console.log("1. State preserved: Initial count maintained after upgrade"); + console.log("2. New functions working: Increment and Reset successfully added"); + } +} +``` + +## Testing + +Create a test file in `test/Counter.t.sol`: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import "forge-std/Test.sol"; +import "../src/Counter.sol"; +import "../src/CounterV2.sol"; +import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +contract CounterTest is Test { + Counter public implementation; + CounterV2 public implementationV2; + ERC1967Proxy public proxy; + address owner = address(this); + + function setUp() public { + // Deploy implementation + implementation = new Counter(); + + // Encode initialize function call + bytes memory data = abi.encodeWithSelector(Counter.initialize.selector); + + // Deploy proxy + proxy = new ERC1967Proxy( + address(implementation), + data + ); + } + + function testIncrement() public { + Counter(address(proxy)).increment(); + assertEq(Counter(address(proxy)).getCount(), 1); + } + + function testUpgrade() public { + // Deploy new implementation + implementationV2 = new CounterV2(); + + // Upgrade + Counter(address(proxy)).upgradeTo(address(implementationV2)); + + // Test new functionality + CounterV2(address(proxy)).increment(); + assertEq(CounterV2(address(proxy)).getCount(), 1); + + CounterV2(address(proxy)).reset(); + assertEq(CounterV2(address(proxy)).getCount(), 0); + } +} +``` + +## Deployment and Upgrade Process + +1. Build the contracts: + +```bash +forge build +``` + +2. Deploy the initial contract: + +```bash +forge script script/DeployCounter.s.sol:DeployCounter --rpc-url $RPC_URL --broadcast +``` + +3. Update your `.env` file with the proxy address: + +``` +PROXY_ADDRESS= +``` + +4. Upgrade the contract: + +```bash +forge script script/UpgradeCounter.s.sol:UpgradeCounter --rpc-url $RPC_URL --broadcast +``` + +5. Run the tests: + +```bash +forge test +``` + +## Interacting with the Contract + +You can interact with your contract using the Foundry's `cast` command: + +```bash +# Get the current count +cast call $PROXY_ADDRESS "getCount()" --rpc-url $RPC_URL + +# Increment the counter +cast send $PROXY_ADDRESS "increment()" --private-key $PRIVATE_KEY --rpc-url $RPC_URL + +# After upgrade, reset the counter +cast send $PROXY_ADDRESS "reset()" --private-key $PRIVATE_KEY --rpc-url $RPC_URL +``` + +This tutorial demonstrates how to implement, deploy, and upgrade UUPS contracts using Foundry on Conflux eSpace. The approach maintains the same functionality as the Hardhat version but leverages Foundry's tools and testing framework. From 8346adfd8fa871cd48d279d2be5fb641f62e26c5 Mon Sep 17 00:00:00 2001 From: Jack Lee <280147597@qq.com> Date: Mon, 4 Nov 2024 17:21:13 +0700 Subject: [PATCH 6/7] remove uups-foundry.md --- .../upgradableContract/uups-foundry.md | 365 ------------------ 1 file changed, 365 deletions(-) delete mode 100644 docs/espace/tutorials/upgradableContract/uups-foundry.md diff --git a/docs/espace/tutorials/upgradableContract/uups-foundry.md b/docs/espace/tutorials/upgradableContract/uups-foundry.md deleted file mode 100644 index bd100d2bf0..0000000000 --- a/docs/espace/tutorials/upgradableContract/uups-foundry.md +++ /dev/null @@ -1,365 +0,0 @@ ---- -displayed_sidebar: eSpaceSidebar -keywords: - - tutorial - - smart contracts - - upgradeable contracts - - UUPS - - Foundry - - eSpace -tags: [Tutorial, Upgradeable Contracts] ---- - -# Deploying Upgradeable Contracts using UUPS with Foundry - -### UUPS (Universal Upgradeable Proxy Standard) - -[Keep the same UUPS explanation section as in the original tutorial...] - -## Project Setup - -1. Create a new Foundry project: - -```bash -forge init uups-proxy-foundry-demo -cd uups-proxy-foundry-demo -``` - -2. Install OpenZeppelin contracts: - -```bash -forge install OpenZeppelin/openzeppelin-contracts -forge install OpenZeppelin/openzeppelin-contracts-upgradeable -``` - -3. Configure remappings by creating a `remappings.txt` file: - -```txt -@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/ -@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ -``` - -4. Update your `foundry.toml`: - -```toml -[profile.default] -src = "src" -out = "out" -libs = ["lib"] -solc = "0.8.24" -remappings = [ - "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", - "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/" -] -``` - -5. Create a `.env` file: - -```env -PRIVATE_KEY=your_private_key_here -RPC_URL=https://evmtestnet.confluxrpc.com -``` - -6. Load environment variables (for Unix-based systems): - -```bash -source .env -``` - -For Windows PowerShell: -```powershell -$env:PRIVATE_KEY="your_private_key_here" -$env:RPC_URL="https://evmtestnet.confluxrpc.com" -``` - -## Writing Smart Contracts - -1. Create the initial Counter contract in `src/Counter.sol`: - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; - -contract Counter is UUPSUpgradeable, OwnableUpgradeable { - uint256 private count; - - event CountChanged(uint256 count); - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { - _disableInitializers(); - } - - function initialize() public initializer { - __Ownable_init(msg.sender); - __UUPSUpgradeable_init(); - } - - function increment() public { - count += 1; - emit CountChanged(count); - } - - function getCount() public view returns (uint256) { - return count; - } - - function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} -} -``` - -2. Create CounterV2 in `src/CounterV2.sol`: - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; -import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; - -contract CounterV2 is UUPSUpgradeable, OwnableUpgradeable { - uint256 private count; - - event CountChanged(uint256 count); - - /// @custom:oz-upgrades-unsafe-allow constructor - constructor() { - _disableInitializers(); - } - - function initialize() public initializer { - __Ownable_init(msg.sender); - __UUPSUpgradeable_init(); - } - - function increment() public { - count += 1; - emit CountChanged(count); - } - - function getCount() public view returns (uint256) { - return count; - } - - function reset() public { - count = 0; - emit CountChanged(count); - } - - function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} -} -``` - -## Deployment Scripts - -1. Create a deployment script in `script/DeployCounter.s.sol`: - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import "forge-std/Script.sol"; -import "../src/Counter.sol"; -import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; - -contract DeployCounter is Script { - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address deployer = vm.addr(deployerPrivateKey); - - vm.startBroadcast(deployerPrivateKey); - - // Deploy implementation - Counter counter = new Counter(); - console.log("Implementation deployed to:", address(counter)); - - // Encode initialize function call - bytes memory data = abi.encodeWithSelector(Counter.initialize.selector); - - // Deploy proxy - ERC1967Proxy proxy = new ERC1967Proxy( - address(counter), - data - ); - console.log("Proxy deployed to:", address(proxy)); - - // Verify deployment - Counter proxiedCounter = Counter(address(proxy)); - console.log("Initial count:", proxiedCounter.getCount()); - - vm.stopBroadcast(); - } -} -``` - -2. Create an upgrade script in `script/UpgradeCounter.s.sol`: - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import "forge-std/Script.sol"; -import "../src/Counter.sol"; -import "../src/CounterV2.sol"; - -contract UpgradeCounter is Script { - function run() external { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address proxyAddress = vm.envAddress("PROXY_ADDRESS"); - - // Test before upgrade - console.log("============ Before Upgrade ============"); - Counter counter = Counter(proxyAddress); - uint256 valueBefore = counter.getCount(); - console.log("Current count:", valueBefore); - - vm.startBroadcast(deployerPrivateKey); - - // Deploy new implementation - CounterV2 counterV2 = new CounterV2(); - console.log("\n============ Deploying New Implementation ============"); - console.log("New implementation:", address(counterV2)); - - // Upgrade proxy to new implementation - Counter(proxyAddress).upgradeTo(address(counterV2)); - - vm.stopBroadcast(); - - // Test after upgrade - console.log("\n============ After Upgrade ============"); - CounterV2 upgradedCounter = CounterV2(proxyAddress); - uint256 valueAfter = upgradedCounter.getCount(); - console.log("Count after upgrade:", valueAfter); - - vm.startBroadcast(deployerPrivateKey); - upgradedCounter.increment(); - vm.stopBroadcast(); - - uint256 valueAfterIncrement = upgradedCounter.getCount(); - console.log("Count after increment:", valueAfterIncrement); - - vm.startBroadcast(deployerPrivateKey); - upgradedCounter.reset(); - vm.stopBroadcast(); - - uint256 valueAfterReset = upgradedCounter.getCount(); - console.log("Count after reset:", valueAfterReset); - - // Verify upgrade results - require(valueAfter == valueBefore, "State verification failed: Value changed during upgrade"); - require(valueAfterIncrement == valueAfter + 1, "Function verification failed: Increment not working"); - require(valueAfterReset == 0, "Function verification failed: Reset not working"); - - console.log("\n============ Upgrade Successful ============"); - console.log("1. State preserved: Initial count maintained after upgrade"); - console.log("2. New functions working: Increment and Reset successfully added"); - } -} -``` - -## Testing - -Create a test file in `test/Counter.t.sol`: - -```solidity -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.24; - -import "forge-std/Test.sol"; -import "../src/Counter.sol"; -import "../src/CounterV2.sol"; -import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; - -contract CounterTest is Test { - Counter public implementation; - CounterV2 public implementationV2; - ERC1967Proxy public proxy; - address owner = address(this); - - function setUp() public { - // Deploy implementation - implementation = new Counter(); - - // Encode initialize function call - bytes memory data = abi.encodeWithSelector(Counter.initialize.selector); - - // Deploy proxy - proxy = new ERC1967Proxy( - address(implementation), - data - ); - } - - function testIncrement() public { - Counter(address(proxy)).increment(); - assertEq(Counter(address(proxy)).getCount(), 1); - } - - function testUpgrade() public { - // Deploy new implementation - implementationV2 = new CounterV2(); - - // Upgrade - Counter(address(proxy)).upgradeTo(address(implementationV2)); - - // Test new functionality - CounterV2(address(proxy)).increment(); - assertEq(CounterV2(address(proxy)).getCount(), 1); - - CounterV2(address(proxy)).reset(); - assertEq(CounterV2(address(proxy)).getCount(), 0); - } -} -``` - -## Deployment and Upgrade Process - -1. Build the contracts: - -```bash -forge build -``` - -2. Deploy the initial contract: - -```bash -forge script script/DeployCounter.s.sol:DeployCounter --rpc-url $RPC_URL --broadcast -``` - -3. Update your `.env` file with the proxy address: - -``` -PROXY_ADDRESS= -``` - -4. Upgrade the contract: - -```bash -forge script script/UpgradeCounter.s.sol:UpgradeCounter --rpc-url $RPC_URL --broadcast -``` - -5. Run the tests: - -```bash -forge test -``` - -## Interacting with the Contract - -You can interact with your contract using the Foundry's `cast` command: - -```bash -# Get the current count -cast call $PROXY_ADDRESS "getCount()" --rpc-url $RPC_URL - -# Increment the counter -cast send $PROXY_ADDRESS "increment()" --private-key $PRIVATE_KEY --rpc-url $RPC_URL - -# After upgrade, reset the counter -cast send $PROXY_ADDRESS "reset()" --private-key $PRIVATE_KEY --rpc-url $RPC_URL -``` - -This tutorial demonstrates how to implement, deploy, and upgrade UUPS contracts using Foundry on Conflux eSpace. The approach maintains the same functionality as the Hardhat version but leverages Foundry's tools and testing framework. From 8e7f10df2b839ee9df793f127a861f7ac84b07c2 Mon Sep 17 00:00:00 2001 From: Jack Lee <280147597@qq.com> Date: Thu, 7 Nov 2024 16:14:08 +0700 Subject: [PATCH 7/7] update content --- .../upgradableContract/transparent-proxy-foundry.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md b/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md index 2ff2f2849e..e1bd9c4e3f 100644 --- a/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md +++ b/docs/espace/tutorials/upgradableContract/transparent-proxy-foundry.md @@ -32,7 +32,7 @@ forge install OpenZeppelin/openzeppelin-contracts forge install OpenZeppelin/openzeppelin-contracts-upgradeable ``` -3. Add the following to `remappings.txt`: +1. Add the following to `remappings.txt`: ``` @openzeppelin/=lib/openzeppelin-contracts/ @@ -160,6 +160,12 @@ contract DeployBox is Script { )))); vm.stopBroadcast(); + + console.log("Box implementation deployed to:", address(box)); + console.log("Proxy deployed to:", address(proxy)); + console.log("ProxyAdmin deployed to:", proxyAdminAddress); + } +} ```