-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'refs/heads/feat/calldata-compression'
# Conflicts: # packages/foundry/script/ChildPoolDeploy.s.sol # packages/foundry/script/ConceroDeploy.s.sol # packages/foundry/script/OrchestratorDeploy.s.sol # packages/foundry/test/Bridge/StartBridge.t.sol # packages/foundry/test/Bridge/wrappers/InfraOrchestratorWrapper.sol # packages/foundry/test/ParentPool/wrappers/ParentPoolDepositWrapper.sol # packages/foundry/test/ParentPool/wrappers/ParentPoolWrapper.sol # packages/foundry/test/utils/BaseTest.t.sol # packages/hardhat/contracts/InfraCLF.sol
- Loading branch information
Showing
6 changed files
with
367 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule solady
added at
362b2e
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity 0.8.20; | ||
|
||
import {BaseTest, console} from "../BaseTest.t.sol"; | ||
import {OrchestratorWrapper} from "./wrappers/OrchestratorWrapper.sol"; | ||
import {IDexSwap} from "contracts/Interfaces/IDexSwap.sol"; | ||
import {IInfraStorage} from "contracts/Interfaces/IInfraStorage.sol"; | ||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
|
||
contract BridgeBaseTest is BaseTest { | ||
/*////////////////////////////////////////////////////////////// | ||
VARIABLES | ||
//////////////////////////////////////////////////////////////*/ | ||
address internal constant WRAPPED_NATIVE_BASE = 0x4200000000000000000000000000000000000006; | ||
address internal constant UNI_V3_ROUTER_BASE = 0x2626664c2603336E57B271c5C0b26F421741e481; | ||
address constant UNI_V3_ROUTER_AVALANCHE = 0xbb00FF08d01D300023C629E8fFfFcb65A5a578cE; | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
SETUP | ||
//////////////////////////////////////////////////////////////*/ | ||
function setUp() public virtual override { | ||
vm.selectFork(forkId); | ||
_deployOrchestratorProxy(); | ||
_deployDexSwap(); | ||
deployBridgesInfra(); | ||
deployPoolsInfra(); | ||
|
||
vm.prank(deployer); | ||
baseOrchestratorImplementation = new OrchestratorWrapper( | ||
vm.envAddress("CLF_ROUTER_BASE"), | ||
address(dexSwap), | ||
address(baseBridgeImplementation), | ||
address(parentPoolProxy), | ||
address(baseOrchestratorProxy), | ||
1, // IInfraStorage.Chain.base | ||
[vm.envAddress("POOL_MESSENGER_0_ADDRESS"), address(0), address(0)] | ||
); | ||
_setProxyImplementation( | ||
address(baseOrchestratorProxy), | ||
address(baseOrchestratorImplementation) | ||
); | ||
|
||
/// @dev set destination chain selector and contracts on Base | ||
_setDstSelectorAndPool(arbitrumChainSelector, arbitrumChildProxy); | ||
_setDstSelectorAndBridge(arbitrumChainSelector, address(1)); // arbitrumOrchestratorProxy | ||
_setDstSelectorAndPool(avalancheChainSelector, address(2)); // avalancheChildProxy | ||
_setDstSelectorAndBridge(avalancheChainSelector, address(3)); // avalancheOrchestratorProxy | ||
|
||
_allowUniV3Router(); | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
UTILITY | ||
//////////////////////////////////////////////////////////////*/ | ||
function _startBridge(address _caller, uint256 _amount, uint64 _dstChainSelector) internal { | ||
IInfraStorage.BridgeData memory bridgeData = IInfraStorage.BridgeData({ | ||
tokenType: IInfraStorage.CCIPToken.usdc, | ||
amount: _amount, | ||
dstChainSelector: _dstChainSelector, | ||
receiver: msg.sender | ||
}); | ||
IDexSwap.SwapData[] memory dstSwapData; | ||
|
||
vm.prank(_caller); | ||
(bool success, ) = address(baseOrchestratorProxy).call( | ||
abi.encodeWithSignature( | ||
"bridge((uint8,uint256,uint64,address),(uint8,address,uint256,address,uint256,uint256,bytes)[])", | ||
bridgeData, | ||
dstSwapData | ||
) | ||
); | ||
require(success, "bridge call failed"); | ||
} | ||
|
||
function _startBridgeWithDstSwapData( | ||
address _caller, | ||
uint256 _amount, | ||
uint64 _dstChainSelector, | ||
IDexSwap.SwapData[] memory _dstSwapData | ||
) internal { | ||
IInfraStorage.BridgeData memory bridgeData = IInfraStorage.BridgeData({ | ||
tokenType: IInfraStorage.CCIPToken.usdc, | ||
amount: _amount, | ||
dstChainSelector: _dstChainSelector, | ||
receiver: msg.sender | ||
}); | ||
|
||
vm.prank(_caller); | ||
(bool success, ) = address(baseOrchestratorProxy).call( | ||
abi.encodeWithSignature( | ||
"bridge((uint8,uint256,uint64,address),(uint8,address,uint256,address,uint256,uint256,bytes)[])", | ||
bridgeData, | ||
_dstSwapData | ||
) | ||
); | ||
require(success, "bridge call failed"); | ||
} | ||
|
||
function _dealUsdcAndApprove(address _caller, uint256 _usdcAmount) internal { | ||
deal(usdc, _caller, _usdcAmount); | ||
vm.prank(_caller); | ||
IERC20(usdc).approve(address(baseOrchestratorProxy), type(uint256).max); | ||
} | ||
|
||
function _dealLinkToProxy(uint256 _amount) internal { | ||
deal(link, address(baseOrchestratorProxy), _amount); | ||
} | ||
|
||
function _allowUniV3Router() internal { | ||
vm.prank(deployer); | ||
(bool success, ) = address(baseOrchestratorProxy).call( | ||
abi.encodeWithSignature("setDexRouterAddress(address,uint256)", UNI_V3_ROUTER_BASE, 1) | ||
); | ||
/// @dev assert it is set correctly | ||
(, bytes memory returnData) = address(baseOrchestratorProxy).call( | ||
abi.encodeWithSignature("s_routerAllowed(address)", UNI_V3_ROUTER_BASE) | ||
); | ||
uint256 returnedValue = abi.decode(returnData, (uint256)); | ||
assertEq(returnedValue, 1); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,237 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity 0.8.20; | ||
|
||
import {console, Vm} from "forge-std/Test.sol"; | ||
import {BridgeBaseTest} from "./BridgeBaseTest.t.sol"; | ||
import {IDexSwap} from "contracts/Interfaces/IDexSwap.sol"; | ||
import {LibZip} from "solady/src/utils/LibZip.sol"; | ||
import {IInfraStorage} from "contracts/Interfaces/IInfraStorage.sol"; | ||
import {FunctionsRouter, IFunctionsRouter} from "@chainlink/contracts/src/v0.8/functions/dev/v1_X/FunctionsRouter.sol"; | ||
import {FunctionsResponse} from "@chainlink/contracts/src/v0.8/functions/dev/v1_X/libraries/FunctionsResponse.sol"; | ||
import {FunctionsCoordinator, FunctionsBillingConfig} from "@chainlink/contracts/src/v0.8/functions/dev/v1_X/FunctionsCoordinator.sol"; | ||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
|
||
contract BridgeCompressionTest is BridgeBaseTest { | ||
/*////////////////////////////////////////////////////////////// | ||
VARIABLES | ||
//////////////////////////////////////////////////////////////*/ | ||
bytes encodedDstSwapData; | ||
bytes compressedDstSwapData; | ||
address usdcAvalanche = vm.envAddress("USDC_AVALANCHE"); | ||
address constant DAI_AVALANCHE = 0xd586E7F844cEa2F87f50152665BCbc2C279D8d70; | ||
address constant CLF_TRANSMITTER_AVALANCHE = 0x88793C4E85aa6dDE4A84864B834Fe64DD6e1Bf94; | ||
uint256 constant TO_AMOUNT = USER_FUNDS / 2; | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
SETUP | ||
//////////////////////////////////////////////////////////////*/ | ||
function setUp() public virtual override { | ||
BridgeBaseTest.setUp(); | ||
|
||
_setStorageVars(); | ||
_dealUsdcAndApprove(user1, USER_FUNDS); | ||
_dealLinkToProxy(STANDARD_TOKEN_DECIMALS * 10); | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
DECOMPRESSION | ||
//////////////////////////////////////////////////////////////*/ | ||
/// @dev fromToken needs to be usdc address on dst chain! | ||
/// anvil --fork-url https://rpc.ankr.com/avalanche --port 8546 | ||
function test_calldataCompression_dstSwap_decompression() public { | ||
/// @dev create dstSwapData | ||
IDexSwap.SwapData[] memory dstSwapData = _createDstSwapData(); | ||
|
||
/// @dev encode and compress dstSwapData (simulating what happens before and during CLF) | ||
encodedDstSwapData = _swapDataToBytes(dstSwapData); | ||
compressedDstSwapData = LibZip.cdCompress(encodedDstSwapData); | ||
|
||
bytes32 conceroMessageId = keccak256( | ||
abi.encodePacked(user1, user1, USER_FUNDS, block.timestamp) | ||
); | ||
|
||
/// @dev fork avalanche mainnet and deploy infra contracts | ||
vm.selectFork(avalancheFork); | ||
_deployAvalancheInfra(); | ||
|
||
/// @dev prank messenger to call addUnconfirmedTX | ||
vm.recordLogs(); | ||
vm.prank(messenger); | ||
(bool success, ) = address(avalancheOrchestratorProxy).call( | ||
abi.encodeWithSignature( | ||
"addUnconfirmedTX(bytes32,address,address,uint256,uint64,uint8,uint256,bytes)", | ||
conceroMessageId, | ||
user1, | ||
user1, | ||
USER_FUNDS, | ||
baseChainSelector, | ||
IInfraStorage.CCIPToken.usdc, | ||
block.timestamp, | ||
compressedDstSwapData | ||
) | ||
); | ||
require(success, "addUnconfirmedTX delegate call failed"); | ||
|
||
/// @dev get the clfRequestId, callbackGasLimit and estimatedTotalCostJuels | ||
bytes32 clfRequestId; | ||
uint32 callbackGasLimit; | ||
uint96 estimatedTotalCostJuels; | ||
Vm.Log[] memory logs = vm.getRecordedLogs(); | ||
bytes32 eventSignature = keccak256( | ||
"RequestStart(bytes32,bytes32,uint64,address,address,address,bytes,uint16,uint32,uint96)" | ||
); | ||
for (uint256 i = 0; i < logs.length; i++) { | ||
if (logs[i].topics[0] == eventSignature) { | ||
clfRequestId = logs[i].topics[1]; | ||
|
||
(, , , , , callbackGasLimit, estimatedTotalCostJuels) = abi.decode( | ||
logs[i].data, | ||
(address, address, address, bytes, uint16, uint32, uint96) | ||
); | ||
} | ||
} | ||
|
||
/// @dev check storage has updated with the correctly compressed data | ||
(, bytes memory retData) = address(avalancheOrchestratorProxy).call( | ||
abi.encodeWithSignature("getTransaction(bytes32)", conceroMessageId) | ||
); | ||
IInfraStorage.Transaction memory transaction = abi.decode( | ||
retData, | ||
(IInfraStorage.Transaction) | ||
); | ||
bytes memory dstSwapDataInStorage = transaction.dstSwapData; | ||
bytes memory decompressedDstSwapData = LibZip.cdDecompress(dstSwapDataInStorage); | ||
assertEq(dstSwapDataInStorage, compressedDstSwapData); | ||
assertEq(encodedDstSwapData, decompressedDstSwapData); | ||
|
||
/// @dev make sure the child pool has funds to facilitate tx and allow the uni router on avalanche | ||
deal(usdcAvalanche, address(avalancheChildProxy), USER_FUNDS * 10); | ||
_allowUniV3Avalanche(); | ||
|
||
uint256 toBalanceBefore = IERC20(DAI_AVALANCHE).balanceOf(user1); | ||
|
||
/// @dev prank CLF fulfillRequest | ||
_fulfillRequest(clfRequestId, callbackGasLimit, estimatedTotalCostJuels); | ||
|
||
/// @dev assert swap successful | ||
uint256 toTokenBalance = IERC20(DAI_AVALANCHE).balanceOf(user1); | ||
assertGe(toTokenBalance, TO_AMOUNT); | ||
assertEq(toBalanceBefore, 0); | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
UTILITY | ||
//////////////////////////////////////////////////////////////*/ | ||
function _swapDataToBytes( | ||
IDexSwap.SwapData[] memory _swapData | ||
) internal pure returns (bytes memory _encodedData) { | ||
if (_swapData.length == 0) { | ||
_encodedData = new bytes(1); | ||
} else { | ||
_encodedData = abi.encode(_swapData); | ||
} | ||
} | ||
|
||
function _createDstSwapData() internal returns (IDexSwap.SwapData[] memory dstSwapData) { | ||
address routerAddress = UNI_V3_ROUTER_AVALANCHE; | ||
uint24 fee = 3000; | ||
uint160 sqrtPriceLimitX96 = 0; | ||
uint256 deadline = block.timestamp + 3600; | ||
bytes memory dexData = abi.encode(routerAddress, fee, sqrtPriceLimitX96, deadline); | ||
|
||
IDexSwap.SwapData[] memory dstSwapData = new IDexSwap.SwapData[](1); | ||
IDexSwap.SwapData memory singleSwap = IDexSwap.SwapData({ | ||
dexType: IDexSwap.DexType.UniswapV3Single, | ||
fromToken: usdcAvalanche, | ||
fromAmount: USER_FUNDS / 2, | ||
toToken: DAI_AVALANCHE, | ||
toAmount: TO_AMOUNT, | ||
toAmountMin: USER_FUNDS / 3, | ||
dexData: dexData | ||
}); | ||
dstSwapData[0] = singleSwap; | ||
|
||
return dstSwapData; | ||
} | ||
|
||
function _fulfillRequest( | ||
bytes32 _requestId, | ||
uint32 _callbackGasLimit, | ||
uint96 _estimatedTotalCostJuels | ||
) internal { | ||
FunctionsRouter functionsRouter = FunctionsRouter(vm.envAddress("CLF_ROUTER_AVALANCHE")); | ||
/// @dev get coordinator to call functions router | ||
address coordinator = functionsRouter.getContractById(vm.envBytes32("CLF_DONID_AVALANCHE")); | ||
|
||
/// @dev create fulfill params | ||
bytes memory response = abi.encode(1); // we dont use the response in this usecase | ||
bytes memory err = ""; | ||
uint96 juelsPerGas = 1_000_000_000; | ||
uint96 costWithoutFulfillment = 0; | ||
|
||
/// @dev get adminFee from the config | ||
FunctionsRouter.Config memory config = functionsRouter.getConfig(); | ||
uint72 adminFee = config.adminFee; | ||
/// @dev get timeoutTimestamp from billing config | ||
FunctionsBillingConfig memory billingConfig = FunctionsCoordinator(coordinator).getConfig(); | ||
uint32 timeoutTimestamp = uint32(block.timestamp + billingConfig.requestTimeoutSeconds); | ||
|
||
/// @notice some of these values have been hardcoded, directly from the logs | ||
/// @dev create the commitment params | ||
FunctionsResponse.Commitment memory commitment = FunctionsResponse.Commitment( | ||
_requestId, | ||
coordinator, | ||
_estimatedTotalCostJuels, | ||
address(avalancheOrchestratorProxy), // client | ||
uint64(vm.envUint("CLF_SUBID_AVALANCHE")), | ||
_callbackGasLimit, | ||
adminFee, // adminFee | ||
0, // donFee | ||
133000, // gasOverheadBeforeCallback | ||
57000, // gasOverheadAfterCallback | ||
timeoutTimestamp // timeoutTimestamp | ||
); | ||
|
||
/// @dev prank the coordinator to call fulfill on functionsRouter | ||
vm.prank(coordinator); | ||
(FunctionsResponse.FulfillResult resultCode, uint96 callbackGasCostJuels) = functionsRouter | ||
.fulfill( | ||
response, | ||
err, | ||
juelsPerGas, | ||
costWithoutFulfillment, | ||
CLF_TRANSMITTER_AVALANCHE, | ||
commitment | ||
); | ||
} | ||
|
||
function _allowUniV3Avalanche() internal { | ||
vm.prank(deployer); | ||
(bool success2, ) = address(avalancheOrchestratorProxy).call( | ||
abi.encodeWithSignature( | ||
"setDexRouterAddress(address,uint256)", | ||
UNI_V3_ROUTER_AVALANCHE, | ||
1 | ||
) | ||
); | ||
} | ||
|
||
// { | ||
// /// @dev startBridge and record logs to get messageId | ||
// vm.recordLogs(); | ||
// // _startBridgeWithDstSwapData(user1, USER_FUNDS, avalancheChainSelector, dstSwapData); | ||
// bytes32 messageId; | ||
// Vm.Log[] memory logs = vm.getRecordedLogs(); | ||
// bytes32 eventSignature = keccak256( | ||
// "ConceroBridgeSent(bytes32,uint8,uint256,uint64,address,bytes32)" | ||
// ); | ||
// for (uint256 i = 0; i < logs.length; i++) { | ||
// if (logs[i].topics[0] == eventSignature) { | ||
// messageId = logs[i].topics[0]; | ||
// } | ||
// } | ||
// uint256 blockNumber = block.timestamp; | ||
|
||
// } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.