diff --git a/src/oracles/wormhole/WormholeOracle.sol b/src/oracles/wormhole/WormholeOracle.sol index b4c6c2bd..e38b7d37 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..3d76a4fa 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 = 0x7c8361d4493d8b4de5a6c57a35458f238cf987f59dde1ea190656b122f77bef9; // Emitter cap address + bytes32 remoteFiller = 0x1611edd9a9d42dbcd9ae773ffa22be0f6017b00590959dd5c767e4efcd34cd0b; + bytes32 recipient = 0x000000000000000000000000006217c47ffa5eb3f3c92247fffe22ad998242c5; + bytes32 outputToken = 0x5ef2fcf809fb9535ea0aeaea421f683026f06c34569aafc42bcde652ef6dd270; + uint256 remoteChainId = 100; + bytes32 solverBytes = 0x000000000000000000000000ea22232eee6365d797fec0f804da81f3e3f18c2d; + 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..9bb604fa 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"; 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..f0b2bdd2 100644 --- a/test/oracle/wormhole/submit.t.sol +++ b/test/oracle/wormhole/submit.t.sol @@ -82,4 +82,45 @@ contract TestSubmitWormholeOracleProofs is Test { emit PackagePublished(0, expectedPayload, 15); oracle.submit(address(filler), payloads); } + + /** + * @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")))); + 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"" + }); + + 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); + } }