Skip to content

Commit 6cc29a8

Browse files
committed
add base testnet deployment
1 parent edf4dec commit 6cc29a8

File tree

4 files changed

+680
-359
lines changed

4 files changed

+680
-359
lines changed
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity ^0.8.18;
3+
4+
import 'forge-std/Script.sol';
5+
import 'forge-std/console.sol';
6+
7+
import {AaveV3BatchOrchestration} from '../src/deployments/projects/aave-v3-batched/AaveV3BatchOrchestration.sol';
8+
import {IAaveV3ConfigEngine} from '../src/contracts/extensions/v3-config-engine/IAaveV3ConfigEngine.sol';
9+
import {EngineFlags} from '../src/contracts/extensions/v3-config-engine/EngineFlags.sol';
10+
import {MockAggregatorSetPrice} from '../tests/invariants/utils/mocks/MockAggregatorSetPrice.sol';
11+
import {SequencerOracle} from '../src/contracts/mocks/oracle/SequencerOracle.sol';
12+
import {TestnetERC20} from '../src/contracts/mocks/testnet-helpers/TestnetERC20.sol';
13+
import {IERC20} from '../src/contracts/dependencies/openzeppelin/contracts/IERC20.sol';
14+
import {IPool} from '../src/contracts/interfaces/IPool.sol';
15+
import {IACLManager} from '../src/contracts/interfaces/IACLManager.sol';
16+
import {MarketReport, MarketConfig, DeployFlags, Roles} from '../src/deployments/interfaces/IMarketReportTypes.sol';
17+
18+
struct DeployedAssets {
19+
TestnetERC20 usdc;
20+
TestnetERC20 usdt;
21+
TestnetERC20 wbtc;
22+
TestnetERC20 weth;
23+
MockAggregatorSetPrice usdcOracle;
24+
MockAggregatorSetPrice usdtOracle;
25+
MockAggregatorSetPrice wbtcOracle;
26+
MockAggregatorSetPrice wethOracle;
27+
SequencerOracle sequencerOracle;
28+
}
29+
30+
/**
31+
* @notice One-shot script to deploy a full Aave v3 market on Base Sepolia, together with
32+
* 4 mock assets and adjustable price feeds.
33+
* @dev Transactions are broadcasted; set PRIVATE_KEY env. RPC handled by forge args.
34+
*/
35+
contract DeployAaveV3BaseSepolia is Script {
36+
function run() external {
37+
uint256 deployerPk = _getPrivateKey();
38+
address deployer = vm.addr(deployerPk);
39+
40+
vm.startBroadcast(deployerPk);
41+
42+
// 1) Deploy test tokens
43+
DeployedAssets memory a;
44+
a.usdc = new TestnetERC20('USDC', 'USDC', 6, deployer);
45+
a.usdt = new TestnetERC20('USDT', 'USDT', 6, deployer);
46+
a.wbtc = new TestnetERC20('WBTC', 'WBTC', 8, deployer);
47+
a.weth = new TestnetERC20('WETH', 'WETH', 18, deployer);
48+
49+
// Mint 1e9 units (before decimals) to deployer
50+
uint256 baseSupply = 1e9;
51+
a.usdc.mint(deployer, baseSupply * 10 ** a.usdc.decimals());
52+
a.usdt.mint(deployer, baseSupply * 10 ** a.usdt.decimals());
53+
a.wbtc.mint(deployer, baseSupply * 10 ** a.wbtc.decimals());
54+
a.weth.mint(deployer, baseSupply * 10 ** a.weth.decimals());
55+
56+
// 2) Deploy adjustable price feeds (8 decimals)
57+
a.usdcOracle = new MockAggregatorSetPrice(1e8); // $1
58+
a.usdtOracle = new MockAggregatorSetPrice(1e8); // $1
59+
a.wbtcOracle = new MockAggregatorSetPrice(88700e8); // $88,700
60+
a.wethOracle = new MockAggregatorSetPrice(3078e8); // $3,078
61+
62+
// 3) Deploy sequencer uptime oracle (for sentinel) and mark as healthy
63+
a.sequencerOracle = new SequencerOracle(deployer);
64+
a.sequencerOracle.setAnswer(false, block.timestamp);
65+
66+
// 4) Build config + deploy Aave V3 (L2Pool)
67+
Roles memory roles;
68+
roles.marketOwner = deployer;
69+
roles.poolAdmin = deployer;
70+
roles.emergencyAdmin = deployer;
71+
72+
MarketConfig memory config;
73+
config.marketId = 'Aave V3 Base Sepolia';
74+
config.providerId = 84532;
75+
config.oracleDecimals = 8;
76+
config.flashLoanPremium = 0.0005e4; // 0.05%
77+
config.networkBaseTokenPriceInUsdProxyAggregator = address(a.wethOracle);
78+
config.marketReferenceCurrencyPriceInUsdProxyAggregator = address(a.usdcOracle);
79+
config.l2SequencerUptimeFeed = address(a.sequencerOracle);
80+
config.l2PriceOracleSentinelGracePeriod = 1 hours;
81+
config.wrappedNativeToken = address(a.weth); // treated as plain ERC20
82+
83+
DeployFlags memory flags;
84+
flags.l2 = true;
85+
86+
MarketReport memory report;
87+
report = AaveV3BatchOrchestration.deployAaveV3(deployer, roles, config, flags, report);
88+
89+
// 5) List assets via ConfigEngine with custom implementations
90+
IAaveV3ConfigEngine configEngine = IAaveV3ConfigEngine(report.configEngine);
91+
IAaveV3ConfigEngine.PoolContext memory context = IAaveV3ConfigEngine.PoolContext({
92+
networkName: 'Base Sepolia',
93+
networkAbbreviation: 'BSP'
94+
});
95+
96+
IAaveV3ConfigEngine.ListingWithCustomImpl[] memory listings = new IAaveV3ConfigEngine
97+
.ListingWithCustomImpl[](4);
98+
99+
IAaveV3ConfigEngine.InterestRateInputData memory rateParams = IAaveV3ConfigEngine
100+
.InterestRateInputData({
101+
optimalUsageRatio: 45_00,
102+
baseVariableBorrowRate: 0,
103+
variableRateSlope1: 4_00,
104+
variableRateSlope2: 60_00
105+
});
106+
107+
listings[0] = _buildListing(
108+
address(a.usdc),
109+
'USDC',
110+
address(a.usdcOracle),
111+
rateParams,
112+
report.aToken,
113+
report.variableDebtToken
114+
);
115+
listings[1] = _buildListing(
116+
address(a.usdt),
117+
'USDT',
118+
address(a.usdtOracle),
119+
rateParams,
120+
report.aToken,
121+
report.variableDebtToken
122+
);
123+
listings[2] = _buildListing(
124+
address(a.wbtc),
125+
'WBTC',
126+
address(a.wbtcOracle),
127+
rateParams,
128+
report.aToken,
129+
report.variableDebtToken
130+
);
131+
listings[3] = _buildListing(
132+
address(a.weth),
133+
'WETH',
134+
address(a.wethOracle),
135+
rateParams,
136+
report.aToken,
137+
report.variableDebtToken
138+
);
139+
140+
IACLManager(report.aclManager).addAssetListingAdmin(address(configEngine));
141+
IACLManager(report.aclManager).addRiskAdmin(address(configEngine));
142+
configEngine.listAssetsCustom(context, listings);
143+
144+
// Log outputs for convenience
145+
console.log('Deployer', deployer);
146+
console.log('USDC', address(a.usdc));
147+
console.log('USDT', address(a.usdt));
148+
console.log('WBTC', address(a.wbtc));
149+
console.log('WETH', address(a.weth));
150+
console.log('Oracle USDC', address(a.usdcOracle));
151+
console.log('Oracle USDT', address(a.usdtOracle));
152+
console.log('Oracle WBTC', address(a.wbtcOracle));
153+
console.log('Oracle WETH', address(a.wethOracle));
154+
console.log('Sequencer oracle', address(a.sequencerOracle));
155+
console.log('Pool', report.poolProxy);
156+
console.log('ConfigEngine', report.configEngine);
157+
console.log('AaveOracle', report.aaveOracle);
158+
159+
// Ensure sequencer oracle timestamp is older than grace period to allow borrowing
160+
a.sequencerOracle.setAnswer(false, block.timestamp - 2 hours);
161+
162+
_quickHealthCheck(report, deployer, a);
163+
164+
vm.stopBroadcast();
165+
}
166+
167+
function _buildListing(
168+
address asset,
169+
string memory symbol,
170+
address priceFeed,
171+
IAaveV3ConfigEngine.InterestRateInputData memory rateParams,
172+
address aTokenImpl,
173+
address vTokenImpl
174+
) internal pure returns (IAaveV3ConfigEngine.ListingWithCustomImpl memory listing) {
175+
listing.base = IAaveV3ConfigEngine.Listing({
176+
asset: asset,
177+
assetSymbol: symbol,
178+
priceFeed: priceFeed,
179+
rateStrategyParams: rateParams,
180+
enabledToBorrow: EngineFlags.ENABLED,
181+
borrowableInIsolation: EngineFlags.DISABLED,
182+
withSiloedBorrowing: EngineFlags.DISABLED,
183+
flashloanable: EngineFlags.ENABLED,
184+
ltv: 82_50,
185+
liqThreshold: 86_00,
186+
liqBonus: 5_00,
187+
reserveFactor: 10_00,
188+
supplyCap: 0, // uncapped
189+
borrowCap: 0, // uncapped
190+
debtCeiling: 0,
191+
liqProtocolFee: 10_00
192+
});
193+
194+
listing.implementations = IAaveV3ConfigEngine.TokenImplementations({
195+
aToken: aTokenImpl,
196+
vToken: vTokenImpl
197+
});
198+
}
199+
200+
/// @dev Simple post-deploy sanity: supply/borrow/repay flow + price bump
201+
function _quickHealthCheck(
202+
MarketReport memory report,
203+
address deployer,
204+
DeployedAssets memory a
205+
) internal {
206+
IPool pool = IPool(report.poolProxy);
207+
208+
// Approvals
209+
IERC20(address(a.usdc)).approve(address(pool), type(uint256).max);
210+
IERC20(address(a.weth)).approve(address(pool), type(uint256).max);
211+
212+
// a) Supply 1 WETH as collateral
213+
uint256 wethAmount = 1 ether;
214+
pool.supply(address(a.weth), wethAmount, deployer, 0);
215+
pool.setUserUseReserveAsCollateral(address(a.weth), true);
216+
217+
// b) Supply 1000 USDC, keep collateral disabled
218+
uint256 usdcAmount = 1000 * 1e6;
219+
pool.supply(address(a.usdc), usdcAmount, deployer, 0);
220+
pool.setUserUseReserveAsCollateral(address(a.usdc), false);
221+
222+
// c) Borrow 900 USDC (variable rate)
223+
uint256 borrowAmount = 900 * 1e6;
224+
pool.borrow(address(a.usdc), borrowAmount, 2, 0, deployer);
225+
226+
// d) Repay all USDC (repay max to cover interest)
227+
pool.repay(address(a.usdc), type(uint256).max, 2, deployer);
228+
229+
// e) Update WETH price to 3100 USD
230+
a.wethOracle.setLatestAnswer(3100e8);
231+
232+
// Log account data
233+
(
234+
uint256 totalCollateralBase,
235+
uint256 totalDebtBase,
236+
uint256 availableBorrowsBase,
237+
uint256 currentLiquidationThreshold,
238+
uint256 ltv,
239+
uint256 healthFactor
240+
) = pool.getUserAccountData(deployer);
241+
242+
console.log('Post-check collateral (base)', totalCollateralBase);
243+
console.log('Post-check debt (base)', totalDebtBase);
244+
console.log('Available borrows (base)', availableBorrowsBase);
245+
console.log('Liq threshold', currentLiquidationThreshold);
246+
console.log('LTV', ltv);
247+
console.log('Health factor', healthFactor);
248+
console.log('WETH price now', a.wethOracle.latestAnswer());
249+
console.log('USDC price (ref currency)', a.usdcOracle.latestAnswer());
250+
}
251+
252+
function _getPrivateKey() internal returns (uint256) {
253+
string memory raw = vm.envString('PRIVATE_KEY');
254+
bytes memory b = bytes(raw);
255+
bool hasPrefix = b.length >= 2 && b[0] == '0' && (b[1] == 'x' || b[1] == 'X');
256+
string memory normalized = hasPrefix ? raw : string(abi.encodePacked('0x', raw));
257+
return uint256(vm.parseBytes32(normalized));
258+
}
259+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
## 是否需要新增脚本
2+
- 需要。现有 `scripts/DeployAaveV3MarketBatched*.sol` 偏向通用批量部署/预编译库,不直接覆盖“本地测试 + base sepolia L2Pool + 自定义 mock token/oracle”的需求。
3+
4+
## 计划新增/修改的文件与功能
5+
- `scripts/DeployAaveV3BaseSepolia.sol`(新):一站式 forge script,完成:
6+
- 部署四个测试代币(USDC 6d、USDT 6d、WBTC 8d、WETH 18d,初始供应分配给部署者)。
7+
- 部署四个 `MockAggregatorSetPrice`,初始价格 1/1/88700/3078,可后续手动调价。
8+
- 部署并初始化 L2 核心组件:`POOL`(L2Pool)、`POOL_CONFIGURATOR``PRICE_ORACLE`(注入上述 sources)、`ACL_MANAGER``ACL_ADMIN``PRICE_ORACLE_SENTINEL``DATA_PROVIDER`,以及外围 `LiquidationDataProvider`,把需要的角色都授予部署者。
9+
- 将代币上市到池子(配置利率策略/ltv 等可用固定参数),确保可正常存借清算。
10+
- 输出部署产物(JSON/console)便于后续测试复用。
11+
- `scripts/misc/DeployAaveV3MarketBatchedBase.sol`(可选小改):如果复用其部分初始化逻辑,可能添加帮助函数暴露更细粒度的部署步骤,否则不改。
12+
- `src/design-doc/deployment.md`(仅参考,不改代码):作为需求来源。
13+
14+
## 复用思路
15+
- 复用 `TestnetERC20``MockAggregatorSetPrice` 作为资产与喂价。
16+
- 参考 `DeployAaveV3MarketBatched.sol` 的结构组织脚本,但针对 base-sepolia chainId、L2Pool 构造和自定义 token/oracle。
17+
- 使用 forge standard-json 或 broadcast 方式,脚本中支持从环境变量读取私钥/RPC,避免硬编码。
18+
19+
## 其他说明
20+
- 说明阶段不改代码,执行阶段再按上面文件清单落地。
21+
22+
## 运行指引(执行阶段给你的操作步骤)
23+
- 依赖:`forge`,RPC 指向 `https://base-sepolia-public.nodies.app`(或自选 base sepolia 节点)。
24+
- 环境变量:`PRIVATE_KEY`(部署者私钥,对应文档中的 0xd75Fe... 地址)。
25+
- 执行命令示例:
26+
- `forge script scripts/DeployAaveV3BaseSepolia.sol:DeployAaveV3BaseSepolia --rpc-url https://base-sepolia-public.nodies.app --broadcast --verify=false`
27+
- 结果:控制台打印四个 token 地址、对应 oracle、SequencerOracle、Pool、ConfigEngine、AaveOracle。价格可通过 `MockAggregatorSetPrice.setLatestAnswer(int256)` 调整,Sequencer 状态可通过 `SequencerOracle.setAnswer(bool,uint256)` 手动改。

src/design-doc/deployment.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
## Integration Test
2+
3+
### 目标
4+
在base Sepolia Testnet 部署一套完整的aave v3 marketplace合约,用于简单的测试验证。
5+
6+
### 环境信息
7+
部署者地址: 0xd75Fea05fdafD031e2a584Db1d4E6155c862B875
8+
RPC endpoint: https://base-sepolia-public.nodies.app
9+
chain-id: 84532
10+
### 细节要求
11+
1. 部署测试测试token(TestnetERC20.sol),比如USDC(decimal为6),USDT(decimal为6),WBTC(decimal为8),WETH(decimal为18),每个token的初始supply是1e9个(不考虑decimal)分配给合约部署者。 注意把WETH当成普通的ERC20 合约即可,不需要和ETH之间互换。
12+
2. 分别部署USDC,USDT,WBTC, WETH 四种token的oracle price mock合约(复用MockAggregatorSetPrice.sol), 最初的价格是:1,1,88700,3078。 这些价格可以被随时调整。
13+
3. 由于base testnet是L2, 所以我们部署的核心协议是L2Pool.sol 而不是Pool.sol。现在需要将整套 aave v3部署到 base testnt上:
14+
a. 部署必要的核心合约,主要是以下几个:'POOL','POOL_CONFIGURATOR','PRICE_ORACLE','ACL_MANAGER','ACL_ADMIN', 'PRICE_ORACLE_SENTINEL', 'DATA_PROVIDER'
15+
b. 部署一个周边合约 LiquidationDataProvider.sol
16+
4. 务必配置和初始化好各个合约使其能够工作,所有需要设置权限的地方都配置为部署者地址。
17+
18+
19+
以上步骤请先充分了解本项目的代码结构,尤其是 tests和scripts文件夹下已经有的部署代码和脚本。
20+
21+
### 输出要求
22+
分为两个阶段:
23+
24+
说明阶段:
25+
1. 在deployment-explain.md中说明是否需要在scripts下构建新的脚本,你准备添加的额外文件和对应的功能。
26+
27+
在我的明确指令下进行执行阶段:
28+
1. 根据deployment-explain.md的内容,完成新的代码/文档的编写和测试
29+
2. 在部署脚本中,增加一个简单的测试用例:
30+
a. 部署者质押1个WETH并允许作为一个质押品。
31+
b. 部署者质押1000个USDC但不允许作为一个质押品。
32+
c. 部署者借出900个USDC。
33+
d. 部署者归还所需的所有USDC。
34+
e. 部署者设置 WETH的价格为3100.
35+

0 commit comments

Comments
 (0)