diff --git a/contracts/src/factory/AaveHyperdriveFactory.sol b/contracts/src/factory/AaveHyperdriveFactory.sol index f901f367c..9331d85b2 100644 --- a/contracts/src/factory/AaveHyperdriveFactory.sol +++ b/contracts/src/factory/AaveHyperdriveFactory.sol @@ -25,13 +25,17 @@ contract AaveHyperdriveFactory is HyperdriveFactory { /// @param _feeCollector The address which should be set as the fee collector in new deployments /// @param _fees The fees each deployed instance from this contract will have /// @param _defaultPausers The default addresses which will be set to have the pauser role + /// @param _linkerFactory The address of the linker factory + /// @param _linkerCodeHash The hash of the linker contract's constructor code. constructor( address _governance, IHyperdriveDeployer _deployer, address _hyperdriveGovernance, address _feeCollector, IHyperdrive.Fees memory _fees, - address[] memory _defaultPausers + address[] memory _defaultPausers, + address _linkerFactory, + bytes32 _linkerCodeHash ) HyperdriveFactory( _governance, @@ -39,23 +43,19 @@ contract AaveHyperdriveFactory is HyperdriveFactory { _hyperdriveGovernance, _feeCollector, _fees, - _defaultPausers + _defaultPausers, + _linkerFactory, + _linkerCodeHash ) {} /// @notice Deploys a copy of hyperdrive with the given params /// @param _config The configuration of the Hyperdrive pool. - /// @param _linkerCodeHash The hash of the ERC20 linker contract's - /// constructor code. - /// @param _linkerFactory The address of the factory which is used to deploy - /// the ERC20 linker contracts. /// @param _contribution Base token to call init with /// @param _apr The apr to call init with /// @return The hyperdrive address deployed function deployAndInitialize( IHyperdrive.PoolConfig memory _config, - bytes32 _linkerCodeHash, - address _linkerFactory, bytes32[] memory, uint256 _contribution, uint256 _apr @@ -81,14 +81,7 @@ contract AaveHyperdriveFactory is HyperdriveFactory { // Deploy and initialize the hyperdrive instance. return - super.deployAndInitialize( - _config, - _linkerCodeHash, - _linkerFactory, - extraData, - _contribution, - _apr - ); + super.deployAndInitialize(_config, extraData, _contribution, _apr); } /// @notice This deploys a data provider for the aave hyperdrive instance diff --git a/contracts/src/factory/DsrHyperdriveFactory.sol b/contracts/src/factory/DsrHyperdriveFactory.sol index 7674f1f64..cf8f03709 100644 --- a/contracts/src/factory/DsrHyperdriveFactory.sol +++ b/contracts/src/factory/DsrHyperdriveFactory.sol @@ -25,6 +25,8 @@ contract DsrHyperdriveFactory is HyperdriveFactory { /// @param _feeCollector The address which should be set as the fee collector in new deployments /// @param _fees The fees each deployed instance from this contract will have /// @param _defaultPausers The default addresses which will be set to have the pauser role + /// @param _linkerFactory The address of the linker factory + /// @param _linkerCodeHash The hash of the linker contract's constructor code. /// @param dsrManager The Maker DSR manger contract address constructor( address _governance, @@ -33,6 +35,8 @@ contract DsrHyperdriveFactory is HyperdriveFactory { address _feeCollector, IHyperdrive.Fees memory _fees, address[] memory _defaultPausers, + address _linkerFactory, + bytes32 _linkerCodeHash, address dsrManager ) HyperdriveFactory( @@ -41,7 +45,9 @@ contract DsrHyperdriveFactory is HyperdriveFactory { _hyperdriveGovernance, _feeCollector, _fees, - _defaultPausers + _defaultPausers, + _linkerFactory, + _linkerCodeHash ) { manager = DsrManager(dsrManager); diff --git a/contracts/src/factory/ERC4626HyperdriveFactory.sol b/contracts/src/factory/ERC4626HyperdriveFactory.sol index 4e22ddd04..b997542a8 100644 --- a/contracts/src/factory/ERC4626HyperdriveFactory.sol +++ b/contracts/src/factory/ERC4626HyperdriveFactory.sol @@ -25,6 +25,8 @@ contract ERC4626HyperdriveFactory is HyperdriveFactory { /// @param _feeCollector The address which should be set as the fee collector in new deployments /// @param _fees The fees each deployed instance from this contract will have /// @param _defaultPausers The default addresses which will be set to have the pauser role + /// @param _linkerFactory The address of the linker factory + /// @param _linkerCodeHash The hash of the linker contract's constructor code. /// @param _pool The Maker ERC4626 manger contract address constructor( address _governance, @@ -33,6 +35,8 @@ contract ERC4626HyperdriveFactory is HyperdriveFactory { address _feeCollector, IHyperdrive.Fees memory _fees, address[] memory _defaultPausers, + address _linkerFactory, + bytes32 _linkerCodeHash, IERC4626 _pool ) HyperdriveFactory( @@ -41,7 +45,9 @@ contract ERC4626HyperdriveFactory is HyperdriveFactory { _hyperdriveGovernance, _feeCollector, _fees, - _defaultPausers + _defaultPausers, + _linkerFactory, + _linkerCodeHash ) { pool = _pool; diff --git a/contracts/src/factory/HyperdriveFactory.sol b/contracts/src/factory/HyperdriveFactory.sol index d419e5374..0ffc677c6 100644 --- a/contracts/src/factory/HyperdriveFactory.sol +++ b/contracts/src/factory/HyperdriveFactory.sol @@ -26,6 +26,12 @@ abstract contract HyperdriveFactory { // The address which should control hyperdrive instances address public hyperdriveGovernance; + // The linker factory which is used to deploy the ERC20 linker contracts. + address public linkerFactory; + + // The hash of the ERC20 linker contract's constructor code. + bytes32 public linkerCodeHash; + // The address which should receive hyperdriveFees address public feeCollector; @@ -38,6 +44,16 @@ abstract contract HyperdriveFactory { // A constant for the ETH value address internal constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; + // An event that is emitted when a new Hyperdrive instance is deployed. + event Deployed( + uint256 indexed version, + address hyperdrive, + IHyperdrive.PoolConfig config, + address linkerFactory, + bytes32 linkerCodeHash, + bytes32[] extraData + ); + /// @notice Deploys the contract /// @param _governance The address which can update this factory. /// @param _deployer The contract which holds the bytecode and deploys new versions. @@ -45,13 +61,17 @@ abstract contract HyperdriveFactory { /// @param _feeCollector The address which should be set as the fee collector in new deployments /// @param _fees The fees each deployed instance from this contract will have /// @param _defaultPausers The default addresses which will be set to have the pauser role + /// @param _linkerFactory The address of the linker factory + /// @param _linkerCodeHash The hash of the linker contract's constructor code. constructor( address _governance, IHyperdriveDeployer _deployer, address _hyperdriveGovernance, address _feeCollector, IHyperdrive.Fees memory _fees, - address[] memory _defaultPausers + address[] memory _defaultPausers, + address _linkerFactory, + bytes32 _linkerCodeHash ) { governance = _governance; hyperdriveDeployer = _deployer; @@ -60,6 +80,8 @@ abstract contract HyperdriveFactory { feeCollector = _feeCollector; fees = _fees; defaultPausers = _defaultPausers; + linkerFactory = _linkerFactory; + linkerCodeHash = _linkerCodeHash; } modifier onlyGovernance() { @@ -94,6 +116,25 @@ abstract contract HyperdriveFactory { hyperdriveGovernance = newGovernance; } + /// @notice Allows governance to change the linker factory. + /// @param newLinkerFactory The new linker code hash. + function updateLinkerFactory( + address newLinkerFactory + ) external onlyGovernance { + // Update the linker factory + linkerFactory = newLinkerFactory; + } + + /// @notice Allows governance to change the linker code hash. This allows + /// governance to update the implementation of the ERC20Forwarder. + /// @param newLinkerCodeHash The new linker code hash. + function updateLinkerCodeHash( + bytes32 newLinkerCodeHash + ) external onlyGovernance { + // Update the linker code hash + linkerCodeHash = newLinkerCodeHash; + } + /// @notice Allows governance to change the fee collector address /// @param newFeeCollector The new governor address function updateFeeCollector( @@ -123,18 +164,12 @@ abstract contract HyperdriveFactory { /// @notice Deploys a copy of hyperdrive with the given params /// @param _config The configuration of the Hyperdrive pool. - /// @param _linkerCodeHash The hash of the ERC20 linker contract's - /// constructor code. - /// @param _linkerFactory The address of the factory which is used to deploy - /// the ERC20 linker contracts. /// @param _extraData The extra data is used by some factories /// @param _contribution Base token to call init with /// @param _apr The apr to call init with /// @return The hyperdrive address deployed function deployAndInitialize( IHyperdrive.PoolConfig memory _config, - bytes32 _linkerCodeHash, - address _linkerFactory, bytes32[] memory _extraData, uint256 _contribution, uint256 _apr @@ -149,8 +184,8 @@ abstract contract HyperdriveFactory { address dataProvider = deployDataProvider( _config, _extraData, - _linkerCodeHash, - _linkerFactory + linkerCodeHash, + linkerFactory ); // Then we call the simplified factory @@ -158,8 +193,8 @@ abstract contract HyperdriveFactory { hyperdriveDeployer.deploy( _config, dataProvider, - _linkerCodeHash, - _linkerFactory, + linkerCodeHash, + linkerFactory, _extraData ) ); @@ -197,7 +232,18 @@ abstract contract HyperdriveFactory { // Mark as a version isOfficial[address(hyperdrive)] = versionCounter; - return (hyperdrive); + // Emit a deployed event. + _config.governance = hyperdriveGovernance; + emit Deployed( + versionCounter, + address(hyperdrive), + _config, + linkerFactory, + linkerCodeHash, + _extraData + ); + + return hyperdrive; } /// @notice This should deploy a data provider which matches the type of the hyperdrives diff --git a/contracts/src/factory/StethHyperdriveFactory.sol b/contracts/src/factory/StethHyperdriveFactory.sol index df6ab41a6..2a3a53f99 100644 --- a/contracts/src/factory/StethHyperdriveFactory.sol +++ b/contracts/src/factory/StethHyperdriveFactory.sol @@ -27,6 +27,8 @@ contract StethHyperdriveFactory is HyperdriveFactory { /// @param _feeCollector The address which should be set as the fee collector in new deployments /// @param _fees The fees each deployed instance from this contract will have /// @param _defaultPausers The default addresses which will be set to have the pauser role + /// @param _linkerFactory The address of the linker factory + /// @param _linkerCodeHash The hash of the linker contract's constructor code. /// @param _lido The Lido contract. constructor( address _governance, @@ -35,6 +37,8 @@ contract StethHyperdriveFactory is HyperdriveFactory { address _feeCollector, IHyperdrive.Fees memory _fees, address[] memory _defaultPausers, + address _linkerFactory, + bytes32 _linkerCodeHash, ILido _lido ) HyperdriveFactory( @@ -43,7 +47,9 @@ contract StethHyperdriveFactory is HyperdriveFactory { _hyperdriveGovernance, _feeCollector, _fees, - _defaultPausers + _defaultPausers, + _linkerFactory, + _linkerCodeHash ) { lido = _lido; diff --git a/test/integrations/ERC4626.t.sol b/test/integrations/ERC4626.t.sol index 3d4bb05b0..a7eadc27f 100644 --- a/test/integrations/ERC4626.t.sol +++ b/test/integrations/ERC4626.t.sol @@ -10,6 +10,7 @@ import { IHyperdriveDeployer } from "contracts/src/interfaces/IHyperdriveDeploye import { AssetId } from "contracts/src/libraries/AssetId.sol"; import { Errors } from "contracts/src/libraries/Errors.sol"; import { FixedPointMath } from "contracts/src/libraries/FixedPointMath.sol"; +import { ForwarderFactory } from "contracts/src/token/ForwarderFactory.sol"; import { HyperdriveTest } from "../utils/HyperdriveTest.sol"; import { Mock4626, ERC20 } from "../mocks/Mock4626.sol"; import { MockERC4626Hyperdrive } from "../mocks/Mock4626Hyperdrive.sol"; @@ -27,7 +28,7 @@ contract HyperdriveER4626Test is HyperdriveTest { function setUp() public override __mainnet_fork(16_685_972) { vm.startPrank(deployer); - // Deploy a new yield source + // Deploy the ERC4626Hyperdrive factory and deployer. pool = IERC4626( address(new Mock4626(ERC20(address(dai)), "yearn dai", "yDai")) ); @@ -37,7 +38,7 @@ contract HyperdriveER4626Test is HyperdriveTest { ); address[] memory defaults = new address[](1); defaults[0] = bob; - + forwarderFactory = new ForwarderFactory(); factory = new ERC4626HyperdriveFactory( alice, simpleDeployer, @@ -45,11 +46,12 @@ contract HyperdriveER4626Test is HyperdriveTest { bob, IHyperdrive.Fees(0, 0, 0), defaults, + address(forwarderFactory), + forwarderFactory.ERC20LINK_HASH(), pool ); address daiWhale = 0x075e72a5eDf65F0A5f44699c7654C1a76941Ddc8; - whaleTransfer(daiWhale, dai, alice); IHyperdrive.PoolConfig memory config = IHyperdrive.PoolConfig({ @@ -88,6 +90,9 @@ contract HyperdriveER4626Test is HyperdriveTest { dai.approve(address(hyperdrive), type(uint256).max); dai.approve(address(mockHyperdrive), type(uint256).max); vm.stopPrank(); + + // Start recording events. + vm.recordLogs(); } function test_erc4626_deposit() external { @@ -105,7 +110,7 @@ contract HyperdriveER4626Test is HyperdriveTest { assertEq(sharesMinted, 666666666666666666); assertEq(pool.balanceOf(address(mockHyperdrive)), 666666666666666666); - //Now we try to do a deposit from alice's shares + // Now we try to do a deposit from alice's shares pool.approve(address(mockHyperdrive), type(uint256).max); (sharesMinted, sharePrice) = mockHyperdrive.deposit(3e18, false); assertEq(sharePrice, 1.5e18 + 1); @@ -149,7 +154,8 @@ contract HyperdriveER4626Test is HyperdriveTest { function test_erc4626_testDeploy() external { setUp(); vm.startPrank(alice); - uint256 apr = 1e16; // 1% apr + uint256 apr = 0.01e18; // 1% apr + uint256 contribution = 2_500e18; IHyperdrive.PoolConfig memory config = IHyperdrive.PoolConfig({ baseToken: dai, initialSharePrice: FixedPointMath.ONE_18, @@ -165,10 +171,8 @@ contract HyperdriveER4626Test is HyperdriveTest { dai.approve(address(factory), type(uint256).max); hyperdrive = factory.deployAndInitialize( config, - bytes32(0), - address(0), new bytes32[](0), - 2500e18, + contribution, apr ); @@ -180,5 +184,14 @@ contract HyperdriveER4626Test is HyperdriveTest { ); // lp shares should equal number of share reserves initialized with assertEq(createdShares, 2500e18); + + // Verify that the correct events were emitted. + verifyFactoryEvents( + factory, + alice, + contribution, + apr, + new bytes32[](0) + ); } } diff --git a/test/integrations/HyperdriveAaveDeploy.t.sol b/test/integrations/HyperdriveAaveDeploy.t.sol index e777ef362..299b9daf9 100644 --- a/test/integrations/HyperdriveAaveDeploy.t.sol +++ b/test/integrations/HyperdriveAaveDeploy.t.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.19; +import { VmSafe } from "forge-std/Vm.sol"; import { IPool } from "@aave/interfaces/IPool.sol"; import { IERC20 } from "contracts/src/interfaces/IERC20.sol"; import { AaveHyperdriveDeployer, IPool } from "contracts/src/factory/AaveHyperdriveDeployer.sol"; @@ -11,8 +12,10 @@ import { IHyperdriveDeployer } from "contracts/src/interfaces/IHyperdriveDeploye import { AssetId } from "contracts/src/libraries/AssetId.sol"; import { Errors } from "contracts/src/libraries/Errors.sol"; import { FixedPointMath } from "contracts/src/libraries/FixedPointMath.sol"; +import { ForwarderFactory } from "contracts/src/token/ForwarderFactory.sol"; import { HyperdriveTest } from "../utils/HyperdriveTest.sol"; import { HyperdriveUtils } from "../utils/HyperdriveUtils.sol"; +import { Lib } from "../utils/Lib.sol"; contract HyperdriveAaveTest is HyperdriveTest { using FixedPointMath for *; @@ -31,13 +34,16 @@ contract HyperdriveAaveTest is HyperdriveTest { address[] memory defaults = new address[](1); defaults[0] = bob; + forwarderFactory = new ForwarderFactory(); factory = new AaveHyperdriveFactory( alice, simpleDeployer, bob, bob, IHyperdrive.Fees(0, 0, 0), - defaults + defaults, + address(forwarderFactory), + forwarderFactory.ERC20LINK_HASH() ); address daiWhale = 0x075e72a5eDf65F0A5f44699c7654C1a76941Ddc8; @@ -52,33 +58,33 @@ contract HyperdriveAaveTest is HyperdriveTest { vm.startPrank(bob); dai.approve(address(hyperdrive), type(uint256).max); vm.stopPrank(); + + // Start recording event logs. + vm.recordLogs(); } + // We've just copied the values used by the original tests to ensure this runs function test_hyperdrive_aave_deploy_and_init() external { - setUp(); - // We've just copied the values used by the original tests to ensure this runs - vm.startPrank(alice); dai.approve(address(factory), type(uint256).max); uint256 apr = 1e16; // 1% apr + uint256 contribution = 2_500e18; IHyperdrive.PoolConfig memory config = IHyperdrive.PoolConfig({ baseToken: dai, initialSharePrice: FixedPointMath.ONE_18, positionDuration: 365 days, checkpointDuration: 1 days, timeStretch: HyperdriveUtils.calculateTimeStretch(apr), - governance: address(0), - feeCollector: address(0), - fees: IHyperdrive.Fees(0, 0, 0), + governance: governance, + feeCollector: feeCollector, + fees: IHyperdrive.Fees(0.05e18, 0.0005e18, 0.15e18), oracleSize: 2, updateGap: 0 }); hyperdrive = factory.deployAndInitialize( config, - bytes32(0), - address(0), new bytes32[](0), - 2500e18, + contribution, apr ); @@ -90,13 +96,25 @@ contract HyperdriveAaveTest is HyperdriveTest { ); // lp shares should equal number of share reserves initialized with - assertEq(createdShares, 2500e18); + assertEq(createdShares, contribution); + + // Verify that the correct events were emitted. + bytes32[] memory expectedExtraData = new bytes32[](1); + address aToken = pool + .getReserveData(address(hyperdrive.getPoolConfig().baseToken)) + .aTokenAddress; + expectedExtraData[0] = bytes32(uint256(uint160(aToken))); + verifyFactoryEvents( + factory, + alice, + contribution, + apr, + expectedExtraData + ); } + // We've just copied the values used by the original tests to ensure this runs function testEnsureNonZeroInit() public { - setUp(); - // We've just copied the values used by the original tests to ensure this runs - vm.startPrank(alice); dai.approve(address(factory), type(uint256).max); IHyperdrive.PoolConfig memory config = IHyperdrive.PoolConfig({ @@ -117,8 +135,6 @@ contract HyperdriveAaveTest is HyperdriveTest { vm.expectRevert(); hyperdrive = factory.deployAndInitialize( config, - bytes32(0), - address(0), new bytes32[](0), 2500e18, //1% apr diff --git a/test/integrations/HyperdriveDSRDeploy.t.sol b/test/integrations/HyperdriveDSRDeploy.t.sol index 56937ef11..0ba6aa52a 100644 --- a/test/integrations/HyperdriveDSRDeploy.t.sol +++ b/test/integrations/HyperdriveDSRDeploy.t.sol @@ -10,6 +10,7 @@ import { IHyperdrive } from "contracts/src/interfaces/IHyperdrive.sol"; import { AssetId } from "contracts/src/libraries/AssetId.sol"; import { Errors } from "contracts/src/libraries/Errors.sol"; import { FixedPointMath } from "contracts/src/libraries/FixedPointMath.sol"; +import { ForwarderFactory } from "contracts/src/token/ForwarderFactory.sol"; import { DsrManager } from "contracts/test/MockDsrHyperdrive.sol"; import { HyperdriveTest } from "../utils/HyperdriveTest.sol"; import { HyperdriveUtils } from "../utils/HyperdriveUtils.sol"; @@ -25,12 +26,13 @@ contract HyperdriveDSRTest is HyperdriveTest { function setUp() public override __mainnet_fork(16_685_972) { vm.startPrank(deployer); + // Deploy the DsrHyperdrive deployer and factory. DsrHyperdriveDeployer simpleDeployer = new DsrHyperdriveDeployer( manager ); address[] memory defaults = new address[](1); defaults[0] = bob; - + forwarderFactory = new ForwarderFactory(); factory = new DsrHyperdriveFactory( alice, simpleDeployer, @@ -38,21 +40,24 @@ contract HyperdriveDSRTest is HyperdriveTest { bob, IHyperdrive.Fees(0, 0, 0), defaults, + address(forwarderFactory), + forwarderFactory.ERC20LINK_HASH(), address(manager) ); + // Set up DAI balances for Alice and Bob. address daiWhale = 0x075e72a5eDf65F0A5f44699c7654C1a76941Ddc8; - whaleTransfer(daiWhale, dai, alice); - vm.stopPrank(); vm.startPrank(alice); dai.approve(address(hyperdrive), type(uint256).max); - vm.stopPrank(); vm.startPrank(bob); dai.approve(address(hyperdrive), type(uint256).max); vm.stopPrank(); + + // Start recording event logs. + vm.recordLogs(); } function test_hyperdrive_dsr_deploy_and_init() external { @@ -62,7 +67,8 @@ contract HyperdriveDSRTest is HyperdriveTest { vm.startPrank(alice); bytes32[] memory empty = new bytes32[](0); dai.approve(address(factory), type(uint256).max); - uint256 apr = 1e16; // 1% apr + uint256 apr = 0.01e18; // 1% apr + uint256 contribution = 2_500e18; IHyperdrive.PoolConfig memory config = IHyperdrive.PoolConfig({ baseToken: dai, initialSharePrice: FixedPointMath.ONE_18, @@ -77,10 +83,8 @@ contract HyperdriveDSRTest is HyperdriveTest { }); hyperdrive = factory.deployAndInitialize( config, - bytes32(0), - address(0), empty, - 2500e18, + contribution, apr ); @@ -93,5 +97,14 @@ contract HyperdriveDSRTest is HyperdriveTest { // lp shares should equal number of shares reserves initialized with assertEq(createdShares, 2500e18); + + // Verify that the correct events were emitted. + verifyFactoryEvents( + factory, + alice, + contribution, + apr, + new bytes32[](0) + ); } } diff --git a/test/integrations/StethHyperdrive.t.sol b/test/integrations/StethHyperdrive.t.sol index 4d346e336..df79230e5 100644 --- a/test/integrations/StethHyperdrive.t.sol +++ b/test/integrations/StethHyperdrive.t.sol @@ -13,6 +13,7 @@ import { AssetId } from "contracts/src/libraries/AssetId.sol"; import { Errors } from "contracts/src/libraries/Errors.sol"; import { FixedPointMath } from "contracts/src/libraries/FixedPointMath.sol"; import { HyperdriveMath } from "contracts/src/libraries/HyperdriveMath.sol"; +import { ForwarderFactory } from "contracts/src/token/ForwarderFactory.sol"; import { HyperdriveTest } from "test/utils/HyperdriveTest.sol"; import { HyperdriveUtils } from "test/utils/HyperdriveUtils.sol"; import { Lib } from "test/utils/Lib.sol"; @@ -34,6 +35,8 @@ contract StethHyperdriveTest is HyperdriveTest { address internal STETH_WHALE = 0x1982b2F5814301d4e9a8b0201555376e62F82428; address internal ETH_WHALE = 0x00000000219ab540356cBB839Cbe05303d7705Fa; + StethHyperdriveFactory factory; + function setUp() public override __mainnet_fork(17_376_154) { super.setUp(); @@ -44,13 +47,16 @@ contract StethHyperdriveTest is HyperdriveTest { ); address[] memory defaults = new address[](1); defaults[0] = bob; - StethHyperdriveFactory factory = new StethHyperdriveFactory( + forwarderFactory = new ForwarderFactory(); + factory = new StethHyperdriveFactory( alice, simpleDeployer, bob, bob, IHyperdrive.Fees(0, 0, 0), defaults, + address(forwarderFactory), + forwarderFactory.ERC20LINK_HASH(), LIDO ); @@ -74,8 +80,6 @@ contract StethHyperdriveTest is HyperdriveTest { uint256 contribution = 10_000e18; hyperdrive = factory.deployAndInitialize{ value: contribution }( config, - bytes32(0), - address(0), new bytes32[](0), contribution, FIXED_RATE @@ -94,6 +98,59 @@ contract StethHyperdriveTest is HyperdriveTest { accounts[1] = bob; accounts[2] = celine; fundAccounts(address(hyperdrive), IERC20(LIDO), STETH_WHALE, accounts); + + // Start recording event logs. + vm.recordLogs(); + } + + /// Deploy and Initialize /// + + function test__steth__deployAndInitialize() external { + vm.stopPrank(); + vm.startPrank(bob); + IHyperdrive.PoolConfig memory config = IHyperdrive.PoolConfig({ + baseToken: IERC20(ETH), + initialSharePrice: LIDO.getTotalPooledEther().divDown( + LIDO.getTotalShares() + ), + positionDuration: POSITION_DURATION, + checkpointDuration: CHECKPOINT_DURATION, + timeStretch: HyperdriveUtils.calculateTimeStretch(0.05e18), + governance: governance, + feeCollector: feeCollector, + fees: IHyperdrive.Fees({ curve: 0, flat: 0, governance: 0 }), + oracleSize: ORACLE_SIZE, + updateGap: UPDATE_GAP + }); + uint256 contribution = 10_000e18; + hyperdrive = factory.deployAndInitialize{ value: contribution }( + config, + new bytes32[](0), + contribution, + FIXED_RATE + ); + + // Ensure that the share reserves and LP total supply are equal and correct. + assertEq( + hyperdrive.getPoolInfo().shareReserves, + contribution.mulDivDown( + LIDO.getTotalShares(), + LIDO.getTotalPooledEther() + ) + ); + assertEq( + hyperdrive.getPoolInfo().lpTotalSupply, + hyperdrive.getPoolInfo().shareReserves + ); + + // Verify that the correct events were emitted. + verifyFactoryEvents( + factory, + bob, + contribution, + FIXED_RATE, + new bytes32[](0) + ); } /// Price Per Share /// diff --git a/test/units/hyperdrive/HyperdriveDeploy.t.sol b/test/units/hyperdrive/HyperdriveDeploy.t.sol index a6d6cd85f..9f2c24c7c 100644 --- a/test/units/hyperdrive/HyperdriveDeploy.t.sol +++ b/test/units/hyperdrive/HyperdriveDeploy.t.sol @@ -1,28 +1,27 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.19; +import { IHyperdrive } from "contracts/src/interfaces/IHyperdrive.sol"; +import { IHyperdriveDeployer } from "contracts/src/interfaces/IHyperdriveDeployer.sol"; import { AssetId } from "contracts/src/libraries/AssetId.sol"; import { Errors } from "contracts/src/libraries/Errors.sol"; import { DsrHyperdriveDeployer } from "contracts/src/factory/DsrHyperdriveDeployer.sol"; import { DsrHyperdriveFactory } from "contracts/src/factory/DsrHyperdriveFactory.sol"; -import { IHyperdrive } from "contracts/src/interfaces/IHyperdrive.sol"; -import { HyperdriveTest } from "../../utils/HyperdriveTest.sol"; +import { ForwarderFactory } from "contracts/src/token/ForwarderFactory.sol"; import { DsrManager } from "contracts/test/MockDsrHyperdrive.sol"; -import { IHyperdriveDeployer } from "contracts/src/interfaces/IHyperdriveDeployer.sol"; +import { HyperdriveTest } from "../../utils/HyperdriveTest.sol"; contract HyperdriveFactoryTest is HyperdriveTest { function test_hyperdrive_factory_admin_functions() external { + // Deploy the DsrHyperdrive factory and deployer. DsrManager manager = DsrManager( address(0x373238337Bfe1146fb49989fc222523f83081dDb) ); - DsrHyperdriveDeployer simpleDeployer = new DsrHyperdriveDeployer( manager ); - address[] memory defaults = new address[](1); defaults[0] = bob; - DsrHyperdriveFactory factory = new DsrHyperdriveFactory( alice, simpleDeployer, @@ -30,11 +29,13 @@ contract HyperdriveFactoryTest is HyperdriveTest { bob, IHyperdrive.Fees(0, 0, 0), defaults, + address(0), + bytes32(0), address(manager) ); assertEq(factory.governance(), alice); - // Bob can't change the implementations + // Bob can't change access the admin functions. vm.stopPrank(); vm.startPrank(bob); vm.expectRevert(Errors.Unauthorized.selector); @@ -44,6 +45,10 @@ contract HyperdriveFactoryTest is HyperdriveTest { vm.expectRevert(Errors.Unauthorized.selector); factory.updateHyperdriveGovernance(bob); vm.expectRevert(Errors.Unauthorized.selector); + factory.updateLinkerFactory(address(uint160(0xdeadbeef))); + vm.expectRevert(Errors.Unauthorized.selector); + factory.updateLinkerCodeHash(bytes32(uint256(0xdeadbeef))); + vm.expectRevert(Errors.Unauthorized.selector); factory.updateFeeCollector(bob); vm.expectRevert(Errors.Unauthorized.selector); factory.updateFees(IHyperdrive.Fees(1, 2, 4)); @@ -65,6 +70,10 @@ contract HyperdriveFactoryTest is HyperdriveTest { // Bob can change the other values as well. factory.updateHyperdriveGovernance(alice); assertEq(factory.hyperdriveGovernance(), alice); + factory.updateLinkerFactory(address(uint160(0xdeadbeef))); + assertEq(factory.linkerFactory(), address(uint160(0xdeadbeef))); + factory.updateLinkerCodeHash(bytes32(uint256(0xdeadbeef))); + assertEq(factory.linkerCodeHash(), bytes32(uint256(0xdeadbeef))); factory.updateFees(IHyperdrive.Fees(1, 2, 3)); (uint256 curve, uint256 flat, uint256 govFee) = factory.fees(); assertEq(curve, 1); diff --git a/test/utils/HyperdriveTest.sol b/test/utils/HyperdriveTest.sol index b8c596109..10b549431 100644 --- a/test/utils/HyperdriveTest.sol +++ b/test/utils/HyperdriveTest.sol @@ -1,7 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 pragma solidity 0.8.19; +import { VmSafe } from "forge-std/Vm.sol"; import { HyperdriveBase } from "contracts/src/HyperdriveBase.sol"; +import { HyperdriveFactory } from "contracts/src/factory/HyperdriveFactory.sol"; import { IERC20 } from "contracts/src/interfaces/IERC20.sol"; import { IHyperdrive } from "contracts/src/interfaces/IHyperdrive.sol"; import { AssetId } from "contracts/src/libraries/AssetId.sol"; @@ -11,11 +13,13 @@ import { YieldSpaceMath } from "contracts/src/libraries/YieldSpaceMath.sol"; import { ForwarderFactory } from "contracts/src/token/ForwarderFactory.sol"; import { ERC20Mintable } from "contracts/test/ERC20Mintable.sol"; import { MockHyperdrive, MockHyperdriveDataProvider } from "../mocks/MockHyperdrive.sol"; +import { Lib } from "../utils/Lib.sol"; import { BaseTest } from "./BaseTest.sol"; import { HyperdriveUtils } from "./HyperdriveUtils.sol"; contract HyperdriveTest is BaseTest { using FixedPointMath for uint256; + using Lib for *; ERC20Mintable baseToken; IHyperdrive hyperdrive; @@ -334,6 +338,15 @@ contract HyperdriveTest is BaseTest { /// Event Utils /// + event Deployed( + uint256 indexed version, + address hyperdrive, + IHyperdrive.PoolConfig config, + address linkerFactory, + bytes32 linkerCodeHash, + bytes32[] extraData + ); + event Initialize( address indexed provider, uint256 lpAmount, @@ -391,4 +404,81 @@ contract HyperdriveTest is BaseTest { uint256 baseAmount, uint256 bondAmount ); + + function verifyFactoryEvents( + HyperdriveFactory factory, + address deployer, + uint256 contribution, + uint256 apr, + bytes32[] memory expectedExtraData + ) internal { + // Ensure that the correct `Deployed` and `Initialize` events were emitted. + VmSafe.Log[] memory logs = vm.getRecordedLogs(); + + // Verify that a single `Deployed` event was emitted. + { + VmSafe.Log[] memory filteredLogs = logs.filterLogs( + Deployed.selector + ); + assertEq(filteredLogs.length, 1); + VmSafe.Log memory log = filteredLogs[0]; + + // Verify the event topics. + assertEq(log.topics[0], Deployed.selector); + assertEq(uint256(log.topics[1]), factory.versionCounter()); + + // Verify the event data. + ( + address eventHyperdrive, + IHyperdrive.PoolConfig memory eventConfig, + address eventLinkerFactory, + bytes32 eventLinkerCodeHash, + bytes32[] memory eventExtraData + ) = abi.decode( + log.data, + ( + address, + IHyperdrive.PoolConfig, + address, + bytes32, + bytes32[] + ) + ); + assertEq(eventHyperdrive, address(hyperdrive)); + assertEq( + keccak256(abi.encode(eventConfig)), + keccak256(abi.encode(hyperdrive.getPoolConfig())) + ); + assertEq(eventLinkerFactory, address(forwarderFactory)); + assertEq(eventLinkerCodeHash, forwarderFactory.ERC20LINK_HASH()); + assertEq( + keccak256(abi.encode(eventExtraData)), + keccak256(abi.encode(expectedExtraData)) + ); + } + + // Verify that the second log is the expected `Initialize` event. + { + VmSafe.Log[] memory filteredLogs = Lib.filterLogs( + logs, + Initialize.selector + ); + assertEq(filteredLogs.length, 1); + VmSafe.Log memory log = filteredLogs[0]; + + // Verify the event topics. + assertEq(log.topics[0], Initialize.selector); + assertEq(address(uint160(uint256(log.topics[1]))), deployer); + + // Verify the event data. + ( + uint256 eventLpAmount, + uint256 eventBaseAmount, + uint256 eventApr + ) = abi.decode(log.data, (uint256, uint256, uint256)); + assertEq(eventLpAmount, hyperdrive.getPoolInfo().shareReserves); + assertEq(eventBaseAmount, contribution); + assertEq(eventApr, apr); + } + } }