diff --git a/Dockerfile b/Dockerfile index 251a55f8a..7e30b897a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,8 +16,8 @@ COPY ./test/ ./test/ COPY ./foundry.toml ./foundry.toml # Copy the script used to run the migrations and set its permissions. -COPY ./run_migrations.sh ./run_migrations.sh -RUN chmod a+x ./run_migrations.sh +COPY ./migrate.sh ./migrate.sh +RUN chmod a+x ./migrate.sh # Install the dependencies and compile the contracts. RUN forge install && forge build @@ -33,6 +33,6 @@ ENV RPC_URL=http://localhost:8545 # the post-migrations state. RUN anvil --dump-state ./data & \ ANVIL="$!" && \ - ./run_migrations.sh && \ + ./migrate.sh && \ kill $ANVIL && \ sleep 1s # HACK(jalextowle): Ensure that "./data" is written before exiting. diff --git a/contracts/test/MockERC4626.sol b/contracts/test/MockERC4626.sol new file mode 100644 index 000000000..e6f1967f7 --- /dev/null +++ b/contracts/test/MockERC4626.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.19; + +import { ERC20 } from "solmate/tokens/ERC20.sol"; +import { ERC4626 } from "solmate/mixins/ERC4626.sol"; +import { FixedPointMath } from "../src/libraries/FixedPointMath.sol"; +import { ERC20Mintable } from "./ERC20Mintable.sol"; + +/// @author DELV +/// @title MockERC4626 +/// @notice This mock yield source will accrue interest at a specified rate +/// Every stateful interaction will accrue interest, so the interest +/// accrual will approximate continuous compounding as the contract +/// is called more frequently. +/// @custom:disclaimer The language used in this code is for coding convenience +/// only, and is not intended to, and does not, have any +/// particular legal or regulatory significance. +contract MockERC4626 is ERC4626 { + using FixedPointMath for uint256; + + uint256 internal _rate; + uint256 internal _lastUpdated; + + constructor( + ERC20Mintable _asset, + string memory _name, + string memory _symbol, + uint256 _initialRate + ) ERC4626(ERC20(address(_asset)), _name, _symbol) { + _rate = _initialRate; + _lastUpdated = block.timestamp; + } + + /// Overrides /// + + function deposit( + uint256 _assets, + address _receiver + ) public override returns (uint256) { + _accrue(); + return super.deposit(_assets, _receiver); + } + + function mint( + uint256 _shares, + address _receiver + ) public override returns (uint256) { + _accrue(); + return super.mint(_shares, _receiver); + } + + function withdraw( + uint256 _assets, + address _receiver, + address _owner + ) public override returns (uint256) { + _accrue(); + return super.withdraw(_assets, _receiver, _owner); + } + + function redeem( + uint256 _shares, + address _receiver, + address _owner + ) public override returns (uint256) { + _accrue(); + return super.redeem(_shares, _receiver, _owner); + } + + function totalAssets() public view override returns (uint256) { + return asset.balanceOf(address(this)) + _getAccruedInterest(); + } + + /// Mock /// + + function setRate(uint256 _rate_) external { + _accrue(); + _rate = _rate_; + } + + function getRate() external view returns (uint256) { + return _rate; + } + + function _accrue() internal { + ERC20Mintable(address(asset)).mint(_getAccruedInterest()); + _lastUpdated = block.timestamp; + } + + function _getAccruedInterest() internal view returns (uint256) { + // base_balance = base_balance * (1 + r * t) + uint256 timeElapsed = (block.timestamp - _lastUpdated).divDown( + 365 days + ); + return + asset.balanceOf(address(this)).mulDown(_rate.mulDown(timeElapsed)); + } +} diff --git a/contracts/test/MockHyperdriveTestnet.sol b/contracts/test/MockHyperdriveTestnet.sol deleted file mode 100644 index de65131aa..000000000 --- a/contracts/test/MockHyperdriveTestnet.sol +++ /dev/null @@ -1,208 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.19; - -import { ERC20PresetMinterPauser } from "openzeppelin-contracts/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol"; -import { Hyperdrive } from "../src/Hyperdrive.sol"; -import { HyperdriveDataProvider } from "../src/HyperdriveDataProvider.sol"; -import { IERC20 } from "../src/interfaces/IERC20.sol"; -import { IHyperdrive } from "../src/interfaces/IHyperdrive.sol"; -import { FixedPointMath } from "../src/libraries/FixedPointMath.sol"; -import { MultiTokenDataProvider } from "../src/token/MultiTokenDataProvider.sol"; -import { ERC20Mintable } from "./ERC20Mintable.sol"; - -contract MockHyperdriveTestnet is Hyperdrive { - using FixedPointMath for uint256; - - uint256 internal rate; - uint256 internal lastUpdated; - uint256 internal totalShares; - - constructor( - address _dataProvider, - ERC20Mintable _baseToken, - uint256 _initialRate, - uint256 _initialSharePrice, - uint256 _minimumShareReserves, - uint256 _positionDuration, - uint256 _checkpointDuration, - uint256 _timeStretch, - IHyperdrive.Fees memory _fees, - address _governance - ) - Hyperdrive( - IHyperdrive.PoolConfig({ - baseToken: IERC20(address(_baseToken)), - initialSharePrice: _initialSharePrice, - minimumShareReserves: _minimumShareReserves, - positionDuration: _positionDuration, - checkpointDuration: _checkpointDuration, - timeStretch: _timeStretch, - governance: _governance, - feeCollector: _feeCollector, - fees: _fees, - oracleSize: 2, - updateGap: 0 - }), - _dataProvider, - bytes32(0), - address(0) - ) - { - rate = _initialRate; - lastUpdated = block.timestamp; - } - - /// Overrides /// - - error UnsupportedOption(); - - function _deposit( - uint256 _amount, - bool _asUnderlying - ) internal override returns (uint256 sharesMinted, uint256 sharePrice) { - if (!_asUnderlying) revert UnsupportedOption(); - - // Accrue interest. - accrueInterest(); - - // Take custody of the base. - bool success = _baseToken.transferFrom( - msg.sender, - address(this), - _amount - ); - if (!success) { - revert IHyperdrive.TransferFailed(); - } - - // Update the total shares calculation. - if (totalShares == 0) { - totalShares = _amount; - return (_amount, FixedPointMath.ONE_18); - } else { - sharePrice = _pricePerShare(); - sharesMinted = _amount.divDown(sharePrice); - totalShares += sharesMinted; - return (sharesMinted, sharePrice); - } - } - - function _withdraw( - uint256 _shares, - address _destination, - bool _asUnderlying - ) internal override returns (uint256 amountWithdrawn) { - if (!_asUnderlying) revert UnsupportedOption(); - - // Accrue interest. - accrueInterest(); - - // Transfer the base to the destination. - uint256 sharePrice = _pricePerShare(); - amountWithdrawn = _shares.mulDown(sharePrice); - bool success = _baseToken.transfer(_destination, amountWithdrawn); - if (!success) { - revert IHyperdrive.TransferFailed(); - } - - // Remove shares from the total supply - totalShares -= _shares; - - return amountWithdrawn; - } - - function _pricePerShare() internal view override returns (uint256) { - uint256 underlying = _baseToken.balanceOf(address(this)) + - getAccruedInterest(); - return underlying.divDown(totalShares); - } - - /// Configuration /// - - function setRate(uint256 _rate) external { - // Accrue interest. - accrueInterest(); - - // Update the rate. - rate = _rate; - } - - /// Helpers /// - - function getAccruedInterest() internal view returns (uint256) { - // base_balance = base_balance * (1 + r * t) - uint256 timeElapsed = (block.timestamp - lastUpdated).divDown(365 days); - return - _baseToken.balanceOf(address(this)).mulDown( - rate.mulDown(timeElapsed) - ); - } - - function accrueInterest() internal { - ERC20Mintable(address(_baseToken)).mint(getAccruedInterest()); - lastUpdated = block.timestamp; - } -} - -contract MockHyperdriveDataProviderTestnet is - MultiTokenDataProvider, - HyperdriveDataProvider -{ - using FixedPointMath for uint256; - - uint256 internal rate; - uint256 internal lastUpdated; - uint256 internal totalShares; - - constructor( - ERC20Mintable _baseToken, - uint256 _initialRate, - uint256 _initialSharePrice, - uint256 _minimumShareReserves, - uint256 _positionDuration, - uint256 _checkpointDuration, - uint256 _timeStretch, - IHyperdrive.Fees memory _fees, - address _governance - ) - HyperdriveDataProvider( - IHyperdrive.PoolConfig({ - baseToken: IERC20(address(_baseToken)), - initialSharePrice: _initialSharePrice, - minimumShareReserves: _minimumShareReserves, - positionDuration: _positionDuration, - checkpointDuration: _checkpointDuration, - timeStretch: _timeStretch, - governance: _governance, - feeCollector: _feeCollector, - fees: _fees, - oracleSize: 2, - updateGap: 0 - }) - ) - MultiTokenDataProvider(_linkerCodeHash, _factory) - {} - - function getRate() external view returns (uint256) { - _revert(abi.encode(rate)); - } - - /// Overrides /// - - function _pricePerShare() internal view override returns (uint256) { - uint256 underlying = _baseToken.balanceOf(address(this)) + - getAccruedInterest(); - return underlying.divDown(totalShares); - } - - /// Helpers /// - - function getAccruedInterest() internal view returns (uint256) { - // base_balance = base_balance * (1 + r * t) - uint256 timeElapsed = (block.timestamp - lastUpdated).divDown(365 days); - return - _baseToken.balanceOf(address(this)).mulDown( - rate.mulDown(timeElapsed) - ); - } -} diff --git a/run_migrations.sh b/migrate.sh similarity index 81% rename from run_migrations.sh rename to migrate.sh index 4db58a81f..9d8933270 100755 --- a/run_migrations.sh +++ b/migrate.sh @@ -1,5 +1,4 @@ #!/bin/sh - set -ex # Sleep for a few seconds to allow the Ethereum service to start up. @@ -8,8 +7,8 @@ sleep 2 # Create an artifacts directory if it doesn't already exist. mkdir -p ./artifacts -# Deploy the MockHyperdrive instance and the MockHyperdriveMath contract. -forge script script/MockHyperdrive.s.sol:MockHyperdriveScript \ +# Execute the devnet migration. +forge script script/DevnetMigration.s.sol:DevnetMigration \ --sender "${ETH_FROM}" \ --private-key "${PRIVATE_KEY}" \ --rpc-url "${RPC_URL}" \ diff --git a/python/contract_size.py b/python/contract_size.py index 147ac8c57..1ac3489f0 100644 --- a/python/contract_size.py +++ b/python/contract_size.py @@ -41,11 +41,7 @@ def should_check_code_size(artifact): compilation_target = get_compilation_target(artifact) if not compilation_target: return False - return ( - "contracts/src/" in compilation_target - or "MockHyperdriveTestnet" in compilation_target - or "MockMakerDsrHyperdrive" in compilation_target - ) + return "contracts/src/" in compilation_target ARTIFACTS_PATH = sys.argv[1] diff --git a/script/CloseLong.s.sol b/script/CloseLong.s.sol deleted file mode 100644 index 53b98f9db..000000000 --- a/script/CloseLong.s.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.19; - -import "forge-std/Script.sol"; -import "forge-std/console.sol"; - -import { IERC20 } from "contracts/src/interfaces/IERC20.sol"; -import { AssetId } from "contracts/src/libraries/AssetId.sol"; -import { FixedPointMath } from "contracts/src/libraries/FixedPointMath.sol"; -import { IHyperdrive } from "contracts/src/interfaces/IHyperdrive.sol"; -import { ERC20Mintable } from "contracts/test/ERC20Mintable.sol"; -import { HyperdriveUtils } from "test/utils/HyperdriveUtils.sol"; -import { Lib } from "test/utils/Lib.sol"; - -contract CloseLongScript is Script { - using FixedPointMath for uint256; - using HyperdriveUtils for IHyperdrive; - using Lib for *; - - IHyperdrive internal constant HYPERDRIVE = - IHyperdrive(0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9); - ERC20Mintable internal constant BASE = - ERC20Mintable(0x5FbDB2315678afecb367f032d93F642f64180aa3); - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - uint256 maturityTime = 1719878400; - - uint256 baseProceeds = HYPERDRIVE.closeLong( - maturityTime, - 10_000e18, - 0, - msg.sender, - true - ); - - console.log( - "Bob closed a long position for %s base", - baseProceeds.toString(18) - ); - console.log( - "Bob's long balance is now %s bonds", - HYPERDRIVE.balanceOf( - AssetId.encodeAssetId( - AssetId.AssetIdPrefix.Long, - HYPERDRIVE.latestCheckpoint() + - HYPERDRIVE.getPoolConfig().positionDuration - ), - msg.sender - ) - ); - - vm.stopBroadcast(); - } - - function createUser(string memory name) public returns (address _user) { - _user = address(uint160(uint256(keccak256(abi.encode(name))))); - vm.label(_user, name); - vm.deal(_user, 10000 ether); - } -} diff --git a/script/DevnetMigration.s.sol b/script/DevnetMigration.s.sol new file mode 100644 index 000000000..62438ffe7 --- /dev/null +++ b/script/DevnetMigration.s.sol @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.19; + +import { Script } from "forge-std/Script.sol"; +import { stdJson } from "forge-std/StdJson.sol"; +import { ERC4626HyperdriveDeployer } from "contracts/src/factory/ERC4626HyperdriveDeployer.sol"; +import { ERC4626HyperdriveFactory } from "contracts/src/factory/ERC4626HyperdriveFactory.sol"; +import { HyperdriveFactory } from "contracts/src/factory/HyperdriveFactory.sol"; +import { IERC20 } from "contracts/src/interfaces/IERC20.sol"; +import { IERC4626 } from "contracts/src/interfaces/IERC4626.sol"; +import { IHyperdrive } from "contracts/src/interfaces/IHyperdrive.sol"; +import { ForwarderFactory } from "contracts/src/token/ForwarderFactory.sol"; +import { ERC20Mintable } from "contracts/test/ERC20Mintable.sol"; +import { MockERC4626 } from "contracts/test/MockERC4626.sol"; +import { MockHyperdriveMath } from "contracts/test/MockHyperdriveMath.sol"; +import { HyperdriveUtils } from "test/utils/HyperdriveUtils.sol"; + +/// @author DELV +/// @title DevnetMigration +/// @notice This script deploys a mock ERC4626 yield source and a Hyperdrive +/// factory on top of it. For convenience, it also deploys a Hyperdrive +/// pool with a 1 week duration. +/// @custom:disclaimer The language used in this code is for coding convenience +/// only, and is not intended to, and does not, have any +/// particular legal or regulatory significance. +contract DevnetMigration is Script { + using stdJson for string; + using HyperdriveUtils for *; + + function setUp() external {} + + function run() external { + vm.startBroadcast(); + + // Deploy the base token and yield source. + ERC20Mintable baseToken = new ERC20Mintable(); + MockERC4626 pool = new MockERC4626( + baseToken, + "Delvnet Yield Source", + "DELV", + 0.05e18 // initial rate of 5% + ); + + // Deploy the Hyperdrive factory. + ERC4626HyperdriveFactory factory; + { + address[] memory defaultPausers = new address[](1); + defaultPausers[0] = msg.sender; + HyperdriveFactory.FactoryConfig + memory factoryConfig = HyperdriveFactory.FactoryConfig({ + governance: msg.sender, + hyperdriveGovernance: msg.sender, + feeCollector: msg.sender, + fees: IHyperdrive.Fees({ + curve: 0.1e18, // 10% + flat: 0.0005e18, // 0.05% + governance: 0.15e18 // 15% + }), + maxFees: IHyperdrive.Fees({ + curve: 0.3e18, // 30% + flat: 0.0015e18, // 0.15% + governance: 0.30e18 // 30% + }), + defaultPausers: defaultPausers + }); + ForwarderFactory forwarderFactory = new ForwarderFactory(); + ERC4626HyperdriveDeployer deployer = new ERC4626HyperdriveDeployer( + IERC4626(address(pool)) + ); + factory = new ERC4626HyperdriveFactory( + factoryConfig, + deployer, + address(forwarderFactory), + forwarderFactory.ERC20LINK_HASH(), + IERC4626(address(pool)), + new address[](0) + ); + } + + // Deploy and initialize a 1 week pool for the devnet. + IHyperdrive hyperdrive; + { + uint256 contribution = 100_000_000e18; + uint256 fixedRate = 0.05e18; + baseToken.mint(msg.sender, contribution); + baseToken.approve(address(factory), contribution); + IHyperdrive.PoolConfig memory poolConfig = IHyperdrive.PoolConfig({ + baseToken: IERC20(address(baseToken)), + initialSharePrice: 1e18, + minimumShareReserves: 10e18, + positionDuration: 1 weeks, + checkpointDuration: 1 hours, + timeStretch: 0.05e18.calculateTimeStretch(), + governance: msg.sender, + feeCollector: msg.sender, + fees: IHyperdrive.Fees({ + curve: 0.1e18, // 10% + flat: 0.0005e18, // 0.05% + governance: 0.15e18 // 15% + }), + oracleSize: 10, + updateGap: 1 hours + }); + hyperdrive = factory.deployAndInitialize( + poolConfig, + new bytes32[](0), + contribution, + fixedRate + ); + } + + // Deploy the MockHyperdriveMath contract. + MockHyperdriveMath mockHyperdriveMath = new MockHyperdriveMath(); + + vm.stopBroadcast(); + + // Writes the addresses to a file. + string memory result = "result"; + vm.serializeAddress(result, "baseToken", address(baseToken)); + vm.serializeAddress(result, "hyperdriveFactory", address(factory)); + // TODO: This is deprecated and should be removed by 0.0.11. + vm.serializeAddress(result, "mockHyperdrive", address(hyperdrive)); + result = vm.serializeAddress( + result, + "mockHyperdriveMath", + address(mockHyperdriveMath) + ); + result.write("./artifacts/script_addresses.json"); + } +} diff --git a/script/DevnetSmokeTest.s.sol b/script/DevnetSmokeTest.s.sol new file mode 100644 index 000000000..0822e376f --- /dev/null +++ b/script/DevnetSmokeTest.s.sol @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.19; + +import { Script } from "forge-std/Script.sol"; +import { console } from "forge-std/console.sol"; +import { IERC20 } from "contracts/src/interfaces/IERC20.sol"; +import { AssetId } from "contracts/src/libraries/AssetId.sol"; +import { FixedPointMath } from "contracts/src/libraries/FixedPointMath.sol"; +import { IHyperdrive } from "contracts/src/interfaces/IHyperdrive.sol"; +import { ERC20Mintable } from "contracts/test/ERC20Mintable.sol"; +import { HyperdriveUtils } from "test/utils/HyperdriveUtils.sol"; +import { Lib } from "test/utils/Lib.sol"; + +/// @author DELV +/// @title DevnetSmokeTest +/// @notice This script executes a smoke test against a Hyperdrive devnet. +/// @custom:disclaimer The language used in this code is for coding convenience +/// only, and is not intended to, and does not, have any +/// particular legal or regulatory significance. +contract DevnetSmokeTest is Script { + using FixedPointMath for uint256; + using HyperdriveUtils for IHyperdrive; + using Lib for *; + + IHyperdrive internal HYPERDRIVE = IHyperdrive(vm.envAddress("HYPERDRIVE")); + ERC20Mintable internal BASE = ERC20Mintable(vm.envAddress("BASE")); + + function setUp() external {} + + function run() external { + // Execute all transactions with the ETH_FROM address. + vm.startBroadcast(msg.sender); + + console.log("Starting pool info:"); + _logInfo(); + console.log(""); + + // Execute the smoke tests. + _testLp(); + _testLong(); + _testShort(); + + console.log("Ending pool info:"); + _logInfo(); + console.log(""); + + vm.stopBroadcast(); + } + + function _testLong() internal { + // Open a long. + console.log("sender=%s: Opening a long position...", msg.sender); + BASE.mint(msg.sender, 10_000e18); + BASE.approve(address(HYPERDRIVE), 10_000e18); + (uint256 maturityTime, uint256 bondAmount) = HYPERDRIVE.openLong( + 10_000e18, + 0, + msg.sender, + true + ); + console.log( + "sender=%s: Opened a long position: maturity=%s, amount=%s", + msg.sender, + maturityTime, + bondAmount.toString(18) + ); + + // Close the long. + console.log("sender=%s: Closing the long position..."); + uint256 baseProceeds = HYPERDRIVE.closeLong( + maturityTime, + bondAmount, + 0, + msg.sender, + true + ); + console.log( + "sender=%s: Closed the long position: baseProceeds=%s", + msg.sender, + baseProceeds.toString(18) + ); + } + + function _testLp() internal { + // Add liquidity. + console.log("sender=%s: Adding liquidity...", msg.sender); + BASE.mint(msg.sender, 10_000e18); + BASE.approve(address(HYPERDRIVE), 10_000e18); + uint256 lpShares = HYPERDRIVE.addLiquidity( + 10_000e18, + 0, + type(uint256).max, + msg.sender, + true + ); + console.log( + "sender=%s: Added liquidity: lpShares=%s", + msg.sender, + lpShares.toString(18) + ); + + // Removing liquidity. + console.log("sender=%s: Removing liquidity...", msg.sender); + (uint256 proceeds, uint256 withdrawalShares) = HYPERDRIVE + .removeLiquidity(lpShares, 0, msg.sender, true); + console.log( + "sender=%s: Removed liquidity: proceeds=%s, withdrawalShares=%s", + msg.sender, + proceeds.toString(18), + withdrawalShares.toString(18) + ); + } + + function _testShort() internal { + // Open a short. + console.log("sender=%s: Opening a short position...", msg.sender); + BASE.mint(msg.sender, 10_000e18); + BASE.approve(address(HYPERDRIVE), 10_000e18); + (uint256 maturityTime, uint256 bondAmount) = HYPERDRIVE.openShort( + 10_000e18, + type(uint256).max, + msg.sender, + true + ); + console.log( + "sender=%s: Opened a short position: maturity=%s, amount=%s", + msg.sender, + maturityTime, + bondAmount.toString(18) + ); + + // Close the short. + console.log("sender=%s: Closing the short position..."); + uint256 baseProceeds = HYPERDRIVE.closeShort( + maturityTime, + bondAmount, + 0, + msg.sender, + true + ); + console.log( + "sender=%s: Closed the short position: baseProceeds=%s", + msg.sender, + baseProceeds.toString(18) + ); + } + + function _logInfo() internal view { + IHyperdrive.PoolInfo memory info = HYPERDRIVE.getPoolInfo(); + console.log("shareReserves: %s", info.shareReserves.toString(18)); + console.log("bondReserves: %s", info.bondReserves.toString(18)); + console.log("sharePrice: %s", info.sharePrice.toString(18)); + console.log("longsOutstanding: %s", info.longsOutstanding.toString(18)); + console.log( + "shortsOutstanding: %s", + info.shortsOutstanding.toString(18) + ); + } +} diff --git a/script/Faucet.s.sol b/script/Faucet.s.sol deleted file mode 100644 index 496e8fa8b..000000000 --- a/script/Faucet.s.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.19; - -import "forge-std/Script.sol"; - -interface Faucet { - function mint(address token, address to, uint256 amount) external; -} - -contract FaucetScript is Script { - function setUp() public {} - - function run() public { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address deployerAddress = vm.addr(deployerPrivateKey); - vm.startBroadcast(deployerPrivateKey); - - // Mint dai token to deployer address - address dai = address(0x11fE4B6AE13d2a6055C8D9cF65c55bac32B5d844); - Faucet faucet = Faucet( - address(0xe2bE5BfdDbA49A86e27f3Dd95710B528D43272C2) - ); - faucet.mint(dai, deployerAddress, 50_000e18); - - vm.stopBroadcast(); - } -} diff --git a/script/Hyperdrive.s.sol b/script/Hyperdrive.s.sol deleted file mode 100644 index ccf1de481..000000000 --- a/script/Hyperdrive.s.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.19; - -import "forge-std/Script.sol"; - -contract HyperdriveScript is Script { - function setUp() public {} - - function run() public { - vm.broadcast(); - } -} diff --git a/script/HyperdriveInitialize.s.sol b/script/HyperdriveInitialize.s.sol deleted file mode 100644 index 562acfc40..000000000 --- a/script/HyperdriveInitialize.s.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.19; - -import "forge-std/Script.sol"; -import { FixedPointMath } from "contracts/src/libraries/FixedPointMath.sol"; -import { IHyperdrive } from "contracts/src/interfaces/IHyperdrive.sol"; -import { HyperdriveUtils } from "test/utils/HyperdriveUtils.sol"; -import { IERC20 } from "contracts/src/interfaces/IERC20.sol"; - -contract HyperdriveInitialize is Script { - using FixedPointMath for uint256; - - function setUp() public {} - - function run() public { - uint256 deployerPrivateKey = vm.envUint("PRIVATE_KEY"); - address deployerAddress = vm.addr(deployerPrivateKey); - vm.startBroadcast(deployerPrivateKey); - - IHyperdrive hyperdrive = IHyperdrive( - address(0xB311B825171AF5A60d69aAD590B857B1E5ed23a2) - ); - IERC20 baseToken = IERC20(hyperdrive.baseToken()); - // Initialize Hyperdrive to have an APR equal to 1%. - uint256 apr = 0.01e18; - uint256 contribution = 50_000e18; - baseToken.approve(address(hyperdrive), contribution); - hyperdrive.initialize(contribution, apr, deployerAddress, true); - - vm.stopBroadcast(); - } -} diff --git a/script/MockHyperdrive.s.sol b/script/MockHyperdrive.s.sol deleted file mode 100644 index 46627f841..000000000 --- a/script/MockHyperdrive.s.sol +++ /dev/null @@ -1,78 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity ^0.8.13; - -import { stdJson } from "forge-std/StdJson.sol"; -import { Script } from "forge-std/Script.sol"; -import { FixedPointMath } from "contracts/src/libraries/FixedPointMath.sol"; -import { MockHyperdriveMath } from "contracts/test/MockHyperdriveMath.sol"; -import { ERC20Mintable } from "contracts/test/ERC20Mintable.sol"; -import { MockHyperdriveTestnet, MockHyperdriveDataProviderTestnet } from "contracts/test/MockHyperdriveTestnet.sol"; -import { IHyperdrive } from "contracts/src/interfaces/IHyperdrive.sol"; - -contract MockHyperdriveScript is Script { - using stdJson for string; - using FixedPointMath for uint256; - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - // Mock ERC20 - ERC20Mintable baseToken = new ERC20Mintable(); - baseToken.mint(1_000_000e18); - - // Mock Hyperdrive, 1 year term - MockHyperdriveDataProviderTestnet dataProvider = new MockHyperdriveDataProviderTestnet( - baseToken, - 5e18, - FixedPointMath.ONE_18, - FixedPointMath.ONE_18, - 365 days, - 1 days, - FixedPointMath.ONE_18.divDown(22.186877016851916266e18), - IHyperdrive.Fees({ - curve: 0.1e18, - flat: 0.05e18, - governance: 0.1e18 - }), - address(0) - ); - MockHyperdriveTestnet hyperdrive = new MockHyperdriveTestnet( - address(dataProvider), - baseToken, - 5e18, - FixedPointMath.ONE_18, - FixedPointMath.ONE_18, - 365 days, - 1 days, - FixedPointMath.ONE_18.divDown(22.186877016851916266e18), - IHyperdrive.Fees({ - curve: 0.1e18, - flat: 0.05e18, - governance: 0.1e18 - }), - address(0) - ); - - // Initializes the Hyperdrive pool. - baseToken.approve(address(hyperdrive), 10_000_000e18); - hyperdrive.initialize(100_000e18, 0.05e18, msg.sender, true); - - // Deploy the MockHyperdriveMath contract. - MockHyperdriveMath mockHyperdriveMath = new MockHyperdriveMath(); - - vm.stopBroadcast(); - - // Writes the addresses to a file. - string memory result = "result"; - vm.serializeAddress(result, "baseToken", address(baseToken)); - vm.serializeAddress(result, "mockHyperdrive", address(hyperdrive)); - result = vm.serializeAddress( - result, - "mockHyperdriveMath", - address(mockHyperdriveMath) - ); - result.write("./artifacts/script_addresses.json"); - } -} diff --git a/script/MockHyperdriveMath.s.sol b/script/MockHyperdriveMath.s.sol deleted file mode 100644 index 04d4021fb..000000000 --- a/script/MockHyperdriveMath.s.sol +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.19; - -import { stdJson } from "forge-std/StdJson.sol"; -import { Script } from "forge-std/Script.sol"; -import { console } from "forge-std/console.sol"; -import { MockHyperdriveMath } from "contracts/test/MockHyperdriveMath.sol"; - -contract MockHyperdriveMathScript is Script { - using stdJson for string; - - function setUp() external {} - - function run() external { - vm.startBroadcast(); - - MockHyperdriveMath mockHyperdriveMath = new MockHyperdriveMath(); - - vm.stopBroadcast(); - - string memory result = "result"; - result = vm.serializeAddress( - result, - "mockHyperdriveMath", - address(mockHyperdriveMath) - ); - result.write("./artifacts/script_addresses.json"); - } -} diff --git a/script/OpenLong.s.sol b/script/OpenLong.s.sol deleted file mode 100644 index b41f0179d..000000000 --- a/script/OpenLong.s.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -pragma solidity 0.8.19; - -import "forge-std/Script.sol"; -import "forge-std/console.sol"; - -import { IERC20 } from "contracts/src/interfaces/IERC20.sol"; -import { AssetId } from "contracts/src/libraries/AssetId.sol"; -import { FixedPointMath } from "contracts/src/libraries/FixedPointMath.sol"; -import { IHyperdrive } from "contracts/src/interfaces/IHyperdrive.sol"; -import { ERC20Mintable } from "contracts/test/ERC20Mintable.sol"; -import { HyperdriveUtils } from "test/utils/HyperdriveUtils.sol"; -import { Lib } from "test/utils/Lib.sol"; - -contract OpenLongScript is Script { - using FixedPointMath for uint256; - using HyperdriveUtils for IHyperdrive; - using Lib for *; - - IHyperdrive internal constant HYPERDRIVE = - IHyperdrive(0xCf7Ed3AccA5a467e9e704C703E8D87F634fB0Fc9); - ERC20Mintable internal constant BASE = - ERC20Mintable(0x5FbDB2315678afecb367f032d93F642f64180aa3); - - function setUp() public {} - - function run() public { - vm.startBroadcast(); - - BASE.mint(msg.sender, 10_000e18); - BASE.approve(address(HYPERDRIVE), 10_000e18); - (uint256 maturityTime, uint256 longAmount) = HYPERDRIVE.openLong( - 10_000e18, - 0, - msg.sender, - true - ); - - console.log( - "Bob opened a long position of %s bonds that matures in timestamp %s", - longAmount, - maturityTime - ); - console.log( - "Bob's long balance is now %s bonds", - HYPERDRIVE.balanceOf( - AssetId.encodeAssetId( - AssetId.AssetIdPrefix.Long, - HYPERDRIVE.latestCheckpoint() + - HYPERDRIVE.getPoolConfig().positionDuration - ), - msg.sender - ) - ); - - vm.stopBroadcast(); - } - - function createUser(string memory name) public returns (address _user) { - _user = address(uint160(uint256(keccak256(abi.encode(name))))); - vm.label(_user, name); - vm.deal(_user, 10000 ether); - } -} diff --git a/smoke_test.sh b/smoke_test.sh new file mode 100755 index 000000000..7937bee59 --- /dev/null +++ b/smoke_test.sh @@ -0,0 +1,13 @@ +#!/bin/sh +set -ex + +# Sleep for a few seconds to allow the Ethereum service to start up. +sleep 2 + +# Execute the devnet smoke test. +forge script script/DevnetSmokeTest.s.sol:DevnetSmokeTest \ + --sender "${ETH_FROM}" \ + --private-key "${PRIVATE_KEY}" \ + --rpc-url "${RPC_URL}" \ + --slow \ + --broadcast diff --git a/test/utils/HyperdriveUtils.sol b/test/utils/HyperdriveUtils.sol index e4beea906..1b4729791 100644 --- a/test/utils/HyperdriveUtils.sol +++ b/test/utils/HyperdriveUtils.sol @@ -320,4 +320,185 @@ library HyperdriveUtils { hyperdrive.totalSupply(AssetId._WITHDRAWAL_SHARE_ASSET_ID) - hyperdrive.getPoolInfo().withdrawalSharesReadyToWithdraw; } + + function decodeError( + bytes memory _error + ) internal pure returns (string memory) { + // All of the Hyperdrive errors have a selector. + if (_error.length < 4) { + revert("Invalid error"); + } + + // Convert the selector to the correct error message. + bytes4 selector; + assembly { + selector := mload(add(_error, 0x20)) + } + return selector.decodeError(); + } + + function decodeError( + bytes4 _selector + ) internal pure returns (string memory) { + // Convert the selector to the correct error message. + if (_selector == IHyperdrive.BaseBufferExceedsShareReserves.selector) { + return "BaseBufferExceedsShareReserves"; + } + if (_selector == IHyperdrive.BelowMinimumContribution.selector) { + return "BelowMinimumContribution"; + } + if (_selector == IHyperdrive.BelowMinimumShareReserves.selector) { + return "BelowMinimumShareReserves"; + } + if (_selector == IHyperdrive.InvalidApr.selector) { + return "InvalidApr"; + } + if (_selector == IHyperdrive.InvalidBaseToken.selector) { + return "InvalidBaseToken"; + } + if (_selector == IHyperdrive.InvalidCheckpointTime.selector) { + return "InvalidCheckpointTime"; + } + if (_selector == IHyperdrive.InvalidCheckpointDuration.selector) { + return "InvalidCheckpointDuration"; + } + if (_selector == IHyperdrive.InvalidInitialSharePrice.selector) { + return "InvalidInitialSharePrice"; + } + if (_selector == IHyperdrive.InvalidMaturityTime.selector) { + return "InvalidMaturityTime"; + } + if (_selector == IHyperdrive.InvalidMinimumShareReserves.selector) { + return "InvalidMinimumShareReserves"; + } + if (_selector == IHyperdrive.InvalidPositionDuration.selector) { + return "InvalidPositionDuration"; + } + if (_selector == IHyperdrive.InvalidShareReserves.selector) { + return "InvalidShareReserves"; + } + if (_selector == IHyperdrive.InvalidFeeAmounts.selector) { + return "InvalidFeeAmounts"; + } + if (_selector == IHyperdrive.NegativeInterest.selector) { + return "NegativeInterest"; + } + if (_selector == IHyperdrive.OutputLimit.selector) { + return "OutputLimit"; + } + if (_selector == IHyperdrive.Paused.selector) { + return "Paused"; + } + if (_selector == IHyperdrive.PoolAlreadyInitialized.selector) { + return "PoolAlreadyInitialized"; + } + if (_selector == IHyperdrive.TransferFailed.selector) { + return "TransferFailed"; + } + if (_selector == IHyperdrive.UnexpectedAssetId.selector) { + return "UnexpectedAssetId"; + } + if (_selector == IHyperdrive.UnexpectedSender.selector) { + return "UnexpectedSender"; + } + if (_selector == IHyperdrive.UnsupportedToken.selector) { + return "UnsupportedToken"; + } + if (_selector == IHyperdrive.ApprovalFailed.selector) { + return "ApprovalFailed"; + } + if (_selector == IHyperdrive.ZeroAmount.selector) { + return "ZeroAmount"; + } + if (_selector == IHyperdrive.ZeroLpTotalSupply.selector) { + return "ZeroLpTotalSupply"; + } + if (_selector == IHyperdrive.NoAssetsToWithdraw.selector) { + return "NoAssetsToWithdraw"; + } + if (_selector == IHyperdrive.NotPayable.selector) { + return "NotPayable"; + } + if (_selector == IHyperdrive.QueryOutOfRange.selector) { + return "QueryOutOfRange"; + } + if (_selector == IHyperdrive.ReturnData.selector) { + return "ReturnData"; + } + if (_selector == IHyperdrive.CallFailed.selector) { + return "CallFailed"; + } + if (_selector == IHyperdrive.UnexpectedSuccess.selector) { + return "UnexpectedSuccess"; + } + if (_selector == IHyperdrive.Unauthorized.selector) { + return "Unauthorized"; + } + if (_selector == IHyperdrive.InvalidContribution.selector) { + return "InvalidContribution"; + } + if (_selector == IHyperdrive.InvalidToken.selector) { + return "InvalidToken"; + } + if (_selector == IHyperdrive.MaxFeeTooHigh.selector) { + return "MaxFeeTooHigh"; + } + if (_selector == IHyperdrive.FeeTooHigh.selector) { + return "FeeTooHigh"; + } + if (_selector == IHyperdrive.NonPayableInitialization.selector) { + return "NonPayableInitialization"; + } + if (_selector == IHyperdrive.BatchInputLengthMismatch.selector) { + return "BatchInputLengthMismatch"; + } + if (_selector == IHyperdrive.ExpiredDeadline.selector) { + return "ExpiredDeadline"; + } + if (_selector == IHyperdrive.ExpiredDeadline.selector) { + return "InvalidSignature"; + } + if (_selector == IHyperdrive.InvalidERC20Bridge.selector) { + return "InvalidERC20Bridge"; + } + if (_selector == IHyperdrive.RestrictedZeroAddress.selector) { + return "RestrictedZeroAddress"; + } + if (_selector == IHyperdrive.AlreadyClosed.selector) { + return "AlreadyClosed"; + } + if (_selector == IHyperdrive.BondMatured.selector) { + return "BondMatured"; + } + if (_selector == IHyperdrive.BondNotMatured.selector) { + return "BondNotMatured"; + } + if (_selector == IHyperdrive.InsufficientPrice.selector) { + return "InsufficientPrice"; + } + if (_selector == IHyperdrive.MintPercentTooHigh.selector) { + return "MintPercentTooHigh"; + } + if (_selector == IHyperdrive.InvalidTimestamp.selector) { + return "InvalidTimestamp"; + } + if (_selector == IHyperdrive.FixedPointMath_AddOverflow.selector) { + return "FixedPointMath_AddOverflow"; + } + if (_selector == IHyperdrive.FixedPointMath_SubOverflow.selector) { + return "FixedPointMath_SubOverflow"; + } + if (_selector == IHyperdrive.FixedPointMath_InvalidExponent.selector) { + return "FixedPointMath_InvalidExponent"; + } + if ( + _selector == IHyperdrive.FixedPointMath_NegativeOrZeroInput.selector + ) { + return "FixedPointMath_NegativeOrZeroInput"; + } + if (_selector == IHyperdrive.FixedPointMath_NegativeInput.selector) { + return "FixedPointMath_NegativeInput"; + } + revert("Unknown selector"); + } }