Set up proxy contract and forge

This commit is contained in:
gator-boi 2022-10-11 18:16:07 +00:00
parent 30453c7953
commit cf2b4884cd
No known key found for this signature in database
GPG Key ID: 09C00ED7DAE976E7
14 changed files with 426 additions and 62 deletions

4
evm/.gitignore vendored
View File

@ -3,3 +3,7 @@
/lib/
/node_modules/
addresses.json
anvil.log
deploy.out
cache
broadcast

View File

@ -6,70 +6,34 @@ pragma solidity >=0.8.0 <0.9.0;
import "./libraries/external/BytesLib.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IWormhole {
function publishMessage(
uint32 nonce,
bytes memory payload,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
function messageFee() external view returns (uint256);
}
interface ITokenBridge {
function wrapAndTransferETH(
uint16 recipientChain,
bytes32 recipient,
uint256 arbiterFee,
uint32 nonce
) external payable returns (uint64 sequence);
function chainId() external view returns (uint16);
function WETH() external view returns (IWETH);
}
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint amount) external;
}
import "./interfaces/ITokenBridge.sol";
// reuse Portal Transfer struct for convenience
struct Transfer {
// PayloadID uint8 = 1
uint8 payloadID;
// Amount being transferred (big-endian uint256)
uint256 amount;
// Address of the token. Left-zero-padded if shorter than 32 bytes
bytes32 tokenAddress;
// Chain ID of the token
uint16 tokenChain;
// Address of the recipient. Left-zero-padded if shorter than 32 bytes
bytes32 to;
// Chain ID of the recipient
uint16 toChain;
// Amount of tokens (big-endian uint256) that the user is willing to pay as relayer fee. Must be <= Amount.
uint256 fee;
}
contract FastTransfer {
IWormhole wormhole;
ITokenBridge portal;
constructor(address wormholeAddress, address portalAddress) {
wormhole = IWormhole(wormholeAddress);
portal = ITokenBridge(portalAddress);
}
import "./FastTransferMessages.sol";
import "./FastTransferSetters.sol";
contract FastTransfer is FastTransferMessages, FastTransferSetters {
function wrapAndTransferETH(
uint16 recipientChain,
bytes32 recipient,
uint256 arbiterFee,
uint32 nonce
) public payable returns (uint64 fastSequence, uint64 portalSequence) {
// cache Wormhole and Portal instance
IWormhole wormhole = wormhole();
ITokenBridge portal = portal();
// Portal accounts for 1 fee, but we must account for 2
uint wormholeFee = wormhole.messageFee();
require(wormholeFee * 2 < msg.value, "value is smaller than wormhole fees");
// compute amound less fees
uint amount = msg.value - wormholeFee * 2;
// Portal will normalize the amount to 8 decimals, so we should do the same
// normalize amount the same way that Portal does
uint normalizedAmount = normalizeAmount(amount, 18);
Transfer memory fastTransfer = Transfer({
// create fast transfer message and publish it
ITokenBridge.Transfer memory fastTransfer = ITokenBridge.Transfer({
payloadID: 1,
amount: normalizedAmount,
tokenAddress: bytes32(uint256(uint160(address(portal.WETH())))),
@ -78,21 +42,15 @@ contract FastTransfer {
toChain: recipientChain,
fee: 0
});
fastSequence = wormhole.publishMessage{
value : wormholeFee
}(
0,
abi.encodePacked(
fastTransfer.payloadID,
fastTransfer.amount,
fastTransfer.tokenAddress,
fastTransfer.tokenChain,
fastTransfer.to,
fastTransfer.toChain,
fastTransfer.fee
),
200
nonce,
encodeFastTransfer(fastTransfer),
finality(true)
);
// Forward the remaining value sans the first fee
portalSequence = portal.wrapAndTransferETH{
value: msg.value - wormholeFee

View File

@ -0,0 +1,35 @@
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.0 <0.9.0;
import "./libraries/external/BytesLib.sol";
import "./interfaces/ITokenBridge.sol";
import "./FastTransferState.sol";
contract FastTransferGetters is FastTransferState {
function owner() public view returns (address) {
return _state.owner;
}
function isInitialized(address impl) public view returns (bool) {
return _state.initializedImplementations[impl];
}
function chainId() public view returns (uint16) {
return _state.chainId;
}
function wormhole() internal view returns (IWormhole) {
return _state.wormhole;
}
function portal() public view returns (ITokenBridge) {
return _state.portal;
}
function finality(bool isFast) public view returns (uint8) {
return isFast ? _state.fastFinality : _state.finality;
}
}

View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.0 <0.9.0;
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
import "./FastTransfer.sol";
contract FastTransferImplementation is FastTransfer, ERC1967Upgrade {
function initialize() initializer public virtual {
// this function needs to be exposed for an upgrade to pass
}
modifier initializer() {
address impl = ERC1967Upgrade._getImplementation();
require(
!isInitialized(impl),
"already initialized"
);
setInitialized(impl);
_;
}
}

View File

@ -0,0 +1,13 @@
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.0 <0.9.0;
import "./libraries/external/BytesLib.sol";
import "./FastTransferGetters.sol";
contract FastTransferMessages is FastTransferGetters {
function encodeFastTransfer(ITokenBridge.Transfer memory transfer) public view returns (bytes memory) {
return portal().encodeTransfer(transfer);
}
}

View File

@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.0 <0.9.0;
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
contract FastTransferProxy is ERC1967Proxy {
constructor (address implementation, bytes memory initData)
ERC1967Proxy(
implementation,
initData
)
{}
}

View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.0 <0.9.0;
import "./interfaces/ITokenBridge.sol";
import "./FastTransferState.sol";
contract FastTransferSetters is FastTransferState {
function setOwner(address owner) internal {
_state.owner = owner;
}
function setInitialized(address implementatiom) internal {
_state.initializedImplementations[implementatiom] = true;
}
function setChainId(uint16 chainId) internal {
_state.chainId = chainId;
}
function setWormhole(address wormholeAddress) internal {
_state.wormhole = IWormhole(payable(wormholeAddress));
}
function setPortal(address portalAddress) internal {
_state.portal = ITokenBridge(payable(portalAddress));
}
function setFinality(uint8 finality_) internal {
_state.finality = finality_;
}
function setFastFinality(uint8 fastFinality_) internal {
_state.fastFinality = fastFinality_;
}
}

View File

@ -0,0 +1,41 @@
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.0 <0.9.0;
import "@openzeppelin/contracts/proxy/ERC1967/ERC1967Upgrade.sol";
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import "./FastTransferSetters.sol";
contract FastTransferSetup is FastTransferSetters, ERC1967Upgrade, Context {
function setup(
address implementation,
uint16 chainId,
address wormhole,
address portal,
uint8 finality
) public {
require(wormhole != address(0), "invalid wormhole address");
require(portal != address(0), "invalid portal address");
require(implementation != address(0), "invalid implementation");
setOwner(_msgSender());
setChainId(chainId);
setWormhole(wormhole);
setPortal(portal);
setFinality(finality);
// fast finality is always 200
setFastFinality(200);
_upgradeTo(implementation);
/// @dev call initialize function of the new implementation
(bool success, bytes memory reason) = implementation.delegatecall(abi.encodeWithSignature("initialize()"));
require(success, string(reason));
}
}

View File

@ -0,0 +1,37 @@
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.0 <0.9.0;
import "./interfaces/ITokenBridge.sol";
contract FastTransferStorage {
struct State {
// address of contract owner
address owner;
// chainId of this contract
uint16 chainId;
// wormhole message finality
uint8 finality;
// portal finality for fast transfers
uint8 fastFinality;
// portal instance
ITokenBridge portal;
// wormhole instance
IWormhole wormhole;
/// mapping of initialized implementations
mapping(address => bool) initializedImplementations;
// storage gap
uint256[50] ______gap;
}
}
contract FastTransferState {
FastTransferStorage.State _state;
}

View File

@ -0,0 +1,93 @@
// contracts/Bridge.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "./IWETH.sol";
import "./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;
}
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 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 wrappedAsset(uint16 tokenChainId, bytes32 tokenAddress) 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);
}

View File

@ -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;
}

View File

@ -0,0 +1,72 @@
// contracts/Messages.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
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;
}
event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel);
function publishMessage(
uint32 nonce,
bytes memory payload,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
function parseAndVerifyVM(bytes calldata encodedVM) external view returns (VM memory vm, bool valid, string memory reason);
function verifyVM(VM memory vm) external view returns (bool valid, string memory reason);
function verifySignatures(bytes32 hash, Signature[] memory signatures, GuardianSet memory guardianSet) external pure returns (bool valid, string memory reason);
function parseVM(bytes memory encodedVM) external pure returns (VM memory vm);
function getGuardianSet(uint32 index) external view returns (GuardianSet memory);
function getCurrentGuardianSetIndex() external view returns (uint32);
function getGuardianSetExpiry() external view returns (uint32);
function governanceActionIsConsumed(bytes32 hash) external view returns (bool);
function isInitialized(address impl) external view returns (bool);
function chainId() external view returns (uint16);
function governanceChainId() external view returns (uint16);
function governanceContract() external view returns (bytes32);
function messageFee() external view returns (uint256);
function evmChainId() external view returns (uint256);
function nextSequence(address emitter) external view returns (uint64);
}

View File

@ -0,0 +1,12 @@
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "forge-std/console.sol";
import "../contracts/FastTransfer.sol";
contract TestFastTransfer is Test {
using BytesLib for bytes;
}

11
evm/foundry.toml Normal file
View File

@ -0,0 +1,11 @@
[profile.default]
src = 'contracts'
out = 'build'
test = 'forge-test'
libs = [
"lib",
"node_modules"
]
# See more config options https://github.com/foundry-rs/foundry/tree/master/config