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
20 changes: 17 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,19 @@
update:; forge update

# Build & test
build :; forge build --sizes
test :; forge test -vvv
build-simple :; forge build
build-simple-sizes :; forge build --sizes
test-simple :; forge test -vvv
generate :; forge script scripts/utils/GenerateBytecodeScript.s.sol
build :
make build-simple
make generate
build-sizes :
make build-simple-sizes
make generate
test :
make build
make test-simple

# Utilities
download :; cast etherscan-source --chain ${chain} -d src/etherscan/${chain}_${address} ${address}
Expand All @@ -16,7 +27,10 @@ git-diff :
@npx prettier ${before} ${after} --write
@printf '%s\n%s\n%s\n' "\`\`\`diff" "$$(git diff --no-index --diff-algorithm=patience --ignore-space-at-eol ${before} ${after})" "\`\`\`" > diffs/${out}.md

gas-report :; forge test --mp 'tests/gas/**'
gas-report :
make build
make test-gas-report
test-gas-report :; forge test --mp 'tests/gas/**'

# Coverage
coverage-base :; FOUNDRY_PROFILE=coverage forge coverage --report lcov --no-match-coverage "(scripts|tests|deployments|mocks)"
Expand Down
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ fs_permissions = [
{ access = "read", path = "tests/helpers/mocks/JsonBindings.sol" },
{ access = "read", path = "./config" },
{ access = "read", path = "./out" },
{ access = "read-write", path = "./tests/bin" },
{ access = "read-write", path = "./output" }
]
skip = ["tests/helpers/mocks/JsonBindings.sol"]
Expand Down
19 changes: 19 additions & 0 deletions scripts/utils/GenerateBytecodeScript.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Aave Labs
pragma solidity ^0.8.0;

import {Script} from 'forge-std/Script.sol';

contract GenerateHubBytecodeScript is Script {
Comment thread
Kogaroshi marked this conversation as resolved.
string private constant HUB_BYTECODE_PATH = 'tests/bin/hub.bytecode';
string private constant SPOKE_INSTANCE_BYTECODE_PATH = 'tests/bin/spokeInstance.bytecode';

function run() external {
bytes memory hubBytecode = vm.getCode('src/hub/instances/HubInstance.sol:HubInstance');
vm.writeFileBinary(HUB_BYTECODE_PATH, hubBytecode);

string memory artifact = vm.readFile('out/SpokeInstance.sol/SpokeInstance.json');
string memory spokeHex = vm.parseJsonString(artifact, '.bytecode.object');
Comment thread
Kogaroshi marked this conversation as resolved.
vm.writeFile(SPOKE_INSTANCE_BYTECODE_PATH, spokeHex);
Comment thread
Kogaroshi marked this conversation as resolved.
}
Comment thread
Kogaroshi marked this conversation as resolved.
}
8 changes: 4 additions & 4 deletions snapshots/SignatureGateway.Operations.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"borrowWithSig": "222144",
"repayWithSig": "192513",
"borrowWithSig": "222132",
"repayWithSig": "192501",
"setSelfAsUserPositionManagerWithSig": "75138",
"setUsingAsCollateralWithSig": "85380",
"supplyWithSig": "155914",
"supplyWithSig": "155904",
"updateUserDynamicConfigWithSig": "63113",
"updateUserRiskPremiumWithSig": "61995",
"updateUserRiskPremiumWithSig": "62007",
"withdrawWithSig": "135124"
}
2 changes: 1 addition & 1 deletion snapshots/Spoke.Operations.ZeroRiskPremium.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"repay: full": "129276",
"repay: partial": "134234",
"setUserPositionManagersWithSig: disable": "46772",
"setUserPositionManagersWithSig: enable": "68684",
"setUserPositionManagersWithSig: enable": "68672",
"supply + enable collateral (multicall)": "146316",
"supply: 0 borrows, collateral disabled": "127753",
"supply: 0 borrows, collateral enabled": "110724",
Expand Down
2 changes: 1 addition & 1 deletion snapshots/Spoke.Operations.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"repay: full": "123355",
"repay: partial": "142713",
"setUserPositionManagersWithSig: disable": "46772",
"setUserPositionManagersWithSig: enable": "68684",
"setUserPositionManagersWithSig: enable": "68672",
"supply + enable collateral (multicall)": "146316",
"supply: 0 borrows, collateral disabled": "127753",
"supply: 0 borrows, collateral enabled": "110724",
Expand Down
Binary file added tests/bin/hub.bytecode
Binary file not shown.
1 change: 1 addition & 0 deletions tests/bin/spokeInstance.bytecode

Large diffs are not rendered by default.

34 changes: 34 additions & 0 deletions tests/unit/scripts/GenerateBytecode.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Aave Labs
pragma solidity ^0.8.0;

import {Test} from 'forge-std/Test.sol';

import {BytecodeLoader} from 'tests/utils/BytecodeLoader.sol';

contract GenerateBytecodeScript is Test {
Comment thread
Kogaroshi marked this conversation as resolved.
string private constant HUB_BYTECODE_PATH = 'tests/bin/hub.bytecode';
string private constant SPOKE_INSTANCE_BYTECODE_PATH = 'tests/bin/spokeInstance.bytecode';
Comment thread
Kogaroshi marked this conversation as resolved.

function test_generated_HubBytecode() public view {
bytes memory hubBytecode = vm.getCode('src/hub/instances/HubInstance.sol:HubInstance');

assertEq(
hubBytecode,
BytecodeLoader.loadHubBytecode(),
'Loaded Hub bytecode does not match generated bytecode'
);
}

function test_generated_SpokeInstanceBytecode() public view {
bytes memory spokeInstanceBytecode = vm.getCode(
'src/spoke/instances/SpokeInstance.sol:SpokeInstance'
);

assertEq(
spokeInstanceBytecode,
BytecodeLoader.loadSpokeInstanceBytecode(),
'Loaded SpokeInstance bytecode does not match generated bytecode'
);
}
}
32 changes: 32 additions & 0 deletions tests/utils/BytecodeLoader.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Aave Labs
pragma solidity ^0.8.0;

import {Vm} from 'forge-std/Vm.sol';

import {LiquidationLogic} from 'src/spoke/libraries/LiquidationLogic.sol';

library BytecodeLoader {
Vm private constant vm = Vm(address(bytes20(uint160(uint256(keccak256('hevm cheat code'))))));

string private constant HUB_BYTECODE_PATH = 'tests/bin/hub.bytecode';
string private constant SPOKE_INSTANCE_BYTECODE_PATH = 'tests/bin/spokeInstance.bytecode';

Comment thread
Kogaroshi marked this conversation as resolved.
string private constant LIQUIDATION_LOGIC_PLACEHOLDER =
'__$a48140799943db40fec4e369e92a011fa5$__';
Comment thread
Kogaroshi marked this conversation as resolved.

function loadHubBytecode() public view returns (bytes memory) {
return vm.readFileBinary(HUB_BYTECODE_PATH);
}

function loadSpokeInstanceBytecode() public view returns (bytes memory) {
string memory hexBytecode = vm.readFile(SPOKE_INSTANCE_BYTECODE_PATH);
string memory addrHex = vm.replace(
vm.toString(abi.encodePacked(address(LiquidationLogic))),
'0x',
''
);
string memory linked = vm.replace(hexBytecode, LIQUIDATION_LOGIC_PLACEHOLDER, addrHex);
return vm.parseBytes(linked);
}
Comment thread
Kogaroshi marked this conversation as resolved.
}
20 changes: 20 additions & 0 deletions tests/utils/DeployHub.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Aave Labs
pragma solidity ^0.8.0;

import {BytecodeLoader} from 'tests/utils/BytecodeLoader.sol';

import {Create2Utils} from 'src/deployments/utils/libraries/Create2Utils.sol';

/// @notice Helper to deploy Hub from custom profile precompiled bytecode
contract DeployHub {
/// @notice Deploys a Hub contract using CREATE2 and the stored bytecode with the provided deployment arguments.
/// @param deployArgs The constructor arguments for the Hub contract, encoded as bytes.
/// @param salt The salt to use for the CREATE2 deployment, allowing for deterministic address generation.
/// @return The address of the deployed Hub contract.
function deployHub(bytes memory deployArgs, bytes32 salt) public returns (address) {
bytes memory bytecode = BytecodeLoader.loadHubBytecode();

return Create2Utils.create2Deploy(salt, abi.encodePacked(bytecode, deployArgs));
}
}
29 changes: 29 additions & 0 deletions tests/utils/DeploySpoke.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Aave Labs
pragma solidity ^0.8.0;

import {BytecodeLoader} from 'tests/utils/BytecodeLoader.sol';

import {Create2Utils} from 'src/deployments/utils/libraries/Create2Utils.sol';

/// @notice Helper to deploy Spoke from custom profile precompiled bytecode
contract DeploySpoke {
/// @notice Deploys a proxified SpokeInstance contract using CREATE2 and the stored bytecode with the provided deployment arguments, and proxified using initialize arguments.
/// @param deployArgs The constructor arguments for the SpokeInstance implementation contract, encoded as bytes.
/// @param initArgs The initialization arguments for the TransparentUpgradeableProxy, encoded as bytes.
/// @param salt The salt to use for the CREATE2 deployment, allowing for deterministic address generation.
/// @return The address of the deployed SpokeInstance implementation
/// @return The address of the deployed SpokeInstance proxy
function deploySpoke(
bytes memory deployArgs,
bytes memory initArgs,
bytes32 salt
) public returns (address, address) {
bytes memory bytecode = BytecodeLoader.loadSpokeInstanceBytecode();

address impl = Create2Utils.create2Deploy(salt, abi.encodePacked(bytecode, deployArgs));
address proxy = Create2Utils.proxify(salt, impl, msg.sender, initArgs);

return (impl, proxy);
}
}
Loading