From 9e7cb18ec432bd9c7eb3f4feae4b2a761df73251 Mon Sep 17 00:00:00 2001 From: BkChoy Date: Mon, 4 Mar 2024 08:56:01 +1300 Subject: [PATCH] ccip stage 2 deployment scripts --- hardhat.config.ts | 5 +- .../{ => stage-1}/deploy-ccip-dest-tokens.ts | 2 +- .../ccip/{ => stage-1}/deploy-ccip-stage-1.ts | 4 +- .../ccip/stage-2/1-deploy-primary-chain.ts | 103 ++++++++++++++++ .../ccip/stage-2/2-deploy-secondary-chain.ts | 114 ++++++++++++++++++ .../ccip/stage-2/3-upgrade-primary-chain.ts | 86 +++++++++++++ scripts/utils/helpers.ts | 4 +- 7 files changed, 311 insertions(+), 7 deletions(-) rename scripts/prod/ccip/{ => stage-1}/deploy-ccip-dest-tokens.ts (92%) rename scripts/prod/ccip/{ => stage-1}/deploy-ccip-stage-1.ts (87%) create mode 100644 scripts/prod/ccip/stage-2/1-deploy-primary-chain.ts create mode 100644 scripts/prod/ccip/stage-2/2-deploy-secondary-chain.ts create mode 100644 scripts/prod/ccip/stage-2/3-upgrade-primary-chain.ts diff --git a/hardhat.config.ts b/hardhat.config.ts index 44fd5fe2..10a9efd4 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -31,12 +31,13 @@ const config: HardhatUserConfig = { url: 'http://127.0.0.1:8545', accounts, }, - rinkeby: { + sepolia: { url: '', accounts, }, - ropsten: { + 'arbitrum-sepolia': { url: '', + chainId: 421614, accounts, }, mainnet: { diff --git a/scripts/prod/ccip/deploy-ccip-dest-tokens.ts b/scripts/prod/ccip/stage-1/deploy-ccip-dest-tokens.ts similarity index 92% rename from scripts/prod/ccip/deploy-ccip-dest-tokens.ts rename to scripts/prod/ccip/stage-1/deploy-ccip-dest-tokens.ts index d1e0faff..7c2b443c 100644 --- a/scripts/prod/ccip/deploy-ccip-dest-tokens.ts +++ b/scripts/prod/ccip/stage-1/deploy-ccip-dest-tokens.ts @@ -1,4 +1,4 @@ -import { updateDeployments, deploy } from '../../utils/deployment' +import { updateDeployments, deploy } from '../../../utils/deployment' // SDL const sdl = { diff --git a/scripts/prod/ccip/deploy-ccip-stage-1.ts b/scripts/prod/ccip/stage-1/deploy-ccip-stage-1.ts similarity index 87% rename from scripts/prod/ccip/deploy-ccip-stage-1.ts rename to scripts/prod/ccip/stage-1/deploy-ccip-stage-1.ts index e53582f0..11b024e1 100644 --- a/scripts/prod/ccip/deploy-ccip-stage-1.ts +++ b/scripts/prod/ccip/stage-1/deploy-ccip-stage-1.ts @@ -1,5 +1,5 @@ -import { WrappedTokenBridge } from '../../../typechain-types' -import { updateDeployments, deploy, getContract } from '../../utils/deployment' +import { WrappedTokenBridge } from '../../../../typechain-types' +import { updateDeployments, deploy, getContract } from '../../../utils/deployment' // should be deployed on primary chain (Ethereum Mainnet) diff --git a/scripts/prod/ccip/stage-2/1-deploy-primary-chain.ts b/scripts/prod/ccip/stage-2/1-deploy-primary-chain.ts new file mode 100644 index 00000000..4881fdd0 --- /dev/null +++ b/scripts/prod/ccip/stage-2/1-deploy-primary-chain.ts @@ -0,0 +1,103 @@ +import { toEther } from '../../../utils/helpers' +import { updateDeployments, deploy } from '../../../utils/deployment' +import { RewardsInitiator, SDLPoolCCIPControllerPrimary } from '../../../../typechain-types' +import { ethers, upgrades } from 'hardhat' +import { getContract } from '../../../utils/deployment' + +// Execute on Ethereum Mainnet + +const ccipRouter = '0x80226fc0Ee2b096224EeAc085Bb9a8cba1146f7D' +const multisigAddress = '0xB351EC0FEaF4B99FdFD36b484d9EC90D0422493D' + +// Linear Boost Controller +const LinearBoostControllerParams = { + minLockingDuration: 86400, // minimum locking duration in seconds + maxLockingDuration: 4 * 365 * 86400, // maximum locking duration in seconds + maxBoost: 8, // maximum boost amount +} +// SDL Pool CCIP Controller Primary +const SDLPoolCCIPControllerParams = { + maxLINKFee: toEther(10), // max LINK fee to paid on outgoing CCIP updates + updateInitiator: '', // address authorized to send CCIP updates +} +// Rewards Initiator +const RewardsInitiatorParams = { + whitelistedCaller: '', // address authorized to initiate rebase and rewards distribution +} + +async function main() { + const sdlPool = await getContract('SDLPool') + const sdlToken = await getContract('SDLToken') + const linkToken = await getContract('LINKToken') + const wstLINKToken = await getContract('LINK_WrappedSDToken') + const stakingPool = await getContract('LINK_StakingPool') + + const boostController = await deploy('LinearBoostController', [ + LinearBoostControllerParams.minLockingDuration, + LinearBoostControllerParams.maxLockingDuration, + LinearBoostControllerParams.maxBoost, + ]) + console.log('LinearBoostController deployed: ', boostController.address) + + const sdlPoolPrimaryImp = (await upgrades.prepareUpgrade( + sdlPool.address, + await ethers.getContractFactory('SDLPoolPrimary'), + { + kind: 'uups', + } + )) as string + console.log('SDLPoolPrimary implementation deployed at: ', sdlPoolPrimaryImp) + + const ccipController = (await deploy('SDLPoolCCIPControllerPrimary', [ + ccipRouter, + linkToken.address, + sdlToken.address, + sdlPool.address, + SDLPoolCCIPControllerParams.maxLINKFee, + SDLPoolCCIPControllerParams.updateInitiator, + ])) as SDLPoolCCIPControllerPrimary + console.log('SDLPoolCCIPControllerPrimary deployed: ', ccipController.address) + + const reSDLTokenBridge = await deploy('RESDLTokenBridge', [ + linkToken.address, + sdlToken.address, + sdlPool.address, + ccipController.address, + ]) + console.log('RESDLTokenBridge deployed: ', reSDLTokenBridge.address) + + const rewardsInitiator = (await deploy('RewardsInitiator', [ + stakingPool.address, + ccipController.address, + ])) as RewardsInitiator + console.log('RewardsInitiator deployed: ', rewardsInitiator.address) + + updateDeployments({ + LinearBoostController: boostController.address, + SDLPoolCCIPControllerPrimary: ccipController.address, + RESDLTokenBridge: reSDLTokenBridge.address, + RewardsInitiator: rewardsInitiator.address, + }) + + await (await boostController.transferOwnership(multisigAddress)).wait() + + await ( + await rewardsInitiator.whitelistCaller(RewardsInitiatorParams.whitelistedCaller, true) + ).wait() + await (await rewardsInitiator.transferOwnership(multisigAddress)).wait() + + await ( + await ccipController.setWrappedRewardToken(stakingPool.address, wstLINKToken.address) + ).wait() + await (await ccipController.approveRewardTokens([wstLINKToken.address])).wait() + await (await ccipController.setRESDLTokenBridge(reSDLTokenBridge.address)).wait() + await (await ccipController.setRewardsInitiator(rewardsInitiator.address)).wait() + await (await ccipController.transferOwnership(multisigAddress)).wait() +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) diff --git a/scripts/prod/ccip/stage-2/2-deploy-secondary-chain.ts b/scripts/prod/ccip/stage-2/2-deploy-secondary-chain.ts new file mode 100644 index 00000000..bb7ccc18 --- /dev/null +++ b/scripts/prod/ccip/stage-2/2-deploy-secondary-chain.ts @@ -0,0 +1,114 @@ +import { toEther } from '../../../utils/helpers' +import { + updateDeployments, + deploy, + deployUpgradeable, + getContract, +} from '../../../utils/deployment' +import { + RESDLTokenBridge, + SDLPoolCCIPControllerSecondary, + SDLPoolSecondary, +} from '../../../../typechain-types' + +// Execute on Arbitrum Mainnet + +const ccipRouter = '0x141fa059441E0ca23ce184B6A78bafD2A517DdE8' +const multisigAddress = '' + +const primaryChainSelector = '5009297550715157269' // ETH Mainnet +const primaryChainSDLPoolCCIPController = '' // ETH Mainnet + +// Linear Boost Controller +const LinearBoostControllerParams = { + minLockingDuration: 86400, // minimum locking duration in seconds + maxLockingDuration: 4 * 365 * 86400, // maximum locking duration + maxBoost: 8, // maximum boost amount +} +// SDL Pool Secondary +const SDLPoolParams = { + derivativeTokenName: 'Reward Escrowed SDL', // SDL staking derivative token name + derivativeTokenSymbol: 'reSDL', // SDL staking derivative token symbol + queuedNewLockLimit: 50, // max number of queued new locks a user can have at once + baseURI: '', // base URI for reSDL NFTs +} +// SDL Pool CCIP Controller Secondary +const SDLPoolCCIPControllerParams = { + maxLINKFee: toEther(10), // max LINK fee to be paid on outgoing CCIP updates + updateInitiator: '', // address authorized to send CCIP updates + minTimeBetweenUpdates: '82800', // min time between updates in seconds +} + +async function main() { + const sdlToken = await getContract('SDLToken') + const linkToken = await getContract('LINKToken') + const wstLINKToken = await getContract('LINK_WrappedSDToken') + + const boostController = await deploy('LinearBoostController', [ + LinearBoostControllerParams.minLockingDuration, + LinearBoostControllerParams.maxLockingDuration, + LinearBoostControllerParams.maxBoost, + ]) + console.log('LinearBoostController deployed: ', boostController.address) + + const sdlPool = (await deployUpgradeable('SDLPoolSecondary', [ + SDLPoolParams.derivativeTokenName, + SDLPoolParams.derivativeTokenSymbol, + sdlToken.address, + boostController.address, + SDLPoolParams.queuedNewLockLimit, + ])) as SDLPoolSecondary + console.log('SDLPoolSecondary deployed: ', sdlPool.address) + + const rewardsPool = await deploy('RewardsPool', [sdlPool.address, wstLINKToken.address]) + console.log('wstLINK_SDLRewardsPool deployed: ', rewardsPool.address) + + const ccipController = (await deploy('SDLPoolCCIPControllerSecondary', [ + ccipRouter, + linkToken.address, + sdlToken.address, + sdlPool.address, + primaryChainSelector, + primaryChainSDLPoolCCIPController, + SDLPoolCCIPControllerParams.maxLINKFee, + SDLPoolCCIPControllerParams.updateInitiator, + SDLPoolCCIPControllerParams.minTimeBetweenUpdates, + ])) as SDLPoolCCIPControllerSecondary + console.log('SDLPoolCCIPControllerSecondary deployed: ', ccipController.address) + + const reSDLTokenBridge = (await deploy('RESDLTokenBridge', [ + linkToken.address, + sdlToken.address, + sdlPool.address, + ccipController.address, + ])) as RESDLTokenBridge + console.log('RESDLTokenBridge deployed: ', reSDLTokenBridge.address) + + await (await boostController.transferOwnership(multisigAddress)).wait() + + await (await sdlPool.setCCIPController(ccipController.address)).wait() + await (await sdlPool.addToken(wstLINKToken.address, rewardsPool.address)).wait() + await (await sdlPool.setBaseURI(SDLPoolParams.baseURI)).wait() + await (await sdlPool.transferOwnership(multisigAddress)).wait() + + await (await ccipController.setRESDLTokenBridge(reSDLTokenBridge.address)).wait() + await (await ccipController.transferOwnership(multisigAddress)).wait() + + updateDeployments( + { + SDLPoolSecondary: sdlPool.address, + LinearBoostController: boostController.address, + wstLINK_SDLRewardsPool: rewardsPool.address, + SDLPoolCCIPControllerSecondary: ccipController.address, + RESDLTokenBridge: reSDLTokenBridge.address, + }, + { wstLINK_SDLRewardsPool: 'RewardsPool' } + ) +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) diff --git a/scripts/prod/ccip/stage-2/3-upgrade-primary-chain.ts b/scripts/prod/ccip/stage-2/3-upgrade-primary-chain.ts new file mode 100644 index 00000000..3e3a0e4d --- /dev/null +++ b/scripts/prod/ccip/stage-2/3-upgrade-primary-chain.ts @@ -0,0 +1,86 @@ +import { getContract } from '../../../utils/deployment' +import { SDLPoolCCIPControllerPrimary, SDLPoolPrimary } from '../../../../typechain-types' +import { getAccounts } from '../../../utils/helpers' +import Safe, { EthersAdapter } from '@safe-global/protocol-kit' +import SafeApiKit from '@safe-global/api-kit' +import { ethers } from 'hardhat' +import { MetaTransactionData } from '@safe-global/safe-core-sdk-types' + +// Execute on Ethereum Mainnet + +const multisigAddress = '0xB351EC0FEaF4B99FdFD36b484d9EC90D0422493D' +const secondaryChainSelector = '4949039107694359620' // Arbitrum Mainnet +const secondaryChainSDLPoolCCIPController = '' // Arbitrum Mainnet +const sdlPoolPrimaryImplementation = '' + +async function main() { + const { signers, accounts } = await getAccounts() + const ethAdapter = new EthersAdapter({ + ethers, + signerOrProvider: signers[0], + }) + const safeSdk = await Safe.create({ ethAdapter, safeAddress: multisigAddress }) + const safeService = new SafeApiKit({ + txServiceUrl: 'https://safe-transaction-mainnet.safe.global', + ethAdapter, + }) + + const sdlPool = (await getContract('SDLPool')) as SDLPoolPrimary + const ccipController = (await getContract( + 'SDLPoolCCIPControllerPrimary' + )) as SDLPoolCCIPControllerPrimary + + const safeTransactionData: MetaTransactionData[] = [ + { + to: sdlPool.address, + data: + ( + await sdlPool.populateTransaction.upgradeToAndCall( + sdlPoolPrimaryImplementation, + sdlPool.interface.encodeFunctionData('initialize', [ + '', + '', + ethers.constants.AddressZero, + ethers.constants.AddressZero, + ]) + ) + ).data || '', + value: '0', + }, + { + to: sdlPool.address, + data: + (await sdlPool.populateTransaction.setCCIPController(ccipController.address)).data || '', + value: '0', + }, + { + to: ccipController.address, + data: + ( + await ccipController.populateTransaction.addWhitelistedChain( + secondaryChainSelector, + secondaryChainSDLPoolCCIPController + ) + ).data || '', + value: '0', + }, + ] + const safeTransaction = await safeSdk.createTransaction({ safeTransactionData }) + const safeTxHash = await safeSdk.getTransactionHash(safeTransaction) + const senderSignature = await safeSdk.signTransactionHash(safeTxHash) + + await safeService.proposeTransaction({ + safeAddress: multisigAddress, + safeTransactionData: safeTransaction.data, + safeTxHash, + senderAddress: accounts[0], + senderSignature: senderSignature.data, + }) +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error) + process.exit(1) + }) diff --git a/scripts/utils/helpers.ts b/scripts/utils/helpers.ts index 3827998b..13580793 100644 --- a/scripts/utils/helpers.ts +++ b/scripts/utils/helpers.ts @@ -1,5 +1,5 @@ import { ethers } from 'hardhat' -import { BigNumber } from 'ethers' +import { BigNumber, Signer } from 'ethers' import { ERC677 } from '../../typechain-types' export const toEther = (amount: string | number) => { @@ -10,7 +10,7 @@ export const fromEther = (amount: BigNumber) => { return Number(ethers.utils.formatEther(amount)) } -export const getAccounts = async () => { +export const getAccounts = async (): Promise => { const signers = await ethers.getSigners() const accounts = await Promise.all(signers.map(async (signer) => signer.getAddress())) return { signers, accounts }