-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
b0311c7
commit 83c0f99
Showing
2 changed files
with
146 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol"; | ||
import "wormhole-solidity-sdk/interfaces/IWormholeReceiver.sol"; | ||
import "wormhole-solidity-sdk/WormholeRelayerSDK.sol"; | ||
|
||
contract HelloWormholeConfirmation is Base, IWormholeReceiver { | ||
event GreetingReceived(string greeting, uint16 senderChain, address sender); | ||
event GreetingSuccess(string greeting, address sender); | ||
|
||
uint256 constant SENDING_GAS_LIMIT = 550_000; | ||
uint256 constant CONFIRMATION_GAS_LIMIT = 50_000; | ||
|
||
string public latestGreeting; | ||
string public latestConfirmedSentGreeting; | ||
|
||
uint16 chainId; | ||
|
||
enum MessageType {GREETING, CONFIRMATION} | ||
|
||
constructor(address _wormholeRelayer, address _wormhole) Base(_wormholeRelayer, _wormhole) {} | ||
|
||
function quoteCrossChainGreeting(uint16 targetChain, uint256 receiverValue) public view returns (uint256 cost) { | ||
(cost,) = wormholeRelayer.quoteEVMDeliveryPrice(targetChain, receiverValue, SENDING_GAS_LIMIT); | ||
} | ||
|
||
// receiverValueForSecondDeliveryPayment will be determined in a front-end calculation (by calling quoteConfirmation on the target chain) | ||
// We recommend baking in a buffer to account for the possibility of the price of targetChain->sourceChain changing during the sourceChain->targetChain delivery | ||
function sendCrossChainGreeting(uint16 targetChain, address targetAddress, string memory greeting, uint256 receiverValueForSecondDeliveryPayment) public payable { | ||
uint256 cost = quoteCrossChainGreeting(targetChain, receiverValueForSecondDeliveryPayment); | ||
require(msg.value == cost); | ||
|
||
wormholeRelayer.sendPayloadToEvm{value: cost}( | ||
targetChain, | ||
targetAddress, | ||
abi.encode(MessageType.GREETING, greeting, msg.sender), // payload | ||
receiverValueForSecondDeliveryPayment, // will be used to pay for the confirmation | ||
SENDING_GAS_LIMIT, | ||
// we add a refund chain and address as the requester of the cross chain greeting | ||
// The refund from this 'send' will be tacked on to the confirmation delivery | ||
// (because we will request the confirmation using the 'forward' feature) | ||
chainId, | ||
msg.sender | ||
); | ||
} | ||
|
||
function quoteConfirmation(uint16 targetChain) public view returns (uint256 cost) { | ||
(cost,) = wormholeRelayer.quoteEVMDeliveryPrice(targetChain, 0, CONFIRMATION_GAS_LIMIT); | ||
} | ||
|
||
function receiveWormholeMessages( | ||
bytes memory payload, | ||
bytes[] memory, // additionalVaas | ||
bytes32 sourceAddress, | ||
uint16 sourceChain, | ||
bytes32 deliveryHash | ||
) | ||
public | ||
payable | ||
override | ||
onlyWormholeRelayer | ||
isRegisteredSender(sourceChain, sourceAddress) | ||
replayProtect(deliveryHash) | ||
{ | ||
MessageType msgType = abi.decode(payload, (MessageType)); | ||
|
||
if(msgType == MessageType.GREETING) { | ||
(,string memory greeting, address sender) = abi.decode(payload, (MessageType, string, address)); | ||
latestGreeting = greeting; | ||
emit GreetingReceived(latestGreeting, sourceChain, sender); | ||
|
||
uint256 confirmationCost = quoteConfirmation(sourceChain); | ||
require(msg.value >= confirmationCost, "Didn't receive enough value for the forward!"); | ||
wormholeRelayer.forwardPayloadToEvm{value: msg.value}( | ||
sourceChain, | ||
fromWormholeFormat(sourceAddress), | ||
abi.encode(MessageType.CONFIRMATION, greeting, sender), | ||
0, | ||
CONFIRMATION_GAS_LIMIT | ||
); | ||
} else if(msgType == MessageType.CONFIRMATION) { | ||
(,string memory greeting, address sender) = abi.decode(payload, (MessageType, string, address)); | ||
emit GreetingSuccess(greeting, sender); | ||
latestConfirmedSentGreeting = greeting; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.13; | ||
|
||
import "../../src/extensions/HelloWormholeConfirmation.sol"; | ||
|
||
import "wormhole-solidity-sdk/testing/WormholeRelayerTest.sol"; | ||
|
||
contract HelloWormholeConfirmationTest is WormholeRelayerBasicTest { | ||
event GreetingReceived(string greeting, uint16 senderChain, address sender); | ||
|
||
HelloWormholeConfirmation helloSource; | ||
HelloWormholeConfirmation helloTarget; | ||
|
||
function setUpSource() public override { | ||
helloSource = new HelloWormholeConfirmation(address(relayerSource), address(wormholeSource)); | ||
} | ||
|
||
function setUpTarget() public override { | ||
helloTarget = new HelloWormholeConfirmation(address(relayerTarget), address(wormholeTarget)); | ||
} | ||
|
||
function performRegistrations() public { | ||
vm.selectFork(targetFork); | ||
helloTarget.setRegisteredSender(sourceChain, toWormholeFormat(address(helloSource))); | ||
|
||
vm.selectFork(sourceFork); | ||
helloSource.setRegisteredSender(targetChain, toWormholeFormat(address(helloTarget))); | ||
} | ||
|
||
function testGreeting() public { | ||
|
||
performRegistrations(); | ||
|
||
// Front-end calculation for how much receiver value to request the greeting with | ||
// to ensure a confirmation is able to come back! | ||
vm.selectFork(targetFork); | ||
// We bake in a 10% buffer to account for the possibility of a price change after the initial delivery but before the return delivery | ||
uint256 receiverValueForConfirmation = helloTarget.quoteConfirmation(sourceChain) * 11 / 10; | ||
vm.selectFork(sourceFork); | ||
// end front-end calculation | ||
|
||
uint256 cost = helloSource.quoteCrossChainGreeting(targetChain, receiverValueForConfirmation); | ||
|
||
vm.recordLogs(); | ||
|
||
helloSource.sendCrossChainGreeting{value: cost}(targetChain, address(helloTarget), "Hello Wormhole!", receiverValueForConfirmation); | ||
|
||
performDelivery(); | ||
|
||
vm.selectFork(targetFork); | ||
assertEq(helloTarget.latestGreeting(), "Hello Wormhole!"); | ||
|
||
performDelivery(); | ||
|
||
vm.selectFork(sourceFork); | ||
assertEq(helloSource.latestConfirmedSentGreeting(), "Hello Wormhole!"); | ||
} | ||
} |