Skip to content
Open
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
267 changes: 267 additions & 0 deletions scripts/DeployAaveV3BaseSepolia.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;

import 'forge-std/Script.sol';
import 'forge-std/console.sol';

import {AaveV3BatchOrchestration} from '../src/deployments/projects/aave-v3-batched/AaveV3BatchOrchestration.sol';
import {IAaveV3ConfigEngine} from '../src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
import {EngineFlags} from '../src/contracts/extensions/v3-config-engine/EngineFlags.sol';
import {MockAggregatorSetPrice} from '../tests/invariants/utils/mocks/MockAggregatorSetPrice.sol';
import {SequencerOracle} from '../src/contracts/mocks/oracle/SequencerOracle.sol';
import {TestnetERC20} from '../src/contracts/mocks/testnet-helpers/TestnetERC20.sol';
import {IERC20} from '../src/contracts/dependencies/openzeppelin/contracts/IERC20.sol';
import {IPool} from '../src/contracts/interfaces/IPool.sol';
import {IACLManager} from '../src/contracts/interfaces/IACLManager.sol';
import {IPoolAddressesProvider} from '../src/contracts/interfaces/IPoolAddressesProvider.sol';
import {LiquidationDataProvider} from '../src/contracts/helpers/LiquidationDataProvider.sol';
import {MarketReport, MarketConfig, DeployFlags, Roles} from '../src/deployments/interfaces/IMarketReportTypes.sol';

struct DeployedAssets {
TestnetERC20 usdc;
TestnetERC20 usdt;
TestnetERC20 wbtc;
TestnetERC20 weth;
MockAggregatorSetPrice usdcOracle;
MockAggregatorSetPrice usdtOracle;
MockAggregatorSetPrice wbtcOracle;
MockAggregatorSetPrice wethOracle;
SequencerOracle sequencerOracle;
}

/**
* @notice One-shot script to deploy a full Aave v3 market on Base Sepolia, together with
* 4 mock assets and adjustable price feeds.
* @dev Transactions are broadcasted; set PRIVATE_KEY env. RPC handled by forge args.
*/
contract DeployAaveV3BaseSepolia is Script {
function run() external {
uint256 deployerPk = _getPrivateKey();
address deployer = vm.addr(deployerPk);

vm.startBroadcast(deployerPk);

// 1) Deploy test tokens
DeployedAssets memory a;
a.usdc = new TestnetERC20('USDC', 'USDC', 6, deployer);
a.usdt = new TestnetERC20('USDT', 'USDT', 6, deployer);
a.wbtc = new TestnetERC20('WBTC', 'WBTC', 8, deployer);
a.weth = new TestnetERC20('WETH', 'WETH', 18, deployer);

// Mint 1e9 units (before decimals) to deployer
uint256 baseSupply = 1e9;
a.usdc.mint(deployer, baseSupply * 10 ** a.usdc.decimals());
a.usdt.mint(deployer, baseSupply * 10 ** a.usdt.decimals());
a.wbtc.mint(deployer, baseSupply * 10 ** a.wbtc.decimals());
a.weth.mint(deployer, baseSupply * 10 ** a.weth.decimals());

// 2) Deploy adjustable price feeds (8 decimals)
a.usdcOracle = new MockAggregatorSetPrice(1e8); // $1
a.usdtOracle = new MockAggregatorSetPrice(1e8); // $1
a.wbtcOracle = new MockAggregatorSetPrice(88700e8); // $88,700
a.wethOracle = new MockAggregatorSetPrice(3078e8); // $3,078

// 3) Deploy sequencer uptime oracle (for sentinel) and mark as healthy
a.sequencerOracle = new SequencerOracle(deployer);
a.sequencerOracle.setAnswer(false, block.timestamp);

// 4) Build config + deploy Aave V3 (L2Pool)
Roles memory roles;
roles.marketOwner = deployer;
roles.poolAdmin = deployer;
roles.emergencyAdmin = deployer;

MarketConfig memory config;
config.marketId = 'Aave V3 Base Sepolia';
config.providerId = 84532;
config.oracleDecimals = 8;
config.flashLoanPremium = 0.0005e4; // 0.05%
config.networkBaseTokenPriceInUsdProxyAggregator = address(a.wethOracle);
config.marketReferenceCurrencyPriceInUsdProxyAggregator = address(a.usdcOracle);
config.l2SequencerUptimeFeed = address(a.sequencerOracle);
config.l2PriceOracleSentinelGracePeriod = 1 hours;
config.wrappedNativeToken = address(a.weth); // treated as plain ERC20

DeployFlags memory flags;
flags.l2 = true;

MarketReport memory report;
report = AaveV3BatchOrchestration.deployAaveV3(deployer, roles, config, flags, report);

// 5) List assets via ConfigEngine with custom implementations
IAaveV3ConfigEngine configEngine = IAaveV3ConfigEngine(report.configEngine);
IAaveV3ConfigEngine.PoolContext memory context = IAaveV3ConfigEngine.PoolContext({
networkName: 'Base Sepolia',
networkAbbreviation: 'BSP'
});

IAaveV3ConfigEngine.ListingWithCustomImpl[] memory listings = new IAaveV3ConfigEngine
.ListingWithCustomImpl[](4);

IAaveV3ConfigEngine.InterestRateInputData memory rateParams = IAaveV3ConfigEngine
.InterestRateInputData({
optimalUsageRatio: 45_00,
baseVariableBorrowRate: 0,
variableRateSlope1: 4_00,
variableRateSlope2: 60_00
});

listings[0] = _buildListing(
address(a.usdc),
'USDC',
address(a.usdcOracle),
rateParams,
report.aToken,
report.variableDebtToken
);
listings[1] = _buildListing(
address(a.usdt),
'USDT',
address(a.usdtOracle),
rateParams,
report.aToken,
report.variableDebtToken
);
listings[2] = _buildListing(
address(a.wbtc),
'WBTC',
address(a.wbtcOracle),
rateParams,
report.aToken,
report.variableDebtToken
);
listings[3] = _buildListing(
address(a.weth),
'WETH',
address(a.wethOracle),
rateParams,
report.aToken,
report.variableDebtToken
);

IACLManager(report.aclManager).addAssetListingAdmin(address(configEngine));
IACLManager(report.aclManager).addRiskAdmin(address(configEngine));
configEngine.listAssetsCustom(context, listings);

LiquidationDataProvider liquidationDataProvider = new LiquidationDataProvider(
report.poolProxy,
report.poolAddressesProvider
);

// Log outputs for convenience
console.log('Deployer', deployer);
console.log('USDC', address(a.usdc));
console.log('USDT', address(a.usdt));
console.log('WBTC', address(a.wbtc));
console.log('WETH', address(a.weth));
console.log('Oracle USDC', address(a.usdcOracle));
console.log('Oracle USDT', address(a.usdtOracle));
console.log('Oracle WBTC', address(a.wbtcOracle));
console.log('Oracle WETH', address(a.wethOracle));
console.log('Sequencer oracle', address(a.sequencerOracle));
console.log('Pool', report.poolProxy);
console.log('ConfigEngine', report.configEngine);
console.log('AaveOracle', report.aaveOracle);
console.log('LiquidationDataProvider', address(liquidationDataProvider));

// Ensure sequencer oracle timestamp is older than grace period to allow borrowing
a.sequencerOracle.setAnswer(false, block.timestamp - 2 hours);

_quickHealthCheck(report, deployer, a);

vm.stopBroadcast();
}

function _buildListing(
address asset,
string memory symbol,
address priceFeed,
IAaveV3ConfigEngine.InterestRateInputData memory rateParams,
address aTokenImpl,
address vTokenImpl
) internal pure returns (IAaveV3ConfigEngine.ListingWithCustomImpl memory listing) {
listing.base = IAaveV3ConfigEngine.Listing({
asset: asset,
assetSymbol: symbol,
priceFeed: priceFeed,
rateStrategyParams: rateParams,
enabledToBorrow: EngineFlags.ENABLED,
borrowableInIsolation: EngineFlags.DISABLED,
withSiloedBorrowing: EngineFlags.DISABLED,
flashloanable: EngineFlags.ENABLED,
ltv: 82_50,
liqThreshold: 86_00,
liqBonus: 5_00,
reserveFactor: 10_00,
supplyCap: 0, // uncapped
borrowCap: 0, // uncapped
debtCeiling: 0,
liqProtocolFee: 10_00
});

listing.implementations = IAaveV3ConfigEngine.TokenImplementations({
aToken: aTokenImpl,
vToken: vTokenImpl
});
}

/// @dev Simple post-deploy sanity: supply/borrow/repay flow + price bump
function _quickHealthCheck(
MarketReport memory report,
address deployer,
DeployedAssets memory a
) internal {
IPool pool = IPool(report.poolProxy);

// Approvals
IERC20(address(a.usdc)).approve(address(pool), type(uint256).max);
IERC20(address(a.weth)).approve(address(pool), type(uint256).max);

// a) Supply 1 WETH as collateral
uint256 wethAmount = 1 ether;
pool.supply(address(a.weth), wethAmount, deployer, 0);
pool.setUserUseReserveAsCollateral(address(a.weth), true);

// b) Supply 1000 USDC, keep collateral disabled
uint256 usdcAmount = 1000 * 1e6;
pool.supply(address(a.usdc), usdcAmount, deployer, 0);
pool.setUserUseReserveAsCollateral(address(a.usdc), false);

// c) Borrow 900 USDC (variable rate)
uint256 borrowAmount = 900 * 1e6;
pool.borrow(address(a.usdc), borrowAmount, 2, 0, deployer);

// d) Repay all USDC (repay max to cover interest)
pool.repay(address(a.usdc), type(uint256).max, 2, deployer);

// e) Update WETH price to 3100 USD
a.wethOracle.setLatestAnswer(3100e8);

// Log account data
(
uint256 totalCollateralBase,
uint256 totalDebtBase,
uint256 availableBorrowsBase,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
) = pool.getUserAccountData(deployer);

console.log('Post-check collateral (base)', totalCollateralBase);
console.log('Post-check debt (base)', totalDebtBase);
console.log('Available borrows (base)', availableBorrowsBase);
console.log('Liq threshold', currentLiquidationThreshold);
console.log('LTV', ltv);
console.log('Health factor', healthFactor);
console.log('WETH price now', a.wethOracle.latestAnswer());
console.log('USDC price (ref currency)', a.usdcOracle.latestAnswer());
}

function _getPrivateKey() internal returns (uint256) {
string memory raw = vm.envString('PRIVATE_KEY');
bytes memory b = bytes(raw);
bool hasPrefix = b.length >= 2 && b[0] == '0' && (b[1] == 'x' || b[1] == 'X');
string memory normalized = hasPrefix ? raw : string(abi.encodePacked('0x', raw));
return uint256(vm.parseBytes32(normalized));
}
}
41 changes: 41 additions & 0 deletions src/design-doc/data.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
## 计算依赖数据定义

### 全局配置数据
- `currentTimestamp` (`uint64`): 当前区块时间戳,用于判断 `liquidationGracePeriodUntil` 是否已过。
- `liquidationAllowed` (`bool`): 价格哨兵返回值,决定在 `healthFactor` 低于 0.95 且高于 1.0 的区间内是否允许清算。
- `eModeCategories` (`map[uint8]EModeCategory`): eMode 配置集合,用于确定清算加成、LTV、LT 时的替代参数。

### EModeCategory
- `ltv` (`uint64`): 该类别下的 LTV(bps,1e4 精度)。
- `liquidationThreshold` (`uint64`): 该类别下的清算阈值(bps)。
- `liquidationBonus` (`uint64`): 该类别下的清算奖励(bps)。
- `collateralBitmap` (`uint128`): 允许作为抵押的资产位图。

### 资产级数据(每个 Reserve)
- `asset` (`Address`): 资产地址标识(`common.Address`)。
- `id` (`uint16`): 资产在位图中的索引。
- `decimals` (`uint8`): 资产精度,用于计算 `assetUnit = 10^decimals`。
- `priceInBaseCurrency` (`*big.Int`): 资产价格(基准币计价)。
- `ltv` (`uint64`): 资产的 LTV(bps)。
- `liquidationThreshold` (`uint64`): 资产的清算阈值(bps)。
- `liquidationBonus` (`uint64`): 资产的清算奖励(bps)。
- `liquidationProtocolFee` (`uint64`): 清算协议费(bps)。
- `normalizedIncome` (`*big.Int`): 归一化收益指数(ray,1e27),用于将 aToken scaled balance 转为实际余额。
- `normalizedDebt` (`*big.Int`): 归一化借款指数(ray,1e27),用于将 variable debt scaled balance 转为实际债务。
- `isActive` (`bool`): 资产是否可用。
- `isPaused` (`bool`): 是否暂停。
- `liquidationGracePeriodUntil` (`uint64`): 清算宽限期截止时间戳。
- `aTokenAddress` (`string`): 对应的 aToken。
- `variableDebtTokenAddress` (`string`): 对应的可变债务 Token。

### 用户级数据
- `userAddress` (`Address`): 用户标识(`common.Address`)。
- `userEModeCategory` (`uint8`): 用户当前选择的 eMode 类别。
- `positions` (`[]UserReservePosition`): 用户在各资产上的仓位明细。

### UserReservePosition
- `asset` (`Address`): 资产地址标识(`common.Address`)。
- `useAsCollateral` (`bool`): 是否将该资产用作抵押。
- `isBorrowing` (`bool`): 用户是否在该资产上有借款开关。
- `collateralScaledBalance` (`*big.Int`): aToken 的 scaled balance(保持不变,需乘以 `normalizedIncome` 得到实际余额)。
- `debtScaledBalance` (`*big.Int`): variable debt 的 scaled balance(保持不变,需乘以 `normalizedDebt` 得到实际债务)。
27 changes: 27 additions & 0 deletions src/design-doc/deployment-explain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## 是否需要新增脚本
- 需要。现有 `scripts/DeployAaveV3MarketBatched*.sol` 偏向通用批量部署/预编译库,不直接覆盖“本地测试 + base sepolia L2Pool + 自定义 mock token/oracle”的需求。

## 计划新增/修改的文件与功能
- `scripts/DeployAaveV3BaseSepolia.sol`(新):一站式 forge script,完成:
- 部署四个测试代币(USDC 6d、USDT 6d、WBTC 8d、WETH 18d,初始供应分配给部署者)。
- 部署四个 `MockAggregatorSetPrice`,初始价格 1/1/88700/3078,可后续手动调价。
- 部署并初始化 L2 核心组件:`POOL`(L2Pool)、`POOL_CONFIGURATOR`、`PRICE_ORACLE`(注入上述 sources)、`ACL_MANAGER`、`ACL_ADMIN`、`PRICE_ORACLE_SENTINEL`、`DATA_PROVIDER`,以及外围 `LiquidationDataProvider`,把需要的角色都授予部署者。
- 将代币上市到池子(配置利率策略/ltv 等可用固定参数),确保可正常存借清算。
- 输出部署产物(JSON/console)便于后续测试复用。
- `scripts/misc/DeployAaveV3MarketBatchedBase.sol`(可选小改):如果复用其部分初始化逻辑,可能添加帮助函数暴露更细粒度的部署步骤,否则不改。
- `src/design-doc/deployment.md`(仅参考,不改代码):作为需求来源。

## 复用思路
- 复用 `TestnetERC20` 与 `MockAggregatorSetPrice` 作为资产与喂价。
- 参考 `DeployAaveV3MarketBatched.sol` 的结构组织脚本,但针对 base-sepolia chainId、L2Pool 构造和自定义 token/oracle。
- 使用 forge standard-json 或 broadcast 方式,脚本中支持从环境变量读取私钥/RPC,避免硬编码。

## 其他说明
- 说明阶段不改代码,执行阶段再按上面文件清单落地。

## 运行指引(执行阶段给你的操作步骤)
- 依赖:`forge`,RPC 指向 `https://base-sepolia-public.nodies.app`(或自选 base sepolia 节点)。
- 环境变量:`PRIVATE_KEY`(部署者私钥,对应文档中的 0xd75Fe... 地址)。
- 执行命令示例:
- `forge script scripts/DeployAaveV3BaseSepolia.sol:DeployAaveV3BaseSepolia --rpc-url https://base-sepolia-public.nodies.app --broadcast --verify=false`
- 结果:控制台打印四个 token 地址、对应 oracle、SequencerOracle、Pool、ConfigEngine、AaveOracle。价格可通过 `MockAggregatorSetPrice.setLatestAnswer(int256)` 调整,Sequencer 状态可通过 `SequencerOracle.setAnswer(bool,uint256)` 手动改。
35 changes: 35 additions & 0 deletions src/design-doc/deployment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## Integration Test

### 目标
在base Sepolia Testnet 部署一套完整的aave v3 marketplace合约,用于简单的测试验证。

### 环境信息
部署者地址: 0xd75Fea05fdafD031e2a584Db1d4E6155c862B875
RPC endpoint: https://base-sepolia-public.nodies.app
chain-id: 84532
### 细节要求
1. 部署测试测试token(TestnetERC20.sol),比如USDC(decimal为6),USDT(decimal为6),WBTC(decimal为8),WETH(decimal为18),每个token的初始supply是1e9个(不考虑decimal)分配给合约部署者。 注意把WETH当成普通的ERC20 合约即可,不需要和ETH之间互换。
2. 分别部署USDC,USDT,WBTC, WETH 四种token的oracle price mock合约(复用MockAggregatorSetPrice.sol), 最初的价格是:1,1,88700,3078。 这些价格可以被随时调整。
3. 由于base testnet是L2, 所以我们部署的核心协议是L2Pool.sol 而不是Pool.sol。现在需要将整套 aave v3部署到 base testnt上:
a. 部署必要的核心合约,主要是以下几个:'POOL','POOL_CONFIGURATOR','PRICE_ORACLE','ACL_MANAGER','ACL_ADMIN', 'PRICE_ORACLE_SENTINEL', 'DATA_PROVIDER'
b. 部署一个周边合约 LiquidationDataProvider.sol
4. 务必配置和初始化好各个合约使其能够工作,所有需要设置权限的地方都配置为部署者地址。


以上步骤请先充分了解本项目的代码结构,尤其是 tests和scripts文件夹下已经有的部署代码和脚本。

### 输出要求
分为两个阶段:

说明阶段:
1. 在deployment-explain.md中说明是否需要在scripts下构建新的脚本,你准备添加的额外文件和对应的功能。

在我的明确指令下进行执行阶段:
1. 根据deployment-explain.md的内容,完成新的代码/文档的编写和测试
2. 在部署脚本中,增加一个简单的测试用例:
a. 部署者质押1个WETH并允许作为一个质押品。
b. 部署者质押1000个USDC但不允许作为一个质押品。
c. 部署者借出900个USDC。
d. 部署者归还所需的所有USDC。
e. 部署者设置 WETH的价格为3100.

18 changes: 18 additions & 0 deletions src/design-doc/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## golang 程序实现 aave v3 协议部分重要功能的模拟

### 背景

我们的目的是做 aave的清算者,我们需要快速计算出用户的仓位的Ltv,healthy factor等参数以及getLiquidationInfo等信息。
AAVE提供了以下两个我们主要需要的合约函数:
1. LiquidationDataProvider.sol: getUserPositionFullInfo
2. LiquidationDataProvider.sol: getLiquidationInfo

我们的目的是需要用golang来实现上面的两个函数,从而在本地可以进行快速计算而不需要再调用区块链节点来查询。

### 实现路径

1. 请首先仔细阅读本项目(即aave v3)的代码(src/contracts目录下),理解其逻辑和实现方式。重点关注和上面两个函数相关的逻辑和数据。
2. 对两个函数中代码执行过程中需要用到的数据进行梳理,数据分为3类,一类全局配置数据,一类是和资产相关的数据,一类是和用户相关的数据,将数据定义写在design-doc/data.md中,如果还需要定义其他类别的数据,请在design-doc/data.md中一并添加。
3. 根据数据定义,编写golang代码实现两个函数,函数的输入包好步骤2中定义的数据结构。
4. 为了确保代码的正确性,请设计测试文档到design-doc/test.md中。
5. golang代码写在 src/go 文件夹下,适当拆分为2-3个文件。
Loading