wormhole/ethereum/contracts/Wormhole.sol

240 lines
21 KiB
Solidity
Raw Normal View History

2020-07-26 09:04:45 -07:00
// contracts/Wormhole.sol
2020-08-03 06:09:40 -07:00
// SPDX-License-Identifier: Apache 2
2020-07-26 09:04:45 -07:00
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
2020-08-03 06:09:40 -07:00
import "./BytesLib.sol";
import "./SchnorrSECP256K1.sol";
import "./WrappedAsset.sol";
2020-07-26 09:04:45 -07:00
contract Wormhole {
using SafeERC20 for IERC20;
2020-08-03 06:09:40 -07:00
using BytesLib for bytes;
2020-07-26 09:04:45 -07:00
2020-08-03 06:09:40 -07:00
// Bytecode of WrappedAsset.sol
bytes wrappedAssetBytecode = hex"60806040523480156200001157600080fd5b506040518060400160405280601681526020017f576f726d686f6c652057726170706564204173736574000000000000000000008152506040518060400160405280600681526020017f5741535345540000000000000000000000000000000000000000000000000000815250816003908051906020019062000096929190620000d4565b508060049080519060200190620000af929190620000d4565b506012600560006101000a81548160ff021916908360ff16021790555050506200017a565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200011757805160ff191683800117855562000148565b8280016001018555821562000148579182015b82811115620001475782518255916020019190600101906200012a565b5b5090506200015791906200015b565b5090565b5b80821115620001765760008160009055506001016200015c565b5090565b6119dc806200018a6000396000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c806339509351116100a25780639dc29fac116100715780639dc29fac146104ec578063a457c2d71461053a578063a9059cbb1461059e578063dd62ed3e14610602578063e78cea921461067a57610116565b8063395093511461035f57806340c10f19146103c357806370a082311461041157806395d89b411461046957610116565b8063158ef93e116100e9578063158ef93e1461025e57806318160ddd1461027e5780631ba46cfd1461029c57806323b872dd146102ba578063313ce5671461033e57610116565b8063026b05391461011b57806302a095851461013c57806306fdde0314610177578063095ea7b3146101fa575b600080fd5b6101236106ae565b604051808260ff16815260200191505060405180910390f35b6101756004803603604081101561015257600080fd5b81019080803560ff169060200190929190803590602001909291905050506106c1565b005b61017f6107c6565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101bf5780820151818401526020810190506101a4565b50505050905090810190601f1680156101ec5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6102466004803603604081101561021057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610868565b60405180821515815260200191505060405180910390f35b610266610886565b60405180821515815260200191505060405180910390f35b610286610899565b6040518082815260200191505060405180910390f35b6102a46108a3565b6040518082815260200191505060405180910390f35b610326600480360360608110156102d057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506108a9565b60405180821515815260200191505060405180910390f35b610346610982565b604051808260ff16815260200191505060405180910390f35b6103ab6004803603604081101561037557600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610999565b60405180821515815260200191505060405180910390f35b61040f600480360360408110156103d957600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a4c565b005b6104536004803603602081101561042757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b00565b6040518082815260200191505060405180910390f35b610471610b48565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104b1578082015181840152602081019050610496565b50505050905090810190601f1680156104de5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6105386004803603604081101561050257600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610bea565b005b6105866004803603604081101561055057600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610c9e565b60405180821515815260200191505060405180910390f35b6105ea600480360360408110156105b457600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610d6b565b60405180821515815260200191505060405180910390f35b6106646004803603604081101561061857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610d89
2020-07-26 09:04:45 -07:00
2020-08-03 06:09:40 -07:00
// Chain ID of Ethereum
2020-07-26 09:04:45 -07:00
uint256 CHAIN_ID = 2;
2020-08-03 06:09:40 -07:00
// Address of the official WETH contract
address constant WETHAddress = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
struct GuardianSet {
uint256 x;
uint8 parity;
uint32 expiration_time;
2020-07-26 09:04:45 -07:00
}
2020-08-03 06:09:40 -07:00
event LogGuardianSetChanged(
GuardianSet indexed oldGuardian,
GuardianSet indexed newGuardian
2020-07-26 09:04:45 -07:00
);
event LogTokensLocked(
address indexed token,
bytes32 indexed recipient,
2020-08-03 06:09:40 -07:00
uint8 indexed target_chain,
2020-07-26 09:04:45 -07:00
uint256 amount
);
event LogTokensUnlocked(
address indexed token,
bytes32 indexed sender,
address indexed recipient,
uint256 amount
);
2020-08-03 06:09:40 -07:00
// Mapping of guardian_set_index => guardian set
mapping(uint32 => GuardianSet) private guardian_sets;
// Current active guardian set
uint32 public guardian_set_index;
// Period for which an vaa is valid in seconds
uint32 public vaa_expirity;
// Mapping of already consumedVAAs
mapping(bytes32 => bool) consumedVAAs;
// Mapping of wrapped asset ERC20 contracts
mapping(bytes32 => address) wrappedAssets;
mapping(address => bool) isWrappedAsset;
constructor(GuardianSet memory initial_guardian_set) public {
guardian_sets[0] = initial_guardian_set;
// Explicitly set for doc purposes
guardian_set_index = 0;
}
function submitVAA(
bytes calldata vaa
) public {
uint8 version = vaa.toUint8(0);
require(version == 1, "VAA version incompatible");
// Load 4 bytes starting from index 1
uint32 vaa_guardian_set_index = vaa.toUint32(1);
uint256 signature = vaa.toUint256(2);
address sig_address = vaa.toAddress(34);
2020-07-26 09:04:45 -07:00
2020-08-03 06:09:40 -07:00
// Load 4 bytes starting from index 77
uint32 timestamp = vaa.toUint32(77);
2020-07-26 09:04:45 -07:00
2020-08-03 06:09:40 -07:00
// Verify that the VAA is still valid
require(timestamp + vaa_expirity < block.timestamp, "VAA has expired");
2020-07-26 09:04:45 -07:00
2020-08-03 06:09:40 -07:00
// Hash the body
bytes32 hash = keccak256(vaa.slice(77, vaa.length - 77));
require(!consumedVAAs[hash], "VAA was already executed");
2020-07-26 09:04:45 -07:00
2020-08-03 06:09:40 -07:00
GuardianSet memory guardian_set = guardian_sets[vaa_guardian_set_index];
require(guardian_set.expiration_time == 0 || guardian_set.expiration_time > block.timestamp, "guardian set has expired");
require(
Schnorr.verifySignature(
guardian_set.x,
guardian_set.parity,
signature,
uint256(hash),
sig_address
),
"VAA signataure invalid");
uint8 action = vaa.toUint8(81);
uint8 payload_len = vaa.toUint8(82);
bytes memory payload = vaa.slice(83, payload_len);
// Process VAA
if (action == 0x01) {
vaaUpdateGuardianSet(payload);
} else if (action == 0x10) {
vaaTransfer(payload);
} else {
revert("invalid VAA action");
2020-07-26 09:04:45 -07:00
}
2020-08-03 06:09:40 -07:00
// Set the VAA as consumed
consumedVAAs[hash] = true;
2020-07-26 09:04:45 -07:00
}
2020-08-03 06:09:40 -07:00
function vaaUpdateGuardianSet(bytes memory data) private {
uint256 new_key_x = data.toUint256(0);
uint256 new_key_y = data.toUint256(32);
uint32 new_guardian_set_index = data.toUint32(64);
require(new_guardian_set_index > guardian_set_index, "index of new guardian set must be > current");
require(new_key_x < Schnorr.HALF_Q, "invalid key for fast Schnorr verification");
uint32 old_guardian_set_index = guardian_set_index;
guardian_set_index = new_guardian_set_index;
2020-07-26 09:04:45 -07:00
2020-08-03 06:09:40 -07:00
GuardianSet memory new_guardian_set = GuardianSet(new_key_x, uint8(new_key_y % 2), 0);
guardian_sets[guardian_set_index] = new_guardian_set;
guardian_sets[old_guardian_set_index].expiration_time = uint32(block.timestamp) + vaa_expirity;
2020-07-26 09:04:45 -07:00
2020-08-03 06:09:40 -07:00
emit LogGuardianSetChanged(guardian_sets[old_guardian_set_index], new_guardian_set);
2020-07-26 09:04:45 -07:00
}
2020-08-03 06:09:40 -07:00
function vaaTransfer(bytes memory data) private {
//uint64 nonce = data.toUint64(0);
uint8 source_chain = data.toUint8(8);
uint8 target_chain = data.toUint8(9);
//bytes32 target_address = data.toBytes32(10);
address target_address = data.toAddress(10 + 12);
uint8 token_chain = data.toUint8(42);
//bytes32 token_address = data.toBytes32(43);
uint256 amount = data.toUint8(75);
require(source_chain != target_chain, "same chain transfers are not supported");
require(target_chain == CHAIN_ID, "transfer must be incoming");
if (token_chain != CHAIN_ID) {
bytes32 token_address = data.toBytes32(43);
bytes32 asset_id = keccak256(abi.encodePacked(token_chain, token_address));
// if yes: mint to address
// if no: create and mint
address wrapped_asset = wrappedAssets[asset_id];
if (wrapped_asset == address(0)) {
wrapped_asset = deployWrappedAsset(asset_id, token_chain, token_address);
2020-07-26 09:04:45 -07:00
}
2020-08-03 06:09:40 -07:00
WrappedAsset(wrapped_asset).mint(target_address, amount);
} else {
address token_address = data.toAddress(43 + 12);
IERC20(token_address).safeTransfer(target_address, amount);
2020-07-26 09:04:45 -07:00
}
2020-08-03 06:09:40 -07:00
// Safely transfer tokens out
}
2020-07-26 09:04:45 -07:00
2020-08-03 06:09:40 -07:00
function deployWrappedAsset(bytes32 seed, uint8 token_chain, bytes32 token_address) private returns (address){
bytes memory bytecode = wrappedAssetBytecode;
address addr;
2020-07-26 09:04:45 -07:00
2020-08-03 06:09:40 -07:00
assembly {
addr := create2(0, add(bytecode, 0x20), mload(bytecode), seed)
}
2020-07-26 09:04:45 -07:00
2020-08-03 06:09:40 -07:00
// Initialize asset
WrappedAsset(addr).initialize(token_chain, token_address);
// Store address
wrappedAssets[seed] = addr;
isWrappedAsset[addr] = true;
2020-07-26 09:04:45 -07:00
2020-08-03 06:09:40 -07:00
return addr;
2020-07-26 09:04:45 -07:00
}
function lockAssets(
address asset,
uint256 amount,
2020-08-03 06:09:40 -07:00
bytes32 recipient,
uint8 target_chain,
bool wrapped_allowance
2020-07-26 09:04:45 -07:00
) public {
require(amount != 0, "amount must not be 0");
2020-08-03 06:09:40 -07:00
if (isWrappedAsset[asset]) {
if (wrapped_allowance) {
// First transfer to cover the case where the user has given an allowance
IERC20(asset).safeTransferFrom(msg.sender, address(this), amount);
WrappedAsset(asset).burn(address(this), amount);
} else {
WrappedAsset(asset).burn(msg.sender, amount);
}
} else {
IERC20(asset).safeTransferFrom(msg.sender, address(this), amount);
}
emit LogTokensLocked(asset, recipient, target_chain, amount);
2020-07-26 09:04:45 -07:00
}
function lockETH(
2020-08-03 06:09:40 -07:00
bytes32 recipient,
uint8 target_chain
2020-07-26 09:04:45 -07:00
) public payable {
require(msg.value != 0, "amount must not be 0");
2020-07-28 03:39:38 -07:00
// Wrap tx value in WETH
WETH(WETHAddress).deposit{value : msg.value}();
// Log deposit of WETH
2020-08-03 06:09:40 -07:00
emit LogTokensLocked(WETHAddress, recipient, target_chain, msg.value);
2020-07-26 09:04:45 -07:00
}
2020-07-28 03:39:38 -07:00
2020-08-03 06:09:40 -07:00
fallback() external payable {revert("please use lockETH to transfer ETH to Solana");}
receive() external payable {revert("please use lockETH to transfer ETH to Solana");}
2020-07-26 09:04:45 -07:00
}
2020-07-28 03:39:38 -07:00
interface WETH is IERC20 {
2020-08-03 06:09:40 -07:00
function deposit() external payable;
2020-07-26 09:04:45 -07:00
2020-08-03 06:09:40 -07:00
function withdraw(uint256 amount) external;
2020-07-26 09:04:45 -07:00
}