Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
8bafe86
feat : procedures to deploy contracts
Kogaroshi Nov 3, 2025
d95101b
feat : add batch contracts for deploy
Kogaroshi Nov 5, 2025
00ba92a
feat : add Logger for Deploy Engine
Kogaroshi Nov 5, 2025
9f6c70d
feat : Input helper for Deploy Engine
Kogaroshi Nov 5, 2025
4a51e33
Merge branch 'main' of https://github.com/aave/aave-v4 into feat/payl…
Kogaroshi Nov 7, 2025
cecd4f3
feat : add orchestration methods & 1st draft deploy payload
Kogaroshi Nov 7, 2025
460c2e0
fix : separate batch for configurators
Kogaroshi Nov 10, 2025
2aaeb00
feat : roles procedures & small fixes
Kogaroshi Nov 10, 2025
4581387
feat : test env orchestration (WIP)
Kogaroshi Nov 11, 2025
b94ed13
fix : fix errors test env orchestration
Kogaroshi Nov 11, 2025
3ba9fad
Merge branch 'main' of https://github.com/aave/aave-v4 into feat/payl…
Kogaroshi Nov 12, 2025
e455afb
fix : apply latest main changes
Kogaroshi Nov 12, 2025
00433e9
feat : procedures for Hub & Spoke configs setup (WIP)
Kogaroshi Nov 12, 2025
945619f
feat : finish test config engine & reorder structs
Kogaroshi Nov 13, 2025
8680dd6
fix : fix Licenses for payload engine
Kogaroshi Nov 14, 2025
21033d1
feat : use orchestration in test setup (WIP)
Kogaroshi Nov 14, 2025
5304e6c
pull latest from main
Kogaroshi Dec 1, 2025
9545217
fix : missing action
Kogaroshi Dec 1, 2025
4d36444
fix: clean up file paths; refactor
yan-man Dec 2, 2025
a951c30
feat: use toml; clean up logs; rft; naming
yan-man Dec 3, 2025
384aeef
fix: StdToml import
yan-man Dec 3, 2025
ac1dc8a
fix: debug mismatch of spoke instance
yan-man Dec 4, 2025
1fd5830
tmp: push deploy progress w output json
yan-man Dec 5, 2025
dd459c1
fix: min logger interface; mv input toml file; resolve computeCreated…
yan-man Dec 5, 2025
11be630
rft: DeployCore lib
yan-man Dec 5, 2025
7b16818
rft: metadata logger
yan-man Dec 5, 2025
8f7d6f7
feat: include timestamp in log output
yan-man Dec 5, 2025
63c93f9
fix: debug set roles
yan-man Dec 5, 2025
e38a93c
fix: clean up comments
yan-man Dec 5, 2025
563f04f
feat: broadcast flag; BatchTestProcedures tests
yan-man Dec 6, 2025
a455a57
test: batch deployment
yan-man Dec 6, 2025
e3642d3
fix: move test files
yan-man Dec 6, 2025
b06da1a
fix: CI
yan-man Dec 6, 2025
05c0dc0
fix: comments
yan-man Dec 6, 2025
d5affff
fix: CI
yan-man Dec 6, 2025
265a3a7
fix: CI
yan-man Dec 6, 2025
8d4cef0
fix: clean up;
yan-man Dec 6, 2025
beea923
fix: CI; move back to json
yan-man Dec 6, 2025
31509cf
fix: clean up logger
yan-man Dec 6, 2025
ac59e59
fix: lint
yan-man Dec 6, 2025
a224fe8
rft: hub/spoke roles + tests
yan-man Dec 8, 2025
17db1f2
feat: loadWarnings; abstract contract; zero addr native wrapper case
yan-man Dec 8, 2025
c49467f
fix: warnings
yan-man Dec 8, 2025
65b44c9
fix: consolidate setting roles
yan-man Dec 8, 2025
25a3ae4
feat: grantRoles bool; conditional test logic
yan-man Dec 9, 2025
e597f1c
rft: test suite setup
yan-man Dec 12, 2025
6c5c0b5
fix: rename roles for clarity
yan-man Dec 13, 2025
70e4501
fix: specify admin inputs
yan-man Dec 13, 2025
8f14c51
fix: AaveV4DeployBatchScript configurable inputs
yan-man Dec 13, 2025
e7f4ea5
fix: user warnings prompt
yan-man Dec 13, 2025
f6787b9
test: computeCreateAddress; pr comments
yan-man Dec 13, 2025
27260d0
fix: pr comments; re-order imports
yan-man Dec 13, 2025
8c26cf3
test: fix
yan-man Dec 13, 2025
0ad7618
fix: pr comments
yan-man Dec 16, 2025
198fc50
fix: rm named arg
yan-man Dec 16, 2025
5b13830
fix: pr comments
yan-man Dec 17, 2025
4358d33
fix: rename validation func
yan-man Dec 17, 2025
b3cf5e5
fix: func names
yan-man Dec 17, 2025
92eab04
test: AaveV4DeployBatchBaseScriptTest
yan-man Dec 17, 2025
d851888
fix: create2 factory; expand and clean up tests
yan-man Dec 17, 2025
5492653
chore: clean up logging
yan-man Dec 17, 2025
90f133f
fix: pragma consistency; errors
yan-man Dec 19, 2025
b8acf9c
fix: pr comments
yan-man Dec 20, 2025
7a82a0b
fix: tmp fix for CI
yan-man Dec 20, 2025
dfe4808
test: resolve
yan-man Dec 23, 2025
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
3 changes: 1 addition & 2 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ libs = ['lib']
fs_permissions = [
{ access = "read", path = "tests/mocks/JsonBindings.sol" },
{ access = "read", path = "./config" },
{ access = "read-write", path = "./output" },
{ access = "read-write", path = "./scripts/deploy/inputs" }
{ access = "read-write", path = "./output" }
]
solc_version = "0.8.28"
evm_version = "cancun"
Expand Down
125 changes: 64 additions & 61 deletions scripts/deploy/AaveV4DeployBatchBase.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@ import {
import {Script} from 'forge-std/Script.sol';

abstract contract AaveV4DeployBatchBaseScript is Script, InputUtils {
string internal constant INPUT_PATH = 'scripts/deploy/inputs/';
string internal constant OUTPUT_DIR = 'output/reports/deployments/';
struct Warnings {
string[] s;
}

string internal constant INPUT_PATH = 'config/';
string internal constant OUTPUT_DIR = 'output/reports/deployments/';
string internal _inputFileName;
string internal _outputFileName;
Warnings internal _warnings;

constructor(string memory inputFileName_, string memory outputFileName_) {
_inputFileName = inputFileName_;
Expand All @@ -30,8 +34,7 @@ abstract contract AaveV4DeployBatchBaseScript is Script, InputUtils {
string.concat(INPUT_PATH, _inputFileName)
);
(, address deployer, ) = vm.readCallers();
_loadWarnings(logger, inputs);
inputs = _sanitizeInputs(inputs, deployer);
inputs = _loadWarningsAndSanitizeInputs(logger, inputs, deployer);

logger.log('...Starting Aave V4 Batch Deployment...');
vm.startBroadcast(deployer);
Expand All @@ -44,119 +47,119 @@ abstract contract AaveV4DeployBatchBaseScript is Script, InputUtils {
logger.save({fileName: _outputFileName, withTimestamp: true});
}

function _loadWarnings(MetadataLogger logger, FullDeployInputs memory inputs) internal virtual {
function _loadWarningsAndSanitizeInputs(
MetadataLogger logger,
FullDeployInputs memory inputs,
address deployer
) internal virtual returns (FullDeployInputs memory) {
FullDeployInputs memory sanitizedInputs = inputs;
bool hadWarnings = false;
string memory warnings = '';
if (inputs.grantRoles) {
warnings = _logAndAppend(logger, warnings, 'WARNING: Roles are being set');
_logAndAppend(logger, 'WARNING: Roles are being set');
hadWarnings = true;
if (inputs.accessManagerAdmin == address(0)) {
warnings = _logAndAppend(
_logAndAppend(
logger,
warnings,
'WARNING: Access Manager Admin is zero address; role will be granted to deployer by default'
Copy link
Member

@miguelmtzinf miguelmtzinf Dec 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's define a memory variable here for this, for better maintainability (don't dup strings)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see latest

);
sanitizedInputs.accessManagerAdmin = deployer;
}
if (inputs.hubConfiguratorOwner == address(0)) {
warnings = _logAndAppend(
_logAndAppend(
logger,
warnings,
'WARNING: Hub Configurator Owner is zero address; role will be granted to deployer by default'
);
sanitizedInputs.hubConfiguratorOwner = deployer;
}
if (inputs.spokeConfiguratorOwner == address(0)) {
warnings = _logAndAppend(
_logAndAppend(
logger,
warnings,
'WARNING: Spoke Configurator Owner is zero address; role will be granted to deployer by default'
);
sanitizedInputs.spokeConfiguratorOwner = deployer;
}
if (inputs.spokeProxyAdminOwner == address(0)) {
warnings = _logAndAppend(
_logAndAppend(
logger,
warnings,
'WARNING: Spoke Proxy Admin Owner is zero address; role will be granted to deployer by default'
);
sanitizedInputs.spokeProxyAdminOwner = deployer;
}
if (inputs.treasurySpokeOwner == address(0)) {
warnings = _logAndAppend(
_logAndAppend(
logger,
warnings,
'WARNING: Treasury Spoke Owner is zero address; role will be granted to deployer by default'
);
sanitizedInputs.treasurySpokeOwner = deployer;
}
if (inputs.spokeAdmin == address(0)) {
warnings = _logAndAppend(
_logAndAppend(
logger,
warnings,
'WARNING: Spoke Admin is zero address; spoke admin roles will be granted to deployer by default'
);
sanitizedInputs.spokeAdmin = deployer;
}
if (inputs.hubAdmin == address(0)) {
_logAndAppend(
logger,
'WARNING: Hub Admin is zero address; hub admin roles will be granted to deployer by default'
);
sanitizedInputs.hubAdmin = deployer;
}
}
if (inputs.hubLabels.length == 0) {
warnings = _logAndAppend(logger, warnings, 'WARNING: Hub will not be deployed');
_logAndAppend(logger, 'WARNING: Hub will not be deployed');
hadWarnings = true;
sanitizedInputs.hubLabels = new string[](0);
}
if (inputs.spokeLabels.length == 0) {
warnings = _logAndAppend(logger, warnings, 'WARNING: Spoke will not be deployed');
_logAndAppend(logger, 'WARNING: Spoke will not be deployed');
hadWarnings = true;
sanitizedInputs.spokeLabels = new string[](0);
}
if (inputs.nativeWrapper == address(0)) {
warnings = _logAndAppend(
_logAndAppend(
logger,
warnings,
'WARNING: Native wrapper zero address; NativeTokenGateway & SignatureGateway will not be deployed'
);
hadWarnings = true;
sanitizedInputs.nativeWrapper = address(0);
}
if (inputs.gatewayOwner == address(0)) {
_logAndAppend(
logger,
'WARNING: Gateway owner zero address; role will be granted to deployer by default'
);
hadWarnings = true;
sanitizedInputs.gatewayOwner = deployer;
}
logger.log('');

if (hadWarnings) {
_executeUserPrompt(warnings);
_executeUserPrompt();
}
return sanitizedInputs;
}

function _executeUserPrompt(string memory warnings) internal virtual {
string memory ack = vm.prompt(string.concat(warnings, "\nEnter 'y' to continue"));
function _executeUserPrompt() internal virtual {
string memory ack = vm.prompt(
string.concat(_joinWarnings(_warnings), "\nEnter 'y' to continue")
);
if (keccak256(bytes(ack)) != keccak256(bytes('y'))) {
revert('User did not acknowledge warnings. Please try again.');
}
}

function _sanitizeInputs(
FullDeployInputs memory deployInputs,
address deployer
) internal view virtual returns (FullDeployInputs memory) {
// if any admin is zero address, default to deployer as admin
InputUtils.FullDeployInputs memory sanitizedInputs = deployInputs;
sanitizedInputs.accessManagerAdmin = deployInputs.accessManagerAdmin != address(0)
? deployInputs.accessManagerAdmin
: deployer;
sanitizedInputs.hubConfiguratorOwner = deployInputs.hubConfiguratorOwner != address(0)
? deployInputs.hubConfiguratorOwner
: deployer;
sanitizedInputs.treasurySpokeOwner = deployInputs.treasurySpokeOwner != address(0)
? deployInputs.treasurySpokeOwner
: deployer;
sanitizedInputs.spokeProxyAdminOwner = deployInputs.spokeProxyAdminOwner != address(0)
? deployInputs.spokeProxyAdminOwner
: deployer;
sanitizedInputs.spokeConfiguratorOwner = deployInputs.spokeConfiguratorOwner != address(0)
? deployInputs.spokeConfiguratorOwner
: deployer;
sanitizedInputs.gatewayOwner = deployInputs.gatewayOwner != address(0)
? deployInputs.gatewayOwner
: deployer;

return sanitizedInputs;
function _logAndAppend(MetadataLogger logger, string memory warning) internal virtual {
logger.log(warning);
_warnings.s.push(warning);
}

function _logAndAppend(
MetadataLogger logger,
string memory warnings,
string memory warning
) internal virtual returns (string memory) {
logger.log(warning);
return string.concat(warnings, warning, '\n');
function _joinWarnings(Warnings storage warnings) internal view virtual returns (string memory) {
uint256 n = warnings.s.length;
if (n == 0) return '';
string memory out = warnings.s[0];
for (uint256 i = 1; i < n; i++) {
out = string.concat(out, '\n', warnings.s[i]);
}
return string.concat(out, '\n');
}
}
6 changes: 3 additions & 3 deletions snapshots/Hub.Operations.json
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are they changing? foundry version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no foundry version update. was trying to dig into this but couldnt id exactly why, base set up was just refactored.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"add": "88006",
"add: with transfer": "109642",
"add: with transfer": "109652",
"draw": "105931",
"eliminateDeficit: full": "59781",
"eliminateDeficit: partial": "69429",
Expand All @@ -11,8 +11,8 @@
"remove: partial": "81640",
"reportDeficit": "115225",
"restore: full": "80471",
"restore: full - with transfer": "173405",
"restore: full - with transfer": "173415",
"restore: partial": "89137",
"restore: partial - with transfer": "147429",
"restore: partial - with transfer": "147439",
"transferShares": "71180"
}
2 changes: 1 addition & 1 deletion snapshots/SignatureGateway.Operations.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"borrowWithSig": "215605",
"repayWithSig": "188872",
"setSelfAsUserPositionManagerWithSig": "75402",
"setUsingAsCollateralWithSig": "85065",
"setUsingAsCollateralWithSig": "85053",
"supplyWithSig": "153205",
"updateUserDynamicConfigWithSig": "62769",
"updateUserRiskPremiumWithSig": "61579",
Expand Down
1 change: 0 additions & 1 deletion src/deployments/batches/AaveV4AccessBatch.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ contract AaveV4AccessBatch is AaveV4AccessManagerEnumerableDeployProcedure {
BatchReports.AccessBatchReport internal _report;

constructor(address admin_) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why _ suffix? I think it's fine without wdyt?

Copy link
Contributor Author

@yan-man yan-man Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just consistency w the rest of the src contracts, prefer to keep it just for that reason

assert(admin_ != address(0));
address accessManager = _deployAccessManagerEnumerable(admin_);
_report = BatchReports.AccessBatchReport({accessManager: accessManager});
}
Expand Down
3 changes: 0 additions & 3 deletions src/deployments/batches/AaveV4ConfiguratorBatch.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ contract AaveV4ConfiguratorBatch is
BatchReports.ConfiguratorBatchReport internal _report;

constructor(address hubConfiguratorOwner_, address spokeConfiguratorOwner_) {
assert(hubConfiguratorOwner_ != address(0));
assert(spokeConfiguratorOwner_ != address(0));

address hubConfigurator = _deployHubConfigurator(hubConfiguratorOwner_);
address spokeConfigurator = _deploySpokeConfigurator(spokeConfiguratorOwner_);

Expand Down
3 changes: 0 additions & 3 deletions src/deployments/batches/AaveV4GatewayBatch.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ contract AaveV4GatewayBatch is
BatchReports.GatewaysBatchReport internal _report;

constructor(address owner_, address nativeWrapper_) {
assert(owner_ != address(0));
assert(nativeWrapper_ != address(0));

address nativeGateway = _deployNativeTokenGateway({
nativeWrapper: nativeWrapper_,
owner: owner_
Expand Down
3 changes: 0 additions & 3 deletions src/deployments/batches/AaveV4HubBatch.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ contract AaveV4HubBatch is
BatchReports.HubBatchReport internal _report;

constructor(address treasurySpokeOwner_, address accessManager_) {
assert(treasurySpokeOwner_ != address(0));
assert(accessManager_ != address(0));

address hub = _deployHub(accessManager_);
address irStrategy = _deployInterestRateStrategy(hub);
address treasurySpoke = _deployTreasurySpoke(treasurySpokeOwner_, hub);
Expand Down
11 changes: 3 additions & 8 deletions src/deployments/batches/AaveV4SpokeInstanceBatch.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ contract AaveV4SpokeInstanceBatch is AaveV4SpokeDeployProcedure, AaveV4AaveOracl
uint8 oracleDecimals_,
string memory oracleDescription_
) {
assert(spokeProxyAdminOwner_ != address(0));
assert(accessManager_ != address(0));
assert(oracleDecimals_ > 0);
assert(bytes(oracleDescription_).length > 0);

// additional 2 nonces for AaveOracle, SpokeInstance, starting from contract nonce of 1
address predictedSpokeInstance = Utils.computeCreateAddress(address(this), 3);

Expand All @@ -42,9 +37,9 @@ contract AaveV4SpokeInstanceBatch is AaveV4SpokeDeployProcedure, AaveV4AaveOracl
oracle: aaveOracle
});

assert(spokeProxy == predictedSpokeInstance);
assert(ISpoke(spokeProxy).ORACLE() == aaveOracle);
assert(IAaveOracle(aaveOracle).SPOKE() == spokeProxy);
require(spokeProxy == predictedSpokeInstance, InvalidParam('predicted spoke instance'));
require(ISpoke(spokeProxy).ORACLE() == aaveOracle, InvalidParam('spoke oracle'));
require(IAaveOracle(aaveOracle).SPOKE() == spokeProxy, InvalidParam('aave oracle spoke'));

_report = BatchReports.SpokeInstanceBatchReport({
aaveOracle: aaveOracle,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ library AaveV4DeployOrchestration {

if (deployInputs.accessManagerAdmin != initialAdmin) {
logger.log('...Granting AccessManager Root Admin role...');
AaveV4AccessManagerRolesProcedure.grantRootAdminRole({
AaveV4AccessManagerRolesProcedure.replaceDefaultAdminRole({
accessManager: report.accessBatchReport.accessManager,
adminToAdd: deployInputs.accessManagerAdmin,
adminToRemove: initialAdmin
Expand Down
10 changes: 10 additions & 0 deletions src/deployments/procedures/AaveV4DeployProcedureBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Aave Labs
pragma solidity ^0.8.0;

contract AaveV4DeployProcedureBase {
error InvalidParam(string errorMessage);
function _validateZeroAddress(address addr, string memory errorMessage) internal pure {
require(addr != address(0), InvalidParam(errorMessage));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
pragma solidity ^0.8.0;

import {AccessManagerEnumerable} from 'src/access/AccessManagerEnumerable.sol';
import {AaveV4DeployProcedureBase} from 'src/deployments/procedures/AaveV4DeployProcedureBase.sol';

contract AaveV4AccessManagerEnumerableDeployProcedure {
contract AaveV4AccessManagerEnumerableDeployProcedure is AaveV4DeployProcedureBase {
function _deployAccessManagerEnumerable(address admin) internal returns (address) {
_validateZeroAddress(admin, 'admin');
return address(new AccessManagerEnumerable({initialAdmin_: admin}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
pragma solidity ^0.8.0;

import {HubConfigurator} from 'src/hub/HubConfigurator.sol';

contract AaveV4HubConfiguratorDeployProcedure {
import {AaveV4DeployProcedureBase} from 'src/deployments/procedures/AaveV4DeployProcedureBase.sol';
contract AaveV4HubConfiguratorDeployProcedure is AaveV4DeployProcedureBase {
function _deployHubConfigurator(address owner) internal returns (address) {
_validateZeroAddress(owner, 'owner');
return address(new HubConfigurator({owner_: owner}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
pragma solidity ^0.8.0;

import {Hub} from 'src/hub/Hub.sol';

contract AaveV4HubDeployProcedure {
import {AaveV4DeployProcedureBase} from 'src/deployments/procedures/AaveV4DeployProcedureBase.sol';
contract AaveV4HubDeployProcedure is AaveV4DeployProcedureBase {
function _deployHub(address accessManager) internal returns (address) {
_validateZeroAddress(accessManager, 'access manager');
return address(new Hub({authority_: accessManager}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
pragma solidity ^0.8.0;

import {AssetInterestRateStrategy} from 'src/hub/AssetInterestRateStrategy.sol';

contract AaveV4InterestRateStrategyDeployProcedure {
import {AaveV4DeployProcedureBase} from 'src/deployments/procedures/AaveV4DeployProcedureBase.sol';
contract AaveV4InterestRateStrategyDeployProcedure is AaveV4DeployProcedureBase {
function _deployInterestRateStrategy(address hub) internal returns (address) {
require(hub != address(0), InvalidParam('hub'));
return address(new AssetInterestRateStrategy({hub_: hub}));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
pragma solidity ^0.8.0;

import {NativeTokenGateway} from 'src/position-manager/NativeTokenGateway.sol';

contract AaveV4NativeTokenGatewayDeployProcedure {
import {AaveV4DeployProcedureBase} from 'src/deployments/procedures/AaveV4DeployProcedureBase.sol';
contract AaveV4NativeTokenGatewayDeployProcedure is AaveV4DeployProcedureBase {
function _deployNativeTokenGateway(
address nativeWrapper,
address owner
) internal returns (address) {
_validateZeroAddress(nativeWrapper, 'native wrapper');
_validateZeroAddress(owner, 'owner');
return address(new NativeTokenGateway({nativeWrapper_: nativeWrapper, initialOwner_: owner}));
}
}
Loading
Loading