diff --git a/.gitignore b/.gitignore index 85198aa..6628cca 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ cache/ out/ # Ignores development broadcast logs -!/broadcast +/broadcast /broadcast/*/31337/ /broadcast/**/dry-run/ @@ -12,3 +12,4 @@ docs/ # Dotenv file .env +.DS_Store diff --git a/.gitmodules b/.gitmodules index 690924b..9296efd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "lib/openzeppelin-contracts"] path = lib/openzeppelin-contracts url = https://github.com/OpenZeppelin/openzeppelin-contracts +[submodule "lib/openzeppelin-contracts-upgradeable"] + path = lib/openzeppelin-contracts-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable diff --git a/deployAddr/testChain.json b/deployAddr/testChain.json new file mode 100644 index 0000000..d57f95b --- /dev/null +++ b/deployAddr/testChain.json @@ -0,0 +1,10 @@ +{ + "deployments": { + "WETH": "0x482AD89125E927101a68b7987698D0e6C0640E9b", + "VaultProxy": "0x5492D259a670cF407348660Ca3e3f6B9158B9522", + "WeightedPoolFactory": "0x9C0a10b973Bfd995D2D7d791662d47973439b68e", + "TokenA": "0x6A964F0A36C49C037Faa59CDFA046d95dcF2c205", + "TokenB": "0xe1A80Ad9F9855C0D92E9621c155E91A3D1951c30", + "WeightedPool": "0x4d48A8CD07865F5E62Ccb1A43394D476Ef756505" + } + } \ No newline at end of file diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable new file mode 160000 index 0000000..723f8ca --- /dev/null +++ b/lib/openzeppelin-contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit 723f8cab09cdae1aca9ec9cc1cfa040c2d4b06c1 diff --git a/script/Counter.s.sol b/script/Counter.s.sol deleted file mode 100644 index dc63722..0000000 --- a/script/Counter.s.sol +++ /dev/null @@ -1,21 +0,0 @@ -/* -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.13; - -import {Script, console} from "forge-std/Script.sol"; -import {Counter} from "../src/Counter.sol"; - -contract CounterScript is Script { - Counter public counter; - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - counter = new Counter(); - - vm.stopBroadcast(); - } -} -*/ diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol new file mode 100644 index 0000000..cc67e29 --- /dev/null +++ b/script/Deploy.s.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "forge-std/Script.sol"; +import {Vault} from "../src/Vault.sol"; +import {WeightedPoolFactory} from "../src/WeightedPool/WeightedPoolFactory.sol"; +import {WeightedPool} from "../src/WeightedPool/WeightedPool.sol"; +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import {WETH9} from "../src/mocks/WETH9.sol"; +import {MockToken} from "../test/Context.t.sol"; + +contract DeployScript is Script { + function run() external { + uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); + address deployer = vm.addr(deployerPrivateKey); + vm.startBroadcast(deployerPrivateKey); + + // 部署WETH + WETH9 weth = new WETH9(); + + // 部署Vault逻辑合约 + Vault vaultImplementation = new Vault(address(weth)); + + // 部署透明代理 + bytes memory initData = abi.encodeWithSelector(Vault.initialize.selector, deployer); + ERC1967Proxy vaultProxy = new ERC1967Proxy(address(vaultImplementation), initData); + Vault vault = Vault(payable(address(vaultProxy))); + + // 部署WeightedPoolFactory + WeightedPoolFactory factory = new WeightedPoolFactory(address(vault)); + vault.addFactory(address(factory)); + + // 部署MockTokens + MockToken tokenA = new MockToken("Token A", "TKA"); + MockToken tokenB = new MockToken("Token B", "TKB"); + + // 创建权重数组 + uint256[] memory weights = new uint256[](3); + weights[0] = 1e17; // 10% + weights[1] = 4e17; // 40% + weights[2] = 5e17; // 50% + + // 创建代币数组 + address[] memory tokens = new address[](3); + tokens[0] = address(tokenA); + tokens[1] = address(tokenB); + tokens[2] = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); // ETH_ADDRESS + + // 创建池子 + WeightedPool pool = WeightedPool(factory.createPool(tokens, weights, 3e15)); // 0.3% swap fee + + vm.stopBroadcast(); + + // 输出部署的合约地址 + console.log("WETH deployed at:", address(weth)); + console.log("Vault Proxy deployed at:", address(vault)); + console.log("WeightedPoolFactory deployed at:", address(factory)); + console.log("Token A deployed at:", address(tokenA)); + console.log("Token B deployed at:", address(tokenB)); + console.log("WeightedPool deployed at:", address(pool)); + } +} diff --git a/src/Vault.sol b/src/Vault.sol index f3dfe60..1193fe8 100644 --- a/src/Vault.sol +++ b/src/Vault.sol @@ -1,6 +1,8 @@ pragma solidity ^0.8.0; -import "@openzeppelin/contracts/access/Ownable2Step.sol"; +import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "./interfaces/IWETH.sol"; import {VaultFunding} from "./VaultFunding.sol"; import {VaultSwap} from "./VaultSwap.sol"; @@ -8,8 +10,7 @@ import {IVault} from "./interfaces/IVault.sol"; import {VaultStorage} from "./VaultStorage.sol"; import "forge-std/Test.sol"; - -contract Vault is IVault, Ownable2Step, VaultFunding, VaultSwap { +contract Vault is IVault, Initializable, Ownable2StepUpgradeable, UUPSUpgradeable, VaultFunding, VaultSwap { mapping(address => bool) public authorizedFactories; event FactoryAdded(address indexed factory); @@ -23,9 +24,17 @@ contract Vault is IVault, Ownable2Step, VaultFunding, VaultSwap { _; } - constructor(address _weth) Ownable(msg.sender) { - globalPause = false; + /// @custom:oz-upgrades-unsafe-allow constructor + constructor(address _weth) { WETH = IWETH(_weth); + _disableInitializers(); + } + + function initialize(address _owner) public initializer { + __Ownable2Step_init(); + __UUPSUpgradeable_init(); + _transferOwnership(_owner); // 设置初始所有者 + globalPause = false; } // =================== Owner functions ======================== @@ -70,4 +79,5 @@ contract Vault is IVault, Ownable2Step, VaultFunding, VaultSwap { return (poolInfo.tokens, poolInfo.balances); } + function _authorizeUpgrade(address newImplementation) internal override onlyOwner {} } \ No newline at end of file diff --git a/test/Context.t.sol b/test/Context.t.sol index 5e026e3..a25b853 100644 --- a/test/Context.t.sol +++ b/test/Context.t.sol @@ -8,6 +8,7 @@ import {WeightedPool} from "../src/WeightedPool/WeightedPool.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {IWETH} from "../src/interfaces/IWETH.sol"; import {WETH9} from "../src/mocks/WETH9.sol"; +import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; contract MockToken is ERC20 { constructor(string memory name, string memory symbol) ERC20(name, symbol) {} @@ -51,8 +52,20 @@ contract Context is Test { // 部署WETH weth = new WETH9(); - // 部署Vault - vault = new Vault(address(weth)); + // 部署Vault逻辑合约 + Vault vaultImplementation = new Vault(address(weth)); + + // 准备初始化数据 + bytes memory initData = abi.encodeWithSelector( + Vault.initialize.selector, + owner // 设置owner为初始所有者 + ); + + // 部署代理合约 + ERC1967Proxy vaultProxy = new ERC1967Proxy(address(vaultImplementation), initData); + + // 将代理合约地址转换为Vault接口 + vault = Vault(payable(address(vaultProxy))); } function createWeightedPoolFactory() public {