From bac99e912fa3957701395520e6b933b1a652f1de Mon Sep 17 00:00:00 2001 From: Reptile <43194093+gator-boi@users.noreply.github.com> Date: Fri, 28 Oct 2022 12:28:40 -0400 Subject: [PATCH] evm: add token and nft bridge interface (#1710) * Add token and nft bridge interface * Add wormhole method to nft and bridge interface * ethereum: add test to ensure interface match * Add all public methods to interfaces Co-authored-by: gator-boi Co-authored-by: Evan Gray --- ethereum/Makefile | 3 + ethereum/compare-method-identifiers.sh | 15 ++ ethereum/contracts/bridge/BridgeGetters.sol | 7 +- .../bridge/interfaces/ITokenBridge.sol | 152 ++++++++++++++++++ .../contracts/bridge/interfaces/IWETH.sol | 11 ++ .../mock/MockTokenBridgeIntegration.sol | 6 +- ethereum/contracts/interfaces/IWormhole.sol | 126 +++++++++++++-- .../contracts/nft/interfaces/INFTBridge.sol | 107 ++++++++++++ 8 files changed, 403 insertions(+), 24 deletions(-) create mode 100755 ethereum/compare-method-identifiers.sh create mode 100644 ethereum/contracts/bridge/interfaces/ITokenBridge.sol create mode 100644 ethereum/contracts/bridge/interfaces/IWETH.sol create mode 100644 ethereum/contracts/nft/interfaces/INFTBridge.sol diff --git a/ethereum/Makefile b/ethereum/Makefile index ae0983215..bd624cf28 100644 --- a/ethereum/Makefile +++ b/ethereum/Makefile @@ -48,6 +48,9 @@ test-upgrade: build .env node_modules .PHONY: test-forge: dependencies + ./compare-method-identifiers.sh contracts/Implementation.sol:Implementation contracts/interfaces/IWormhole.sol:IWormhole + ./compare-method-identifiers.sh contracts/bridge/BridgeImplementation.sol:BridgeImplementation contracts/bridge/interfaces/ITokenBridge.sol:ITokenBridge + ./compare-method-identifiers.sh contracts/nft/NFTBridgeImplementation.sol:NFTBridgeImplementation contracts/nft/interfaces/INFTBridge.sol:INFTBridge forge test clean: diff --git a/ethereum/compare-method-identifiers.sh b/ethereum/compare-method-identifiers.sh new file mode 100755 index 000000000..f690263ef --- /dev/null +++ b/ethereum/compare-method-identifiers.sh @@ -0,0 +1,15 @@ +#!/bin/bash +TMP=$(mktemp -d) +f1="$TMP/$1.interface" +f2="$TMP/$2.interface" +mkdir -p $(dirname "$f1") +mkdir -p $(dirname "$f2") +function clean_up () { + ARG=$? + rm -rf "$TMP" + exit $ARG +} +trap clean_up SIGINT SIGTERM EXIT +forge inspect $1 mi > "$f1" +forge inspect $2 mi > "$f2" +git diff --no-index "$f1" "$f2" --exit-code && echo "✅ Method interfaces are identical" || (echo "❌ Method interfaces are different" >&2 && exit 1) diff --git a/ethereum/contracts/bridge/BridgeGetters.sol b/ethereum/contracts/bridge/BridgeGetters.sol index 05512433d..ea570525a 100644 --- a/ethereum/contracts/bridge/BridgeGetters.sol +++ b/ethereum/contracts/bridge/BridgeGetters.sol @@ -6,6 +6,7 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "../interfaces/IWormhole.sol"; +import "./interfaces/IWETH.sol"; import "./BridgeState.sol"; @@ -74,9 +75,3 @@ contract BridgeGetters is BridgeState { return _state.provider.finality; } } - -interface IWETH is IERC20 { - function deposit() external payable; - - function withdraw(uint amount) external; -} diff --git a/ethereum/contracts/bridge/interfaces/ITokenBridge.sol b/ethereum/contracts/bridge/interfaces/ITokenBridge.sol new file mode 100644 index 000000000..446d67551 --- /dev/null +++ b/ethereum/contracts/bridge/interfaces/ITokenBridge.sol @@ -0,0 +1,152 @@ +// contracts/Bridge.sol +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.0; + +import "./IWETH.sol"; +import "../../interfaces/IWormhole.sol"; + +interface ITokenBridge { + struct Transfer { + uint8 payloadID; + uint256 amount; + bytes32 tokenAddress; + uint16 tokenChain; + bytes32 to; + uint16 toChain; + uint256 fee; + } + + struct TransferWithPayload { + uint8 payloadID; + uint256 amount; + bytes32 tokenAddress; + uint16 tokenChain; + bytes32 to; + uint16 toChain; + bytes32 fromAddress; + bytes payload; + } + + struct AssetMeta { + uint8 payloadID; + bytes32 tokenAddress; + uint16 tokenChain; + uint8 decimals; + bytes32 symbol; + bytes32 name; + } + + struct RegisterChain { + bytes32 module; + uint8 action; + uint16 chainId; + + uint16 emitterChainID; + bytes32 emitterAddress; + } + + struct UpgradeContract { + bytes32 module; + uint8 action; + uint16 chainId; + + bytes32 newContract; + } + + struct RecoverChainId { + bytes32 module; + uint8 action; + + uint256 evmChainId; + uint16 newChainId; + } + + event ContractUpgraded(address indexed oldContract, address indexed newContract); + + function _parseTransferCommon(bytes memory encoded) external pure returns (Transfer memory transfer); + + function attestToken(address tokenAddress, uint32 nonce) external payable returns (uint64 sequence); + + function wrapAndTransferETH(uint16 recipientChain, bytes32 recipient, uint256 arbiterFee, uint32 nonce) external payable returns (uint64 sequence); + + function wrapAndTransferETHWithPayload(uint16 recipientChain, bytes32 recipient, uint32 nonce, bytes memory payload) external payable returns (uint64 sequence); + + function transferTokens(address token, uint256 amount, uint16 recipientChain, bytes32 recipient, uint256 arbiterFee, uint32 nonce) external payable returns (uint64 sequence); + + function transferTokensWithPayload(address token, uint256 amount, uint16 recipientChain, bytes32 recipient, uint32 nonce, bytes memory payload) external payable returns (uint64 sequence); + + function updateWrapped(bytes memory encodedVm) external returns (address token); + + function createWrapped(bytes memory encodedVm) external returns (address token); + + function completeTransferWithPayload(bytes memory encodedVm) external returns (bytes memory); + + function completeTransferAndUnwrapETHWithPayload(bytes memory encodedVm) external returns (bytes memory); + + function completeTransfer(bytes memory encodedVm) external; + + function completeTransferAndUnwrapETH(bytes memory encodedVm) external; + + function encodeAssetMeta(AssetMeta memory meta) external pure returns (bytes memory encoded); + + function encodeTransfer(Transfer memory transfer) external pure returns (bytes memory encoded); + + function encodeTransferWithPayload(TransferWithPayload memory transfer) external pure returns (bytes memory encoded); + + function parsePayloadID(bytes memory encoded) external pure returns (uint8 payloadID); + + function parseAssetMeta(bytes memory encoded) external pure returns (AssetMeta memory meta); + + function parseTransfer(bytes memory encoded) external pure returns (Transfer memory transfer); + + function parseTransferWithPayload(bytes memory encoded) external pure returns (TransferWithPayload memory transfer); + + function governanceActionIsConsumed(bytes32 hash) external view returns (bool); + + function isInitialized(address impl) external view returns (bool); + + function isTransferCompleted(bytes32 hash) external view returns (bool); + + function wormhole() external view returns (IWormhole); + + function chainId() external view returns (uint16); + + function evmChainId() external view returns (uint256); + + function isFork() external view returns (bool); + + function governanceChainId() external view returns (uint16); + + function governanceContract() external view returns (bytes32); + + function wrappedAsset(uint16 tokenChainId, bytes32 tokenAddress) external view returns (address); + + function bridgeContracts(uint16 chainId_) external view returns (bytes32); + + function tokenImplementation() external view returns (address); + + function WETH() external view returns (IWETH); + + function outstandingBridged(address token) external view returns (uint256); + + function isWrappedAsset(address token) external view returns (bool); + + function finality() external view returns (uint8); + + function implementation() external view returns (address); + + function initialize() external; + + function registerChain(bytes memory encodedVM) external; + + function upgrade(bytes memory encodedVM) external; + + function submitRecoverChainId(bytes memory encodedVM) external; + + function parseRegisterChain(bytes memory encoded) external pure returns (RegisterChain memory chain); + + function parseUpgrade(bytes memory encoded) external pure returns (UpgradeContract memory chain); + + function parseRecoverChainId(bytes memory encodedRecoverChainId) external pure returns (RecoverChainId memory rci); +} diff --git a/ethereum/contracts/bridge/interfaces/IWETH.sol b/ethereum/contracts/bridge/interfaces/IWETH.sol new file mode 100644 index 000000000..dbea14135 --- /dev/null +++ b/ethereum/contracts/bridge/interfaces/IWETH.sol @@ -0,0 +1,11 @@ +// contracts/Bridge.sol +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface IWETH is IERC20 { + function deposit() external payable; + function withdraw(uint amount) external; +} \ No newline at end of file diff --git a/ethereum/contracts/bridge/mock/MockTokenBridgeIntegration.sol b/ethereum/contracts/bridge/mock/MockTokenBridgeIntegration.sol index d4d4e6a88..c670c2566 100644 --- a/ethereum/contracts/bridge/mock/MockTokenBridgeIntegration.sol +++ b/ethereum/contracts/bridge/mock/MockTokenBridgeIntegration.sol @@ -9,11 +9,7 @@ import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "../../libraries/external/BytesLib.sol"; import "../../interfaces/IWormhole.sol"; - -interface ITokenBridge { - function completeTransferWithPayload(bytes memory encodedVm) external returns (bytes memory); - function wrappedAsset(uint16 tokenChainId, bytes32 tokenAddress) external view returns (address); -} +import "../interfaces/ITokenBridge.sol"; contract MockTokenBridgeIntegration { using BytesLib for bytes; diff --git a/ethereum/contracts/interfaces/IWormhole.sol b/ethereum/contracts/interfaces/IWormhole.sol index 0a3c48fa3..eba9f6f2e 100644 --- a/ethereum/contracts/interfaces/IWormhole.sol +++ b/ethereum/contracts/interfaces/IWormhole.sol @@ -3,10 +3,80 @@ pragma solidity ^0.8.0; -import "../Structs.sol"; +interface IWormhole { + struct GuardianSet { + address[] keys; + uint32 expirationTime; + } + + struct Signature { + bytes32 r; + bytes32 s; + uint8 v; + uint8 guardianIndex; + } + + struct VM { + uint8 version; + uint32 timestamp; + uint32 nonce; + uint16 emitterChainId; + bytes32 emitterAddress; + uint64 sequence; + uint8 consistencyLevel; + bytes payload; + + uint32 guardianSetIndex; + Signature[] signatures; + + bytes32 hash; + } + + struct ContractUpgrade { + bytes32 module; + uint8 action; + uint16 chain; + + address newContract; + } + + struct GuardianSetUpgrade { + bytes32 module; + uint8 action; + uint16 chain; + + GuardianSet newGuardianSet; + uint32 newGuardianSetIndex; + } + + struct SetMessageFee { + bytes32 module; + uint8 action; + uint16 chain; + + uint256 messageFee; + } + + struct TransferFees { + bytes32 module; + uint8 action; + uint16 chain; + + uint256 amount; + bytes32 recipient; + } + + struct RecoverChainId { + bytes32 module; + uint8 action; + + uint256 evmChainId; + uint16 newChainId; + } -interface IWormhole is Structs { event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel); + event ContractUpgraded(address indexed oldContract, address indexed newContract); + event GuardianSetAdded(uint32 indexed index); function publishMessage( uint32 nonce, @@ -14,29 +84,59 @@ interface IWormhole is Structs { uint8 consistencyLevel ) external payable returns (uint64 sequence); - function parseAndVerifyVM(bytes calldata encodedVM) external view returns (Structs.VM memory vm, bool valid, string memory reason); + function initialize() external; - function verifyVM(Structs.VM memory vm) external view returns (bool valid, string memory reason); + function parseAndVerifyVM(bytes calldata encodedVM) external view returns (VM memory vm, bool valid, string memory reason); - function verifySignatures(bytes32 hash, Structs.Signature[] memory signatures, Structs.GuardianSet memory guardianSet) external pure returns (bool valid, string memory reason) ; + function verifyVM(VM memory vm) external view returns (bool valid, string memory reason); - function parseVM(bytes memory encodedVM) external pure returns (Structs.VM memory vm); + function verifySignatures(bytes32 hash, Signature[] memory signatures, GuardianSet memory guardianSet) external pure returns (bool valid, string memory reason); - function getGuardianSet(uint32 index) external view returns (Structs.GuardianSet memory) ; + function parseVM(bytes memory encodedVM) external pure returns (VM memory vm); - function getCurrentGuardianSetIndex() external view returns (uint32) ; + function quorum(uint numGuardians) external pure returns (uint numSignaturesRequiredForQuorum); - function getGuardianSetExpiry() external view returns (uint32) ; + function getGuardianSet(uint32 index) external view returns (GuardianSet memory); - function governanceActionIsConsumed(bytes32 hash) external view returns (bool) ; + function getCurrentGuardianSetIndex() external view returns (uint32); - function isInitialized(address impl) external view returns (bool) ; + function getGuardianSetExpiry() external view returns (uint32); - function chainId() external view returns (uint16) ; + function governanceActionIsConsumed(bytes32 hash) external view returns (bool); + + function isInitialized(address impl) external view returns (bool); + + function chainId() external view returns (uint16); + + function isFork() external view returns (bool); function governanceChainId() external view returns (uint16); function governanceContract() external view returns (bytes32); - function messageFee() external view returns (uint256) ; + function messageFee() external view returns (uint256); + + function evmChainId() external view returns (uint256); + + function nextSequence(address emitter) external view returns (uint64); + + function parseContractUpgrade(bytes memory encodedUpgrade) external pure returns (ContractUpgrade memory cu); + + function parseGuardianSetUpgrade(bytes memory encodedUpgrade) external pure returns (GuardianSetUpgrade memory gsu); + + function parseSetMessageFee(bytes memory encodedSetMessageFee) external pure returns (SetMessageFee memory smf); + + function parseTransferFees(bytes memory encodedTransferFees) external pure returns (TransferFees memory tf); + + function parseRecoverChainId(bytes memory encodedRecoverChainId) external pure returns (RecoverChainId memory rci); + + function submitContractUpgrade(bytes memory _vm) external; + + function submitSetMessageFee(bytes memory _vm) external; + + function submitNewGuardianSet(bytes memory _vm) external; + + function submitTransferFees(bytes memory _vm) external; + + function submitRecoverChainId(bytes memory _vm) external; } diff --git a/ethereum/contracts/nft/interfaces/INFTBridge.sol b/ethereum/contracts/nft/interfaces/INFTBridge.sol new file mode 100644 index 000000000..251e05b50 --- /dev/null +++ b/ethereum/contracts/nft/interfaces/INFTBridge.sol @@ -0,0 +1,107 @@ +// contracts/NFTBridge.sol +// SPDX-License-Identifier: Apache 2 + +pragma solidity ^0.8.0; + +import "../../interfaces/IWormhole.sol"; + +interface INFTBridge { + struct Transfer { + bytes32 tokenAddress; + uint16 tokenChain; + bytes32 symbol; + bytes32 name; + uint256 tokenID; + string uri; + bytes32 to; + uint16 toChain; + } + + struct SPLCache { + bytes32 name; + bytes32 symbol; + } + + struct RegisterChain { + bytes32 module; + uint8 action; + uint16 chainId; + + uint16 emitterChainID; + bytes32 emitterAddress; + } + + struct UpgradeContract { + bytes32 module; + uint8 action; + uint16 chainId; + + bytes32 newContract; + } + + struct RecoverChainId { + bytes32 module; + uint8 action; + + uint256 evmChainId; + uint16 newChainId; + } + + event ContractUpgraded(address indexed oldContract, address indexed newContract); + + function transferNFT(address token, uint256 tokenID, uint16 recipientChain, bytes32 recipient, uint32 nonce) external payable returns (uint64 sequence); + + function completeTransfer(bytes memory encodeVm) external; + + function encodeTransfer(Transfer memory transfer) external pure returns (bytes memory encoded); + + function parseTransfer(bytes memory encoded) external pure returns (Transfer memory transfer); + + function onERC721Received(address operator, address, uint256, bytes calldata) external view returns (bytes4); + + function governanceActionIsConsumed(bytes32 hash) external view returns (bool); + + function isInitialized(address impl) external view returns (bool); + + function isTransferCompleted(bytes32 hash) external view returns (bool); + + function wormhole() external view returns (IWormhole); + + function chainId() external view returns (uint16); + + function evmChainId() external view returns (uint256); + + function isFork() external view returns (bool); + + function governanceChainId() external view returns (uint16); + + function governanceContract() external view returns (bytes32); + + function wrappedAsset(uint16 tokenChainId, bytes32 tokenAddress) external view returns (address); + + function bridgeContracts(uint16 chainId_) external view returns (bytes32); + + function tokenImplementation() external view returns (address); + + function isWrappedAsset(address token) external view returns (bool); + + function splCache(uint256 tokenId) external view returns (SPLCache memory); + + function finality() external view returns (uint8); + + function initialize() external; + + function implementation() external view returns (address); + + function registerChain(bytes memory encodedVM) external; + + function upgrade(bytes memory encodedVM) external; + + function submitRecoverChainId(bytes memory encodedVM) external; + + function parseRegisterChain(bytes memory encoded) external pure returns(RegisterChain memory chain); + + function parseUpgrade(bytes memory encoded) external pure returns(UpgradeContract memory chain); + + function parseRecoverChainId(bytes memory encodedRecoverChainId) external pure returns (RecoverChainId memory rci); +}