Merge cf2b4884cd
into 30453c7953
This commit is contained in:
commit
f6e554e160
|
@ -3,3 +3,7 @@
|
|||
/lib/
|
||||
/node_modules/
|
||||
addresses.json
|
||||
anvil.log
|
||||
deploy.out
|
||||
cache
|
||||
broadcast
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
||||
_;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
||||
{}
|
||||
}
|
|
@ -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_;
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue