From b75401c92cdfb6e5cca68ec1f6c36ba41c799f6e Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 12 Dec 2024 10:18:34 +0200 Subject: [PATCH 01/21] initial support --- contracts/scripts/DeployLocal.sol | 10 +++---- .../{FundAgent.sol => FundGateway.sol} | 13 +++------ contracts/src/Assets.sol | 27 +++++++++++++++---- contracts/src/Gateway.sol | 18 ++++++------- contracts/src/Types.sol | 1 + contracts/test/Gateway.t.sol | 2 +- .../{fund-agent.sh => fund-gateway.sh} | 10 +++---- web/packages/test/scripts/set-env.sh | 2 +- 8 files changed, 45 insertions(+), 38 deletions(-) rename contracts/scripts/{FundAgent.sol => FundGateway.sol} (66%) rename web/packages/test/scripts/{fund-agent.sh => fund-gateway.sh} (63%) diff --git a/contracts/scripts/DeployLocal.sol b/contracts/scripts/DeployLocal.sol index 441ab9926c..da06bd9fcc 100644 --- a/contracts/scripts/DeployLocal.sol +++ b/contracts/scripts/DeployLocal.sol @@ -96,15 +96,11 @@ contract DeployLocal is Script { // Deploy WETH for testing new WETH9(); - // Fund the sovereign account for the BridgeHub parachain. Used to reward relayers + // Fund the gateway proxy contract. Used to reward relayers // of messages originating from BridgeHub - uint256 initialDeposit = vm.envUint("BRIDGE_HUB_INITIAL_DEPOSIT"); + uint256 initialDeposit = vm.envUint("GATEWAY_PROXY_INITIAL_DEPOSIT"); - address bridgeHubAgent = IGateway(address(gateway)).agentOf(bridgeHubAgentID); - address assetHubAgent = IGateway(address(gateway)).agentOf(assetHubAgentID); - - payable(bridgeHubAgent).safeNativeTransfer(initialDeposit); - payable(assetHubAgent).safeNativeTransfer(initialDeposit); + payable(gateway).safeNativeTransfer(initialDeposit); // Deploy MockGatewayV2 for testing new MockGatewayV2(); diff --git a/contracts/scripts/FundAgent.sol b/contracts/scripts/FundGateway.sol similarity index 66% rename from contracts/scripts/FundAgent.sol rename to contracts/scripts/FundGateway.sol index 37eedceb6b..6b67bc4484 100644 --- a/contracts/scripts/FundAgent.sol +++ b/contracts/scripts/FundGateway.sol @@ -15,7 +15,7 @@ import {ParaID} from "../src/Types.sol"; import {SafeNativeTransfer} from "../src/utils/SafeTransfer.sol"; import {stdJson} from "forge-std/StdJson.sol"; -contract FundAgent is Script { +contract FundGateway is Script { using SafeNativeTransfer for address payable; using stdJson for string; @@ -26,17 +26,10 @@ contract FundAgent is Script { address deployer = vm.rememberKey(privateKey); vm.startBroadcast(deployer); - uint256 initialDeposit = vm.envUint("BRIDGE_HUB_INITIAL_DEPOSIT"); + uint256 initialDeposit = vm.envUint("GATEWAY_PROXY_INITIAL_DEPOSIT"); address gatewayAddress = vm.envAddress("GATEWAY_PROXY_CONTRACT"); - bytes32 bridgeHubAgentID = vm.envBytes32("BRIDGE_HUB_AGENT_ID"); - bytes32 assetHubAgentID = vm.envBytes32("ASSET_HUB_AGENT_ID"); - - address bridgeHubAgent = IGateway(gatewayAddress).agentOf(bridgeHubAgentID); - address assetHubAgent = IGateway(gatewayAddress).agentOf(assetHubAgentID); - - payable(bridgeHubAgent).safeNativeTransfer(initialDeposit); - payable(assetHubAgent).safeNativeTransfer(initialDeposit); + payable(gatewayAddress).safeNativeTransfer(initialDeposit); vm.stopBroadcast(); } diff --git a/contracts/src/Assets.sol b/contracts/src/Assets.sol index 11fa5c12c9..ab3c81700d 100644 --- a/contracts/src/Assets.sol +++ b/contracts/src/Assets.sol @@ -13,6 +13,7 @@ import {CoreStorage} from "./storage/CoreStorage.sol"; import {SubstrateTypes} from "./SubstrateTypes.sol"; import {ParaID, MultiAddress, Ticket, Costs} from "./Types.sol"; import {Address} from "./utils/Address.sol"; +import {SafeNativeTransfer} from "./utils/SafeTransfer.sol"; import {AgentExecutor} from "./AgentExecutor.sol"; import {Agent} from "./Agent.sol"; import {Call} from "./utils/Call.sol"; @@ -21,6 +22,7 @@ import {Token} from "./Token.sol"; /// @title Library for implementing Ethereum->Polkadot ERC20 transfers. library Assets { using Address for address; + using SafeNativeTransfer for address payable; using SafeTokenTransferFrom for IERC20; /* Errors */ @@ -110,15 +112,17 @@ library Assets { TokenInfo storage info = $.tokenRegistry[token]; - if (!info.isRegistered) { - revert TokenNotRegistered(); - } - if (info.foreignID == bytes32(0)) { + if (!info.isRegistered && token != address(0)) { + revert TokenNotRegistered(); + } return _sendNativeToken( token, sender, destinationChain, destinationAddress, destinationChainFee, maxDestinationChainFee, amount ); } else { + if (!info.isRegistered) { + revert TokenNotRegistered(); + } return _sendForeignToken( info.foreignID, token, @@ -144,7 +148,18 @@ library Assets { AssetsStorage.Layout storage $ = AssetsStorage.layout(); // Lock the funds into AssetHub's agent contract - _transferToAgent($.assetHubAgent, token, sender, amount); + if (token != address(0)) { + // ERC20 + _transferToAgent($.assetHubAgent, token, sender, amount); + ticket.etherAmount = 0; + } else { + // Native ETH + if (msg.value < amount) { + revert TokenAmountTooLow(); + } + payable($.assetHubAgent).safeNativeTransfer(amount); + ticket.etherAmount = amount; + } ticket.dest = $.assetHubParaID; ticket.costs = _sendTokenCosts(destinationChain, destinationChainFee, maxDestinationChainFee); @@ -211,6 +226,7 @@ library Assets { ticket.dest = $.assetHubParaID; ticket.costs = _sendTokenCosts(destinationChain, destinationChainFee, maxDestinationChainFee); + ticket.etherAmount = 0; // Construct a message payload if (destinationChain == $.assetHubParaID && destinationAddress.isAddress32()) { @@ -262,6 +278,7 @@ library Assets { ticket.dest = $.assetHubParaID; ticket.costs = _registerTokenCosts(); ticket.payload = SubstrateTypes.RegisterToken(token, $.assetHubCreateAssetFee); + ticket.etherAmount = 0; emit IGateway.TokenRegistrationSent(token); } diff --git a/contracts/src/Gateway.sol b/contracts/src/Gateway.sol index 685627fe34..b0c234e883 100644 --- a/contracts/src/Gateway.sol +++ b/contracts/src/Gateway.sol @@ -99,6 +99,7 @@ contract Gateway is IGateway, IInitializable, IUpgradable { error InvalidAgentExecutionPayload(); error InvalidConstructorParams(); error TokenNotRegistered(); + error TokenAmountTooLow(); // Message handlers can only be dispatched by the gateway itself modifier onlySelf() { @@ -240,11 +241,11 @@ contract Gateway is IGateway, IInitializable, IUpgradable { // Add the reward to the refund amount. If the sum is more than the funds available // in the channel agent, then reduce the total amount - uint256 amount = Math.min(refund + message.reward, address(channel.agent).balance); + uint256 amount = Math.min(refund + message.reward, address(this).balance); - // Do the payment if there funds available in the agent + // Do the payment if there funds available in the gateway if (amount > _dustThreshold()) { - _transferNativeFromAgent(channel.agent, payable(msg.sender), amount); + payable(msg.sender).safeNativeTransfer(amount); } emit IGateway.InboundMessageDispatched(message.channelID, message.nonce, message.id, success); @@ -530,18 +531,17 @@ contract Gateway is IGateway, IInitializable, IUpgradable { uint256 fee = _calculateFee(ticket.costs); // Ensure the user has enough funds for this message to be accepted - if (msg.value < fee) { + uint256 totalEther = fee + ticket.etherAmount; + if (msg.value < totalEther) { revert FeePaymentToLow(); } channel.outboundNonce = channel.outboundNonce + 1; - // Deposit total fee into agent's contract - payable(channel.agent).safeNativeTransfer(fee); - + // The fee is already collected into the gateway contract // Reimburse excess fee payment - if (msg.value > fee) { - payable(msg.sender).safeNativeTransfer(msg.value - fee); + if (msg.value > totalEther) { + payable(msg.sender).safeNativeTransfer(msg.value - totalEther); } // Generate a unique ID for this message diff --git a/contracts/src/Types.sol b/contracts/src/Types.sol index 02e8bd0acc..d757d2b7d0 100644 --- a/contracts/src/Types.sol +++ b/contracts/src/Types.sol @@ -107,6 +107,7 @@ struct Ticket { ParaID dest; Costs costs; bytes payload; + uint128 etherAmount; } struct TokenInfo { diff --git a/contracts/test/Gateway.t.sol b/contracts/test/Gateway.t.sol index 96705c5c9f..d3abd56545 100644 --- a/contracts/test/Gateway.t.sol +++ b/contracts/test/Gateway.t.sol @@ -986,7 +986,7 @@ contract GatewayTest is Test { vm.expectRevert(Assets.TokenNotRegistered.selector); - IGateway(address(gateway)).sendToken{value: 0.1 ether}(address(0x0), destPara, recipientAddress32, 1, 1); + IGateway(address(gateway)).sendToken{value: 0.1 ether}(address(0x1), destPara, recipientAddress32, 1, 1); } function testSendTokenFromNotMintedAccountWillFail() public { diff --git a/web/packages/test/scripts/fund-agent.sh b/web/packages/test/scripts/fund-gateway.sh similarity index 63% rename from web/packages/test/scripts/fund-agent.sh rename to web/packages/test/scripts/fund-gateway.sh index e094e9c483..453864f49a 100755 --- a/web/packages/test/scripts/fund-agent.sh +++ b/web/packages/test/scripts/fund-gateway.sh @@ -3,19 +3,19 @@ set -eu source scripts/set-env.sh -fund_agent() { +fund_gateway() { pushd "$contract_dir" forge script \ --rpc-url $eth_endpoint_http \ --broadcast \ -vvv \ - scripts/FundAgent.sol:FundAgent + scripts/FundGateway.sol:FundGateway popd - echo "Fund agent success!" + echo "Fund gateway success!" } if [ -z "${from_start_services:-}" ]; then - echo "Funding agent" - fund_agent + echo "Funding gateway" + fund_gateway fi diff --git a/web/packages/test/scripts/set-env.sh b/web/packages/test/scripts/set-env.sh index 1ab1d6c278..2e6ff5f8c7 100755 --- a/web/packages/test/scripts/set-env.sh +++ b/web/packages/test/scripts/set-env.sh @@ -110,7 +110,7 @@ export LOCAL_REWARD="${LOCAL_REWARD:-1000000000000}" export REMOTE_REWARD="${REMOTE_REWARD:-1000000000000000}" ## Vault -export BRIDGE_HUB_INITIAL_DEPOSIT="${ETH_BRIDGE_HUB_INITIAL_DEPOSIT:-10000000000000000000}" +export GATEWAY_PROXY_INITIAL_DEPOSIT="${GATEWAY_PROXY_INITIAL_DEPOSIT:-10000000000000000000}" export GATEWAY_STORAGE_KEY="${GATEWAY_STORAGE_KEY:-0xaed97c7854d601808b98ae43079dafb3}" export GATEWAY_PROXY_CONTRACT="${GATEWAY_PROXY_CONTRACT:-0x87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d}" From 7491ff83b1cc89448787c83fe7399ffb04208855 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Tue, 17 Dec 2024 22:52:00 +0200 Subject: [PATCH 02/21] add error --- contracts/src/Assets.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/src/Assets.sol b/contracts/src/Assets.sol index ab3c81700d..8cc5d27d13 100644 --- a/contracts/src/Assets.sol +++ b/contracts/src/Assets.sol @@ -36,6 +36,7 @@ library Assets { error TokenAlreadyRegistered(); error TokenMintFailed(); error TokenTransferFailed(); + error TokenAmountTooLow(); function isTokenRegistered(address token) external view returns (bool) { return AssetsStorage.layout().tokenRegistry[token].isRegistered; From 4403dab68edadef42c44e6a10beaecb2d6b40ed1 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Tue, 17 Dec 2024 23:45:55 +0200 Subject: [PATCH 03/21] ws --- contracts/src/Assets.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/src/Assets.sol b/contracts/src/Assets.sol index 8cc5d27d13..1aa256d70c 100644 --- a/contracts/src/Assets.sol +++ b/contracts/src/Assets.sol @@ -271,7 +271,7 @@ library Assets { // It means that registration can be retried. // But register a PNA here is not allowed TokenInfo storage info = $.tokenRegistry[token]; - if(info.foreignID != bytes32(0)) { + if (info.foreignID != bytes32(0)) { revert TokenAlreadyRegistered(); } info.isRegistered = true; From a8f7197b84b2f6ae589a5abddc6899d814599e07 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Tue, 17 Dec 2024 23:46:57 +0200 Subject: [PATCH 04/21] test legacy and new unlock messages --- contracts/test/Gateway.t.sol | 85 +++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/contracts/test/Gateway.t.sol b/contracts/test/Gateway.t.sol index 96705c5c9f..9df10ab9e2 100644 --- a/contracts/test/Gateway.t.sol +++ b/contracts/test/Gateway.t.sol @@ -160,6 +160,27 @@ contract GatewayTest is Test { return (Command.CreateAgent, abi.encode((keccak256("6666")))); } + function makeLegacyUnlockWethCommand(bytes32 agentID, address token_, address recipient, uint128 amount) + public + view + returns (Command, bytes memory) + { + bytes memory payload = abi.encode(token_, recipient, amount); + AgentExecuteParams memory params = + AgentExecuteParams({agentID: agentID, payload: abi.encode(AgentExecuteCommand.TransferToken, payload)}); + return (Command.AgentExecute, abi.encode(params)); + } + + function makeUnlockWethCommand(bytes32 agentID, address token_, address recipient, uint128 amount) + public + view + returns (Command, bytes memory) + { + TransferNativeTokenParams memory params = + TransferNativeTokenParams({agentID: agentID, token: token_, recipient: recipient, amount: amount}); + return (Command.TransferNativeToken, abi.encode(params)); + } + function makeMockProof() public pure returns (Verification.Proof memory) { return Verification.Proof({ header: Verification.ParachainHeader({ @@ -196,7 +217,7 @@ contract GatewayTest is Test { (Command command, bytes memory params) = makeCreateAgentCommand(); // Expect the gateway to emit `InboundMessageDispatched` - vm.expectEmit(true, false, false, false); + vm.expectEmit(); emit IGateway.InboundMessageDispatched(assetHubParaID.into(), 1, messageID, true); hoax(relayer, 1 ether); @@ -207,6 +228,68 @@ contract GatewayTest is Test { ); } + function testLegacyUnlockWethHappyPath() public { + address recipient = makeAddr("test_recipeint"); + uint128 amount = 1; + + hoax(assetHubAgent, amount); + token.deposit{value: amount}(); + + (Command command, bytes memory params) = makeLegacyUnlockWethCommand(assetHubAgentID, address(token), recipient, amount); + + assertEq(token.balanceOf(assetHubAgent), amount); + assertEq(token.balanceOf(recipient), 0); + + // Expect WETH.Transfer event. + vm.expectEmit(); + emit WETH9.Transfer(assetHubAgent, recipient, amount); + + // Expect the gateway to emit `InboundMessageDispatched` + vm.expectEmit(); + emit IGateway.InboundMessageDispatched(assetHubParaID.into(), 1, messageID, true); + + hoax(relayer, 1 ether); + IGateway(address(gateway)).submitV1( + InboundMessage(assetHubParaID.into(), 1, command, params, maxDispatchGas, maxRefund, reward, messageID), + proof, + makeMockProof() + ); + + assertEq(token.balanceOf(assetHubAgent), 0); + assertEq(token.balanceOf(recipient), amount); + } + + function testUnlockWethHappyPath() public { + address recipient = makeAddr("test_recipeint"); + uint128 amount = 1; + + hoax(assetHubAgent, amount); + token.deposit{value: amount}(); + + (Command command, bytes memory params) = makeUnlockWethCommand(assetHubAgentID, address(token), recipient, amount); + + assertEq(token.balanceOf(assetHubAgent), amount); + assertEq(token.balanceOf(recipient), 0); + + // Expect WETH.Transfer event. + vm.expectEmit(); + emit WETH9.Transfer(assetHubAgent, recipient, amount); + + // Expect the gateway to emit `InboundMessageDispatched` + vm.expectEmit(true, false, false, true); + emit IGateway.InboundMessageDispatched(assetHubParaID.into(), 1, messageID, true); + + hoax(relayer, 1 ether); + IGateway(address(gateway)).submitV1( + InboundMessage(assetHubParaID.into(), 1, command, params, maxDispatchGas, maxRefund, reward, messageID), + proof, + makeMockProof() + ); + + assertEq(token.balanceOf(assetHubAgent), 0); + assertEq(token.balanceOf(recipient), amount); + } + function testSubmitFailInvalidNonce() public { deal(assetHubAgent, 50 ether); From e9b860c9afc24b7ef5a314acec21d1152f159896 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 18 Dec 2024 00:01:27 +0200 Subject: [PATCH 05/21] fixed tests --- contracts/test/Gateway.t.sol | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/contracts/test/Gateway.t.sol b/contracts/test/Gateway.t.sol index d3abd56545..ac03e39351 100644 --- a/contracts/test/Gateway.t.sol +++ b/contracts/test/Gateway.t.sol @@ -191,8 +191,6 @@ contract GatewayTest is Test { * Message Verification */ function testSubmitHappyPath() public { - deal(assetHubAgent, 50 ether); - (Command command, bytes memory params) = makeCreateAgentCommand(); // Expect the gateway to emit `InboundMessageDispatched` @@ -208,8 +206,6 @@ contract GatewayTest is Test { } function testSubmitFailInvalidNonce() public { - deal(assetHubAgent, 50 ether); - (Command command, bytes memory params) = makeCreateAgentCommand(); hoax(relayer, 1 ether); @@ -242,8 +238,6 @@ contract GatewayTest is Test { } function testSubmitFailInvalidProof() public { - deal(assetHubAgent, 50 ether); - (Command command, bytes memory params) = makeCreateAgentCommand(); MockGateway(address(gateway)).setCommitmentsAreVerified(false); @@ -262,14 +256,15 @@ contract GatewayTest is Test { */ // Message relayer should be rewarded from the agent for a channel - function testRelayerRewardedFromAgent() public { + function testRelayerRewardedFromGateway() public { (Command command, bytes memory params) = makeCreateAgentCommand(); vm.txGasPrice(10 gwei); hoax(relayer, 1 ether); - deal(assetHubAgent, 50 ether); + deal(address(gateway), 50 ether); uint256 relayerBalanceBefore = address(relayer).balance; + uint256 gatewayBalanceBefore = address(address(gateway)).balance; uint256 agentBalanceBefore = address(assetHubAgent).balance; uint256 startGas = gasleft(); @@ -282,19 +277,22 @@ contract GatewayTest is Test { uint256 estimatedActualRefundAmount = (startGas - endGas) * tx.gasprice; assertLt(estimatedActualRefundAmount, maxRefund); - // Check that agent balance decreased and relayer balance increases - assertLt(address(assetHubAgent).balance, agentBalanceBefore); + // Agents do not pay reward+refund so no balance should change. + assertEq(address(assetHubAgent).balance, agentBalanceBefore); + // Relayer balance has increased + assertLt(address(gateway).balance, gatewayBalanceBefore); + // Relayer balance has increased assertGt(relayer.balance, relayerBalanceBefore); // The total amount paid to the relayer - uint256 totalPaid = agentBalanceBefore - address(assetHubAgent).balance; + uint256 totalPaid = gatewayBalanceBefore - address(gateway).balance; // Since we know that the actual refund amount is less than the max refund, // the total amount paid to the relayer is less. assertLt(totalPaid, maxRefund + reward); } - // In this case, the agent has no funds to reward the relayer + // In this case, the gateway has no funds to reward the relayer function testRelayerNotRewarded() public { (Command command, bytes memory params) = makeCreateAgentCommand(); @@ -772,8 +770,6 @@ contract GatewayTest is Test { } function testCreateAgentWithNotEnoughGas() public { - deal(assetHubAgent, 50 ether); - (Command command, bytes memory params) = makeCreateAgentCommand(); hoax(relayer, 1 ether); From 3a8c9d9ec6f4d7eb02a79f16f36de0bb10e743e8 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 18 Dec 2024 00:07:51 +0200 Subject: [PATCH 06/21] fix spelling --- contracts/src/Gateway.sol | 4 ++-- contracts/test/Gateway.t.sol | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/src/Gateway.sol b/contracts/src/Gateway.sol index b0c234e883..05019af004 100644 --- a/contracts/src/Gateway.sol +++ b/contracts/src/Gateway.sol @@ -88,7 +88,7 @@ contract Gateway is IGateway, IInitializable, IUpgradable { error InvalidProof(); error InvalidNonce(); error NotEnoughGas(); - error FeePaymentToLow(); + error FeePaymentTooLow(); error Unauthorized(); error Disabled(); error AgentAlreadyCreated(); @@ -533,7 +533,7 @@ contract Gateway is IGateway, IInitializable, IUpgradable { // Ensure the user has enough funds for this message to be accepted uint256 totalEther = fee + ticket.etherAmount; if (msg.value < totalEther) { - revert FeePaymentToLow(); + revert FeePaymentTooLow(); } channel.outboundNonce = channel.outboundNonce + 1; diff --git a/contracts/test/Gateway.t.sol b/contracts/test/Gateway.t.sol index 88c5885cbc..f5af4b0dd5 100644 --- a/contracts/test/Gateway.t.sol +++ b/contracts/test/Gateway.t.sol @@ -426,7 +426,7 @@ contract GatewayTest is Test { hoax(user); token.approve(address(gateway), 1); - vm.expectRevert(Gateway.FeePaymentToLow.selector); + vm.expectRevert(Gateway.FeePaymentTooLow.selector); hoax(user, 2 ether); IGateway(address(gateway)).sendToken{value: 0.002 ether}( address(token), ParaID.wrap(0), recipientAddress32, 1, 1 From 40b51f8afe4980c6ee338311bdddb8eeb8b6a4b5 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 18 Dec 2024 00:18:38 +0200 Subject: [PATCH 07/21] test registration is blocked --- contracts/test/Gateway.t.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contracts/test/Gateway.t.sol b/contracts/test/Gateway.t.sol index f5af4b0dd5..2aecb94214 100644 --- a/contracts/test/Gateway.t.sol +++ b/contracts/test/Gateway.t.sol @@ -1101,4 +1101,10 @@ contract GatewayTest is Test { vm.expectRevert(Assets.TokenAlreadyRegistered.selector); IGateway(address(gateway)).registerToken{value: fee}(dotToken); } + + function testRegisterTokenWithEthWillReturnInvalidToken() public { + uint256 fee = IGateway(address(gateway)).quoteRegisterTokenFee(); + vm.expectRevert(Assets.InvalidToken.selector); + IGateway(address(gateway)).registerToken{value: fee}(address(0)); + } } From 7fb34f3f931f879c2f61b1e71e1f01135027f9cb Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 18 Dec 2024 00:42:38 +0200 Subject: [PATCH 08/21] fix inbound messages --- contracts/src/Assets.sol | 7 +++- contracts/test/Gateway.t.sol | 69 ++++++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/contracts/src/Assets.sol b/contracts/src/Assets.sol index 1aa256d70c..6ce1c2377b 100644 --- a/contracts/src/Assets.sol +++ b/contracts/src/Assets.sol @@ -311,7 +311,12 @@ library Assets { function transferNativeToken(address executor, address agent, address token, address recipient, uint128 amount) external { - bytes memory call = abi.encodeCall(AgentExecutor.transferToken, (token, recipient, amount)); + bytes memory call; + if (token != address(0)) { + call = abi.encodeCall(AgentExecutor.transferToken, (token, recipient, amount)); + } else { + call = abi.encodeCall(AgentExecutor.transferNative, (payable(recipient), amount)); + } (bool success,) = Agent(payable(agent)).invoke(executor, call); if (!success) { revert TokenTransferFailed(); diff --git a/contracts/test/Gateway.t.sol b/contracts/test/Gateway.t.sol index 2aecb94214..b7a373b7cc 100644 --- a/contracts/test/Gateway.t.sol +++ b/contracts/test/Gateway.t.sol @@ -160,7 +160,7 @@ contract GatewayTest is Test { return (Command.CreateAgent, abi.encode((keccak256("6666")))); } - function makeLegacyUnlockWethCommand(bytes32 agentID, address token_, address recipient, uint128 amount) + function makeLegacyUnlockTokenCommand(bytes32 agentID, address token_, address recipient, uint128 amount) public view returns (Command, bytes memory) @@ -171,7 +171,7 @@ contract GatewayTest is Test { return (Command.AgentExecute, abi.encode(params)); } - function makeUnlockWethCommand(bytes32 agentID, address token_, address recipient, uint128 amount) + function makeUnlockTokenCommand(bytes32 agentID, address token_, address recipient, uint128 amount) public view returns (Command, bytes memory) @@ -233,7 +233,8 @@ contract GatewayTest is Test { hoax(assetHubAgent, amount); token.deposit{value: amount}(); - (Command command, bytes memory params) = makeLegacyUnlockWethCommand(assetHubAgentID, address(token), recipient, amount); + (Command command, bytes memory params) = + makeLegacyUnlockTokenCommand(assetHubAgentID, address(token), recipient, amount); assertEq(token.balanceOf(assetHubAgent), amount); assertEq(token.balanceOf(recipient), 0); @@ -260,12 +261,13 @@ contract GatewayTest is Test { function testUnlockWethHappyPath() public { address recipient = makeAddr("test_recipeint"); uint128 amount = 1; - + hoax(assetHubAgent, amount); token.deposit{value: amount}(); - (Command command, bytes memory params) = makeUnlockWethCommand(assetHubAgentID, address(token), recipient, amount); - + (Command command, bytes memory params) = + makeUnlockTokenCommand(assetHubAgentID, address(token), recipient, amount); + assertEq(token.balanceOf(assetHubAgent), amount); assertEq(token.balanceOf(recipient), 0); @@ -287,7 +289,60 @@ contract GatewayTest is Test { assertEq(token.balanceOf(assetHubAgent), 0); assertEq(token.balanceOf(recipient), amount); } - + + function testLegacyUnlockEthHappyPath() public { + address recipient = makeAddr("test_recipeint"); + uint128 amount = 1; + + deal(assetHubAgent, amount); + + (Command command, bytes memory params) = + makeLegacyUnlockTokenCommand(assetHubAgentID, address(0), recipient, amount); + + assertEq(assetHubAgent.balance, amount); + assertEq(recipient.balance, 0); + + // Expect the gateway to emit `InboundMessageDispatched` + vm.expectEmit(); + emit IGateway.InboundMessageDispatched(assetHubParaID.into(), 1, messageID, true); + + hoax(relayer, 1 ether); + IGateway(address(gateway)).submitV1( + InboundMessage(assetHubParaID.into(), 1, command, params, maxDispatchGas, maxRefund, reward, messageID), + proof, + makeMockProof() + ); + + assertEq(assetHubAgent.balance, 0); + assertEq(recipient.balance, amount); + } + + function testUnlockEthHappyPath() public { + address recipient = makeAddr("test_recipeint"); + uint128 amount = 1; + + deal(assetHubAgent, amount); + + (Command command, bytes memory params) = makeUnlockTokenCommand(assetHubAgentID, address(0), recipient, amount); + + assertEq(assetHubAgent.balance, amount); + assertEq(recipient.balance, 0); + + // Expect the gateway to emit `InboundMessageDispatched` + vm.expectEmit(true, false, false, true); + emit IGateway.InboundMessageDispatched(assetHubParaID.into(), 1, messageID, true); + + hoax(relayer, 1 ether); + IGateway(address(gateway)).submitV1( + InboundMessage(assetHubParaID.into(), 1, command, params, maxDispatchGas, maxRefund, reward, messageID), + proof, + makeMockProof() + ); + + assertEq(assetHubAgent.balance, 0); + assertEq(recipient.balance, amount); + } + function testSubmitFailInvalidNonce() public { (Command command, bytes memory params) = makeCreateAgentCommand(); From f866356929a78c4d1d0ff4a54792e00d690174b4 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 18 Dec 2024 00:58:11 +0200 Subject: [PATCH 09/21] allow gateway proxy to receive funds --- contracts/src/Assets.sol | 2 ++ contracts/src/GatewayProxy.sol | 7 ++----- contracts/test/Gateway.t.sol | 15 ++++++++++++++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/contracts/src/Assets.sol b/contracts/src/Assets.sol index 6ce1c2377b..1ee95011b9 100644 --- a/contracts/src/Assets.sol +++ b/contracts/src/Assets.sol @@ -313,8 +313,10 @@ library Assets { { bytes memory call; if (token != address(0)) { + // ERC20 call = abi.encodeCall(AgentExecutor.transferToken, (token, recipient, amount)); } else { + // Native ETH call = abi.encodeCall(AgentExecutor.transferNative, (payable(recipient), amount)); } (bool success,) = Agent(payable(agent)).invoke(executor, call); diff --git a/contracts/src/GatewayProxy.sol b/contracts/src/GatewayProxy.sol index b66bc91f83..ffc8817c8c 100644 --- a/contracts/src/GatewayProxy.sol +++ b/contracts/src/GatewayProxy.sol @@ -37,9 +37,6 @@ contract GatewayProxy is IInitializable { } } - // Prevent users from unwittingly sending ether to the gateway, as these funds - // would otherwise be lost forever. - receive() external payable { - revert NativeCurrencyNotAccepted(); - } + // Allow the Gateway proxy to receive ether in order to pay out rewards and refunds + receive() external payable {} } diff --git a/contracts/test/Gateway.t.sol b/contracts/test/Gateway.t.sol index b7a373b7cc..37c863c96f 100644 --- a/contracts/test/Gateway.t.sol +++ b/contracts/test/Gateway.t.sol @@ -23,7 +23,7 @@ import {SubstrateTypes} from "./../src/SubstrateTypes.sol"; import {MultiAddress} from "../src/MultiAddress.sol"; import {Channel, InboundMessage, OperatingMode, ParaID, Command, ChannelID, MultiAddress} from "../src/Types.sol"; -import {NativeTransferFailed} from "../src/utils/SafeTransfer.sol"; +import {SafeNativeTransfer} from "../src/utils/SafeTransfer.sol"; import {PricingStorage} from "../src/storage/PricingStorage.sol"; import {IERC20} from "../src/interfaces/IERC20.sol"; import {TokenLib} from "../src/TokenLib.sol"; @@ -393,6 +393,19 @@ contract GatewayTest is Test { * Fees & Rewards */ + // Test that the Gateway Proxy can receive funds to act as a wallet to pay out rewards and refunds + function testGatewayProxyCanRecieveFunds() public { + uint256 amount = 1 ether; + address deployer = makeAddr("deployer"); + hoax(deployer, amount); + + assertEq(address(gateway).balance, 0); + + SafeNativeTransfer.safeNativeTransfer(payable(gateway), amount); + + assertEq(address(gateway).balance, amount); + } + // Message relayer should be rewarded from the agent for a channel function testRelayerRewardedFromGateway() public { (Command command, bytes memory params) = makeCreateAgentCommand(); From c9373384c0eadef6367e582bc7a0e0247ced1fde Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 18 Dec 2024 01:51:00 +0200 Subject: [PATCH 10/21] final tests and fixes --- contracts/src/Assets.sol | 2 +- contracts/test/Gateway.t.sol | 74 ++++++++++++++++++++++++++++++++++-- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/contracts/src/Assets.sol b/contracts/src/Assets.sol index 1ee95011b9..a8251936fb 100644 --- a/contracts/src/Assets.sol +++ b/contracts/src/Assets.sol @@ -63,7 +63,7 @@ library Assets { ) external view returns (Costs memory costs) { AssetsStorage.Layout storage $ = AssetsStorage.layout(); TokenInfo storage info = $.tokenRegistry[token]; - if (!info.isRegistered) { + if (!info.isRegistered && token != address(0)) { revert TokenNotRegistered(); } diff --git a/contracts/test/Gateway.t.sol b/contracts/test/Gateway.t.sol index 37c863c96f..666c14d75f 100644 --- a/contracts/test/Gateway.t.sol +++ b/contracts/test/Gateway.t.sol @@ -162,7 +162,7 @@ contract GatewayTest is Test { function makeLegacyUnlockTokenCommand(bytes32 agentID, address token_, address recipient, uint128 amount) public - view + pure returns (Command, bytes memory) { bytes memory payload = abi.encode(token_, recipient, amount); @@ -173,7 +173,7 @@ contract GatewayTest is Test { function makeUnlockTokenCommand(bytes32 agentID, address token_, address recipient, uint128 amount) public - view + pure returns (Command, bytes memory) { TransferNativeTokenParams memory params = @@ -458,8 +458,7 @@ contract GatewayTest is Test { assertEq(relayer.balance, 1 ether); } - // Users should pay fees to send outbound messages - function testUserPaysFees() public { + function testSendingWethWithFeeSucceeds() public { // Create a mock user address user = makeAddr("user"); deal(address(token), user, 1); @@ -480,6 +479,73 @@ contract GatewayTest is Test { assertEq(user.balance, 0); } + function testSendingEthWithAmountAndFeeSucceeds() public { + // Create a mock user + address user = makeAddr("user"); + uint128 amount = 1; + ParaID paraID = ParaID.wrap(1000); + + uint128 fee = uint128(IGateway(address(gateway)).quoteSendTokenFee(address(0), paraID, 1)); + + vm.expectEmit(); + emit IGateway.TokenSent(address(0), user, paraID, recipientAddress32, amount); + vm.expectEmit(true, false, false, false); + emit IGateway.OutboundMessageAccepted(paraID.into(), 1, messageID, hex""); + hoax(user, amount + fee); + IGateway(address(gateway)).sendToken{value: amount + fee}(address(0), paraID, recipientAddress32, 1, amount); + + assertEq(user.balance, 0); + } + + function testSendingEthWithAmountFeeAndExtraSucceedsWithRefund() public { + // Create a mock user + address user = makeAddr("user"); + uint128 amount = 1 ether; + uint128 extra = 2 ether; + ParaID paraID = ParaID.wrap(1000); + + uint128 fee = uint128(IGateway(address(gateway)).quoteSendTokenFee(address(0), paraID, 1)); + + vm.expectEmit(); + emit IGateway.TokenSent(address(0), user, paraID, recipientAddress32, amount); + vm.expectEmit(true, false, false, false); + emit IGateway.OutboundMessageAccepted(paraID.into(), 1, messageID, hex""); + hoax(user, amount + fee + extra); + IGateway(address(gateway)).sendToken{value: amount + fee + extra}( + address(0), paraID, recipientAddress32, 1, amount + ); + + assertEq(user.balance, extra); + } + + function testSendingEthWithoutFeeFails() public { + // Create a mock user + address user = makeAddr("user"); + uint128 amount = 1; + ParaID paraID = ParaID.wrap(1000); + + uint128 fee = uint128(IGateway(address(gateway)).quoteSendTokenFee(address(0), paraID, 1)); + + vm.expectEmit(); + emit IGateway.TokenSent(address(0), user, paraID, recipientAddress32, amount); + vm.expectRevert(Gateway.FeePaymentTooLow.selector); + hoax(user, amount + fee); + IGateway(address(gateway)).sendToken{value: amount}(address(0), paraID, recipientAddress32, 1, amount); + } + + function testSendingEthWithoutAmountFails() public { + // Create a mock user + address user = makeAddr("user"); + uint128 amount = 1 ether; + ParaID paraID = ParaID.wrap(1000); + + uint128 fee = uint128(IGateway(address(gateway)).quoteSendTokenFee(address(0), paraID, amount)); + + vm.expectRevert(Gateway.TokenAmountTooLow.selector); + hoax(user, amount + fee); + IGateway(address(gateway)).sendToken{value: amount - 1}(address(0), paraID, recipientAddress32, 1, amount); + } + // User doesn't have enough funds to send message function testUserDoesNotProvideEnoughFees() public { // register token first From c01db5c5fb81f342e13b59ce22fe6ac2c66e077a Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 18 Dec 2024 01:51:21 +0200 Subject: [PATCH 11/21] warnings --- contracts/test/Bitfield.t.sol | 1 - contracts/test/mocks/BitfieldWrapper.sol | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/test/Bitfield.t.sol b/contracts/test/Bitfield.t.sol index 2334e399a4..74548e3c92 100644 --- a/contracts/test/Bitfield.t.sol +++ b/contracts/test/Bitfield.t.sol @@ -16,7 +16,6 @@ contract BitfieldTest is Test { string memory json = vm.readFile(string.concat(vm.projectRoot(), "/test/data/beefy-validator-set.json")); uint32 setSize = uint32(json.readUint(".validatorSetSize")); - bytes32 root = json.readBytes32(".validatorRoot"); uint256[] memory bitSetArray = json.readUintArray(".participants"); uint256[] memory initialBitField = bw.createBitfield(bitSetArray, setSize); diff --git a/contracts/test/mocks/BitfieldWrapper.sol b/contracts/test/mocks/BitfieldWrapper.sol index 3ae5e839e7..4521eb4df7 100644 --- a/contracts/test/mocks/BitfieldWrapper.sol +++ b/contracts/test/mocks/BitfieldWrapper.sol @@ -14,6 +14,7 @@ contract BitfieldWrapper { function subsample(uint256 seed, uint256[] memory prior, uint256 n, uint256 length) public + pure returns (uint256[] memory bitfield) { return Bitfield.subsample(seed, prior, n, length); From 0e40439370417481613eeccb5e70386649a10c73 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Wed, 18 Dec 2024 22:53:49 +0200 Subject: [PATCH 12/21] PR feedback --- contracts/src/Assets.sol | 8 ++++---- contracts/src/Gateway.sol | 4 ++-- contracts/src/Types.sol | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/contracts/src/Assets.sol b/contracts/src/Assets.sol index a8251936fb..d232093735 100644 --- a/contracts/src/Assets.sol +++ b/contracts/src/Assets.sol @@ -152,14 +152,14 @@ library Assets { if (token != address(0)) { // ERC20 _transferToAgent($.assetHubAgent, token, sender, amount); - ticket.etherAmount = 0; + ticket.value = 0; } else { // Native ETH if (msg.value < amount) { revert TokenAmountTooLow(); } payable($.assetHubAgent).safeNativeTransfer(amount); - ticket.etherAmount = amount; + ticket.value = amount; } ticket.dest = $.assetHubParaID; @@ -227,7 +227,7 @@ library Assets { ticket.dest = $.assetHubParaID; ticket.costs = _sendTokenCosts(destinationChain, destinationChainFee, maxDestinationChainFee); - ticket.etherAmount = 0; + ticket.value = 0; // Construct a message payload if (destinationChain == $.assetHubParaID && destinationAddress.isAddress32()) { @@ -279,7 +279,7 @@ library Assets { ticket.dest = $.assetHubParaID; ticket.costs = _registerTokenCosts(); ticket.payload = SubstrateTypes.RegisterToken(token, $.assetHubCreateAssetFee); - ticket.etherAmount = 0; + ticket.value = 0; emit IGateway.TokenRegistrationSent(token); } diff --git a/contracts/src/Gateway.sol b/contracts/src/Gateway.sol index 05019af004..6d5bdf8db3 100644 --- a/contracts/src/Gateway.sol +++ b/contracts/src/Gateway.sol @@ -531,7 +531,7 @@ contract Gateway is IGateway, IInitializable, IUpgradable { uint256 fee = _calculateFee(ticket.costs); // Ensure the user has enough funds for this message to be accepted - uint256 totalEther = fee + ticket.etherAmount; + uint256 totalEther = fee + ticket.value; if (msg.value < totalEther) { revert FeePaymentTooLow(); } @@ -540,7 +540,7 @@ contract Gateway is IGateway, IInitializable, IUpgradable { // The fee is already collected into the gateway contract // Reimburse excess fee payment - if (msg.value > totalEther) { + if (msg.value > totalEther && (msg.value - totalEther) > _dustThreshold()) { payable(msg.sender).safeNativeTransfer(msg.value - totalEther); } diff --git a/contracts/src/Types.sol b/contracts/src/Types.sol index d757d2b7d0..1b74918ad8 100644 --- a/contracts/src/Types.sol +++ b/contracts/src/Types.sol @@ -107,7 +107,7 @@ struct Ticket { ParaID dest; Costs costs; bytes payload; - uint128 etherAmount; + uint128 value; } struct TokenInfo { From a9768e1562ebaa3eec223e878c7c869a4bf0bd91 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 19 Dec 2024 01:08:26 +0200 Subject: [PATCH 13/21] migrate ether on upgrade --- contracts/src/Agent.sol | 3 +-- contracts/src/AgentExecutor.sol | 7 ++++++- contracts/src/Gateway.sol | 11 ++++++++++ contracts/src/GatewayProxy.sol | 7 +++++-- contracts/src/interfaces/IGateway.sol | 10 +++++++++ contracts/src/storage/CoreStorage.sol | 2 ++ contracts/src/upgrades/Gateway202410.sol | 15 ++++++++++++- ...kUpgrade.t.sol => ForkUpgrade202410.t.sol} | 21 +++++++++++++++++-- contracts/test/Gateway.t.sol | 3 +++ 9 files changed, 71 insertions(+), 8 deletions(-) rename contracts/test/{ForkUpgrade.t.sol => ForkUpgrade202410.t.sol} (78%) diff --git a/contracts/src/Agent.sol b/contracts/src/Agent.sol index d98092d9f2..5a4c96d908 100644 --- a/contracts/src/Agent.sol +++ b/contracts/src/Agent.sol @@ -20,8 +20,7 @@ contract Agent { } /// @dev Agents can receive ether permissionlessly. - /// This is important, as agents for top-level parachains also act as sovereign accounts from which message relayers - /// are rewarded. + /// This is important, as agents are used to lock ether. receive() external payable {} /// @dev Allow the gateway to invoke some code within the context of this agent diff --git a/contracts/src/AgentExecutor.sol b/contracts/src/AgentExecutor.sol index 629a25381e..5bc4bcf6f7 100644 --- a/contracts/src/AgentExecutor.sol +++ b/contracts/src/AgentExecutor.sol @@ -6,6 +6,7 @@ import {AgentExecuteCommand, ParaID} from "./Types.sol"; import {SubstrateTypes} from "./SubstrateTypes.sol"; import {IERC20} from "./interfaces/IERC20.sol"; +import {IGateway} from "./interfaces/IGateway.sol"; import {SafeTokenTransfer, SafeNativeTransfer} from "./utils/SafeTransfer.sol"; /// @title Code which will run within an `Agent` using `delegatecall`. @@ -16,11 +17,15 @@ contract AgentExecutor { /// @dev Transfer ether to `recipient`. Unlike `_transferToken` This logic is not nested within `execute`, /// as the gateway needs to control an agent's ether balance directly. - /// function transferNative(address payable recipient, uint256 amount) external { recipient.safeNativeTransfer(amount); } + /// @dev Transfer ether to Gateway. Used once off for migration purposes. Can be removed after version 1. + function transferNativeToGateway(address payable gateway, uint256 amount) external { + IGateway(gateway).depositEther{value: amount}(); + } + /// @dev Transfer ERC20 to `recipient`. Only callable via `execute`. function transferToken(address token, address recipient, uint128 amount) external { _transferToken(token, recipient, amount); diff --git a/contracts/src/Gateway.sol b/contracts/src/Gateway.sol index 6d5bdf8db3..641de76708 100644 --- a/contracts/src/Gateway.sol +++ b/contracts/src/Gateway.sol @@ -281,6 +281,17 @@ contract Gateway is IGateway, IInitializable, IUpgradable { return ERC1967.load(); } + function version() public view returns (uint64) { + return CoreStorage.layout().version; + } + + /** + * Fee management + */ + function depositEther() external payable { + emit EtherDeposited(msg.sender, msg.value); + } + /** * Handlers */ diff --git a/contracts/src/GatewayProxy.sol b/contracts/src/GatewayProxy.sol index ffc8817c8c..b66bc91f83 100644 --- a/contracts/src/GatewayProxy.sol +++ b/contracts/src/GatewayProxy.sol @@ -37,6 +37,9 @@ contract GatewayProxy is IInitializable { } } - // Allow the Gateway proxy to receive ether in order to pay out rewards and refunds - receive() external payable {} + // Prevent users from unwittingly sending ether to the gateway, as these funds + // would otherwise be lost forever. + receive() external payable { + revert NativeCurrencyNotAccepted(); + } } diff --git a/contracts/src/interfaces/IGateway.sol b/contracts/src/interfaces/IGateway.sol index 1657b73577..77351c08ec 100644 --- a/contracts/src/interfaces/IGateway.sol +++ b/contracts/src/interfaces/IGateway.sol @@ -38,6 +38,9 @@ interface IGateway { // Emitted when foreign token from polkadot registed event ForeignTokenRegistered(bytes32 indexed tokenID, address token); + // Emitted when ether is deposited + event EtherDeposited(address who, uint256 amount); + /** * Getters */ @@ -53,6 +56,13 @@ interface IGateway { function implementation() external view returns (address); + function version() external view returns (uint64); + + /** + * Fee management + */ + function depositEther() external payable; + /** * Messaging */ diff --git a/contracts/src/storage/CoreStorage.sol b/contracts/src/storage/CoreStorage.sol index 1f8b24a705..0d64c9d5df 100644 --- a/contracts/src/storage/CoreStorage.sol +++ b/contracts/src/storage/CoreStorage.sol @@ -14,6 +14,8 @@ library CoreStorage { mapping(bytes32 agentID => address) agents; // Agent addresses mapping(address agent => bytes32 agentID) agentAddresses; + // Version of the Gateway Implementation + uint64 version; } bytes32 internal constant SLOT = keccak256("org.snowbridge.storage.core"); diff --git a/contracts/src/upgrades/Gateway202410.sol b/contracts/src/upgrades/Gateway202410.sol index d049fa825a..e61c763239 100644 --- a/contracts/src/upgrades/Gateway202410.sol +++ b/contracts/src/upgrades/Gateway202410.sol @@ -25,10 +25,23 @@ contract Gateway202410 is Gateway { {} // Override parent initializer to prevent re-initialization of storage. - function initialize(bytes memory) external view override { + function initialize(bytes memory) external override { // Ensure that arbitrary users cannot initialize storage in this logic contract. if (ERC1967.load() == address(0)) { revert Unauthorized(); } + + // We expect version 0, deploying version 1. + CoreStorage.Layout storage $ = CoreStorage.layout(); + if ($.version != 0) { + revert Unauthorized(); + } + $.version = 1; + + // migrate asset hub agent + address agent = _ensureAgent(hex"81c5ab2571199e3188135178f3c2c8e2d268be1313d029b30f534fa579b69b79"); + bytes memory call = + abi.encodeCall(AgentExecutor.transferNativeToGateway, (payable(address(this)), agent.balance)); + _invokeOnAgent(agent, call); } } diff --git a/contracts/test/ForkUpgrade.t.sol b/contracts/test/ForkUpgrade202410.t.sol similarity index 78% rename from contracts/test/ForkUpgrade.t.sol rename to contracts/test/ForkUpgrade202410.t.sol index 5b1a6069dd..1d8001b2ed 100644 --- a/contracts/test/ForkUpgrade.t.sol +++ b/contracts/test/ForkUpgrade202410.t.sol @@ -16,6 +16,7 @@ contract ForkUpgradeTest is Test { address private constant GatewayProxy = 0x27ca963C279c93801941e1eB8799c23f407d68e7; address private constant BeefyClient = 0x6eD05bAa904df3DE117EcFa638d4CB84e1B8A00C; bytes32 private constant BridgeHubAgent = 0x03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314; + bytes32 private constant AssetHubAgent = 0x81c5ab2571199e3188135178f3c2c8e2d268be1313d029b30f534fa579b69b79; function setUp() public { vm.createSelectFork("https://rpc.tenderly.co/fork/b77e07b8-ad6d-4e83-b5be-30a2001964aa", 20645700); @@ -33,10 +34,20 @@ contract ForkUpgradeTest is Test { UpgradeParams memory params = UpgradeParams({impl: address(newLogic), implCodeHash: address(newLogic).codehash, initParams: bytes("")}); - vm.expectEmit(true, false, false, false); + Gateway gateway = Gateway(GatewayProxy); + + // Check pre-migration of ETH from Asset Hub agent + assertGt(IGateway(GatewayProxy).agentOf(AssetHubAgent).balance, 0); + // Check pre-migration of ETH to Gateway + assertEq(address(GatewayProxy).balance, 0); + + vm.expectEmit(); + emit IGateway.EtherDeposited(gateway.agentOf(AssetHubAgent), 587928061927368450); + + vm.expectEmit(); emit IUpgradable.Upgraded(address(newLogic)); - Gateway(GatewayProxy).upgrade(abi.encode(params)); + gateway.upgrade(abi.encode(params)); } function checkLegacyToken() public { @@ -60,6 +71,12 @@ contract ForkUpgradeTest is Test { } function testSanityCheck() public { + // Check that the version is correctly set. + assertEq(IGateway(GatewayProxy).version(), 1); + // Check migration of ETH from Asset Hub agent + assertEq(IGateway(GatewayProxy).agentOf(AssetHubAgent).balance, 0); + // Check migration of ETH to Gateway + assertGt(address(GatewayProxy).balance, 0); // Check AH channel nonces as expected (uint64 inbound, uint64 outbound) = IGateway(GatewayProxy).channelNoncesOf( ChannelID.wrap(0xc173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539) diff --git a/contracts/test/Gateway.t.sol b/contracts/test/Gateway.t.sol index 666c14d75f..78a354c13a 100644 --- a/contracts/test/Gateway.t.sol +++ b/contracts/test/Gateway.t.sol @@ -401,8 +401,11 @@ contract GatewayTest is Test { assertEq(address(gateway).balance, 0); + vm.expectRevert(GatewayProxy.NativeCurrencyNotAccepted.selector); SafeNativeTransfer.safeNativeTransfer(payable(gateway), amount); + IGateway(address(gateway)).depositEther{value: amount}(); + assertEq(address(gateway).balance, amount); } From f491ebc2b264c9b15c5f1945c728027f849a4795 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 19 Dec 2024 01:33:37 +0200 Subject: [PATCH 14/21] fix scripts --- contracts/scripts/DeployLocal.sol | 2 +- contracts/scripts/FundGateway.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/scripts/DeployLocal.sol b/contracts/scripts/DeployLocal.sol index da06bd9fcc..6360ee9ba7 100644 --- a/contracts/scripts/DeployLocal.sol +++ b/contracts/scripts/DeployLocal.sol @@ -100,7 +100,7 @@ contract DeployLocal is Script { // of messages originating from BridgeHub uint256 initialDeposit = vm.envUint("GATEWAY_PROXY_INITIAL_DEPOSIT"); - payable(gateway).safeNativeTransfer(initialDeposit); + IGateway(address(gateway)).depositEther{value: initialDeposit}(); // Deploy MockGatewayV2 for testing new MockGatewayV2(); diff --git a/contracts/scripts/FundGateway.sol b/contracts/scripts/FundGateway.sol index 6b67bc4484..167da42237 100644 --- a/contracts/scripts/FundGateway.sol +++ b/contracts/scripts/FundGateway.sol @@ -29,7 +29,7 @@ contract FundGateway is Script { uint256 initialDeposit = vm.envUint("GATEWAY_PROXY_INITIAL_DEPOSIT"); address gatewayAddress = vm.envAddress("GATEWAY_PROXY_CONTRACT"); - payable(gatewayAddress).safeNativeTransfer(initialDeposit); + IGateway(address(gatewayAddress)).depositEther{value: initialDeposit}(); vm.stopBroadcast(); } From 53b6ae7d1d856818a2b4571acf87f55e8f0a1b4f Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 19 Dec 2024 01:36:33 +0200 Subject: [PATCH 15/21] update bindings --- relayer/contracts/gateway.go | 189 ++++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 1 deletion(-) diff --git a/relayer/contracts/gateway.go b/relayer/contracts/gateway.go index 1a4eba075a..a81d5266ed 100644 --- a/relayer/contracts/gateway.go +++ b/relayer/contracts/gateway.go @@ -91,7 +91,7 @@ type VerificationProof struct { // GatewayMetaData contains all meta data concerning the Gateway contract. var GatewayMetaData = &bind.MetaData{ - ABI: "[{\"type\":\"function\",\"name\":\"agentOf\",\"inputs\":[{\"name\":\"agentID\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"channelNoncesOf\",\"inputs\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"internalType\":\"ChannelID\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"channelOperatingModeOf\",\"inputs\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"internalType\":\"ChannelID\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint8\",\"internalType\":\"enumOperatingMode\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"implementation\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isTokenRegistered\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"operatingMode\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint8\",\"internalType\":\"enumOperatingMode\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pricingParameters\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"UD60x18\"},{\"name\":\"\",\"type\":\"uint128\",\"internalType\":\"uint128\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"queryForeignTokenID\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"quoteRegisterTokenFee\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"quoteSendTokenFee\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destinationChain\",\"type\":\"uint32\",\"internalType\":\"ParaID\"},{\"name\":\"destinationFee\",\"type\":\"uint128\",\"internalType\":\"uint128\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"registerToken\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"sendToken\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destinationChain\",\"type\":\"uint32\",\"internalType\":\"ParaID\"},{\"name\":\"destinationAddress\",\"type\":\"tuple\",\"internalType\":\"structMultiAddress\",\"components\":[{\"name\":\"kind\",\"type\":\"uint8\",\"internalType\":\"enumKind\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"name\":\"destinationFee\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"amount\",\"type\":\"uint128\",\"internalType\":\"uint128\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"submitV1\",\"inputs\":[{\"name\":\"message\",\"type\":\"tuple\",\"internalType\":\"structInboundMessage\",\"components\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"internalType\":\"ChannelID\"},{\"name\":\"nonce\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"command\",\"type\":\"uint8\",\"internalType\":\"enumCommand\"},{\"name\":\"params\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"maxDispatchGas\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"maxFeePerGas\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"reward\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"id\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"leafProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"headerProof\",\"type\":\"tuple\",\"internalType\":\"structVerification.Proof\",\"components\":[{\"name\":\"header\",\"type\":\"tuple\",\"internalType\":\"structVerification.ParachainHeader\",\"components\":[{\"name\":\"parentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"number\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"stateRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"extrinsicsRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"digestItems\",\"type\":\"tuple[]\",\"internalType\":\"structVerification.DigestItem[]\",\"components\":[{\"name\":\"kind\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"consensusEngineID\",\"type\":\"bytes4\",\"internalType\":\"bytes4\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]},{\"name\":\"headProof\",\"type\":\"tuple\",\"internalType\":\"structVerification.HeadProof\",\"components\":[{\"name\":\"pos\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"width\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}]},{\"name\":\"leafPartial\",\"type\":\"tuple\",\"internalType\":\"structVerification.MMRLeafPartial\",\"components\":[{\"name\":\"version\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"parentNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"parentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"leafProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"leafProofOrder\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"AgentCreated\",\"inputs\":[{\"name\":\"agentID\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"agent\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"AgentFundsWithdrawn\",\"inputs\":[{\"name\":\"agentID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"recipient\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ChannelCreated\",\"inputs\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"ChannelID\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ChannelUpdated\",\"inputs\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"ChannelID\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ForeignTokenRegistered\",\"inputs\":[{\"name\":\"tokenID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"token\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"InboundMessageDispatched\",\"inputs\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"ChannelID\"},{\"name\":\"nonce\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"},{\"name\":\"messageID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"success\",\"type\":\"bool\",\"indexed\":false,\"internalType\":\"bool\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OperatingModeChanged\",\"inputs\":[{\"name\":\"mode\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"enumOperatingMode\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OutboundMessageAccepted\",\"inputs\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"ChannelID\"},{\"name\":\"nonce\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"},{\"name\":\"messageID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"payload\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"PricingParametersChanged\",\"inputs\":[],\"anonymous\":false},{\"type\":\"event\",\"name\":\"TokenRegistrationSent\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"TokenSent\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"sender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"destinationChain\",\"type\":\"uint32\",\"indexed\":true,\"internalType\":\"ParaID\"},{\"name\":\"destinationAddress\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"structMultiAddress\",\"components\":[{\"name\":\"kind\",\"type\":\"uint8\",\"internalType\":\"enumKind\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"name\":\"amount\",\"type\":\"uint128\",\"indexed\":false,\"internalType\":\"uint128\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"TokenTransferFeesChanged\",\"inputs\":[],\"anonymous\":false}]", + ABI: "[{\"type\":\"function\",\"name\":\"agentOf\",\"inputs\":[{\"name\":\"agentID\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"channelNoncesOf\",\"inputs\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"internalType\":\"ChannelID\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"channelOperatingModeOf\",\"inputs\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"internalType\":\"ChannelID\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint8\",\"internalType\":\"enumOperatingMode\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"depositEther\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"implementation\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isTokenRegistered\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"operatingMode\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint8\",\"internalType\":\"enumOperatingMode\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"pricingParameters\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"UD60x18\"},{\"name\":\"\",\"type\":\"uint128\",\"internalType\":\"uint128\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"queryForeignTokenID\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"quoteRegisterTokenFee\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"quoteSendTokenFee\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destinationChain\",\"type\":\"uint32\",\"internalType\":\"ParaID\"},{\"name\":\"destinationFee\",\"type\":\"uint128\",\"internalType\":\"uint128\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"registerToken\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"sendToken\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"destinationChain\",\"type\":\"uint32\",\"internalType\":\"ParaID\"},{\"name\":\"destinationAddress\",\"type\":\"tuple\",\"internalType\":\"structMultiAddress\",\"components\":[{\"name\":\"kind\",\"type\":\"uint8\",\"internalType\":\"enumKind\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"name\":\"destinationFee\",\"type\":\"uint128\",\"internalType\":\"uint128\"},{\"name\":\"amount\",\"type\":\"uint128\",\"internalType\":\"uint128\"}],\"outputs\":[],\"stateMutability\":\"payable\"},{\"type\":\"function\",\"name\":\"submitV1\",\"inputs\":[{\"name\":\"message\",\"type\":\"tuple\",\"internalType\":\"structInboundMessage\",\"components\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"internalType\":\"ChannelID\"},{\"name\":\"nonce\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"command\",\"type\":\"uint8\",\"internalType\":\"enumCommand\"},{\"name\":\"params\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"maxDispatchGas\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"maxFeePerGas\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"reward\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"id\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"leafProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"headerProof\",\"type\":\"tuple\",\"internalType\":\"structVerification.Proof\",\"components\":[{\"name\":\"header\",\"type\":\"tuple\",\"internalType\":\"structVerification.ParachainHeader\",\"components\":[{\"name\":\"parentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"number\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"stateRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"extrinsicsRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"digestItems\",\"type\":\"tuple[]\",\"internalType\":\"structVerification.DigestItem[]\",\"components\":[{\"name\":\"kind\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"consensusEngineID\",\"type\":\"bytes4\",\"internalType\":\"bytes4\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]}]},{\"name\":\"headProof\",\"type\":\"tuple\",\"internalType\":\"structVerification.HeadProof\",\"components\":[{\"name\":\"pos\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"width\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"proof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"}]},{\"name\":\"leafPartial\",\"type\":\"tuple\",\"internalType\":\"structVerification.MMRLeafPartial\",\"components\":[{\"name\":\"version\",\"type\":\"uint8\",\"internalType\":\"uint8\"},{\"name\":\"parentNumber\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"parentHash\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"nextAuthoritySetID\",\"type\":\"uint64\",\"internalType\":\"uint64\"},{\"name\":\"nextAuthoritySetLen\",\"type\":\"uint32\",\"internalType\":\"uint32\"},{\"name\":\"nextAuthoritySetRoot\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}]},{\"name\":\"leafProof\",\"type\":\"bytes32[]\",\"internalType\":\"bytes32[]\"},{\"name\":\"leafProofOrder\",\"type\":\"uint256\",\"internalType\":\"uint256\"}]}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"version\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint64\",\"internalType\":\"uint64\"}],\"stateMutability\":\"view\"},{\"type\":\"event\",\"name\":\"AgentCreated\",\"inputs\":[{\"name\":\"agentID\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"},{\"name\":\"agent\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"AgentFundsWithdrawn\",\"inputs\":[{\"name\":\"agentID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"recipient\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ChannelCreated\",\"inputs\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"ChannelID\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ChannelUpdated\",\"inputs\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"ChannelID\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"EtherDeposited\",\"inputs\":[{\"name\":\"who\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ForeignTokenRegistered\",\"inputs\":[{\"name\":\"tokenID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"token\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"InboundMessageDispatched\",\"inputs\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"ChannelID\"},{\"name\":\"nonce\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"},{\"name\":\"messageID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"success\",\"type\":\"bool\",\"indexed\":false,\"internalType\":\"bool\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OperatingModeChanged\",\"inputs\":[{\"name\":\"mode\",\"type\":\"uint8\",\"indexed\":false,\"internalType\":\"enumOperatingMode\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"OutboundMessageAccepted\",\"inputs\":[{\"name\":\"channelID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"ChannelID\"},{\"name\":\"nonce\",\"type\":\"uint64\",\"indexed\":false,\"internalType\":\"uint64\"},{\"name\":\"messageID\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"payload\",\"type\":\"bytes\",\"indexed\":false,\"internalType\":\"bytes\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"PricingParametersChanged\",\"inputs\":[],\"anonymous\":false},{\"type\":\"event\",\"name\":\"TokenRegistrationSent\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"TokenSent\",\"inputs\":[{\"name\":\"token\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"sender\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"destinationChain\",\"type\":\"uint32\",\"indexed\":true,\"internalType\":\"ParaID\"},{\"name\":\"destinationAddress\",\"type\":\"tuple\",\"indexed\":false,\"internalType\":\"structMultiAddress\",\"components\":[{\"name\":\"kind\",\"type\":\"uint8\",\"internalType\":\"enumKind\"},{\"name\":\"data\",\"type\":\"bytes\",\"internalType\":\"bytes\"}]},{\"name\":\"amount\",\"type\":\"uint128\",\"indexed\":false,\"internalType\":\"uint128\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"TokenTransferFeesChanged\",\"inputs\":[],\"anonymous\":false}]", } // GatewayABI is the input ABI used to generate the binding from. @@ -552,6 +552,58 @@ func (_Gateway *GatewayCallerSession) QuoteSendTokenFee(token common.Address, de return _Gateway.Contract.QuoteSendTokenFee(&_Gateway.CallOpts, token, destinationChain, destinationFee) } +// Version is a free data retrieval call binding the contract method 0x54fd4d50. +// +// Solidity: function version() view returns(uint64) +func (_Gateway *GatewayCaller) Version(opts *bind.CallOpts) (uint64, error) { + var out []interface{} + err := _Gateway.contract.Call(opts, &out, "version") + + if err != nil { + return *new(uint64), err + } + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + + return out0, err + +} + +// Version is a free data retrieval call binding the contract method 0x54fd4d50. +// +// Solidity: function version() view returns(uint64) +func (_Gateway *GatewaySession) Version() (uint64, error) { + return _Gateway.Contract.Version(&_Gateway.CallOpts) +} + +// Version is a free data retrieval call binding the contract method 0x54fd4d50. +// +// Solidity: function version() view returns(uint64) +func (_Gateway *GatewayCallerSession) Version() (uint64, error) { + return _Gateway.Contract.Version(&_Gateway.CallOpts) +} + +// DepositEther is a paid mutator transaction binding the contract method 0x98ea5fca. +// +// Solidity: function depositEther() payable returns() +func (_Gateway *GatewayTransactor) DepositEther(opts *bind.TransactOpts) (*types.Transaction, error) { + return _Gateway.contract.Transact(opts, "depositEther") +} + +// DepositEther is a paid mutator transaction binding the contract method 0x98ea5fca. +// +// Solidity: function depositEther() payable returns() +func (_Gateway *GatewaySession) DepositEther() (*types.Transaction, error) { + return _Gateway.Contract.DepositEther(&_Gateway.TransactOpts) +} + +// DepositEther is a paid mutator transaction binding the contract method 0x98ea5fca. +// +// Solidity: function depositEther() payable returns() +func (_Gateway *GatewayTransactorSession) DepositEther() (*types.Transaction, error) { + return _Gateway.Contract.DepositEther(&_Gateway.TransactOpts) +} + // RegisterToken is a paid mutator transaction binding the contract method 0x09824a80. // // Solidity: function registerToken(address token) payable returns() @@ -1192,6 +1244,141 @@ func (_Gateway *GatewayFilterer) ParseChannelUpdated(log types.Log) (*GatewayCha return event, nil } +// GatewayEtherDepositedIterator is returned from FilterEtherDeposited and is used to iterate over the raw logs and unpacked data for EtherDeposited events raised by the Gateway contract. +type GatewayEtherDepositedIterator struct { + Event *GatewayEtherDeposited // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *GatewayEtherDepositedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(GatewayEtherDeposited) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(GatewayEtherDeposited) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *GatewayEtherDepositedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *GatewayEtherDepositedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// GatewayEtherDeposited represents a EtherDeposited event raised by the Gateway contract. +type GatewayEtherDeposited struct { + Who common.Address + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterEtherDeposited is a free log retrieval operation binding the contract event 0x939e51ac2fd009b158d6344f7e68a83d8d18d9b0cc88cf514aac6aaa9cad2a18. +// +// Solidity: event EtherDeposited(address who, uint256 amount) +func (_Gateway *GatewayFilterer) FilterEtherDeposited(opts *bind.FilterOpts) (*GatewayEtherDepositedIterator, error) { + + logs, sub, err := _Gateway.contract.FilterLogs(opts, "EtherDeposited") + if err != nil { + return nil, err + } + return &GatewayEtherDepositedIterator{contract: _Gateway.contract, event: "EtherDeposited", logs: logs, sub: sub}, nil +} + +// WatchEtherDeposited is a free log subscription operation binding the contract event 0x939e51ac2fd009b158d6344f7e68a83d8d18d9b0cc88cf514aac6aaa9cad2a18. +// +// Solidity: event EtherDeposited(address who, uint256 amount) +func (_Gateway *GatewayFilterer) WatchEtherDeposited(opts *bind.WatchOpts, sink chan<- *GatewayEtherDeposited) (event.Subscription, error) { + + logs, sub, err := _Gateway.contract.WatchLogs(opts, "EtherDeposited") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(GatewayEtherDeposited) + if err := _Gateway.contract.UnpackLog(event, "EtherDeposited", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseEtherDeposited is a log parse operation binding the contract event 0x939e51ac2fd009b158d6344f7e68a83d8d18d9b0cc88cf514aac6aaa9cad2a18. +// +// Solidity: event EtherDeposited(address who, uint256 amount) +func (_Gateway *GatewayFilterer) ParseEtherDeposited(log types.Log) (*GatewayEtherDeposited, error) { + event := new(GatewayEtherDeposited) + if err := _Gateway.contract.UnpackLog(event, "EtherDeposited", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + // GatewayForeignTokenRegisteredIterator is returned from FilterForeignTokenRegistered and is used to iterate over the raw logs and unpacked data for ForeignTokenRegistered events raised by the Gateway contract. type GatewayForeignTokenRegisteredIterator struct { Event *GatewayForeignTokenRegistered // Event containing the contract specifics and raw log From 10b4343ffde4dbf61b7c4642396158f0f536acf4 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 19 Dec 2024 03:50:15 +0200 Subject: [PATCH 16/21] e2e tests --- smoketest/run-tests.sh | 4 + smoketest/tests/send_native_eth.rs | 106 +++++++++++++++ smoketest/tests/transfer_native_eth.rs | 127 ++++++++++++++++++ .../test/scripts/configure-substrate.sh | 8 ++ 4 files changed, 245 insertions(+) create mode 100644 smoketest/tests/send_native_eth.rs create mode 100644 smoketest/tests/transfer_native_eth.rs diff --git a/smoketest/run-tests.sh b/smoketest/run-tests.sh index 58158ade7f..5a905a06a3 100755 --- a/smoketest/run-tests.sh +++ b/smoketest/run-tests.sh @@ -5,6 +5,10 @@ set -xe cargo test --no-run tests=( + # Native ETH + send_native_eth + transfer_native_eth + # ERC20 Tests register_token send_token diff --git a/smoketest/tests/send_native_eth.rs b/smoketest/tests/send_native_eth.rs new file mode 100644 index 0000000000..37271af934 --- /dev/null +++ b/smoketest/tests/send_native_eth.rs @@ -0,0 +1,106 @@ +use ethers::{ + core::types::{Address, U256}, + utils::parse_units, +}; +use futures::StreamExt; +use snowbridge_smoketest::{ + constants::*, + contracts::i_gateway, + helper::{initial_clients, print_event_log_for_unit_tests}, + parachains::assethub::api::{ + foreign_assets::events::Issued, + runtime_types::{ + staging_xcm::v3::multilocation::MultiLocation, + xcm::v3::{ + junction::{Junction::GlobalConsensus, NetworkId}, + junctions::Junctions::X1, + }, + }, + }, +}; +use subxt::{ext::codec::Encode, utils::AccountId32}; + +#[tokio::test] +async fn send_native_eth() { + let test_clients = initial_clients().await.expect("initialize clients"); + let ethereum_client = *(test_clients.ethereum_signed_client.clone()); + let assethub = *(test_clients.asset_hub_client.clone()); + + let gateway_addr: Address = (*GATEWAY_PROXY_CONTRACT).into(); + let gateway = i_gateway::IGateway::new(gateway_addr, ethereum_client.clone()); + + let eth_address: Address = [0; 20].into(); + + let destination_fee = 0; + let fee = gateway + .quote_send_token_fee(eth_address, ASSET_HUB_PARA_ID, destination_fee) + .call() + .await + .unwrap(); + + let value = parse_units("1", "ether").unwrap(); + // Lock tokens into vault + let amount: u128 = U256::from(value).low_u128(); + let receipt = gateway + .send_token( + eth_address, + ASSET_HUB_PARA_ID, + i_gateway::MultiAddress { kind: 1, data: (*SUBSTRATE_RECEIVER).into() }, + destination_fee, + amount, + ) + .value(fee + amount) + .send() + .await + .unwrap() + .await + .unwrap() + .unwrap(); + + println!( + "receipt transaction hash: {:#?}, transaction block: {:#?}", + hex::encode(receipt.transaction_hash), + receipt.block_number + ); + + // Log for OutboundMessageAccepted + let outbound_message_accepted_log = receipt.logs.last().unwrap(); + + // print log for unit tests + print_event_log_for_unit_tests(outbound_message_accepted_log); + + assert_eq!(receipt.status.unwrap().as_u64(), 1u64); + + let wait_for_blocks = (*WAIT_PERIOD) as usize; + let mut blocks = assethub + .blocks() + .subscribe_finalized() + .await + .expect("block subscription") + .take(wait_for_blocks); + + let expected_asset_id: MultiLocation = MultiLocation { + parents: 2, + interior: X1(GlobalConsensus(NetworkId::Ethereum { chain_id: ETHEREUM_CHAIN_ID })), + }; + let expected_owner: AccountId32 = (*SUBSTRATE_RECEIVER).into(); + + let mut issued_event_found = false; + while let Some(Ok(block)) = blocks.next().await { + println!("Polling assethub block {} for issued event.", block.number()); + + let events = block.events().await.unwrap(); + for issued in events.find::() { + println!("Created event found in assethub block {}.", block.number()); + let issued = issued.unwrap(); + assert_eq!(issued.asset_id.encode(), expected_asset_id.encode()); + assert_eq!(issued.owner, expected_owner); + assert_eq!(issued.amount, amount); + issued_event_found = true; + } + if issued_event_found { + break + } + } + assert!(issued_event_found) +} diff --git a/smoketest/tests/transfer_native_eth.rs b/smoketest/tests/transfer_native_eth.rs new file mode 100644 index 0000000000..ebc6f0c824 --- /dev/null +++ b/smoketest/tests/transfer_native_eth.rs @@ -0,0 +1,127 @@ +use assethub::api::polkadot_xcm::calls::TransactionApi; +use ethers::{ + prelude::Middleware, + providers::{Provider, Ws}, + types::Address, +}; +use futures::StreamExt; +use snowbridge_smoketest::{ + constants::*, + contracts::i_gateway::{IGateway, InboundMessageDispatchedFilter}, + helper::AssetHubConfig, + parachains::assethub::{ + self, + api::{ + polkadot_xcm::events::Sent, + runtime_types::{ + staging_xcm::v3::multilocation::MultiLocation, + xcm::{ + v3::{ + junction::{Junction, NetworkId}, + junctions::Junctions, + multiasset::{AssetId, Fungibility, MultiAsset, MultiAssets}, + }, + VersionedAssets, VersionedLocation, + }, + }, + }, + }, +}; +use std::{str::FromStr, sync::Arc, time::Duration}; +use subxt::OnlineClient; +use subxt_signer::{sr25519, SecretUri}; + +#[tokio::test] +async fn transfer_native_eth() { + let ethereum_provider = Provider::::connect((*ETHEREUM_API).to_string()) + .await + .unwrap() + .interval(Duration::from_millis(10u64)); + + let ethereum_client = Arc::new(ethereum_provider); + + let gateway_addr: Address = (*GATEWAY_PROXY_CONTRACT).into(); + let gateway = IGateway::new(gateway_addr, ethereum_client.clone()); + + let assethub: OnlineClient = + OnlineClient::from_url((*ASSET_HUB_WS_URL).to_string()).await.unwrap(); + + let amount: u128 = 1_000_000_000; + let assets = VersionedAssets::V3(MultiAssets(vec![MultiAsset { + id: AssetId::Concrete(MultiLocation { + parents: 2, + interior: Junctions::X1(Junction::GlobalConsensus(NetworkId::Ethereum { + chain_id: ETHEREUM_CHAIN_ID, + })), + }), + fun: Fungibility::Fungible(amount), + }])); + + let destination = VersionedLocation::V3(MultiLocation { + parents: 2, + interior: Junctions::X1(Junction::GlobalConsensus(NetworkId::Ethereum { + chain_id: ETHEREUM_CHAIN_ID, + })), + }); + + let beneficiary = VersionedLocation::V3(MultiLocation { + parents: 0, + interior: Junctions::X1(Junction::AccountKey20 { + network: None, + key: (*ETHEREUM_RECEIVER).into(), + }), + }); + + let suri = SecretUri::from_str(&SUBSTRATE_KEY).expect("Parse SURI"); + + let signer = sr25519::Keypair::from_uri(&suri).expect("valid keypair"); + + let token_transfer_call = + TransactionApi.reserve_transfer_assets(destination, beneficiary, assets, 0); + + let events = assethub + .tx() + .sign_and_submit_then_watch_default(&token_transfer_call, &signer) + .await + .expect("call success") + .wait_for_finalized_success() + .await + .expect("sucessful call"); + + let message_id = events + .find_first::() + .expect("xcm sent") + .expect("xcm sent found") + .message_id; + + let receiver: Address = (*ETHEREUM_RECEIVER).into(); + let balance_before = ethereum_client.get_balance(receiver, None).await.expect("fetch balance"); + + let wait_for_blocks = 500; + let mut stream = ethereum_client.subscribe_blocks().await.unwrap().take(wait_for_blocks); + + let mut transfer_event_found = false; + while let Some(block) = stream.next().await { + println!("Polling ethereum block {:?} for transfer event", block.number.unwrap()); + if let Ok(transfers) = gateway + .event::() + .at_block_hash(block.hash.unwrap()) + .query() + .await + { + for transfer in transfers { + if transfer.message_id.eq(&message_id) { + println!("Transfer event found at ethereum block {:?}", block.number.unwrap()); + assert!(transfer.success, "delivered successfully"); + transfer_event_found = true; + } + } + } + if transfer_event_found { + break + } + } + assert!(transfer_event_found); + let balance_after = ethereum_client.get_balance(receiver, None).await.expect("fetch balance"); + assert_eq!(balance_before, balance_after + amount) +} diff --git a/web/packages/test/scripts/configure-substrate.sh b/web/packages/test/scripts/configure-substrate.sh index 582006fe8d..25641e999b 100755 --- a/web/packages/test/scripts/configure-substrate.sh +++ b/web/packages/test/scripts/configure-substrate.sh @@ -83,6 +83,13 @@ config_xcm_version() { send_governance_transact_from_relaychain $ASSET_HUB_PARAID "$call" } +register_native_eth() { + # Registers Eth and makes it sufficient + # https://polkadot.js.org/apps/?rpc=ws://127.0.0.1:12144#/extrinsics/decode/0x3501020109079edaa80200ce796ae65569a670d0c1cc1ac12515a3ce21b5fbf729d63d7b289baad070139d0104 + local call="0x3501020109079edaa80200ce796ae65569a670d0c1cc1ac12515a3ce21b5fbf729d63d7b289baad070139d0104" + send_governance_transact_from_relaychain $ASSET_HUB_PARAID "$call" +} + configure_substrate() { set_gateway fund_accounts @@ -90,6 +97,7 @@ configure_substrate() { config_xcm_version wait_beacon_chain_ready config_beacon_checkpoint + register_native_eth } if [ -z "${from_start_services:-}" ]; then From 89b9e09a525dda85966169a175a85efca13c152e Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 19 Dec 2024 03:52:27 +0200 Subject: [PATCH 17/21] fix assert --- smoketest/tests/transfer_native_eth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/smoketest/tests/transfer_native_eth.rs b/smoketest/tests/transfer_native_eth.rs index ebc6f0c824..871f95a481 100644 --- a/smoketest/tests/transfer_native_eth.rs +++ b/smoketest/tests/transfer_native_eth.rs @@ -123,5 +123,5 @@ async fn transfer_native_eth() { } assert!(transfer_event_found); let balance_after = ethereum_client.get_balance(receiver, None).await.expect("fetch balance"); - assert_eq!(balance_before, balance_after + amount) + assert_eq!(balance_before + amount, balance_after) } From 889952761dee50c5312bfaea96e26c684533476d Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Thu, 19 Dec 2024 11:55:49 +0200 Subject: [PATCH 18/21] duplicated error removal --- contracts/src/Assets.sol | 3 +-- contracts/src/Gateway.sol | 1 - contracts/test/Gateway.t.sol | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/contracts/src/Assets.sol b/contracts/src/Assets.sol index d232093735..94115813a3 100644 --- a/contracts/src/Assets.sol +++ b/contracts/src/Assets.sol @@ -36,7 +36,6 @@ library Assets { error TokenAlreadyRegistered(); error TokenMintFailed(); error TokenTransferFailed(); - error TokenAmountTooLow(); function isTokenRegistered(address token) external view returns (bool) { return AssetsStorage.layout().tokenRegistry[token].isRegistered; @@ -156,7 +155,7 @@ library Assets { } else { // Native ETH if (msg.value < amount) { - revert TokenAmountTooLow(); + revert InvalidAmount(); } payable($.assetHubAgent).safeNativeTransfer(amount); ticket.value = amount; diff --git a/contracts/src/Gateway.sol b/contracts/src/Gateway.sol index 641de76708..3eb858e806 100644 --- a/contracts/src/Gateway.sol +++ b/contracts/src/Gateway.sol @@ -99,7 +99,6 @@ contract Gateway is IGateway, IInitializable, IUpgradable { error InvalidAgentExecutionPayload(); error InvalidConstructorParams(); error TokenNotRegistered(); - error TokenAmountTooLow(); // Message handlers can only be dispatched by the gateway itself modifier onlySelf() { diff --git a/contracts/test/Gateway.t.sol b/contracts/test/Gateway.t.sol index 78a354c13a..103276bcda 100644 --- a/contracts/test/Gateway.t.sol +++ b/contracts/test/Gateway.t.sol @@ -544,7 +544,7 @@ contract GatewayTest is Test { uint128 fee = uint128(IGateway(address(gateway)).quoteSendTokenFee(address(0), paraID, amount)); - vm.expectRevert(Gateway.TokenAmountTooLow.selector); + vm.expectRevert(Assets.InvalidAmount.selector); hoax(user, amount + fee); IGateway(address(gateway)).sendToken{value: amount - 1}(address(0), paraID, recipientAddress32, 1, amount); } From 6b984bf05770d607c3eecad5125dffa17e5461f2 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Fri, 20 Dec 2024 13:59:52 +0200 Subject: [PATCH 19/21] fix test fixture generator --- relayer/cmd/generate_beacon_data.go | 21 +- relayer/templates/beacon-fixtures.mustache | 270 ++++++++++++++++++++ relayer/templates/inbound-fixtures.mustache | 95 +++++++ 3 files changed, 376 insertions(+), 10 deletions(-) create mode 100644 relayer/templates/beacon-fixtures.mustache create mode 100644 relayer/templates/inbound-fixtures.mustache diff --git a/relayer/cmd/generate_beacon_data.go b/relayer/cmd/generate_beacon_data.go index 06202d84e5..4e28887873 100644 --- a/relayer/cmd/generate_beacon_data.go +++ b/relayer/cmd/generate_beacon_data.go @@ -83,7 +83,7 @@ func generateInboundFixtureCmd() *cobra.Command { } cmd.Flags().String("beacon-config", "/tmp/snowbridge/beacon-relay.json", "Path to the beacon relay config") - cmd.Flags().String("execution-config", "/tmp/snowbridge/execution-relay-asset-hub.json", "Path to the beacon relay config") + cmd.Flags().String("execution-config", "/tmp/snowbridge/execution-relay-asset-hub-0.json", "Path to the beacon relay config") cmd.Flags().Uint32("nonce", 1, "Nonce of the inbound message") cmd.Flags().String("test_case", "register_token", "Inbound test case") return cmd @@ -206,7 +206,7 @@ func generateBeaconTestFixture(cmd *cobra.Command, _ []string) error { client := api.NewBeaconClient(conf.Source.Beacon.Endpoint, conf.Source.Beacon.StateEndpoint) s := syncer.New(client, &store, p) - viper.SetConfigFile("/tmp/snowbridge/execution-relay-asset-hub.json") + viper.SetConfigFile("/tmp/snowbridge/execution-relay-asset-hub-0.json") if err = viper.ReadInConfig(); err != nil { return err @@ -390,13 +390,16 @@ func generateBeaconTestFixture(cmd *cobra.Command, _ []string) error { log.WithFields(log.Fields{ "location": pathToInboundQueueFixtureData, }).Info("writing result file") - err = writeRawDataFile(fmt.Sprintf("%s", pathToInboundQueueFixtureData), rendered) + err = writeRawDataFile(pathToInboundQueueFixtureData, rendered) if err != nil { return err } // Generate test fixture in next period (require waiting a long time) waitUntilNextPeriod, err := cmd.Flags().GetBool("wait_until_next_period") + if err != nil { + return fmt.Errorf("could not parse flag wait_until_next_period: %w", err) + } if waitUntilNextPeriod { log.Info("waiting finalized_update in next period (5 hours later), be patient and wait...") for { @@ -537,11 +540,6 @@ func generateExecutionUpdate(cmd *cobra.Command, _ []string) error { return nil } -func generateInboundTestFixture(ctx context.Context, beaconEndpoint string) error { - - return nil -} - func getEthereumEvent(ctx context.Context, gatewayContract *contracts.Gateway, channelID executionConf.ChannelID, nonce uint32) (*contracts.GatewayOutboundMessageAccepted, error) { maxBlockNumber := uint64(10000) @@ -613,6 +611,9 @@ func getBeaconBlockContainingExecutionHeader(s syncer.Syncer, messageBlockNumber log.WithField("beaconHeaderSlot", beaconHeaderSlot).Info("getting beacon block by slot") beaconBlock, blockNumber, err = getBeaconBlockAndBlockNumber(s, beaconHeaderSlot) + if err != nil { + return api.BeaconBlockResponse{}, 0, err + } } return beaconBlock, blockNumber, nil @@ -813,7 +814,7 @@ func generateInboundFixture(cmd *cobra.Command, _ []string) error { if err != nil { return err } - if testCase != "register_token" && testCase != "send_token" && testCase != "send_token_to_penpal" { + if testCase != "register_token" && testCase != "send_token" && testCase != "send_token_to_penpal" && testCase != "send_native_eth" { return fmt.Errorf("invalid test case: %s", testCase) } @@ -830,7 +831,7 @@ func generateInboundFixture(cmd *cobra.Command, _ []string) error { } pathToInboundQueueFixtureTestCaseData := fmt.Sprintf(pathToInboundQueueFixtureTestCaseData, testCase) - err = writeRawDataFile(fmt.Sprintf("%s", pathToInboundQueueFixtureTestCaseData), rendered) + err = writeRawDataFile(pathToInboundQueueFixtureTestCaseData, rendered) if err != nil { return err } diff --git a/relayer/templates/beacon-fixtures.mustache b/relayer/templates/beacon-fixtures.mustache new file mode 100644 index 0000000000..5942be0563 --- /dev/null +++ b/relayer/templates/beacon-fixtures.mustache @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +// Generated, do not edit! +// See README.md for instructions to generate +#![cfg_attr(not(feature = "std"), no_std)] + +use hex_literal::hex; +use snowbridge_beacon_primitives::{ + types::deneb, AncestryProof, BeaconHeader, ExecutionProof, NextSyncCommitteeUpdate, + SyncAggregate, SyncCommittee, VersionedExecutionPayloadHeader, +}; +use snowbridge_core::inbound::{InboundQueueFixture, Log, Message, Proof}; +use sp_core::U256; +use sp_std::{boxed::Box, vec}; + +const SC_SIZE: usize = 512; +const SC_BITS_SIZE: usize = 64; +type CheckpointUpdate = snowbridge_beacon_primitives::CheckpointUpdate; +type Update = snowbridge_beacon_primitives::Update; + + +pub fn make_checkpoint() -> Box { + Box::new(CheckpointUpdate { + header: BeaconHeader { + slot: {{CheckpointUpdate.Header.Slot}}, + proposer_index: {{CheckpointUpdate.Header.ProposerIndex}}, + parent_root: hex!("{{CheckpointUpdate.Header.ParentRoot}}").into(), + state_root: hex!("{{CheckpointUpdate.Header.StateRoot}}").into(), + body_root: hex!("{{CheckpointUpdate.Header.BodyRoot}}").into(), + }, + current_sync_committee: SyncCommittee { + pubkeys: [ + {{#CheckpointUpdate.CurrentSyncCommittee.Pubkeys}} + hex!("{{.}}").into(), + {{/CheckpointUpdate.CurrentSyncCommittee.Pubkeys}} + ], + aggregate_pubkey: hex!("{{CheckpointUpdate.CurrentSyncCommittee.AggregatePubkey}}").into(), + }, + current_sync_committee_branch: vec![ + {{#CheckpointUpdate.CurrentSyncCommitteeBranch}} + hex!("{{.}}").into(), + {{/CheckpointUpdate.CurrentSyncCommitteeBranch}} + ], + validators_root: hex!("{{CheckpointUpdate.ValidatorsRoot}}").into(), + block_roots_root: hex!("{{CheckpointUpdate.BlockRootsRoot}}").into(), + block_roots_branch: vec![ + {{#CheckpointUpdate.BlockRootsBranch}} + hex!("{{.}}").into(), + {{/CheckpointUpdate.BlockRootsBranch}} + ], + }) +} + +pub fn make_sync_committee_update() -> Box { + Box::new(Update { + attested_header: BeaconHeader { + slot: {{SyncCommitteeUpdate.AttestedHeader.Slot}}, + proposer_index: {{SyncCommitteeUpdate.AttestedHeader.ProposerIndex}}, + parent_root: hex!("{{SyncCommitteeUpdate.AttestedHeader.ParentRoot}}").into(), + state_root: hex!("{{SyncCommitteeUpdate.AttestedHeader.StateRoot}}").into(), + body_root: hex!("{{SyncCommitteeUpdate.AttestedHeader.BodyRoot}}").into(), + }, + sync_aggregate: SyncAggregate{ + sync_committee_bits: hex!("{{SyncCommitteeUpdate.SyncAggregate.SyncCommitteeBits}}"), + sync_committee_signature: hex!("{{SyncCommitteeUpdate.SyncAggregate.SyncCommitteeSignature}}").into(), + }, + signature_slot: {{SyncCommitteeUpdate.SignatureSlot}}, + next_sync_committee_update: Some(NextSyncCommitteeUpdate { + next_sync_committee: SyncCommittee { + pubkeys: [ + {{#SyncCommitteeUpdate.NextSyncCommitteeUpdate.NextSyncCommittee.Pubkeys}} + hex!("{{.}}").into(), + {{/SyncCommitteeUpdate.NextSyncCommitteeUpdate.NextSyncCommittee.Pubkeys}} + ], + aggregate_pubkey: hex!("{{SyncCommitteeUpdate.NextSyncCommitteeUpdate.NextSyncCommittee.AggregatePubkey}}").into(), + }, + next_sync_committee_branch: vec![ + {{#SyncCommitteeUpdate.NextSyncCommitteeUpdate.NextSyncCommitteeBranch}} + hex!("{{.}}").into(), + {{/SyncCommitteeUpdate.NextSyncCommitteeUpdate.NextSyncCommitteeBranch}} + ], + }), + finalized_header: BeaconHeader{ + slot: {{SyncCommitteeUpdate.FinalizedHeader.Slot}}, + proposer_index: {{SyncCommitteeUpdate.FinalizedHeader.ProposerIndex}}, + parent_root: hex!("{{SyncCommitteeUpdate.FinalizedHeader.ParentRoot}}").into(), + state_root: hex!("{{SyncCommitteeUpdate.FinalizedHeader.StateRoot}}").into(), + body_root: hex!("{{SyncCommitteeUpdate.FinalizedHeader.BodyRoot}}").into(), + }, + finality_branch: vec![ + {{#SyncCommitteeUpdate.FinalityBranch}} + hex!("{{.}}").into(), + {{/SyncCommitteeUpdate.FinalityBranch}} + ], + block_roots_root: hex!("{{SyncCommitteeUpdate.BlockRootsRoot}}").into(), + block_roots_branch: vec![ + {{#SyncCommitteeUpdate.BlockRootsBranch}} + hex!("{{.}}").into(), + {{/SyncCommitteeUpdate.BlockRootsBranch}} + ], + }) +} + +pub fn make_finalized_header_update() -> Box { + Box::new(Update { + attested_header: BeaconHeader { + slot: {{FinalizedHeaderUpdate.AttestedHeader.Slot}}, + proposer_index: {{FinalizedHeaderUpdate.AttestedHeader.ProposerIndex}}, + parent_root: hex!("{{FinalizedHeaderUpdate.AttestedHeader.ParentRoot}}").into(), + state_root: hex!("{{FinalizedHeaderUpdate.AttestedHeader.StateRoot}}").into(), + body_root: hex!("{{FinalizedHeaderUpdate.AttestedHeader.BodyRoot}}").into(), + }, + sync_aggregate: SyncAggregate{ + sync_committee_bits: hex!("{{FinalizedHeaderUpdate.SyncAggregate.SyncCommitteeBits}}"), + sync_committee_signature: hex!("{{FinalizedHeaderUpdate.SyncAggregate.SyncCommitteeSignature}}").into(), + }, + signature_slot: {{FinalizedHeaderUpdate.SignatureSlot}}, + next_sync_committee_update: None, + finalized_header: BeaconHeader { + slot: {{FinalizedHeaderUpdate.FinalizedHeader.Slot}}, + proposer_index: {{FinalizedHeaderUpdate.FinalizedHeader.ProposerIndex}}, + parent_root: hex!("{{FinalizedHeaderUpdate.FinalizedHeader.ParentRoot}}").into(), + state_root: hex!("{{FinalizedHeaderUpdate.FinalizedHeader.StateRoot}}").into(), + body_root: hex!("{{FinalizedHeaderUpdate.FinalizedHeader.BodyRoot}}").into(), + }, + finality_branch: vec![ + {{#FinalizedHeaderUpdate.FinalityBranch}} + hex!("{{.}}").into(), + {{/FinalizedHeaderUpdate.FinalityBranch}} + ], + block_roots_root: hex!("{{FinalizedHeaderUpdate.BlockRootsRoot}}").into(), + block_roots_branch: vec![ + {{#FinalizedHeaderUpdate.BlockRootsBranch}} + hex!("{{.}}").into(), + {{/FinalizedHeaderUpdate.BlockRootsBranch}} + ] + }) +} + +pub fn make_execution_proof() -> Box { + Box::new(ExecutionProof { + header: BeaconHeader { + slot: {{HeaderUpdate.Header.Slot}}, + proposer_index: {{HeaderUpdate.Header.ProposerIndex}}, + parent_root: hex!("{{HeaderUpdate.Header.ParentRoot}}").into(), + state_root: hex!("{{HeaderUpdate.Header.StateRoot}}").into(), + body_root: hex!("{{HeaderUpdate.Header.BodyRoot}}").into(), + }, + {{#HeaderUpdate.AncestryProof}} + ancestry_proof: Some(AncestryProof { + header_branch: vec![ + {{#HeaderUpdate.AncestryProof.HeaderBranch}} + hex!("{{.}}").into(), + {{/HeaderUpdate.AncestryProof.HeaderBranch}} + ], + finalized_block_root: hex!("{{HeaderUpdate.AncestryProof.FinalizedBlockRoot}}").into(), + }), + {{/HeaderUpdate.AncestryProof}} + {{^HeaderUpdate.AncestryProof}} + ancestry_proof: None, + {{/HeaderUpdate.AncestryProof}} + execution_header: VersionedExecutionPayloadHeader::Deneb(deneb::ExecutionPayloadHeader { + parent_hash: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.ParentHash}}").into(), + fee_recipient: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.FeeRecipient}}").into(), + state_root: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.StateRoot}}").into(), + receipts_root: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.ReceiptsRoot}}").into(), + logs_bloom: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.LogsBloom}}").into(), + prev_randao: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.PrevRandao}}").into(), + block_number: {{HeaderUpdate.ExecutionHeader.Deneb.BlockNumber}}, + gas_limit: {{HeaderUpdate.ExecutionHeader.Deneb.GasLimit}}, + gas_used: {{HeaderUpdate.ExecutionHeader.Deneb.GasUsed}}, + timestamp: {{HeaderUpdate.ExecutionHeader.Deneb.Timestamp}}, + extra_data: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.ExtraData}}").into(), + base_fee_per_gas: U256::from({{HeaderUpdate.ExecutionHeader.Deneb.BaseFeePerGas}}u64), + block_hash: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.BlockHash}}").into(), + transactions_root: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.TransactionsRoot}}").into(), + withdrawals_root: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.WithdrawalsRoot}}").into(), + blob_gas_used: {{HeaderUpdate.ExecutionHeader.Deneb.BlobGasUsed}}, + excess_blob_gas: {{HeaderUpdate.ExecutionHeader.Deneb.ExcessBlobGas}}, + }), + execution_branch: vec![ + {{#HeaderUpdate.ExecutionBranch}} + hex!("{{.}}").into(), + {{/HeaderUpdate.ExecutionBranch}} + ], + }) +} + +pub fn make_inbound_fixture() -> InboundQueueFixture { + InboundQueueFixture { + message: Message { + event_log: Log { + address: hex!("{{InboundMessage.EventLog.Address}}").into(), + topics: vec![ + {{#InboundMessage.EventLog.Topics}} + hex!("{{.}}").into(), + {{/InboundMessage.EventLog.Topics}} + ], + data: hex!("{{InboundMessage.EventLog.Data}}").into(), + }, + proof: Proof { + block_hash: hex!("{{InboundMessage.Proof.BlockHash}}").into(), + tx_index: {{InboundMessage.Proof.TxIndex}}, + receipt_proof: (vec![ + {{#InboundMessage.Proof.ReceiptProof.Keys}} + hex!("{{.}}").to_vec(), + {{/InboundMessage.Proof.ReceiptProof.Keys}} + ], vec![ + {{#InboundMessage.Proof.ReceiptProof.Values}} + hex!("{{.}}").to_vec(), + {{/InboundMessage.Proof.ReceiptProof.Values}} + ]), + execution_proof: ExecutionProof { + header: BeaconHeader { + slot: {{HeaderUpdate.Header.Slot}}, + proposer_index: {{HeaderUpdate.Header.ProposerIndex}}, + parent_root: hex!("{{HeaderUpdate.Header.ParentRoot}}").into(), + state_root: hex!("{{HeaderUpdate.Header.StateRoot}}").into(), + body_root: hex!("{{HeaderUpdate.Header.BodyRoot}}").into(), + }, + {{#HeaderUpdate.AncestryProof}} + ancestry_proof: Some(AncestryProof { + header_branch: vec![ + {{#HeaderUpdate.AncestryProof.HeaderBranch}} + hex!("{{.}}").into(), + {{/HeaderUpdate.AncestryProof.HeaderBranch}} + ], + finalized_block_root: hex!("{{HeaderUpdate.AncestryProof.FinalizedBlockRoot}}").into(), + }), + {{/HeaderUpdate.AncestryProof}} + {{^HeaderUpdate.AncestryProof}} + ancestry_proof: None, + {{/HeaderUpdate.AncestryProof}} + execution_header: VersionedExecutionPayloadHeader::Deneb(deneb::ExecutionPayloadHeader { + parent_hash: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.ParentHash}}").into(), + fee_recipient: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.FeeRecipient}}").into(), + state_root: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.StateRoot}}").into(), + receipts_root: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.ReceiptsRoot}}").into(), + logs_bloom: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.LogsBloom}}").into(), + prev_randao: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.PrevRandao}}").into(), + block_number: {{HeaderUpdate.ExecutionHeader.Deneb.BlockNumber}}, + gas_limit: {{HeaderUpdate.ExecutionHeader.Deneb.GasLimit}}, + gas_used: {{HeaderUpdate.ExecutionHeader.Deneb.GasUsed}}, + timestamp: {{HeaderUpdate.ExecutionHeader.Deneb.Timestamp}}, + extra_data: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.ExtraData}}").into(), + base_fee_per_gas: U256::from({{HeaderUpdate.ExecutionHeader.Deneb.BaseFeePerGas}}u64), + block_hash: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.BlockHash}}").into(), + transactions_root: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.TransactionsRoot}}").into(), + withdrawals_root: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.WithdrawalsRoot}}").into(), + blob_gas_used: {{HeaderUpdate.ExecutionHeader.Deneb.BlobGasUsed}}, + excess_blob_gas: {{HeaderUpdate.ExecutionHeader.Deneb.ExcessBlobGas}}, + }), + execution_branch: vec![ + {{#HeaderUpdate.ExecutionBranch}} + hex!("{{.}}").into(), + {{/HeaderUpdate.ExecutionBranch}} + ], + } + }, + }, + finalized_header: BeaconHeader { + slot: {{FinalizedHeaderUpdate.FinalizedHeader.Slot}}, + proposer_index: {{FinalizedHeaderUpdate.FinalizedHeader.ProposerIndex}}, + parent_root: hex!("{{FinalizedHeaderUpdate.FinalizedHeader.ParentRoot}}").into(), + state_root: hex!("{{FinalizedHeaderUpdate.FinalizedHeader.StateRoot}}").into(), + body_root: hex!("{{FinalizedHeaderUpdate.FinalizedHeader.BodyRoot}}").into(), + }, + block_roots_root: hex!("{{FinalizedHeaderUpdate.BlockRootsRoot}}").into(), + } +} diff --git a/relayer/templates/inbound-fixtures.mustache b/relayer/templates/inbound-fixtures.mustache new file mode 100644 index 0000000000..b35a263fda --- /dev/null +++ b/relayer/templates/inbound-fixtures.mustache @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2023 Snowfork +// Generated, do not edit! +// See ethereum client README.md for instructions to generate + +use hex_literal::hex; +use snowbridge_beacon_primitives::{ +types::deneb, AncestryProof, BeaconHeader, ExecutionProof, VersionedExecutionPayloadHeader, +}; +use snowbridge_core::inbound::{InboundQueueFixture, Log, Message, Proof}; +use sp_core::U256; +use sp_std::vec; + +pub fn make_{{TestCase}}_message() -> InboundQueueFixture { + InboundQueueFixture { + message: Message { + event_log: Log { + address: hex!("{{InboundMessage.EventLog.Address}}").into(), + topics: vec![ + {{#InboundMessage.EventLog.Topics}} + hex!("{{.}}").into(), + {{/InboundMessage.EventLog.Topics}} + ], + data: hex!("{{InboundMessage.EventLog.Data}}").into(), + }, + proof: Proof { + block_hash: hex!("{{InboundMessage.Proof.BlockHash}}").into(), + tx_index: {{InboundMessage.Proof.TxIndex}}, + receipt_proof: (vec![ + {{#InboundMessage.Proof.ReceiptProof.Keys}} + hex!("{{.}}").to_vec(), + {{/InboundMessage.Proof.ReceiptProof.Keys}} + ], vec![ + {{#InboundMessage.Proof.ReceiptProof.Values}} + hex!("{{.}}").to_vec(), + {{/InboundMessage.Proof.ReceiptProof.Values}} + ]), + execution_proof: ExecutionProof { + header: BeaconHeader { + slot: {{HeaderUpdate.Header.Slot}}, + proposer_index: {{HeaderUpdate.Header.ProposerIndex}}, + parent_root: hex!("{{HeaderUpdate.Header.ParentRoot}}").into(), + state_root: hex!("{{HeaderUpdate.Header.StateRoot}}").into(), + body_root: hex!("{{HeaderUpdate.Header.BodyRoot}}").into(), + }, + {{#HeaderUpdate.AncestryProof}} + ancestry_proof: Some(AncestryProof { + header_branch: vec![ + {{#HeaderUpdate.AncestryProof.HeaderBranch}} + hex!("{{.}}").into(), + {{/HeaderUpdate.AncestryProof.HeaderBranch}} + ], + finalized_block_root: hex!("{{HeaderUpdate.AncestryProof.FinalizedBlockRoot}}").into(), + }), + {{/HeaderUpdate.AncestryProof}} + {{^HeaderUpdate.AncestryProof}} + ancestry_proof: None, + {{/HeaderUpdate.AncestryProof}} + execution_header: VersionedExecutionPayloadHeader::Deneb(deneb::ExecutionPayloadHeader { + parent_hash: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.ParentHash}}").into(), + fee_recipient: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.FeeRecipient}}").into(), + state_root: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.StateRoot}}").into(), + receipts_root: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.ReceiptsRoot}}").into(), + logs_bloom: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.LogsBloom}}").into(), + prev_randao: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.PrevRandao}}").into(), + block_number: {{HeaderUpdate.ExecutionHeader.Deneb.BlockNumber}}, + gas_limit: {{HeaderUpdate.ExecutionHeader.Deneb.GasLimit}}, + gas_used: {{HeaderUpdate.ExecutionHeader.Deneb.GasUsed}}, + timestamp: {{HeaderUpdate.ExecutionHeader.Deneb.Timestamp}}, + extra_data: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.ExtraData}}").into(), + base_fee_per_gas: U256::from({{HeaderUpdate.ExecutionHeader.Deneb.BaseFeePerGas}}u64), + block_hash: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.BlockHash}}").into(), + transactions_root: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.TransactionsRoot}}").into(), + withdrawals_root: hex!("{{HeaderUpdate.ExecutionHeader.Deneb.WithdrawalsRoot}}").into(), + blob_gas_used: {{HeaderUpdate.ExecutionHeader.Deneb.BlobGasUsed}}, + excess_blob_gas: {{HeaderUpdate.ExecutionHeader.Deneb.ExcessBlobGas}}, + }), + execution_branch: vec![ + {{#HeaderUpdate.ExecutionBranch}} + hex!("{{.}}").into(), + {{/HeaderUpdate.ExecutionBranch}} + ], + } + }, + }, + finalized_header: BeaconHeader { + slot: {{FinalizedHeaderUpdate.FinalizedHeader.Slot}}, + proposer_index: {{FinalizedHeaderUpdate.FinalizedHeader.ProposerIndex}}, + parent_root: hex!("{{FinalizedHeaderUpdate.FinalizedHeader.ParentRoot}}").into(), + state_root: hex!("{{FinalizedHeaderUpdate.FinalizedHeader.StateRoot}}").into(), + body_root: hex!("{{FinalizedHeaderUpdate.FinalizedHeader.BodyRoot}}").into(), + }, + block_roots_root: hex!("{{FinalizedHeaderUpdate.BlockRootsRoot}}").into(), + } +} From ad9e7fbc28c123e960dd3bcfe659882fc0dc93ae Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Fri, 20 Dec 2024 20:00:46 +0200 Subject: [PATCH 20/21] template fixes --- relayer/cmd/generate_beacon_data.go | 4 ++-- relayer/templates/inbound-fixtures.mustache | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/relayer/cmd/generate_beacon_data.go b/relayer/cmd/generate_beacon_data.go index 4e28887873..7b9f11c010 100644 --- a/relayer/cmd/generate_beacon_data.go +++ b/relayer/cmd/generate_beacon_data.go @@ -105,9 +105,9 @@ type InboundFixture struct { const ( pathToBeaconTestFixtureFiles = "polkadot-sdk/bridges/snowbridge/pallets/ethereum-client/tests/fixtures" - pathToInboundQueueFixtureTemplate = "polkadot-sdk/bridges/snowbridge/templates/beacon-fixtures.mustache" + pathToInboundQueueFixtureTemplate = "relayer/templates/beacon-fixtures.mustache" pathToInboundQueueFixtureData = "polkadot-sdk/bridges/snowbridge/pallets/ethereum-client/fixtures/src/lib.rs" - pathToInboundQueueFixtureTestCaseTemplate = "polkadot-sdk/bridges/snowbridge/templates/inbound-fixtures.mustache" + pathToInboundQueueFixtureTestCaseTemplate = "relayer/templates/inbound-fixtures.mustache" pathToInboundQueueFixtureTestCaseData = "polkadot-sdk/bridges/snowbridge/pallets/inbound-queue/fixtures/src/%s.rs" ) diff --git a/relayer/templates/inbound-fixtures.mustache b/relayer/templates/inbound-fixtures.mustache index b35a263fda..84f6a7abfe 100644 --- a/relayer/templates/inbound-fixtures.mustache +++ b/relayer/templates/inbound-fixtures.mustache @@ -24,8 +24,6 @@ pub fn make_{{TestCase}}_message() -> InboundQueueFixture { data: hex!("{{InboundMessage.EventLog.Data}}").into(), }, proof: Proof { - block_hash: hex!("{{InboundMessage.Proof.BlockHash}}").into(), - tx_index: {{InboundMessage.Proof.TxIndex}}, receipt_proof: (vec![ {{#InboundMessage.Proof.ReceiptProof.Keys}} hex!("{{.}}").to_vec(), From b24e0c4df70826037439914e2f2cf38fbb34a9e6 Mon Sep 17 00:00:00 2001 From: Alistair Singh Date: Sat, 21 Dec 2024 22:07:30 +0200 Subject: [PATCH 21/21] contract address changes --- web/packages/api/src/environment.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/web/packages/api/src/environment.ts b/web/packages/api/src/environment.ts index ca8785f4e0..95ca9136df 100644 --- a/web/packages/api/src/environment.ts +++ b/web/packages/api/src/environment.ts @@ -65,7 +65,7 @@ export const SNOWBRIDGE_ENV: { [id: string]: SnowbridgeEnvironment } = { erc20tokensReceivable: [ { id: "WETH", - address: "0x87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d", + address: "0x774667629726ec1FaBEbCEc0D9139bD1C8f72a23", minimumTransferAmount: 15_000_000_000_000n, }, ], @@ -86,7 +86,7 @@ export const SNOWBRIDGE_ENV: { [id: string]: SnowbridgeEnvironment } = { erc20tokensReceivable: [ { id: "WETH", - address: "0x87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d", + address: "0x774667629726ec1FaBEbCEc0D9139bD1C8f72a23", minimumTransferAmount: 15_000_000_000_000n, }, ], @@ -107,7 +107,7 @@ export const SNOWBRIDGE_ENV: { [id: string]: SnowbridgeEnvironment } = { erc20tokensReceivable: [ { id: "WETH", - address: "0x87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d", + address: "0x774667629726ec1FaBEbCEc0D9139bD1C8f72a23", minimumTransferAmount: 1n, }, ], @@ -120,8 +120,8 @@ export const SNOWBRIDGE_ENV: { [id: string]: SnowbridgeEnvironment } = { ASSET_HUB_URL: "ws://127.0.0.1:12144", BRIDGE_HUB_URL: "ws://127.0.0.1:11144", PARACHAINS: ["ws://127.0.0.1:13144"], - GATEWAY_CONTRACT: "0xEDa338E4dC46038493b885327842fD3E301CaB39", - BEEFY_CONTRACT: "0x992B9df075935E522EC7950F37eC8557e86f6fdb", + GATEWAY_CONTRACT: "0x87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d", + BEEFY_CONTRACT: "0x2ffa5ecdbe006d30397c7636d3e015eee251369f", ASSET_HUB_PARAID: 1000, BRIDGE_HUB_PARAID: 1002, PRIMARY_GOVERNANCE_CHANNEL_ID: