Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sDAI Corn Integration #1172

Merged
merged 13 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions contracts/src/instances/corn/CornBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ import { IHyperdrive } from "../../interfaces/IHyperdrive.sol";
import { HyperdriveBase } from "../../internal/HyperdriveBase.sol";
import { CornConversions } from "./CornConversions.sol";

// FIXME: Update this comment.
//
// FIXME: Make sure that the vault shares token is 0.
//
/// @author DELV
/// @title CornBase
/// @notice The base contract for the Corn Hyperdrive implementation.
Expand Down
6 changes: 4 additions & 2 deletions hardhat.config.mainnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import baseConfig from "./hardhat.config";
import "./tasks";
import {
MAINNET_CORN_COORDINATOR,
MAINNET_CORN_LBTC_182DAY,
MAINNET_CORN_LBTC_91DAY,
jalextowle marked this conversation as resolved.
Show resolved Hide resolved
MAINNET_CORN_SDAI_91DAY,
MAINNET_ERC4626_COORDINATOR,
MAINNET_EZETH_182DAY,
MAINNET_EZETH_COORDINATOR,
Expand Down Expand Up @@ -47,7 +48,8 @@ const config: HardhatUserConfig = {
MAINNET_MORPHO_BLUE_USDE_DAI_182DAY,
MAINNET_MORPHO_BLUE_WSTETH_USDA_182DAY,
MAINNET_STUSD_182DAY,
MAINNET_CORN_LBTC_182DAY,
MAINNET_CORN_LBTC_91DAY,
MAINNET_CORN_SDAI_91DAY,
],
checkpointRewarders: [],
checkpointSubrewarders: [],
Expand Down
84 changes: 84 additions & 0 deletions tasks/deploy/config/mainnet/corn-sdai-91days.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { Address, keccak256, parseEther, toBytes, zeroAddress } from "viem";
import {
HyperdriveInstanceConfig,
getLinkerDetails,
normalizeFee,
parseDuration,
toBytes32,
} from "../../lib";
import { SDAI_ADDRESS_MAINNET, THREE_MONTHS } from "../../lib/constants";
import { MAINNET_CORN_COORDINATOR_NAME } from "./corn-coordinator";
import { MAINNET_FACTORY_NAME } from "./factory";

// The name of the pool.
export const MAINNET_CORN_SDAI_91DAY_NAME =
"ElementDAO 91 Day Corn sDAI Hyperdrive";

// We use a contribution of 100 sDAI.
const CONTRIBUTION = parseEther("100");

export const MAINNET_CORN_SDAI_91DAY: HyperdriveInstanceConfig<"Corn"> = {
name: MAINNET_CORN_SDAI_91DAY_NAME,
prefix: "Corn",
coordinatorAddress: async (hre) =>
hre.hyperdriveDeploy.deployments.byName(MAINNET_CORN_COORDINATOR_NAME)
.address,
deploymentId: keccak256(toBytes(MAINNET_CORN_SDAI_91DAY_NAME)),
salt: toBytes32("0x69420"),
extraData: "0x",
contribution: CONTRIBUTION,
fixedAPR: parseEther("0.08"),
timestretchAPR: parseEther("0.075"),
options: async (hre) => ({
extraData: "0x",
asBase: true,
destination: (await hre.getNamedAccounts())["deployer"] as Address,
}),
// Prepare to deploy the contract by setting approvals.
prepare: async (hre) => {
let baseToken = await hre.viem.getContractAt(
"contracts/src/interfaces/IERC20.sol:IERC20",
SDAI_ADDRESS_MAINNET,
);
let tx = await baseToken.write.approve([
hre.hyperdriveDeploy.deployments.byName(
MAINNET_CORN_COORDINATOR_NAME,
).address,
CONTRIBUTION,
]);
let pc = await hre.viem.getPublicClient();
await pc.waitForTransactionReceipt({ hash: tx });
},
poolDeployConfig: async (hre) => {
let factoryContract = await hre.viem.getContractAt(
"HyperdriveFactory",
hre.hyperdriveDeploy.deployments.byName(MAINNET_FACTORY_NAME)
.address,
);
return {
baseToken: SDAI_ADDRESS_MAINNET,
vaultSharesToken: zeroAddress,
circuitBreakerDelta: parseEther("0.075"),
minimumShareReserves: parseEther("0.001"), // 1e15
minimumTransactionAmount: parseEther("0.001"), // 1e15
positionDuration: parseDuration(THREE_MONTHS),
checkpointDuration: parseDuration("1 day"),
timeStretch: 0n,
governance: await factoryContract.read.hyperdriveGovernance(),
feeCollector: await factoryContract.read.feeCollector(),
sweepCollector: await factoryContract.read.sweepCollector(),
checkpointRewarder: await factoryContract.read.checkpointRewarder(),
...(await getLinkerDetails(
hre,
hre.hyperdriveDeploy.deployments.byName(MAINNET_FACTORY_NAME)
.address,
)),
fees: {
curve: parseEther("0.01"),
flat: normalizeFee(parseEther("0.0005"), THREE_MONTHS),
governanceLP: parseEther("0.15"),
governanceZombie: parseEther("0.03"),
},
};
},
};
3 changes: 2 additions & 1 deletion tasks/deploy/config/mainnet/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export * from "./corn-coordinator";
export * from "./corn-lbtc-182day";
export * from "./corn-lbtc-91day";
export * from "./corn-sdai-91days";
export * from "./dai-182day";
export * from "./eeth-182day";
export * from "./eeth-coordinator";
Expand Down
173 changes: 173 additions & 0 deletions test/instances/corn/CornHyperdriveInstanceTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.22;

import { stdStorage, StdStorage } from "forge-std/Test.sol";
import { CornHyperdriveCoreDeployer } from "../../../contracts/src/deployers/corn/CornHyperdriveCoreDeployer.sol";
import { CornHyperdriveDeployerCoordinator } from "../../../contracts/src/deployers/corn/CornHyperdriveDeployerCoordinator.sol";
import { CornTarget0Deployer } from "../../../contracts/src/deployers/corn/CornTarget0Deployer.sol";
import { CornTarget1Deployer } from "../../../contracts/src/deployers/corn/CornTarget1Deployer.sol";
import { CornTarget2Deployer } from "../../../contracts/src/deployers/corn/CornTarget2Deployer.sol";
import { CornTarget3Deployer } from "../../../contracts/src/deployers/corn/CornTarget3Deployer.sol";
import { CornTarget4Deployer } from "../../../contracts/src/deployers/corn/CornTarget4Deployer.sol";
import { CornConversions } from "../../../contracts/src/instances/corn/CornConversions.sol";
import { IERC20 } from "../../../contracts/src/interfaces/IERC20.sol";
import { ICornHyperdrive } from "../../../contracts/src/interfaces/ICornHyperdrive.sol";
import { ICornSilo } from "../../../contracts/src/interfaces/ICornSilo.sol";
import { IHyperdrive } from "../../../contracts/src/interfaces/IHyperdrive.sol";
import { FixedPointMath } from "../../../contracts/src/libraries/FixedPointMath.sol";
import { InstanceTest } from "../../utils/InstanceTest.sol";
import { HyperdriveUtils } from "../../utils/HyperdriveUtils.sol";
import { Lib } from "../../utils/Lib.sol";

contract CornHyperdriveInstanceTest is InstanceTest {
using FixedPointMath for uint256;
using HyperdriveUtils for uint256;
using HyperdriveUtils for IHyperdrive;
using Lib for *;
using stdStorage for StdStorage;

/// @dev The Corn Silo.
ICornSilo internal immutable silo;

/// @notice Instantiates the instance testing suite with the configuration.
/// @param _config The instance test configuration.
/// @param _silo The Corn Silo.
constructor(
InstanceTestConfig memory _config,
ICornSilo _silo
) InstanceTest(_config) {
silo = _silo;
}

/// Overrides ///

/// @dev Gets the extra data used to deploy the Corn instance. This is empty.
/// @return The empty extra data.
function getExtraData() internal pure override returns (bytes memory) {
return new bytes(0);
}

/// @dev Converts base amount to the equivalent about in shares.
/// @param baseAmount The base amount.
/// @return The converted share amount.
function convertToShares(
uint256 baseAmount
) internal pure override returns (uint256) {
return CornConversions.convertToShares(baseAmount);
}

/// @dev Converts share amount to the equivalent amount in base.
/// @param shareAmount The share amount.
/// @return The converted base amount.
function convertToBase(
uint256 shareAmount
) internal pure override returns (uint256) {
return CornConversions.convertToBase(shareAmount);
}

/// @dev Deploys the rsETH Linea deployer coordinator contract.
jalextowle marked this conversation as resolved.
Show resolved Hide resolved
/// @param _factory The address of the Hyperdrive factory.
/// @return The coordinator address.
function deployCoordinator(
address _factory
) internal override returns (address) {
vm.startPrank(alice);
return
address(
new CornHyperdriveDeployerCoordinator(
string.concat(config.name, "DeployerCoordinator"),
_factory,
address(new CornHyperdriveCoreDeployer(silo)),
address(new CornTarget0Deployer(silo)),
address(new CornTarget1Deployer(silo)),
address(new CornTarget2Deployer(silo)),
address(new CornTarget3Deployer(silo)),
address(new CornTarget4Deployer(silo))
)
);
}

/// @dev Fetches the total supply of the base and share tokens.
/// @return The total supply of base.
/// @return The total supply of vault shares.
function getSupply() internal view override returns (uint256, uint256) {
return (
config.baseToken.balanceOf(address(silo)),
silo.totalShares(address(config.baseToken))
);
}

/// @dev Fetches the token balance information of an account.
/// @param account The account to query.
/// @return The balance of base.
/// @return The balance of vault shares.
function getTokenBalances(
address account
) internal view override returns (uint256, uint256) {
return (
config.baseToken.balanceOf(account),
silo.sharesOf(account, address(config.baseToken))
);
}

/// Getters ///

/// @dev Test the instances getters.
function test_getters() external view {
assertEq(
address(ICornHyperdrive(address(hyperdrive)).cornSilo()),
address(silo)
);
(, uint256 totalShares) = getTokenBalances(address(hyperdrive));
assertEq(hyperdrive.totalShares(), totalShares);
}

/// Price Per Share ///

/// @dev Fuzz test that verifies that the vault share price is the price
/// that dictates the conversion between base and shares.
/// @param basePaid The fuzz parameter for the base paid.
function test__pricePerVaultShare(uint256 basePaid) external {
// Ensure that the share price is the expected value.
(uint256 totalBase, uint256 totalShares) = getSupply();
uint256 vaultSharePrice = hyperdrive.getPoolInfo().vaultSharePrice;
assertEq(vaultSharePrice, totalBase.divDown(totalShares));

// Ensure that the share price accurately predicts the amount of shares
// that will be minted for depositing a given amount of shares. This will
// be an approximation.
basePaid = basePaid.normalizeToRange(
2 * hyperdrive.getPoolConfig().minimumTransactionAmount,
hyperdrive.calculateMaxLong()
);
(, uint256 hyperdriveSharesBefore) = getTokenBalances(
address(hyperdrive)
);
openLong(bob, basePaid);
(, uint256 hyperdriveSharesAfter) = getTokenBalances(
address(hyperdrive)
);
assertApproxEqAbs(
hyperdriveSharesAfter,
hyperdriveSharesBefore + basePaid.divDown(vaultSharePrice),
config.shareTolerance
);
}

/// Helpers ///

/// @dev Advance time and accrue interest.
/// @param timeDelta The time to advance.
/// @param variableRate The variable rate.
function advanceTime(
uint256 timeDelta,
int256 variableRate
) internal override {
// Corn doesn't accrue interest, so we revert if the variable rate isn't
// zero.
require(variableRate == 0, "CornHyperdriveTest: variableRate isn't 0");

// Advance the time.
vm.warp(block.timestamp + timeDelta);
}
}
96 changes: 96 additions & 0 deletions test/instances/corn/Corn_LBTC_Hyperdrive.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.22;

import { stdStorage, StdStorage } from "forge-std/Test.sol";
import { IERC20 } from "../../../contracts/src/interfaces/IERC20.sol";
import { ICornSilo } from "../../../contracts/src/interfaces/ICornSilo.sol";
import { IHyperdrive } from "../../../contracts/src/interfaces/IHyperdrive.sol";
import { CornHyperdriveInstanceTest } from "./CornHyperdriveInstanceTest.t.sol";

contract Corn_LBTC_Hyperdrive is CornHyperdriveInstanceTest {
using stdStorage for StdStorage;

/// @dev The mainnet Corn Silo.
ICornSilo internal constant CORN_SILO =
ICornSilo(0x8bc93498b861fd98277c3b51d240e7E56E48F23c);

/// @dev The mainnet LBTC token.
IERC20 internal constant LBTC =
IERC20(0x8236a87084f8B84306f72007F36F2618A5634494);

/// @dev Whale accounts.
address internal BASE_TOKEN_WHALE =
0x208567a5FF415f1081fa0f47d3A1bD60b8B03199;
address[] internal baseTokenWhaleAccounts = [BASE_TOKEN_WHALE];

/// @notice Instantiates the instance testing suite with the configuration.
constructor()
CornHyperdriveInstanceTest(
InstanceTestConfig({
name: "Hyperdrive",
kind: "CornHyperdrive",
decimals: 8,
baseTokenWhaleAccounts: baseTokenWhaleAccounts,
vaultSharesTokenWhaleAccounts: new address[](0),
baseToken: LBTC,
vaultSharesToken: IERC20(address(0)),
shareTolerance: 0,
minimumShareReserves: 1e5,
minimumTransactionAmount: 1e5,
positionDuration: POSITION_DURATION,
fees: IHyperdrive.Fees({
curve: 0.001e18,
flat: 0.0001e18,
governanceLP: 0,
governanceZombie: 0
}),
enableBaseDeposits: true,
enableShareDeposits: false,
enableBaseWithdraws: true,
enableShareWithdraws: false,
baseWithdrawError: abi.encodeWithSelector(
IHyperdrive.UnsupportedToken.selector
),
isRebasing: false,
shouldAccrueInterest: false,
// The base test tolerances.
roundTripLpInstantaneousWithBaseTolerance: 1e3,
roundTripLpWithdrawalSharesWithBaseTolerance: 1e5,
roundTripLongInstantaneousWithBaseUpperBoundTolerance: 100,
// NOTE: Since the curve fee isn't zero, this check is ignored.
roundTripLongInstantaneousWithBaseTolerance: 0,
roundTripLongMaturityWithBaseUpperBoundTolerance: 100,
roundTripLongMaturityWithBaseTolerance: 1e3,
roundTripShortInstantaneousWithBaseUpperBoundTolerance: 100,
// NOTE: Since the curve fee isn't zero, this check is ignored.
roundTripShortInstantaneousWithBaseTolerance: 0,
roundTripShortMaturityWithBaseTolerance: 1e3,
// NOTE: Share deposits and withdrawals are disabled, so these are
// 0.
//
// The share test tolerances.
closeLongWithSharesTolerance: 0,
closeShortWithSharesTolerance: 0,
roundTripLpInstantaneousWithSharesTolerance: 0,
roundTripLpWithdrawalSharesWithSharesTolerance: 0,
roundTripLongInstantaneousWithSharesUpperBoundTolerance: 0,
roundTripLongInstantaneousWithSharesTolerance: 0,
roundTripLongMaturityWithSharesUpperBoundTolerance: 0,
roundTripLongMaturityWithSharesTolerance: 0,
roundTripShortInstantaneousWithSharesUpperBoundTolerance: 0,
roundTripShortInstantaneousWithSharesTolerance: 0,
roundTripShortMaturityWithSharesTolerance: 0,
// The verification tolerances.
verifyDepositTolerance: 2,
verifyWithdrawalTolerance: 3
}),
CORN_SILO
)
{}

/// @notice Forge function that is invoked to setup the testing environment.
function setUp() public override __mainnet_fork(20_744_342) {
// Invoke the instance testing suite setup.
super.setUp();
}
}
Loading
Loading