From 19c84c151818cb3af3764f95608dd9338fb317f9 Mon Sep 17 00:00:00 2001 From: ngfam Date: Fri, 21 Jul 2023 16:25:18 +0700 Subject: [PATCH] Added vETH-wETH Aura SY & Adjust deployment script/docs --- README.md | 40 +++++++++- contracts/AuraVethWethSY.sol | 147 +++++++++++++++++++++++++++++++++++ deployments/SY-swETH.json | 8 +- package.json | 2 +- scripts/configuration.ts | 5 +- scripts/deploy-sy.ts | 22 ++++++ scripts/deploy.ts | 18 +++-- scripts/helper.ts | 8 +- scripts/param-helper.ts | 5 +- scripts/seed-liquidity.ts | 9 --- yarn.lock | 8 +- 11 files changed, 239 insertions(+), 33 deletions(-) create mode 100644 contracts/AuraVethWethSY.sol create mode 100644 scripts/deploy-sy.ts diff --git a/README.md b/README.md index 77a945b..0accd67 100644 --- a/README.md +++ b/README.md @@ -10,15 +10,49 @@ ETHERSCAN_KEY=... PRIVATE_KEY=... ``` -**Note**: `ETHERSCAN_KEY` should match the block explorer key of the network you are deploying. +**Note**: `ETHERSCAN_KEY` should match the block explorer key of the network you are deploying. ## Deployment +### Set up + +This repository runs in yarn environment so this step is as simple as running: + +``` +yarn install +``` + ### Contract and Parameters preparation + +#### SY contract First step is to get your contract ready inside `./contracts/` folder as we currently have `./contracts/SwETHSY.sol`. -After that, you will have to fill in the parameters for the following parameters corresponding to your interest bearing token: +Next, you will prepare the deploy implementation for your SY contract in `./scripts/deploy-sy.ts`. Below is an example of SwETHSY deployment. + +```ts +/** + * @dev This function aims to deploy your SY contract + * @dev The below implementation show how to deploy a SwETH SY contract + * + * To deploy your own SY contract, you need to: + * - Change the contract name / type name in "deploy(deployer, 'YOUR_CONTRACT_NAME', [...])" + * - Change the deployment params to match your constructor arguments + */ +export async function deploySY(deployer: SignerWithAddress): Promise { + const sy = await deploy(deployer, 'SwETHSY', [ + MarketConfiguration.name, + MarketConfiguration.symbol, + '0xf951E335afb289353dc249e82926178EaC7DEd78', // SWETH address + ]); + + return await getContractAt('IStandardizedYield', sy.address); +} +``` + +#### Parameters + +Once you are done with SY deploy implementation, you will have to fill in the parameters for the following parameters corresponding to your interest bearing token: ```ts /** * @dev The following parameters are used to calculate the market deployment params @@ -88,3 +122,5 @@ yarn hardhat run scripts/seed-liquidity.ts --network We highly recommend you to use a stable RPC with good nonce management for the deployment. Otherwise sending two transactions in a row could result in transaction replacement. The current script has already put a delay between any two transactions being sent but sometime it is still not enough on bad RPC. + +Please contact us if you run into any problem. \ No newline at end of file diff --git a/contracts/AuraVethWethSY.sol b/contracts/AuraVethWethSY.sol new file mode 100644 index 0000000..7434253 --- /dev/null +++ b/contracts/AuraVethWethSY.sol @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity 0.8.17; + +import "@pendle/core-v2/contracts/core/StandardizedYield/implementations/BalancerStable/base/PendleAuraBalancerStableLPSYV2.sol"; +import "@pendle/core-v2/contracts/core/StandardizedYield/implementations/BalancerStable/base/ComposableStable/ComposableStablePreview.sol"; + +contract AuraWethVethSY is PendleAuraBalancerStableLPSYV2 { + address internal constant VETH = 0x4Bc3263Eb5bb2Ef7Ad9aB6FB68be80E43b43801F; + address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + + uint256 internal constant AURA_PID = 128; + address internal constant LP = 0x156C02f3f7fEf64a3A9D80CCF7085f23ccE91D76; + + address internal constant COMPOSABLE_PREVIEW = 0x4239Ddd3c50463383670E86c119220849BFaF64a; + + bool internal constant NO_TOKENS_EXEMPT = true; + bool internal constant ALL_TOKENS_EXEMPT = false; + + constructor( + string memory _name, + string memory _symbol + ) + PendleAuraBalancerStableLPSYV2( + _name, + _symbol, + LP, + AURA_PID, + ComposableStablePreview(COMPOSABLE_PREVIEW) + ) + //solhint-disable-next-line + { + + } + + function _deposit( + address tokenIn, + uint256 amount + ) internal virtual override returns (uint256 amountSharesOut) { + if (tokenIn == NATIVE) { + IWETH(WETH).deposit{ value: amount }(); + amountSharesOut = super._deposit(WETH, amount); + } else { + amountSharesOut = super._deposit(tokenIn, amount); + } + } + + function _redeem( + address receiver, + address tokenOut, + uint256 amountSharesToRedeem + ) internal virtual override returns (uint256) { + if (tokenOut == NATIVE) { + uint256 amountTokenOut = super._redeem(address(this), WETH, amountSharesToRedeem); + IWETH(WETH).withdraw(amountTokenOut); + _transferOut(NATIVE, receiver, amountTokenOut); + return amountTokenOut; + } else { + return super._redeem(receiver, tokenOut, amountSharesToRedeem); + } + } + + function _previewDeposit( + address tokenIn, + uint256 amountTokenToDeposit + ) internal view virtual override returns (uint256 amountSharesOut) { + if (tokenIn == NATIVE) { + amountSharesOut = super._previewDeposit(WETH, amountTokenToDeposit); + } else { + amountSharesOut = super._previewDeposit(tokenIn, amountTokenToDeposit); + } + } + + function _previewRedeem( + address tokenOut, + uint256 amountSharesToRedeem + ) internal view virtual override returns (uint256 amountTokenOut) { + if (tokenOut == NATIVE) { + amountTokenOut = super._previewRedeem(WETH, amountSharesToRedeem); + } else { + amountTokenOut = super._previewRedeem(tokenOut, amountSharesToRedeem); + } + } + + function _getImmutablePoolData() internal pure override returns (bytes memory ret) { + ComposableStablePreview.ImmutableData memory res; + res.poolTokens = _getPoolTokenAddresses(); + res.rateProviders = _getRateProviders(); + res.rawScalingFactors = _getRawScalingFactors(); + res.isExemptFromYieldProtocolFee = _getExemption(); + res.LP = LP; + res.noTokensExempt = NO_TOKENS_EXEMPT; + res.allTokensExempt = ALL_TOKENS_EXEMPT; + res.bptIndex = _getBPTIndex(); + res.totalTokens = res.poolTokens.length; + + return abi.encode(res); + } + + // --------------------------------- POOL CONSTANTS --------------------------------- + function _getPoolTokenAddresses() internal pure override returns (address[] memory res) { + res = new address[](3); + res[0] = LP; + res[1] = VETH; + res[2] = WETH; + } + + function _getBPTIndex() internal pure override returns (uint256) { + return 0; + } + + function _getRateProviders() internal pure returns (address[] memory res) { + res = new address[](3); + res[0] = 0x0000000000000000000000000000000000000000; + res[1] = 0x12589A727aeFAc3fbE5025F890f1CB97c269BEc2; + res[2] = 0x0000000000000000000000000000000000000000; + } + + function _getRawScalingFactors() internal pure returns (uint256[] memory res) { + res = new uint256[](3); + res[0] = res[1] = res[2] = 1e18; + } + + function _getExemption() internal pure returns (bool[] memory res) { + res = new bool[](3); + res[0] = res[1] = res[2] = false; + } + + function getTokensIn() public view virtual override returns (address[] memory res) { + res = new address[](4); + res[0] = LP; + res[1] = WETH; + res[2] = VETH; + res[3] = NATIVE; + } + + function getTokensOut() public view virtual override returns (address[] memory res) { + return getTokensIn(); + } + + function isValidTokenIn(address token) public view virtual override returns (bool) { + return (token == LP || token == WETH || token == VETH || token == NATIVE); + } + + function isValidTokenOut(address token) public view virtual override returns (bool) { + return isValidTokenIn(token); + } +} diff --git a/deployments/SY-swETH.json b/deployments/SY-swETH.json index 3ce472b..1e9bff1 100644 --- a/deployments/SY-swETH.json +++ b/deployments/SY-swETH.json @@ -1,12 +1,12 @@ { "name": "SY swETH", "symbol": "SY-swETH", - "expiry": 1750896000, - "scalarRoot": "112278200000000000000", - "initialRateAnchor": "1087110000000000000", "doCacheIndex": true, + "expiry": 1750896000, + "scalarRoot": "106799962313000000000", + "initialRateAnchor": "1059899851000000000", "SY": "0x4bF3B85a7ec25Ac743E4Eb21f745B1213116f4ff", "PT": "0x83DdA45D873De56967B7aaE067180b55C7635832", "YT": "0xB21a491068e99631590891b4553deC84555Fe75C", - "market": "0x8F8644d86b2f09839CC9dA52358535e292466c74" + "market": "0xFb3ED50aA7e997Bd588a250E6469F68a971A79DD" } \ No newline at end of file diff --git a/package.json b/package.json index 429437c..d8de2ca 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "@nomiclabs/hardhat-ethers": "^2.0.5", "@nomiclabs/hardhat-etherscan": "^2.1.8", "@nomiclabs/hardhat-waffle": "^2.0.1", - "@pendle/core-v2": "^2.21.1", + "@pendle/core-v2": "^2.22.2", "hardhat-contract-sizer": "^2.8.0", "@typechain/ethers-v5": "^9.0.0", "@typechain/hardhat": "^4.0.0" diff --git a/scripts/configuration.ts b/scripts/configuration.ts index 9fa2734..7b9be0a 100644 --- a/scripts/configuration.ts +++ b/scripts/configuration.ts @@ -1,12 +1,11 @@ import { ZERO_ADDRESS } from './consts'; import { toWei } from './helper'; -import { SUPPORTED_CHAINS } from './types'; -import { calculateParameters } from './param-helper' +import { calculateParameters } from './param-helper'; /** * @dev The following parameters are used to calculate the market deployment params * @minApy and @maxApy are the minimum and maximum APY of the interest bearing asset - * @startTimestamp and @endTimestamp are the start and end time of the market + * @startTimestamp and @endTimestamp are the start and end time of the market */ const minApy = 0.01; // 1% const maxApy = 0.05; // 5% diff --git a/scripts/deploy-sy.ts b/scripts/deploy-sy.ts new file mode 100644 index 0000000..b207174 --- /dev/null +++ b/scripts/deploy-sy.ts @@ -0,0 +1,22 @@ +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import { IStandardizedYield, SwETHSY } from '../typechain-types'; +import { deploy, getContractAt } from './helper'; +import { MarketConfiguration } from './configuration'; + +/** + * @dev This function aims to deploy your SY contract + * @dev The below implementation show how to deploy a SwETH SY contract + * + * To deploy your own SY contract, you need to: + * - Change the contract name / type name in "deploy(deployer, 'YOUR_CONTRACT_NAME', [...])" + * - Change the deployment params to match your constructor arguments + */ +export async function deploySY(deployer: SignerWithAddress): Promise { + const sy = await deploy(deployer, 'SwETHSY', [ + MarketConfiguration.name, + MarketConfiguration.symbol, + '0xf951E335afb289353dc249e82926178EaC7DEd78', // SWETH address + ]); + + return await getContractAt('IStandardizedYield', sy.address); +} diff --git a/scripts/deploy.ts b/scripts/deploy.ts index ef2a949..85bfadd 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -1,11 +1,10 @@ import { ethers } from 'hardhat'; import { JSONReplacerBigNum, delay, deploy, getPendleContracts } from './helper'; -import { SwETHSY } from '../typechain-types'; import { MarketConfiguration } from './configuration'; -import { SUPPORTED_CHAINS } from './types'; import fs from 'fs'; import path from 'path'; import { SAFE_WAIT_TIME } from './consts'; +import { deploySY } from './deploy-sy'; const SWETH = '0xf951E335afb289353dc249e82926178EaC7DEd78'; @@ -14,11 +13,7 @@ async function main() { const pendleContracts = await getPendleContracts(); - const sy = await deploy(deployer, 'SwETHSY', [ - MarketConfiguration.name, - MarketConfiguration.symbol, - SWETH, - ]); + const sy = await deploySY(deployer); await delay(SAFE_WAIT_TIME, 'before deploying PT/YT'); @@ -47,6 +42,15 @@ async function main() { MarketConfiguration.scalarRoot, MarketConfiguration.initialRateAnchor ); + await delay(SAFE_WAIT_TIME, 'after create market'); + + // approve inf tokenIns for the path of pendle router -> sy address + await pendleContracts.router.approveInf([ + { + tokens: await sy.getTokensIn(), + spender: sy.address, + }, + ]); fs.writeFileSync( path.resolve(__dirname, '../deployments', `${MarketConfiguration.symbol}.json`), diff --git a/scripts/helper.ts b/scripts/helper.ts index e8d24fc..8e3fc9b 100644 --- a/scripts/helper.ts +++ b/scripts/helper.ts @@ -44,6 +44,13 @@ export async function verifyContract(contract: string, constructor: any[]) { } } +/** + * + * @param deployer signer for deployer + * @param abiType abi type (contract name) + * @param args constructor arguments + * @returns the contract itself with specified type + */ export async function deploy(deployer: SignerWithAddress, abiType: string, args: any[]) { console.log(`Deploying ${abiType}...`); const contractFactory = await hre.ethers.getContractFactory(abiType); @@ -92,4 +99,3 @@ export async function safeApproveInf(deployer: SignerWithAddress, token: string, const allowance = await contract.allowance(deployer.address, to); if (allowance.lt(INF.div(2))) await contract.connect(deployer).approve(to, INF); } - diff --git a/scripts/param-helper.ts b/scripts/param-helper.ts index c841fa6..f67245a 100644 --- a/scripts/param-helper.ts +++ b/scripts/param-helper.ts @@ -1,4 +1,5 @@ -import { toWei } from "./helper"; +import { BigNumber } from 'ethers'; +import { toWei } from './helper'; /** * Validates the given start and end timestamps. @@ -31,7 +32,7 @@ export function calculateParameters( rateMax: number, startTimestamp: number, endTimestamp: number -): { scalarRoot: BN; initialRateAnchor: BN } { +): { scalarRoot: BigNumber; initialRateAnchor: BigNumber } { validateTimestamps(startTimestamp, endTimestamp); const yearsToExpiry = (endTimestamp - startTimestamp) / 31536000; const rateMinScaled = Math.pow(rateMin + 1, yearsToExpiry); diff --git a/scripts/seed-liquidity.ts b/scripts/seed-liquidity.ts index 14ad10c..d58b7d6 100644 --- a/scripts/seed-liquidity.ts +++ b/scripts/seed-liquidity.ts @@ -26,15 +26,6 @@ async function main() { const YT = await getContractAt('IERC20', marketAddresses.YT); const LP = await getContractAt('IERC20', marketAddresses.market); - // approve inf tokenIns for router - await pendleContracts.router.approveInf([ - { - tokens: await SY.getTokensIn(), - spender: SY.address, - }, - ]); - await delay(SAFE_WAIT_TIME, 'after approveInf on router'); - await pendleContracts.router.mintSyFromToken( deployer.address, SY.address, diff --git a/yarn.lock b/yarn.lock index 837b096..e703e7f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -804,10 +804,10 @@ resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-4.3.3.tgz#ff6ee919fc2a1abaf72b22814bfb72ed129ec137" integrity sha512-tDBopO1c98Yk7Cv/PZlHqrvtVjlgK5R4J6jxLwoO7qxK4xqOiZG+zSkIvGFpPZ0ikc3QOED3plgdqjgNTnBc7g== -"@pendle/core-v2@^2.21.1": - version "2.21.1" - resolved "https://registry.yarnpkg.com/@pendle/core-v2/-/core-v2-2.21.1.tgz#ad6ee6ade06be75bdf083e5ee6b40d0cd70252e5" - integrity sha512-FwFi5GzT1BK1ehxMmNFVBbJqLJQUEy2UvaBG4elZP1dt96ceKSBBy/7XpO5kl6FwT1/TwEqD292MHE4punVi1A== +"@pendle/core-v2@^2.22.2": + version "2.22.2" + resolved "https://registry.yarnpkg.com/@pendle/core-v2/-/core-v2-2.22.2.tgz#e29ab39d0a1e65ff71021f385ee44ad70449286d" + integrity sha512-85Nk2zKqKKpEJChiLmrW5aO3XXf3QJDRpvbyWInFJ7XT3lGck6uZQ7Pa35Mg4yzd/DJHcOtRNfDKFx3HKcb8Wg== dependencies: "@chainlink/contracts" "^0.6.1" "@openzeppelin/contracts" "4.7.3"