Skip to content

Commit

Permalink
Merge branch 'refs/heads/feat/calldata-compression'
Browse files Browse the repository at this point in the history
# 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
olegkron committed Nov 12, 2024
2 parents 48f6302 + e33ceac commit 16d4b93
Show file tree
Hide file tree
Showing 6 changed files with 367 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/foundry/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ remappings = [
"solidity-bytes-utils/=lib/solidity-bytes-utils/",
"sushiswap-v3-periphery/contracts/=node_modules/sushiswap-v3-periphery/contracts/",
"forge-std/=lib/forge-std/src/",
"solady/src=lib/solady/src"
]

allow_paths = ["../hardhat/contracts/"]
1 change: 1 addition & 0 deletions packages/foundry/lib/solady
Submodule solady added at 362b2e
122 changes: 122 additions & 0 deletions packages/foundry/test/Bridge/BridgeBaseTest.t.sol
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);
}
}
237 changes: 237 additions & 0 deletions packages/foundry/test/Bridge/BridgeCompression.t.sol
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;

// }
}
1 change: 1 addition & 0 deletions packages/hardhat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@
"qrcode": "^1.5.1",
"readline": "^1.3.0",
"regenerator-runtime": "^0.14.1",
"solady": "file:../foundry/lib/solady",
"solidity-bytes-utils": "0.8.0",
"sushiswap-v3-periphery": "https://github.com/sushiswap/v3-periphery",
"velodrome": "https://github.com/velodrome-finance/contracts",
Expand Down
Loading

0 comments on commit 16d4b93

Please sign in to comment.