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

scrvUSD mainnet 182day deployment script #1216

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
8 changes: 4 additions & 4 deletions hardhat.config.mainnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ import {
MAINNET_EZETH_182DAY,
MAINNET_EZETH_COORDINATOR,
MAINNET_FACTORY,
MAINNET_MORPHO_BLUE_CBBTC_USDC_182DAY,
MAINNET_MORPHO_BLUE_COORDINATOR,
MAINNET_MORPHO_BLUE_SUSDE_DAI_182DAY,
MAINNET_MORPHO_BLUE_USDE_DAI_182DAY,
MAINNET_MORPHO_BLUE_WBTC_USDC_182DAY,
MAINNET_MORPHO_BLUE_WSTETH_USDA_182DAY,
MAINNET_RETH_182DAY,
MAINNET_RETH_COORDINATOR,
MAINNET_SCRVUSD_182DAY,
MAINNET_STUSD_182DAY,
MAINNET_SUSDE_182DAY,
} from "./tasks/deploy/config/mainnet";
Expand Down Expand Up @@ -46,8 +45,9 @@ const config: HardhatUserConfig = {
MAINNET_MORPHO_BLUE_SUSDE_DAI_182DAY,
MAINNET_MORPHO_BLUE_USDE_DAI_182DAY,
MAINNET_MORPHO_BLUE_WSTETH_USDA_182DAY,
MAINNET_MORPHO_BLUE_WBTC_USDC_182DAY,
MAINNET_MORPHO_BLUE_CBBTC_USDC_182DAY,
// MAINNET_MORPHO_BLUE_WBTC_USDC_182DAY,
// MAINNET_MORPHO_BLUE_CBBTC_USDC_182DAY,
MAINNET_SCRVUSD_182DAY,
MAINNET_STUSD_182DAY,
MAINNET_SUSDE_182DAY,
],
Expand Down
1 change: 1 addition & 0 deletions tasks/deploy/config/mainnet/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export * from "./morpho-blue-wsteth-usda-182day";
export * from "./morpho-blue-wsteth-usdc-182day";
export * from "./reth-182day";
export * from "./reth-coordinator";
export * from "./scrvusd-182day";
export * from "./staking-usds-coordinator";
export * from "./staking-usds-sky-182day";
export * from "./steth-182day";
Expand Down
91 changes: 91 additions & 0 deletions tasks/deploy/config/mainnet/scrvusd-182day.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { Address, keccak256, parseEther, toBytes } from "viem";
import {
HyperdriveInstanceConfig,
getLinkerDetails,
normalizeFee,
parseDuration,
toBytes32,
} from "../../lib";
import {
CRVUSD_ADDRESS_MAINNET,
SCRVUSD_ADDRESS_MAINNET,
SIX_MONTHS,
} from "../../lib/constants";
import { MAINNET_ERC4626_COORDINATOR_NAME } from "./erc4626-coordinator";
import { MAINNET_FACTORY_NAME } from "./factory";

// The name of the pool.
export const MAINNET_SCRVUSD_182DAY_NAME =
"ElementDAO 182 Day scrvUSD Hyperdrive";

// The initial contribution of the pool.
const CONTRIBUTION = parseEther("100");

export const MAINNET_SCRVUSD_182DAY: HyperdriveInstanceConfig = {
name: MAINNET_SCRVUSD_182DAY_NAME,
prefix: "ERC4626",
coordinatorAddress: async (hre) =>
hre.hyperdriveDeploy.deployments.byName(
MAINNET_ERC4626_COORDINATOR_NAME,
).address,
deploymentId: keccak256(toBytes(MAINNET_SCRVUSD_182DAY_NAME)),
salt: toBytes32("0x69420"),
extraData: "0x",
contribution: CONTRIBUTION,
// NOTE: Link to underlying scrvUSD market
// https://crvusd.curve.fi/#/ethereum/scrvUSD
fixedAPR: parseEther("0.10"),
timestretchAPR: parseEther("0.15"),
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",
CRVUSD_ADDRESS_MAINNET,
);
let tx = await baseToken.write.approve([
hre.hyperdriveDeploy.deployments.byName(
MAINNET_ERC4626_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: CRVUSD_ADDRESS_MAINNET,
vaultSharesToken: SCRVUSD_ADDRESS_MAINNET,
circuitBreakerDelta: parseEther("0.05"),
minimumShareReserves: parseEther("0.001"),
minimumTransactionAmount: parseEther("0.001"),
positionDuration: parseDuration(SIX_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"), SIX_MONTHS),
governanceLP: parseEther("0.15"),
governanceZombie: parseEther("0.03"),
},
};
},
};
6 changes: 6 additions & 0 deletions tasks/deploy/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,12 @@ export const WSTETH_ADDRESS_MAINNET =
export const WBTC_ADDRESS_MAINNET =
"0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599" as Address;

export const SCRVUSD_ADDRESS_MAINNET =
"0x0655977FEb2f289A4aB78af67BAB0d17aAb84367" as Address;

export const CRVUSD_ADDRESS_MAINNET =
"0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E" as Address;

// ╭─────────────────────────────────────────────────────────╮
// │ Mainnet Whales │
// ╰─────────────────────────────────────────────────────────╯
Expand Down
154 changes: 154 additions & 0 deletions test/instances/erc4626/ScrvUSD.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
// 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 { IERC4626 } from "../../../contracts/src/interfaces/IERC4626.sol";
import { IHyperdrive } from "../../../contracts/src/interfaces/IHyperdrive.sol";
import { HyperdriveUtils } from "../../utils/HyperdriveUtils.sol";
import { InstanceTest } from "../../utils/InstanceTest.sol";
import { Lib } from "../../utils/Lib.sol";
import { ERC4626HyperdriveInstanceTest } from "./ERC4626HyperdriveInstanceTest.t.sol";

interface ISCRVUSD {
function lastProfitUpdate() external view returns (uint256);
function fullProfitUnlockDate() external view returns (uint256);
}

contract scrvUSDHyperdriveTest is ERC4626HyperdriveInstanceTest {
using HyperdriveUtils for uint256;
using HyperdriveUtils for IHyperdrive;
using Lib for *;
using stdStorage for StdStorage;

/// @dev The crvUSD contract.
IERC20 internal constant CRVUSD =
IERC20(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E);

/// @dev The scrvUSD contract.
IERC4626 internal constant SCRVUSD =
IERC4626(0x0655977FEb2f289A4aB78af67BAB0d17aAb84367);

/// @dev Whale accounts.
address internal CRVUSD_TOKEN_WHALE =
address(0x0a7b9483030994016567b3B1B4bbB865578901Cb);
address[] internal baseTokenWhaleAccounts = [CRVUSD_TOKEN_WHALE];
address internal SCRVUSD_TOKEN_WHALE =
address(0x3Da232a0c0A5C59918D7B5fF77bf1c8Fc93aeE1B);
address[] internal vaultSharesTokenWhaleAccounts = [SCRVUSD_TOKEN_WHALE];

/// @notice Instantiates the instance testing suite with the configuration.
constructor()
InstanceTest(
InstanceTestConfig({
name: "Hyperdrive",
kind: "ERC4626Hyperdrive",
decimals: 18,
baseTokenWhaleAccounts: baseTokenWhaleAccounts,
vaultSharesTokenWhaleAccounts: vaultSharesTokenWhaleAccounts,
baseToken: CRVUSD,
vaultSharesToken: SCRVUSD,
shareTolerance: 1e3,
minimumShareReserves: 1e15,
minimumTransactionAmount: 1e15,
positionDuration: POSITION_DURATION,
fees: IHyperdrive.Fees({
curve: 0,
flat: 0,
governanceLP: 0,
governanceZombie: 0
}),
enableBaseDeposits: true,
enableShareDeposits: true,
enableBaseWithdraws: true,
enableShareWithdraws: true,
baseWithdrawError: new bytes(0),
isRebasing: false,
shouldAccrueInterest: true,
// The base test tolerances.
closeLongWithBaseTolerance: 20,
roundTripLpInstantaneousWithBaseTolerance: 1e5,
roundTripLpWithdrawalSharesWithBaseTolerance: 1e6,
roundTripLongInstantaneousWithBaseUpperBoundTolerance: 1e3,
roundTripLongInstantaneousWithBaseTolerance: 1e5,
roundTripLongMaturityWithBaseUpperBoundTolerance: 1e3,
roundTripLongMaturityWithBaseTolerance: 1e5,
roundTripShortInstantaneousWithBaseUpperBoundTolerance: 1e3,
roundTripShortInstantaneousWithBaseTolerance: 1e5,
roundTripShortMaturityWithBaseTolerance: 1e5,
// The share test tolerances.
closeLongWithSharesTolerance: 20,
closeShortWithSharesTolerance: 100,
roundTripLpInstantaneousWithSharesTolerance: 1e7,
roundTripLpWithdrawalSharesWithSharesTolerance: 1e7,
roundTripLongInstantaneousWithSharesUpperBoundTolerance: 1e3,
roundTripLongInstantaneousWithSharesTolerance: 1e5,
roundTripLongMaturityWithSharesUpperBoundTolerance: 1e3,
roundTripLongMaturityWithSharesTolerance: 1e5,
roundTripShortInstantaneousWithSharesUpperBoundTolerance: 1e3,
roundTripShortInstantaneousWithSharesTolerance: 1e5,
roundTripShortMaturityWithSharesTolerance: 1e5,
// The verification tolerances.
verifyDepositTolerance: 2,
verifyWithdrawalTolerance: 2
})
)
{}

/// @notice Forge function that is invoked to setup the testing environment.
function setUp() public override __mainnet_fork(21_188_049) {
// Invoke the Instance testing suite setup.
super.setUp();
}

/// 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 {
// Get the total assets before advancing time.
uint256 totalAssets = SCRVUSD.totalAssets();

// Advance the time.
vm.warp(block.timestamp + timeDelta);

// Accrue interest in the scrvUSD market. This amounts to manually
// updating the total supply assets by updating the crvUSD balance of
// scrvUSD.
(totalAssets, ) = totalAssets.calculateInterest(
variableRate,
timeDelta
);

// scrvUSD profits can be unlocked over a period of time, which affects
// the totalSupply and pricePerShare according to the unlocking rate.
// We exclude this factor by updating the unlock date and lastProfitUpdate
// according to the timeDelta.

uint256 fullProfitUnlockDate = ISCRVUSD(address(SCRVUSD))
.fullProfitUnlockDate();
uint256 lastProfitUpdate = ISCRVUSD(address(SCRVUSD))
.lastProfitUpdate();

bytes32 fullProfitLocation = bytes32(uint256(38));
bytes32 lastProfitLocation = bytes32(uint256(40));

vm.store(
address(SCRVUSD),
fullProfitLocation,
bytes32(fullProfitUnlockDate + timeDelta)
);
vm.store(
address(SCRVUSD),
lastProfitLocation,
bytes32(lastProfitUpdate + timeDelta)
);

bytes32 idleLocation = bytes32(uint256(22));
vm.store(address(SCRVUSD), idleLocation, bytes32(totalAssets));
}
}
Loading