initial commit

This commit is contained in:
Hendrik Hofstadt 2020-07-26 18:04:45 +02:00
commit 82d07db5e6
8 changed files with 6960 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
build
node_modules
.idea
.arcconfig
*.iml

View File

@ -0,0 +1,22 @@
{
"manifestVersion": "2.2",
"contracts": {},
"dependencies": {},
"name": "wormhole",
"version": "0.1.0",
"compiler": {
"compilerSettings": {
"optimizer": {
"enabled": false,
"runs": "200"
}
},
"typechain": {
"enabled": false
},
"manager": "openzeppelin",
"solcVersion": "0.6.12",
"artifactsDir": "build/contracts",
"contractsDir": "contracts"
}
}

0
contracts/.gitkeep Normal file
View File

197
contracts/Wormhole.sol Normal file
View File

@ -0,0 +1,197 @@
// contracts/Wormhole.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/utils/EnumerableSet.sol";
contract Wormhole {
using SafeERC20 for IERC20;
using EnumerableSet for EnumerableSet.AddressSet;
address constant WETHAddress = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2;
uint256 CHAIN_ID = 2;
struct Signature {
uint8 v;
bytes32 r;
bytes32 s;
}
event LogGuardianKeyChanged(
address indexed oldGuardian,
address indexed newGuardian
);
event LogTokensLocked(
address indexed token,
bytes32 indexed recipient,
uint256 amount
);
event LogTokensUnlocked(
address indexed token,
bytes32 indexed sender,
address indexed recipient,
uint256 amount
);
EnumerableSet.AddressSet private guardians;
mapping(address => address) pendingGuardianTransfers;
// Mappings guardian <=> authorized signer
mapping(address => address) guardianToAuthorizedSigner;
mapping(address => address) authorizedSignerToGuardian;
// Mapping of already completed transactions
mapping(bytes32 => bool) completedTransactions;
constructor(address[] memory _guardians) public {
require(_guardians.length > 0, "no guardians specified");
for (uint i = 0; i < _guardians.length; i++) {
guardians.add(_guardians[i]);
}
}
function() public payable {
revert("please use lockETH to transfer ETH to Solana");
}
function changeGuardianAdmin(address newAddress) public {
require(guardians.contains(msg.sender), "sender is not a guardian");
pendingGuardianTransfers[msg.sender] = newAddress;
}
function confirmGuardianAdminChange(address oldAddress) public {
require(pendingGuardianTransfers[oldAddress] == msg.sender, "no pending key change to this account");
// Swap guardian
require(guardians.remove(oldAddress), "account oldAddress is not a guardian");
require(guardians.add(msg.sender), "sender is already a guardian");
// Migrate authorizedSigner
address oldAuthorizedSigner = guardianToAuthorizedSigner[oldAddress];
authorizedSignerToGuardian[oldAuthorizedSigner] = msg.sender;
guardianToAuthorizedSigner[msg.sender] = oldAuthorizedSigner;
// Remove pending transfer
pendingGuardianTransfers[oldAddress] = address(0);
emit LogGuardianKeyChanged(
oldAddress,
msg.sender
);
}
function changeAuthorizedSigner(address newSigner) public {
require(guardians.contains(msg.sender), "sender is not a guardian");
require(authorizedSignerToGuardian[msg.sender] == address(0), "new signer is already a signer");
// Unset old mapping
address oldAuthorizedSigner = guardianToAuthorizedSigner[msg.sender];
authorizedSignerToGuardian[oldAuthorizedSigner] = address(0);
// Add new mapping
authorizedSignerToGuardian[newSigner] = msg.sender;
guardianToAuthorizedSigner[msg.sender] = newSigner;
}
function unlockERC20(
address asset,
uint256 amount,
uint256 height,
bytes32 sender,
address recipient,
Signature[] calldata signatures
) public {
require(recipient != address(0), "assets should not be burned");
// unlock data structure
// asset 32bytes
// height uint256
// amount uint256
// target_chain 32bytes
// sender 32bytes
// recipient 32bytes
bytes32 hash = keccak256(
abi.encodePacked(
bytes32(uint256(asset)),
amount,
height,
bytes32(CHAIN_ID),
sender,
bytes32(uint256(recipient))
)
);
require(!completedTransactions[hash], "transfer was already executed");
uint nSignatures = 0;
address[] memory alreadySigned = new address[](signatures.length);
for (uint256 i = 0; i < signatures.length; i++) {
address signer = ecrecover(
hash,
signatures[i].v,
signatures[i].r,
signatures[i].s
);
require(
guardians.contains(authorizedSignerToGuardian[signer]),
"signature of non-guardian included"
);
for (uint j = 0; j < alreadySigned.length; j++) {
require(signer != alreadySigned[j], "multiple signatures of the same guardian included");
}
alreadySigned[i] = signer;
nSignatures++;
}
// Check whether the threshold was met
require(
nSignatures > 5,
"not enough valid signatures attached to unlock funds"
);
// Safely transfer tokens out
IERC20(asset).safeTransfer(recipient, amount);
// Set the transfer as completed
completedTransactions[hash] = true;
emit LogTokensUnlocked(asset, sender, recipient, amount);
}
function lockAssets(
address asset,
uint256 amount,
bytes32 recipient
) public {
require(amount != 0, "amount must not be 0");
IERC20(asset).safeTransferFrom(msg.sender, address(this), amount);
emit LogTokensLocked(asset, recipient, amount);
}
function lockETH(
bytes32 recipient
) public payable {
require(msg.value != 0, "amount must not be 0");
WETH(WETHAddress).deposit(msg.value);
emit LogTokensLocked(WETHAddress, recipient, msg.value);
}
}
contract WETH is IERC20 {
function deposit() public payable {}
function withdraw(uint256 amount) public {}
}

17
contracts/WrappedSPL.sol Normal file
View File

@ -0,0 +1,17 @@
// contracts/Wormhole.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract WrappedSPLToken is ERC20 {
constructor(address[] memory _guardians) public {
require(_guardians.length > 0, "no guardians specified");
for (uint i = 0; i < _guardians.length; i++) {
guardians.add(_guardians[i]);
}
}
}

12
networks.js Normal file
View File

@ -0,0 +1,12 @@
module.exports = {
networks: {
development: {
protocol: 'http',
host: 'localhost',
port: 8545,
gas: 5000000,
gasPrice: 5e9,
networkId: '*',
},
},
};

6691
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

16
package.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "wormhole",
"version": "1.0.0",
"description": "",
"main": "networks.js",
"dependencies": {},
"devDependencies": {
"@openzeppelin/cli": "^2.8.2",
"@openzeppelin/contracts": "^3.1.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}