From ae982448ca3bfa52d1dffc90e1150ad5387e42fe Mon Sep 17 00:00:00 2001 From: Asem-Abdelhady Date: Sun, 2 Mar 2025 23:20:28 +0200 Subject: [PATCH 1/4] test: roundtrip order between EVM and SUI --- src/oracles/wormhole/WormholeOracle.sol | 2 + test/TestCatalyst.t.sol | 61 +++++++++++++++++++ test/oracle/wormhole/callworm.t.sol | 5 ++ test/oracle/wormhole/receive.t.sol | 38 ++++++++++++ test/oracle/wormhole/submit.t.sol | 80 ++++++++++++++++++++++++- 5 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 test/oracle/wormhole/receive.t.sol diff --git a/src/oracles/wormhole/WormholeOracle.sol b/src/oracles/wormhole/WormholeOracle.sol index b4c6c2bd..93c8077c 100644 --- a/src/oracles/wormhole/WormholeOracle.sol +++ b/src/oracles/wormhole/WormholeOracle.sol @@ -142,6 +142,8 @@ contract WormholeOracle is BaseOracle, WormholeVerifier, Ownable { // Map remoteMessagingProtocolChainIdentifier to canonical chain id. This ensures we use canonical ids. uint256 remoteChainId = _chainIdentifierToBlockChainId[remoteMessagingProtocolChainIdentifier]; + if(remoteChainId == 0) revert ZeroValue(); + uint256 numPayloads = payloadHashes.length; for (uint256 i; i < numPayloads; ++i) { diff --git a/test/TestCatalyst.t.sol b/test/TestCatalyst.t.sol index e13cebec..a2d962fd 100644 --- a/test/TestCatalyst.t.sol +++ b/test/TestCatalyst.t.sol @@ -271,6 +271,67 @@ contract TestCatalyst is Test { vm.snapshotGasLastCall("finaliseSelf"); } + function test_receive_and_settle_with_real_order() external { + uint256 initialSwapperBalance = token.balanceOf(swapper); + vm.prank(swapper); + uint256 amount = 1e18 / 10; + uint256 tokenId = theCompact.deposit(address(token), alwaysOKAllocator, amount); + + address localOracle = address(wormholeOracle); + // These are the real addresses of fill_and_submit_output.move + bytes32 remoteOracle = hex"7c8361d4493d8b4de5a6c57a35458f238cf987f59dde1ea190656b122f77bef9"; // Emitter cap address + bytes32 remoteFiller = hex"1611edd9a9d42dbcd9ae773ffa22be0f6017b00590959dd5c767e4efcd34cd0b"; + bytes32 recipient = hex"000000000000000000000000006217c47ffa5eb3f3c92247fffe22ad998242c5"; + bytes32 outputToken = hex"5ef2fcf809fb9535ea0aeaea421f683026f06c34569aafc42bcde652ef6dd270"; + uint256 remoteChainId = 100; + bytes32 solverBytes = hex"000000000000000000000000ea22232eee6365d797fec0f804da81f3e3f18c2d"; + address solverAddress = address(uint160(uint256(solverBytes))); + + uint256[2][] memory inputs = new uint256[2][](1); + inputs[0] = [tokenId, amount]; + OutputDescription[] memory outputs = new OutputDescription[](1); + outputs[0] = OutputDescription({ + remoteFiller: remoteFiller, + remoteOracle: remoteOracle, + chainId: remoteChainId, + token: outputToken, + amount: amount, + recipient: recipient, + remoteCall: hex"", + fulfillmentContext: hex"" + }); + CatalystCompactOrder memory order = + CatalystCompactOrder({ user: address(swapper), nonce: 0, originChainId: block.chainid, fillDeadline: type(uint32).max, localOracle: localOracle, inputs: inputs, outputs: outputs }); + + // Make Compact + bytes32 typeHash = TheCompactOrderType.BATCH_COMPACT_TYPE_HASH; + uint256[2][] memory idsAndAmounts = new uint256[2][](1); + idsAndAmounts[0] = [tokenId, amount]; + + bytes memory sponsorSig = getCompactBatchWitnessSignature(swapperPrivateKey, typeHash, address(compactSettler), swapper, 0, type(uint32).max, idsAndAmounts, this.orderHash(order)); + bytes memory allocatorSig = hex""; + + bytes memory signature = abi.encode(sponsorSig, allocatorSig); + bytes32 orderId = compactSettler.orderIdentifier(order); + + bytes[] memory payloads = new bytes[](1); + payloads[0] = OutputEncodingLib.encodeFillDescriptionM(solverBytes, orderId, uint32(block.timestamp), outputs[0]); + + bytes memory expectedMessageEmitted = this.encodeMessage(outputs[0].remoteFiller, payloads); + + bytes memory vaa = makeValidVAA(uint16(remoteChainId), remoteOracle, expectedMessageEmitted); + wormholeOracle.setChainMap(uint16(remoteChainId), remoteChainId); + wormholeOracle.receiveMessage(vaa); + + uint32[] memory timestamps = new uint32[](1); + timestamps[0] = uint32(block.timestamp); + + vm.prank(solverAddress); + compactSettler.finaliseSelf(order, signature, timestamps, solverBytes); + assertEq(token.balanceOf(solverAddress), amount); + assertEq(token.balanceOf(swapper), initialSwapperBalance - amount); + } + function test_entire_flow_different_solvers(bytes32 solverIdentifier2) external { vm.prank(swapper); uint256 amount = 1e18 / 10; diff --git a/test/oracle/wormhole/callworm.t.sol b/test/oracle/wormhole/callworm.t.sol index 86d2f7c9..213b7011 100644 --- a/test/oracle/wormhole/callworm.t.sol +++ b/test/oracle/wormhole/callworm.t.sol @@ -4,6 +4,7 @@ pragma solidity ^0.8.22; import "forge-std/Test.sol"; import { WormholeVerifier } from "src/oracles/wormhole/external/callworm/WormholeVerifier.sol"; +import { WormholeOracle } from "src/oracles/wormhole/WormholeOracle.sol"; import "src/oracles/wormhole/external/wormhole/Messages.sol"; import "src/oracles/wormhole/external/wormhole/Setters.sol"; import "src/oracles/wormhole/external/wormhole/Structs.sol"; @@ -56,6 +57,10 @@ contract TestWormholeCallWorm is Test { return abi.encodePacked(hex"000003e8" hex"00000001", emitterChainId, emitterAddress, hex"0000000000000539" hex"0f"); } + function messagesAddress() internal view returns(address){ + return address(messages); + } + // This test checks the possibility of getting a unsigned message verified through verifyVM function test_compare_wormhole_implementation_and_calldata_version( bytes calldata message diff --git a/test/oracle/wormhole/receive.t.sol b/test/oracle/wormhole/receive.t.sol new file mode 100644 index 00000000..c2376f36 --- /dev/null +++ b/test/oracle/wormhole/receive.t.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.22; + +import { WormholeOracle } from "src/oracles/wormhole/WormholeOracle.sol"; +import { TestWormholeCallWorm } from "./callworm.t.sol"; + +contract TestReceiveWormholeOracleProofs is TestWormholeCallWorm { + event OutputProven(uint256 chainid, bytes32 remoteIdentifier, bytes32 application, bytes32 payloadHash); + + WormholeOracle oracle; + + function test_receive_proof() external { + oracle = new WormholeOracle(address(this), address(messages)); + bytes memory stripped_message = hex"1611edd9a9d42dbcd9ae773ffa22be0f6017b00590959dd5c767e4efcd34cd0b000100b400000000000000000000000000000000000000000000000000000000000000ac82f523c28a9556fdc958116e496b8ce488969b8ffbb8998620c5e890c6156cf5000000005ef2fcf809fb9535ea0aeaea421f683026f06c34569aafc42bcde652ef6dd270640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ec191e4700000000000000000000000000000000"; + bytes memory validVM = makeValidVM(stripped_message); + bytes32 payloadHash = keccak256(hex"00000000000000000000000000000000000000000000000000000000000000ac82f523c28a9556fdc958116e496b8ce488969b8ffbb8998620c5e890c6156cf5000000005ef2fcf809fb9535ea0aeaea421f683026f06c34569aafc42bcde652ef6dd270640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ec191e4700000000000000000000000000000000"); + + vm.prank(address(this)); + oracle.setChainMap(13, 13); + /** + chainId: 13(00x000d) + remoteIdentifier: 0xdeadbeefbeefdead + application: 0x1611edd9a9d42dbcd9ae773ffa22be0f6017b00590959dd5c767e4efcd34cd0b + payload: 00000000000000000000000000000000000000000000000000000000000000ac82f523c28a9556fdc958116e496b8ce488969b8ffbb8998620c5e890c6156cf5000000005ef2fcf809fb9535ea0aeaea421f683026f06c34569aafc42bcde652ef6dd270640000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ec191e4700000000000000000000000000000000 + + The chainId and remoteIdentifier are from the oracle with dummy guardian set. + The application is the coin_filler id from fill_and_submit test in SUI. + The payload is the fill description from the fill_and_submit test in SUI. + */ + + vm.expectEmit(); + emit OutputProven(13, bytes32(uint256(0xdeadbeefbeefdead)), 0x1611edd9a9d42dbcd9ae773ffa22be0f6017b00590959dd5c767e4efcd34cd0b, payloadHash); + + oracle.receiveMessage(validVM); + assert(oracle.isProven(13, bytes32(uint256(0xdeadbeefbeefdead)), 0x1611edd9a9d42dbcd9ae773ffa22be0f6017b00590959dd5c767e4efcd34cd0b, payloadHash)); + } +} \ No newline at end of file diff --git a/test/oracle/wormhole/submit.t.sol b/test/oracle/wormhole/submit.t.sol index be8e2d66..365c9f86 100644 --- a/test/oracle/wormhole/submit.t.sol +++ b/test/oracle/wormhole/submit.t.sol @@ -51,7 +51,6 @@ contract TestSubmitWormholeOracleProofs is Test { function test_fill_then_submit_W(address sender, uint256 amount, address recipient, bytes32 orderId, bytes32 solverIdentifier) external { vm.assume(solverIdentifier != bytes32(0)); - token.mint(sender, amount); vm.prank(sender); token.approve(address(filler), amount); @@ -82,4 +81,81 @@ contract TestSubmitWormholeOracleProofs is Test { emit PackagePublished(0, expectedPayload, 15); oracle.submit(address(filler), payloads); } -} + + // This test is used to test receive against other chains + function test_fill_no_fuzz_output() public { + uint256 amount = 100; + bytes32 solverIdentifier = bytes32(uint256(uint160(makeAddr("solver")))); + bytes32 recipient = bytes32(uint256(uint160(makeAddr("recipient")))); + address sender = makeAddr("sender"); + + token.mint(sender, amount); + vm.prank(sender); + token.approve(address(filler), amount); + + OutputDescription memory output = OutputDescription({ + remoteOracle: bytes32(uint256(uint160(address(oracle)))), + remoteFiller: bytes32(uint256(uint160(address(filler)))), + token: bytes32(abi.encode(address(token))), + amount: amount, + recipient: bytes32(abi.encode(recipient)), + chainId: uint32(block.chainid), + remoteCall: hex"", + fulfillmentContext: hex"" + }); + + /** + Order id for + { + user: @0xab, + nonce: 0, + origin_chain_id: 100, + fill_deadline: 60000, + local_oracle: @0x1611edd9a9d42dbcd9ae773ffa22be0f6017b00590959dd5c767e4efcd34cd0b, + collateral_token: @0xe4d0bcbdc026b98a242f13e2761601107c90de400f0c24cdafea526abf201c26, + collateral_amount: 0, + initiate_deadline: 60000, + challenge_deadline: 120000, + input: 0x0::input_type::Input { + token: @0xe4d0bcbdc026b98a242f13e2761601107c90de400f0c24cdafea526abf201c26, + amount: 100 + }, + outputs: [ + 0x0::output_description_type::OutputDescription { + remote_oracle: 0x0::bytes32::Bytes32 { + bytes: @0x2e234dae75c793f67a35089c9d99245e1c58470b + }, + remote_filler: 0x0::bytes32::Bytes32 { + bytes: @0xf62849f9a0b5bf2913b396098f7c7019b51a820a + }, + chain_id: 31337, + token: 0x0::bytes32::Bytes32 { + bytes: @0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9 + }, + amount: 100, + recipient: 0x0::bytes32::Bytes32 { + bytes: @0x6217c47ffa5eb3f3c92247fffe22ad998242c5 + }, + remote_call: [], + fulfilment_context: [] + } + ] + } + */ + bytes32 orderId = hex"e58f15295d1c9e383c8d4dad01ee03cfa4448f0d4f52925fa957d6e76612bff9"; + vm.expectCall(address(token), abi.encodeWithSignature("transferFrom(address,address,uint256)", address(sender), recipient, amount)); + + vm.prank(sender); + filler.fill(orderId, output, solverIdentifier); + + bytes memory payload = OutputEncodingLib.encodeFillDescriptionM(solverIdentifier, orderId, uint32(block.timestamp), output); + bytes[] memory payloads = new bytes[](1); + payloads[0] = payload; + + bytes memory expectedPayload = this.encodeMessageCalldata(output.remoteFiller, payloads); + + vm.expectEmit(); + emit PackagePublished(0, expectedPayload, 15); + oracle.submit(address(filler), payloads); + } +} \ No newline at end of file From d18429a543bfffceb94b85ec44ac44a4a5fcedf3 Mon Sep 17 00:00:00 2001 From: Asem-Abdelhady Date: Mon, 3 Mar 2025 17:50:37 +0200 Subject: [PATCH 2/4] chore: format --- src/oracles/wormhole/WormholeOracle.sol | 2 +- test/TestCatalyst.t.sol | 10 +++++----- test/oracle/wormhole/callworm.t.sol | 4 ---- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/oracles/wormhole/WormholeOracle.sol b/src/oracles/wormhole/WormholeOracle.sol index 93c8077c..e38b7d37 100644 --- a/src/oracles/wormhole/WormholeOracle.sol +++ b/src/oracles/wormhole/WormholeOracle.sol @@ -142,7 +142,7 @@ contract WormholeOracle is BaseOracle, WormholeVerifier, Ownable { // Map remoteMessagingProtocolChainIdentifier to canonical chain id. This ensures we use canonical ids. uint256 remoteChainId = _chainIdentifierToBlockChainId[remoteMessagingProtocolChainIdentifier]; - if(remoteChainId == 0) revert ZeroValue(); + if (remoteChainId == 0) revert ZeroValue(); uint256 numPayloads = payloadHashes.length; diff --git a/test/TestCatalyst.t.sol b/test/TestCatalyst.t.sol index a2d962fd..3d76a4fa 100644 --- a/test/TestCatalyst.t.sol +++ b/test/TestCatalyst.t.sol @@ -279,12 +279,12 @@ contract TestCatalyst is Test { address localOracle = address(wormholeOracle); // These are the real addresses of fill_and_submit_output.move - bytes32 remoteOracle = hex"7c8361d4493d8b4de5a6c57a35458f238cf987f59dde1ea190656b122f77bef9"; // Emitter cap address - bytes32 remoteFiller = hex"1611edd9a9d42dbcd9ae773ffa22be0f6017b00590959dd5c767e4efcd34cd0b"; - bytes32 recipient = hex"000000000000000000000000006217c47ffa5eb3f3c92247fffe22ad998242c5"; - bytes32 outputToken = hex"5ef2fcf809fb9535ea0aeaea421f683026f06c34569aafc42bcde652ef6dd270"; + bytes32 remoteOracle = 0x7c8361d4493d8b4de5a6c57a35458f238cf987f59dde1ea190656b122f77bef9; // Emitter cap address + bytes32 remoteFiller = 0x1611edd9a9d42dbcd9ae773ffa22be0f6017b00590959dd5c767e4efcd34cd0b; + bytes32 recipient = 0x000000000000000000000000006217c47ffa5eb3f3c92247fffe22ad998242c5; + bytes32 outputToken = 0x5ef2fcf809fb9535ea0aeaea421f683026f06c34569aafc42bcde652ef6dd270; uint256 remoteChainId = 100; - bytes32 solverBytes = hex"000000000000000000000000ea22232eee6365d797fec0f804da81f3e3f18c2d"; + bytes32 solverBytes = 0x000000000000000000000000ea22232eee6365d797fec0f804da81f3e3f18c2d; address solverAddress = address(uint160(uint256(solverBytes))); uint256[2][] memory inputs = new uint256[2][](1); diff --git a/test/oracle/wormhole/callworm.t.sol b/test/oracle/wormhole/callworm.t.sol index 213b7011..9bb604fa 100644 --- a/test/oracle/wormhole/callworm.t.sol +++ b/test/oracle/wormhole/callworm.t.sol @@ -57,10 +57,6 @@ contract TestWormholeCallWorm is Test { return abi.encodePacked(hex"000003e8" hex"00000001", emitterChainId, emitterAddress, hex"0000000000000539" hex"0f"); } - function messagesAddress() internal view returns(address){ - return address(messages); - } - // This test checks the possibility of getting a unsigned message verified through verifyVM function test_compare_wormhole_implementation_and_calldata_version( bytes calldata message From 89d59db2ede62e1d338a17f8cc49fd700a8f51f8 Mon Sep 17 00:00:00 2001 From: Asem-Abdelhady Date: Fri, 7 Mar 2025 14:01:13 +0200 Subject: [PATCH 3/4] chore: format --- test/oracle/wormhole/submit.t.sol | 43 +++---------------------------- 1 file changed, 4 insertions(+), 39 deletions(-) diff --git a/test/oracle/wormhole/submit.t.sol b/test/oracle/wormhole/submit.t.sol index 365c9f86..f3b6262a 100644 --- a/test/oracle/wormhole/submit.t.sol +++ b/test/oracle/wormhole/submit.t.sol @@ -51,6 +51,7 @@ contract TestSubmitWormholeOracleProofs is Test { function test_fill_then_submit_W(address sender, uint256 amount, address recipient, bytes32 orderId, bytes32 solverIdentifier) external { vm.assume(solverIdentifier != bytes32(0)); + token.mint(sender, amount); vm.prank(sender); token.approve(address(filler), amount); @@ -82,7 +83,9 @@ contract TestSubmitWormholeOracleProofs is Test { oracle.submit(address(filler), payloads); } - // This test is used to test receive against other chains + /** + * @dev This is used to test receive against SUI chain + */ function test_fill_no_fuzz_output() public { uint256 amount = 100; bytes32 solverIdentifier = bytes32(uint256(uint160(makeAddr("solver")))); @@ -104,44 +107,6 @@ contract TestSubmitWormholeOracleProofs is Test { fulfillmentContext: hex"" }); - /** - Order id for - { - user: @0xab, - nonce: 0, - origin_chain_id: 100, - fill_deadline: 60000, - local_oracle: @0x1611edd9a9d42dbcd9ae773ffa22be0f6017b00590959dd5c767e4efcd34cd0b, - collateral_token: @0xe4d0bcbdc026b98a242f13e2761601107c90de400f0c24cdafea526abf201c26, - collateral_amount: 0, - initiate_deadline: 60000, - challenge_deadline: 120000, - input: 0x0::input_type::Input { - token: @0xe4d0bcbdc026b98a242f13e2761601107c90de400f0c24cdafea526abf201c26, - amount: 100 - }, - outputs: [ - 0x0::output_description_type::OutputDescription { - remote_oracle: 0x0::bytes32::Bytes32 { - bytes: @0x2e234dae75c793f67a35089c9d99245e1c58470b - }, - remote_filler: 0x0::bytes32::Bytes32 { - bytes: @0xf62849f9a0b5bf2913b396098f7c7019b51a820a - }, - chain_id: 31337, - token: 0x0::bytes32::Bytes32 { - bytes: @0x5991a2df15a8f6a256d3ec51e99254cd3fb576a9 - }, - amount: 100, - recipient: 0x0::bytes32::Bytes32 { - bytes: @0x6217c47ffa5eb3f3c92247fffe22ad998242c5 - }, - remote_call: [], - fulfilment_context: [] - } - ] - } - */ bytes32 orderId = hex"e58f15295d1c9e383c8d4dad01ee03cfa4448f0d4f52925fa957d6e76612bff9"; vm.expectCall(address(token), abi.encodeWithSignature("transferFrom(address,address,uint256)", address(sender), recipient, amount)); From 934b89f028cc4b8c2755e57049fdc325445aaf82 Mon Sep 17 00:00:00 2001 From: Asem-Abdelhady Date: Fri, 7 Mar 2025 14:01:51 +0200 Subject: [PATCH 4/4] chore: format --- test/oracle/wormhole/submit.t.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/oracle/wormhole/submit.t.sol b/test/oracle/wormhole/submit.t.sol index f3b6262a..f0b2bdd2 100644 --- a/test/oracle/wormhole/submit.t.sol +++ b/test/oracle/wormhole/submit.t.sol @@ -123,4 +123,4 @@ contract TestSubmitWormholeOracleProofs is Test { emit PackagePublished(0, expectedPayload, 15); oracle.submit(address(filler), payloads); } -} \ No newline at end of file +}