Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement wormhole adapter #144

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
165943c
WIP: inital conversion into multi-protocol
AntonAndell Jul 20, 2023
d3c2121
feat: setup foundry structure (#79)
sdpisreddevil Sep 18, 2023
0fecbc5
feat: testcase set admin (#80)
sdpisreddevil Sep 18, 2023
c082f15
feat: testcase set protocol fee (#81)
sdpisreddevil Sep 18, 2023
632a3bc
feat: testcase set protocol fee handler (#82)
sdpisreddevil Sep 18, 2023
6227495
fix: add default connection setter (#87)
sdpisreddevil Sep 19, 2023
8959749
fix: rlp encode library encoding fix for protocols (#89)
sdpisreddevil Sep 19, 2023
10d84e8
feat: handle requests multiple testcases (#90)
sdpisreddevil Sep 21, 2023
1237c0f
add successfulResponses record for responses
sdpisreddevil Sep 25, 2023
89b50aa
update successfulResponses variable after response is success
sdpisreddevil Sep 25, 2023
7a96955
feat: execute call testcases (#91)
sdpisreddevil Sep 26, 2023
42b2740
fix: clearing out unused code (#98)
gcranju Sep 26, 2023
e979f07
docs: solidity xcall deployment (#99)
sdpisreddevil Sep 26, 2023
6af358b
feat: rollback testcases (#92)
sdpisreddevil Sep 26, 2023
e1a914e
feat: execute rollback testcases (#93)
sdpisreddevil Sep 26, 2023
d69e2da
fix: testcase for send call message (#88)
sdpisreddevil Sep 26, 2023
665071e
fix: deployment script updated (#100)
gcranju Sep 27, 2023
94d2410
fix: add verify success test cases (#101)
sdpisreddevil Sep 28, 2023
98994e7
tests: Fix testExecuteRollbackMultiProtocol test
sdpisreddevil Oct 11, 2023
b529488
feat: ci add solidity code coverages (#140)
sdpisreddevil Oct 11, 2023
4acea42
refactor: evm folder structure
redlarva Oct 11, 2023
0e901e3
fix: solidity xcall cod coverage
redlarva Oct 11, 2023
e6c5842
feat: added evm multiprotocol dapp
gcranju Oct 11, 2023
086831b
fix: removed unnecessary imports
gcranju Oct 11, 2023
1351c38
fix: remove cache file
redlarva Oct 11, 2023
16c6fb2
fix: add cache to gitignore
redlarva Oct 11, 2023
f683146
Merge pull request #143 from icon-project/refactor/evm-folder-structure
sdpisreddevil Oct 11, 2023
cefa15c
feat: implement wormhole adapter
redlarva Oct 13, 2023
75834d5
fix: removed event listener for request submitted
redlarva Oct 13, 2023
7dc4976
tests: added rollback test for wormhole
redlarva Oct 14, 2023
64b7dd5
tests: added admin test
redlarva Oct 14, 2023
6ca87e9
Merge branch 'feature/solidity-adapters' into feature/implement-wormh…
redlarva Oct 30, 2023
3dda850
fix: xcall test
redlarva Oct 30, 2023
a40b45a
feat: add method to update gas limit
redlarva Oct 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@
[submodule "contracts/evm/xcall/lib/openzeppelin-contracts"]
path = contracts/evm/lib/openzeppelin-contracts
url = https://github.com/Openzeppelin/openzeppelin-contracts
[submodule "contracts/evm/lib/wormhole-solidity-sdk"]
path = contracts/evm/lib/wormhole-solidity-sdk
url = https://github.com/wormhole-foundation/wormhole-solidity-sdk
180 changes: 178 additions & 2 deletions contracts/evm/contracts/adapters/wormhole/WormholeAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,182 @@
pragma abicoder v2;

import "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import "wormhole-solidity-sdk/interfaces/IWormholeRelayer.sol";
import "wormhole-solidity-sdk/interfaces/IWormholeReceiver.sol";
import "wormhole-solidity-sdk/Utils.sol";

contract WormholeAdapter is Initializable {
}
import "./interfaces/IAdapter.sol";

import "@xcall/utils/Types.sol";
import "@iconfoundation/btp2-solidity-library/interfaces/ICallService.sol";
import "@iconfoundation/btp2-solidity-library/interfaces/IConnection.sol";

/**
* @title WormholeAdapter
* @dev This contract serves as a cross-chain xcall adapter, enabling communication between xcall on different blockchain networks via Wormhole.
*/
contract WormholeAdapter is IAdapter, Initializable, IWormholeReceiver, IConnection {
mapping(uint256 => Types.PendingResponse) private pendingResponses;
mapping(string => uint16) private chainIds;
mapping(uint16 => string) private networkIds;
mapping(string => uint256) private gasLimits;
mapping(string => bytes32) private remoteEndpoint;
mapping(bytes32 => bool) public seenDeliveryVaaHashes;
address private wormholeRelayer;
address private xCall;
address private owner;
address private adminAddress;

modifier onlyOwner() {
require(msg.sender == owner, "OnlyOwner");
_;
}

modifier onlyAdmin() {
require(msg.sender == this.admin(), "OnlyAdmin");
_;
}

function initialize(address _wormholeRelayer, address _xCall) public initializer {
owner = msg.sender;
adminAddress = msg.sender;
wormholeRelayer = _wormholeRelayer;
xCall = _xCall;

Check warning on line 46 in contracts/evm/contracts/adapters/wormhole/WormholeAdapter.sol

View check run for this annotation

Codecov / codecov/patch

contracts/evm/contracts/adapters/wormhole/WormholeAdapter.sol#L43-L46

Added lines #L43 - L46 were not covered by tests
}

/**
* @notice Configure connection settings for a destination chain.
* @param networkId The network ID of the destination chain.
* @param chainId The chain ID of the destination chain.
* @param endpoint The endpoint or address of the destination chain.
* @param gasLimit The gas limit for transactions on the destination chain.
*/
function configureConnection(
string calldata networkId,
uint16 chainId,
bytes32 endpoint,
uint256 gasLimit
) external override onlyAdmin {
require(bytes(networkIds[chainId]).length == 0, "Connection already configured");
networkIds[chainId] = networkId;
chainIds[networkId] = chainId;
remoteEndpoint[networkId] = endpoint;
gasLimits[networkId] = gasLimit;

Check warning on line 66 in contracts/evm/contracts/adapters/wormhole/WormholeAdapter.sol

View check run for this annotation

Codecov / codecov/patch

contracts/evm/contracts/adapters/wormhole/WormholeAdapter.sol#L63-L66

Added lines #L63 - L66 were not covered by tests
AntonAndell marked this conversation as resolved.
Show resolved Hide resolved
redlarva marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* @notice set or update gas limit for a destination chain.
* @param networkId The network ID of the destination chain.
* @param gasLimit The gas limit for transactions on the destination chain.
*/
function setGasLimit(
string calldata networkId,
uint256 gasLimit
) external override onlyAdmin {
gasLimits[networkId] = gasLimit;

Check warning on line 78 in contracts/evm/contracts/adapters/wormhole/WormholeAdapter.sol

View check run for this annotation

Codecov / codecov/patch

contracts/evm/contracts/adapters/wormhole/WormholeAdapter.sol#L78

Added line #L78 was not covered by tests
}

/**
* @notice Get the gas fee required to send a message to a specified destination network.
* @param _to The network ID of the target chain.
* @param _response Indicates whether the response fee is included (true) or not (false).
* @return _fee The fee for sending a message to the given destination network.
*/
function getFee(string memory _to, bool _response) external view override returns (uint256 _fee) {
uint256 gasLimit = gasLimits[_to];
(_fee,) = IWormholeRelayer(wormholeRelayer).quoteEVMDeliveryPrice(chainIds[_to], 0, gasLimit);
}

/**
* @notice Send a message to a specified destination network.
* @param _to The network ID of the destination network.
* @param _svc The name of the service.
* @param _sn The serial number of the message.
* @param _msg The serialized bytes of the service message.
*/
function sendMessage(
string calldata _to,
string calldata _svc,
int256 _sn,
bytes calldata _msg
) external override payable {
require(msg.sender == xCall, "Only xCall can send messages");
uint256 fee = msg.value;

if (_sn < 0) {
fee = this.getFee(_to, false);
if (address(this).balance < fee) {
uint256 sn = uint256(- _sn);
pendingResponses[sn] = Types.PendingResponse(_msg, _to);
emit ResponseOnHold(sn);
return;
}
}

IWormholeRelayer(wormholeRelayer).sendPayloadToEvm{value: fee}(
chainIds[_to],
fromWormholeFormat(remoteEndpoint[_to]),
abi.encodePacked(_msg),
0,
gasLimits[_to]
);
}

/**
* @notice Endpoint that the Wormhole Relayer contract calls to deliver the payload.
*/
function receiveWormholeMessages(
bytes memory payload,
bytes[] memory, // additionalVaas
bytes32 sourceAddress,
uint16 sourceChain,
bytes32 deliveryHash
) public payable override {
require(msg.sender == wormholeRelayer, "Only relayer allowed");
require(!seenDeliveryVaaHashes[deliveryHash], "Message already processed");
seenDeliveryVaaHashes[deliveryHash] = true;
string memory nid = networkIds[sourceChain];
require(keccak256(abi.encodePacked(sourceAddress)) == keccak256(abi.encodePacked(remoteEndpoint[nid])), "source address mismatched");
ICallService(xCall).handleMessage(nid, payload);
}

/**
* @notice Pay and trigger the execution of a stored response to be sent back.
* @param _sn The serial number of the message for which the response is being triggered.
*/
function triggerResponse(uint256 _sn) external override payable {
int256 sn = int256(_sn);
Types.PendingResponse memory resp = pendingResponses[_sn];
delete pendingResponses[_sn];
uint256 fee = msg.value;
IWormholeRelayer(wormholeRelayer).sendPayloadToEvm{value: fee}(
chainIds[resp.targetNetwork],
fromWormholeFormat(remoteEndpoint[resp.targetNetwork]),
abi.encodePacked(resp.msg),
0,
gasLimits[resp.targetNetwork]
);
}

/**
* @notice Set the address of the admin.
* @param _address The address of the admin.
*/
function setAdmin(address _address) external onlyAdmin {
adminAddress = _address;
}

/**
@notice Gets the address of admin
@return (Address) the address of admin
*/
function admin(
) external view returns (
address
) {
if (adminAddress == address(0)) {
return owner;

Check warning on line 180 in contracts/evm/contracts/adapters/wormhole/WormholeAdapter.sol

View check run for this annotation

Codecov / codecov/patch

contracts/evm/contracts/adapters/wormhole/WormholeAdapter.sol#L180

Added line #L180 was not covered by tests
}
return adminAddress;
}
}
45 changes: 45 additions & 0 deletions contracts/evm/contracts/adapters/wormhole/interfaces/IAdapter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0;

/**
* @title IAdapter - Interface for Wormhole-xCall Adapter
* @dev This interface defines the functions and events for a Wormhole-xCall adapter,
* allowing communication and message transfer between xCall on different blockchain networks.
*/
interface IAdapter {
/**
* @notice Emitted when a response is put on hold.
* @param _sn The serial number of the response.
*/
event ResponseOnHold(uint256 indexed _sn);

/**
* @notice Configure connection settings for a destination chain.
* @param networkId The network ID of the destination chain.
* @param chainId The chain ID of the destination chain.
* @param endpoint The endpoint or address of the destination chain.
* @param gasLimit The gas limit for transactions on the destination chain.
*/
function configureConnection(
string calldata networkId,
uint16 chainId,
bytes32 endpoint,
uint256 gasLimit
) external;

/**
* @notice set or update gas limit for a destination chain.
* @param networkId The network ID of the destination chain.
* @param gasLimit The gas limit for transactions on the destination chain.
*/
function setGasLimit (
string calldata networkId,
uint256 gasLimit
) external;

/**
* @notice Pay and trigger the execution of a stored response to be sent back.
* @param _sn The serial number of the message for which the response is being triggered.
*/
function triggerResponse(uint256 _sn) external payable;
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import "@iconfoundation/btp2-solidity-library/utils/NetworkAddress.sol";
import "@iconfoundation/btp2-solidity-library/utils/Integers.sol";
import "@iconfoundation/btp2-solidity-library/utils/ParseAddress.sol";
import "@iconfoundation/btp2-solidity-library/utils/Strings.sol";
import "@iconfoundation/btp2-solidity-library/interfaces/ICallService.sol";
import "@iconfoundation/btp2-solidity-library/interfaces/ICallServiceReceiver.sol";

import "openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";

contract MultiProtocolSampleDapp is Initializable {
contract MultiProtocolSampleDapp is Initializable, ICallServiceReceiver {
using Strings for string;
using Integers for uint;
using ParseAddress for address;
Expand All @@ -18,10 +20,10 @@ contract MultiProtocolSampleDapp is Initializable {
address private callSvc;
mapping(string => string[]) private sources;
mapping(string => string[]) private destinations;

event MessageReceived(string indexed from, bytes data);

function initialize(address _callService) public {
function initialize(address _callService) public initializer {
callSvc = _callService;
}

Expand Down Expand Up @@ -53,20 +55,20 @@ contract MultiProtocolSampleDapp is Initializable {
bytes memory data,
bytes memory rollback
) private {
(string memory net,) = to.parseNetworkAddress();
ICallService(callSvc).sendCallMessage{value:value}(to, data, rollback, getSources(net), getDestinations(net));
(string memory net,) = to.parseNetworkAddress();
ICallService(callSvc).sendCallMessage{value: value}(to, data, rollback, getSources(net), getDestinations(net));
}

function handleCallMessage(string memory from, bytes memory data, string[] memory protocols) onlyCallService external {
(string memory netFrom,) = from.parseNetworkAddress();

function handleCallMessage(string memory from, bytes memory data, string[] memory protocols) external onlyCallService {
(string memory netFrom,) = from.parseNetworkAddress();
string memory rollbackAddress = ICallService(callSvc).getNetworkAddress();

if (from.compareTo(rollbackAddress)) {
return;
} else {
require(protocolsEqual(protocols, getSources(netFrom)), "invalid protocols");
require(keccak256(data) != keccak256(abi.encodePacked(from)), "failed");
require(keccak256(data) != keccak256(abi.encodePacked("rollback")), "rollback");
emit MessageReceived(from, data);
}
}
Expand Down
9 changes: 4 additions & 5 deletions contracts/evm/contracts/xcall/CallService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -153,11 +153,10 @@ contract CallService is IBSH, ICallService, IFeeManage, Initializable {
(string memory netTo, string memory dstAccount) = _to.parseNetworkAddress();
string memory from = nid.networkAddress(msg.sender.toString());
uint256 sn = getNextSn();
int256 msgSn = 0;
if (needResponse) {
requests[sn] = Types.CallRequest(msg.sender, netTo, sources, _rollback, false);
msgSn = int256(sn);
}
int256 msgSn = int256(sn);
if (needResponse) {
requests[sn] = Types.CallRequest(msg.sender, netTo, sources, _rollback, false);
}
Types.CSMessageRequest memory reqMsg = Types.CSMessageRequest(
from, dstAccount, sn, needResponse, _data, destinations);
bytes memory _msg = reqMsg.encodeCSMessageRequest();
Expand Down
2 changes: 0 additions & 2 deletions contracts/evm/foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ out = "out"
libs = ["lib"]
test = "test"

remappings = ["@xcall/contracts=./contracts", "@iconfoundation/btp2-solidity-library=./library/btp2", "@xcall/utils=./library/utils"]


[rpc_endpoints]
binance_testnet = "${BINANCE_TESTNET_RPC_URL}"
1 change: 1 addition & 0 deletions contracts/evm/lib/wormhole-solidity-sdk
Submodule wormhole-solidity-sdk added at 5b6e2d
5 changes: 5 additions & 0 deletions contracts/evm/library/utils/Types.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,9 @@ library Types {
int code;
}

struct PendingResponse {
bytes msg;
string targetNetwork;
}

}
12 changes: 12 additions & 0 deletions contracts/evm/remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@xcall/contracts/=./contracts/
@iconfoundation/btp2-solidity-library/=./library/btp2/
@xcall/utils/=./library/utils/
ds-test/=lib/forge-std/lib/ds-test/src/
erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/
forge-std/=lib/forge-std/src/
@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/
@openzeppelin/contracts/=lib/openzeppelin-contracts-upgradeable/lib/openzeppelin-contracts/contracts/
openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/
openzeppelin-contracts/=lib/openzeppelin-contracts/
openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/
wormhole-solidity-sdk/=lib/wormhole-solidity-sdk/src/
Loading