From 57aa81ba3e220e5eeafedb6451dc9b6d6bb26beb Mon Sep 17 00:00:00 2001 From: Julian R Date: Wed, 5 Nov 2025 10:41:07 -0300 Subject: [PATCH 1/4] rlusd aave v3 plugin --- common/configuration.ts | 7 ++ scripts/deploy.ts | 1 + .../collaterals/deploy_aave_v3_rlusd.ts | 111 ++++++++++++++++++ .../verify_aave_v3_rlusd.ts | 74 ++++++++++++ scripts/verify_etherscan.ts | 1 + .../aave-v3/AaveV3FiatCollateral.test.ts | 27 +++++ .../individual-collateral/aave-v3/common.ts | 7 +- .../aave-v3/constants.ts | 4 + 8 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 scripts/deployment/phase2-assets/collaterals/deploy_aave_v3_rlusd.ts create mode 100644 scripts/verification/collateral-plugins/verify_aave_v3_rlusd.ts diff --git a/common/configuration.ts b/common/configuration.ts index 8fff48bd4..7d2708023 100644 --- a/common/configuration.ts +++ b/common/configuration.ts @@ -139,6 +139,10 @@ export interface ITokens { ETHPLUS?: string bsdETH?: string KNOX?: string + + RLUSD?: string + aEthRLUSD?: string + saEthRLUSD?: string } export type ITokensKeys = Array @@ -295,6 +299,8 @@ export const networkConfig: { [key: string]: INetworkConfig } = { USDS: '0xdC035D45d973E3EC169d2276DDab16f1e407384F', sUSDS: '0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD', wOETH: '0xDcEe70654261AF21C44c093C300eD3Bb97b78192', + RLUSD: '0x8292Bb45bf1Ee4d140127049757C2E0fF06317eD', + aEthRLUSD: '0xFa82580c16A31D0c1bC632A36F82e83EfEF3Eec0', }, chainlinkFeeds: { RSR: '0x759bBC1be8F90eE6457C44abc7d443842a976d02', @@ -326,6 +332,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = { USDe: '0xa569d910839Ae8865Da8F8e70FfFb0cBA869F961', USDS: '0xfF30586cD0F29eD462364C7e81375FC0C71219b1', OETHETH: '0x703118C4CbccCBF2AB31913e0f8075fbbb15f563', // OETH/ETH + RLUSD: '0x26C46B7aD0012cA71F2298ada567dC9Af14E7f2A', }, AAVE_INCENTIVES: '0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5', AAVE_EMISSIONS_MGR: '0xEE56e2B3D491590B5b31738cC34d5232F378a8D5', diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 6d1fd0fd6..faf258f22 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -84,6 +84,7 @@ async function main() { 'phase2-assets/collaterals/deploy_aave_v3_usdc.ts', 'phase2-assets/collaterals/deploy_aave_v3_usdt.ts', 'phase2-assets/collaterals/deploy_aave_v3_pyusd.ts', + 'phase2-assets/collaterals/deploy_aave_v3_rlusd.ts', 'phase2-assets/collaterals/deploy_yearn_v2_curve_usdc.ts', 'phase2-assets/collaterals/deploy_yearn_v2_curve_usdp.ts', 'phase2-assets/collaterals/deploy_sfrax.ts', diff --git a/scripts/deployment/phase2-assets/collaterals/deploy_aave_v3_rlusd.ts b/scripts/deployment/phase2-assets/collaterals/deploy_aave_v3_rlusd.ts new file mode 100644 index 000000000..44be48b76 --- /dev/null +++ b/scripts/deployment/phase2-assets/collaterals/deploy_aave_v3_rlusd.ts @@ -0,0 +1,111 @@ +import fs from 'fs' +import hre, { ethers } from 'hardhat' +import { getChainId } from '../../../../common/blockchain-utils' +import { baseL2Chains, networkConfig } from '../../../../common/configuration' +import { expect } from 'chai' +import { CollateralStatus } from '../../../../common/constants' +import { + getDeploymentFile, + getAssetCollDeploymentFilename, + IAssetCollDeployments, + getDeploymentFilename, + fileExists, +} from '../../common' +import { bn, fp } from '#/common/numbers' +import { AaveV3FiatCollateral } from '../../../../typechain' +import { priceTimeout, revenueHiding } from '../../utils' +import { + RLUSD_MAX_TRADE_VOLUME, + RLUSD_ORACLE_TIMEOUT, + RLUSD_ORACLE_ERROR, +} from '../../../../test/plugins/individual-collateral/aave-v3/constants' + +// This file specifically deploys Aave V3 RLUSD collateral on Mainnet + +async function main() { + // ==== Read Configuration ==== + const [deployer] = await hre.ethers.getSigners() + + const chainId = await getChainId(hre) + + console.log(`Deploying Collateral to network ${hre.network.name} (${chainId}) + with burner account: ${deployer.address}`) + + if (!networkConfig[chainId]) { + throw new Error(`Missing network configuration for ${hre.network.name}`) + } + + // Only exists on Mainnet + if (baseL2Chains.includes(hre.network.name)) { + throw new Error(`Invalid network ${hre.network.name} - only available on Mainnet`) + } + + // Get phase1 deployment + const phase1File = getDeploymentFilename(chainId) + if (!fileExists(phase1File)) { + throw new Error(`${phase1File} doesn't exist yet. Run phase 1`) + } + + // Check previous step completed + const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId) + const assetCollDeployments = getDeploymentFile(assetCollDeploymentFilename) + + const deployedCollateral: string[] = [] + + const CollateralFactory = await ethers.getContractFactory('AaveV3FiatCollateral') + const StaticATokenFactory = await hre.ethers.getContractFactory('StaticATokenV3LM') + + /******** Deploy Aave V3 RLUSD ERC20 **************************/ + + const erc20 = await StaticATokenFactory.deploy( + networkConfig[chainId].AAVE_V3_POOL!, + networkConfig[chainId].AAVE_V3_INCENTIVES_CONTROLLER! + ) + await erc20.deployed() + await ( + await erc20.initialize( + networkConfig[chainId].tokens.aEthRLUSD!, + 'Static Aave Ethereum RLUSD', + 'saEthRLUSD' + ) + ).wait() + + /******** Deploy Aave V3 RLUSD collateral plugin **************************/ + + const collateral = await CollateralFactory.connect(deployer).deploy( + { + priceTimeout: priceTimeout, + chainlinkFeed: networkConfig[chainId].chainlinkFeeds.RLUSD!, + oracleError: RLUSD_ORACLE_ERROR, + erc20: erc20.address, + maxTradeVolume: RLUSD_MAX_TRADE_VOLUME, + oracleTimeout: RLUSD_ORACLE_TIMEOUT, + targetName: ethers.utils.formatBytes32String('USD'), + defaultThreshold: fp('0.01').add(RLUSD_ORACLE_ERROR), + delayUntilDefault: bn('86400'), + }, + revenueHiding + ) + await collateral.deployed() + await (await collateral.refresh()).wait() + expect(await collateral.status()).to.equal(CollateralStatus.SOUND) + + console.log( + `Deployed Aave V3 RLUSD collateral to ${hre.network.name} (${chainId}): ${collateral.address}` + ) + + assetCollDeployments.erc20s.saEthRLUSD = erc20.address + assetCollDeployments.collateral.saEthRLUSD = collateral.address + deployedCollateral.push(collateral.address.toString()) + + fs.writeFileSync(assetCollDeploymentFilename, JSON.stringify(assetCollDeployments, null, 2)) + + console.log(`Deployed collateral to ${hre.network.name} (${chainId}) + New deployments: ${deployedCollateral} + Deployment file: ${assetCollDeploymentFilename}`) +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/verification/collateral-plugins/verify_aave_v3_rlusd.ts b/scripts/verification/collateral-plugins/verify_aave_v3_rlusd.ts new file mode 100644 index 000000000..5e710465e --- /dev/null +++ b/scripts/verification/collateral-plugins/verify_aave_v3_rlusd.ts @@ -0,0 +1,74 @@ +import hre, { ethers } from 'hardhat' +import { getChainId } from '../../../common/blockchain-utils' +import { developmentChains, networkConfig } from '../../../common/configuration' +import { + getDeploymentFile, + getAssetCollDeploymentFilename, + IAssetCollDeployments, +} from '../../deployment/common' +import { fp } from '../../../common/numbers' +import { priceTimeout, verifyContract, revenueHiding } from '../../deployment/utils' + +let deployments: IAssetCollDeployments + +async function main() { + // ********** Read config ********** + const chainId = await getChainId(hre) + if (!networkConfig[chainId]) { + throw new Error(`Missing network configuration for ${hre.network.name}`) + } + + if (developmentChains.includes(hre.network.name)) { + throw new Error(`Cannot verify contracts for development chain ${hre.network.name}`) + } + + const assetCollDeploymentFilename = getAssetCollDeploymentFilename(chainId) + deployments = getDeploymentFile(assetCollDeploymentFilename) + + const erc20s: { [key: string]: string } = { + '1': deployments.erc20s.saEthRLUSD!, + } + const erc20 = await ethers.getContractAt('ERC20Mock', erc20s[chainId]) + + const collaterals: { [key: string]: string } = { + '1': deployments.collateral.saEthRLUSD!, + } + const collateral = await ethers.getContractAt('AaveV3FiatCollateral', collaterals[chainId]) + + /******** Verify Aave V3 RLUSD ERC20 **************************/ + await verifyContract( + chainId, + erc20.address, + [networkConfig[chainId].AAVE_V3_POOL!, networkConfig[chainId].AAVE_V3_INCENTIVES_CONTROLLER!], + 'contracts/plugins/assets/aave-v3/vendor/StaticATokenV3LM.sol:StaticATokenV3LM' + ) + + /******** Verify Aave V3 RLUSD plugin **************************/ + + await verifyContract( + chainId, + collateral.address, + [ + { + erc20: await collateral.erc20(), + targetName: ethers.utils.formatBytes32String('USD'), + priceTimeout: priceTimeout.toString(), + chainlinkFeed: await collateral.chainlinkFeed(), + oracleError: await collateral.oracleError(), + oracleTimeout: await collateral.oracleTimeout(), + maxTradeVolume: await collateral.maxTradeVolume(), + defaultThreshold: fp('0.01') + .add(await collateral.oracleError()) + .toString(), + delayUntilDefault: await collateral.delayUntilDefault(), + }, + revenueHiding.toString(), + ], + 'contracts/plugins/assets/aave-v3/AaveV3FiatCollateral.sol:AaveV3FiatCollateral' + ) +} + +main().catch((error) => { + console.error(error) + process.exitCode = 1 +}) diff --git a/scripts/verify_etherscan.ts b/scripts/verify_etherscan.ts index c0e966e95..9370878b9 100644 --- a/scripts/verify_etherscan.ts +++ b/scripts/verify_etherscan.ts @@ -74,6 +74,7 @@ async function main() { 'collateral-plugins/verify_morpho.ts', 'collateral-plugins/verify_aave_v3_usdc.ts', 'collateral-plugins/verify_aave_v3_usdt.ts', + 'collateral-plugins/verify_aave_v3_rlusd.ts', 'collateral-plugins/verify_yearn_v2_curve_usdc.ts', 'collateral-plugins/verify_yearn_v2_curve_usdp.ts', 'collateral-plugins/verify_sfrax.ts', diff --git a/test/plugins/individual-collateral/aave-v3/AaveV3FiatCollateral.test.ts b/test/plugins/individual-collateral/aave-v3/AaveV3FiatCollateral.test.ts index 32c317ac3..64a7deaaa 100644 --- a/test/plugins/individual-collateral/aave-v3/AaveV3FiatCollateral.test.ts +++ b/test/plugins/individual-collateral/aave-v3/AaveV3FiatCollateral.test.ts @@ -22,6 +22,9 @@ import { USDT_MAINNET_MAX_TRADE_VOLUME, USDT_MAINNET_ORACLE_TIMEOUT, USDT_MAINNET_ORACLE_ERROR, + RLUSD_MAINNET_MAX_TRADE_VOLUME, + RLUSD_MAINNET_ORACLE_TIMEOUT, + RLUSD_MAINNET_ORACLE_ERROR, } from './constants' // Mainnet - USDC @@ -121,6 +124,30 @@ makeTests( } ) +// Mainnet - RLUSD +makeTests( + { + priceTimeout: PRICE_TIMEOUT, + chainlinkFeed: networkConfig[1].chainlinkFeeds['RLUSD']!, + oracleError: RLUSD_MAINNET_ORACLE_ERROR, + erc20: '', // to be set + maxTradeVolume: RLUSD_MAINNET_MAX_TRADE_VOLUME, + oracleTimeout: RLUSD_MAINNET_ORACLE_TIMEOUT, + targetName: ethers.utils.formatBytes32String('USD'), + defaultThreshold: fp('0.01').add(RLUSD_MAINNET_ORACLE_TIMEOUT), + delayUntilDefault: bn('86400'), + }, + { + testName: 'RLUSD - Mainnet', + aaveIncentivesController: networkConfig[1].AAVE_V3_INCENTIVES_CONTROLLER!, + aavePool: networkConfig[1].AAVE_V3_POOL!, + aToken: networkConfig[1].tokens['aEthRLUSD']!, + whaleTokenHolder: '0x1073d55Dfb892ed86151015402DB8B1CDb6edE78', + forkBlock: 23726585, + targetNetwork: 'mainnet', + } +) + // Arbitrum - USDC makeTests( { diff --git a/test/plugins/individual-collateral/aave-v3/common.ts b/test/plugins/individual-collateral/aave-v3/common.ts index 6b6c5d268..bd3fd467f 100644 --- a/test/plugins/individual-collateral/aave-v3/common.ts +++ b/test/plugins/individual-collateral/aave-v3/common.ts @@ -121,8 +121,6 @@ export const makeTests = (defaultCollateralOpts: CollateralParams, altParams: Al user: SignerWithAddress, recipient: string ) => { - const requiredCollat = await ctx.staticWrapper.previewMint(amount) - // Impersonate holder await whileImpersonating(altParams.whaleTokenHolder, async (signer) => { await ctx.baseToken.connect(signer).approve(ctx.staticWrapper.address, 0) // required for usdt @@ -131,9 +129,8 @@ export const makeTests = (defaultCollateralOpts: CollateralParams, altParams: Al .connect(signer) .approve(ctx.staticWrapper.address, ethers.constants.MaxUint256) - await ctx.staticWrapper - .connect(signer) - ['deposit(uint256,address,uint16,bool)'](requiredCollat, recipient, 0, true) + // Use mint() for exact shares without rounding loss + await ctx.staticWrapper.connect(signer)['mint(uint256,address)'](amount, recipient) }) } diff --git a/test/plugins/individual-collateral/aave-v3/constants.ts b/test/plugins/individual-collateral/aave-v3/constants.ts index b628119b1..131e22883 100644 --- a/test/plugins/individual-collateral/aave-v3/constants.ts +++ b/test/plugins/individual-collateral/aave-v3/constants.ts @@ -23,3 +23,7 @@ export const USDT_MAINNET_ORACLE_ERROR = fp('0.0025') export const USDT_ARBITRUM_MAX_TRADE_VOLUME = fp('1e6') export const USDT_ARBITRUM_ORACLE_TIMEOUT = bn('86400') export const USDT_ARBITRUM_ORACLE_ERROR = fp('0.001') + +export const RLUSD_MAINNET_MAX_TRADE_VOLUME = fp('1e6') +export const RLUSD_MAINNET_ORACLE_TIMEOUT = bn('86400') +export const RLUSD_MAINNET_ORACLE_ERROR = fp('0.003') From 040a9ec7631c2950ff38b5484632ea02146337d4 Mon Sep 17 00:00:00 2001 From: Julian R Date: Wed, 5 Nov 2025 11:03:51 -0300 Subject: [PATCH 2/4] fix params --- .../collaterals/deploy_aave_v3_rlusd.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/deployment/phase2-assets/collaterals/deploy_aave_v3_rlusd.ts b/scripts/deployment/phase2-assets/collaterals/deploy_aave_v3_rlusd.ts index 44be48b76..a3922ac01 100644 --- a/scripts/deployment/phase2-assets/collaterals/deploy_aave_v3_rlusd.ts +++ b/scripts/deployment/phase2-assets/collaterals/deploy_aave_v3_rlusd.ts @@ -15,9 +15,9 @@ import { bn, fp } from '#/common/numbers' import { AaveV3FiatCollateral } from '../../../../typechain' import { priceTimeout, revenueHiding } from '../../utils' import { - RLUSD_MAX_TRADE_VOLUME, - RLUSD_ORACLE_TIMEOUT, - RLUSD_ORACLE_ERROR, + RLUSD_MAINNET_MAX_TRADE_VOLUME, + RLUSD_MAINNET_ORACLE_ERROR, + RLUSD_MAINNET_ORACLE_TIMEOUT, } from '../../../../test/plugins/individual-collateral/aave-v3/constants' // This file specifically deploys Aave V3 RLUSD collateral on Mainnet @@ -76,12 +76,12 @@ async function main() { { priceTimeout: priceTimeout, chainlinkFeed: networkConfig[chainId].chainlinkFeeds.RLUSD!, - oracleError: RLUSD_ORACLE_ERROR, + oracleError: RLUSD_MAINNET_ORACLE_ERROR, erc20: erc20.address, - maxTradeVolume: RLUSD_MAX_TRADE_VOLUME, - oracleTimeout: RLUSD_ORACLE_TIMEOUT, + maxTradeVolume: RLUSD_MAINNET_MAX_TRADE_VOLUME, + oracleTimeout: RLUSD_MAINNET_ORACLE_TIMEOUT, targetName: ethers.utils.formatBytes32String('USD'), - defaultThreshold: fp('0.01').add(RLUSD_ORACLE_ERROR), + defaultThreshold: fp('0.01').add(RLUSD_MAINNET_ORACLE_ERROR), delayUntilDefault: bn('86400'), }, revenueHiding From bd27d5b506d4f8faf145a6e61079f2c197baa642 Mon Sep 17 00:00:00 2001 From: Julian R Date: Mon, 10 Nov 2025 10:37:35 -0300 Subject: [PATCH 3/4] deployed addresses --- scripts/addresses/1-tmp-assets-collateral.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/addresses/1-tmp-assets-collateral.json b/scripts/addresses/1-tmp-assets-collateral.json index 55fc514a4..1447feabb 100644 --- a/scripts/addresses/1-tmp-assets-collateral.json +++ b/scripts/addresses/1-tmp-assets-collateral.json @@ -66,7 +66,8 @@ "cUSDTv3": "0x1B2256a88Bb9F2E54cC8D355D3161a2F069a320B", "sUSDS": "0xaFf578165bEA370D16d8AC61A4C8c6D435785d58", "saEthUSDT": "0x18A8D9088433aE749cE6c33070b2Fe95e8a263d9", - "wOETH": "0xD9Ee6fEb2a277E5948B6Fd6B93d125B3225339aA" + "wOETH": "0xD9Ee6fEb2a277E5948B6Fd6B93d125B3225339aA", + "saEthRLUSD": "0xb1e61f452CFcF6609C2F4088EC36B4c8dd1806b5" }, "erc20s": { "stkAAVE": "0x4da27a545c0c5B758a6BA100e3a049001de870f5", @@ -133,6 +134,7 @@ "cUSDTv3": "0xEB74EC1d4C1DAB412D5d6674F6833FD19d3118Ce", "sUSDS": "0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD", "saEthUSDT": "0x7133EbCb1a23e3d8Be7Ff09362b02b2A2e4c3dA4", - "wOETH": "0xDcEe70654261AF21C44c093C300eD3Bb97b78192" + "wOETH": "0xDcEe70654261AF21C44c093C300eD3Bb97b78192", + "saEthRLUSD": "0x4C813CE4e2FF315f0213563A994c20BBF4637444" } } \ No newline at end of file From 6a7e502d5e98ff878e15944c84a6ad9d2dec2acc Mon Sep 17 00:00:00 2001 From: Julian R Date: Mon, 10 Nov 2025 10:52:29 -0300 Subject: [PATCH 4/4] add deployed addrs --- .../addresses/mainnet-4.2.0/1-tmp-assets-collateral.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/addresses/mainnet-4.2.0/1-tmp-assets-collateral.json b/scripts/addresses/mainnet-4.2.0/1-tmp-assets-collateral.json index ae697a0ad..611344f19 100644 --- a/scripts/addresses/mainnet-4.2.0/1-tmp-assets-collateral.json +++ b/scripts/addresses/mainnet-4.2.0/1-tmp-assets-collateral.json @@ -56,7 +56,8 @@ "sUSDe": "0x4f30165072351923A1A4BC3926050986318f9B34", "pyUSD": "0xe0941A6e0DFC823CF44e95664a5B151041C13D42", "sUSDS": "0x8a1a3B46749b81Cf91d56dF6042E12CE50E1b08A", - "wOETH": "0xa4D38731434e875d7E30e13d8b65BEfEd7d47Ac2" + "wOETH": "0xa4D38731434e875d7E30e13d8b65BEfEd7d47Ac2", + "saEthRLUSD": "0xb1e61f452CFcF6609C2F4088EC36B4c8dd1806b5" }, "erc20s": { "stkAAVE": "0x4da27a545c0c5B758a6BA100e3a049001de870f5", @@ -113,6 +114,7 @@ "CVX": "0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B", "pyUSD": "0x6c3ea9036406852006290770bedfcaba0e23a0e8", "sUSDS": "0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD", - "wOETH": "0xDcEe70654261AF21C44c093C300eD3Bb97b78192" + "wOETH": "0xDcEe70654261AF21C44c093C300eD3Bb97b78192", + "saEthRLUSD": "0x4C813CE4e2FF315f0213563A994c20BBF4637444" } }