账户
0x68...59ce
Wormhole

Wormhole

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.8.1+commit.df193b15
语言
Solidity
合同源代码
文件 1 的 2:Migratable.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0 <0.9.0;

abstract contract Migratable {

    event PrepareMigration(uint256 migrationTimestamp, address source, address destination);

    event ApproveMigration(uint256 migrationTimestamp, address source, address destination);

    event ExecuteMigration(uint256 migrationTimestamp, address source, address destination);

    address public controller;

    uint256 public migrationTimestamp;

    address public migrationDestination;

    bool public isMigrated;

    modifier _controller_() {
        require(msg.sender == controller, 'Migratable._controller_: can only called by controller');
        _;
    }

    modifier _valid_() {
        require(!isMigrated, 'Migratable._valid_: cannot proceed, this contract has been migrated');
        _;
    }

    function setController(address newController) public _controller_ _valid_ {
        require(newController != address(0), 'Migratable.setController: to 0 address');
        controller = newController;
    }

    function prepareMigration(address destination, uint256 graceDays) public _controller_ _valid_ {
        require(destination != address(0), 'Migratable.prepareMigration: to 0 address');
        require(graceDays >= 3 && graceDays <= 365, 'Migratable.prepareMigration: graceDays must be 3-365 days');

        migrationTimestamp = block.timestamp + graceDays * 1 days;
        migrationDestination = destination;

        emit PrepareMigration(migrationTimestamp, address(this), migrationDestination);
    }

    function approveMigration() public virtual;

    function executeMigration(address source) public virtual;

}
合同源代码
文件 2 的 2:Wormhole.sol
// SPDX-License-Identifier: MIT

pragma solidity >=0.8.0 <0.9.0;

import './Migratable.sol';

contract Wormhole is Migratable {

    event Freeze (
        address indexed account,
        uint256 amount,
        uint256 fromChainId,
        address fromWormhole,
        uint256 toChainId,
        address toWormhole,
        uint256 nonce,
        uint256 timestamp
    );

    event Claim (
        address indexed account,
        uint256 amount,
        uint256 fromChainId,
        address fromWormhole,
        uint256 toChainId,
        address toWormhole,
        uint256 nonce,
        uint256 timestamp
    );

    string public constant name = 'Wormhole';

    address public tokenAddress;

    bool public allowMintBurn;

    uint256 public nonce;

    uint256 public chainId;

    mapping (bytes32 => bool) public usedHash;

    bytes32 public constant DOMAIN_TYPEHASH = keccak256('EIP712Domain(string name,uint256 chainId,address verifyingContract)');

    bytes32 public constant CLAIM_TYPEHASH = keccak256(
        'Claim(address account,uint256 amount,uint256 fromChainId,address fromWormhole,uint256 toChainId,address toWormhole,uint256 nonce)'
    );

    constructor (address tokenAddress_, bool allowMintBurn_) {
        controller = msg.sender;
        tokenAddress = tokenAddress_;
        allowMintBurn = allowMintBurn_;
        uint256 _chainId;
        assembly {
            _chainId := chainid()
        }
        chainId = _chainId;
    }

    function approveMigration() public override _controller_ _valid_ {
        require(migrationTimestamp != 0 && block.timestamp >= migrationTimestamp, 'Wormhole.approveMigration: migrationTimestamp not met yet');
        if (allowMintBurn) {
            IERC20(tokenAddress).setController(migrationDestination);
        } else {
            IERC20(tokenAddress).approve(migrationDestination, type(uint256).max);
        }
        isMigrated = true;

        emit ApproveMigration(migrationTimestamp, address(this), migrationDestination);
    }

    function executeMigration(address source) public override _controller_ _valid_ {
        uint256 _migrationTimestamp = IWormhole(source).migrationTimestamp();
        address _migrationDestination = IWormhole(source).migrationDestination();
        require(_migrationTimestamp != 0 && block.timestamp >= _migrationTimestamp, 'Wormhole.executeMigration: migrationTimestamp not met yet');
        require(_migrationDestination == address(this), 'Wormhole.executeMigration: not destination address');

        if (!IWormhole(source).allowMintBurn()) {
            IERC20(tokenAddress).transferFrom(source, address(this), IERC20(tokenAddress).balanceOf(source));
        }

        emit ExecuteMigration(_migrationTimestamp, source, address(this));
    }

    function freeze(uint256 amount, uint256 toChainId, address toWormhole) public _valid_ {
        require(amount > 0, 'Wormhole.freeze: 0 amount');
        require(toChainId != chainId, 'Wormhole.freeze: to the same chain');
        if (allowMintBurn) {
            IERC20(tokenAddress).burn(msg.sender, amount);
        } else {
            IERC20(tokenAddress).transferFrom(msg.sender, address(this), amount);
        }
        emit Freeze(msg.sender, amount, chainId, address(this), toChainId, toWormhole, nonce++, block.timestamp);
    }

    function claim(uint256 amount, uint256 fromChainId, address fromWormhole, uint256 fromNonce, uint8 v, bytes32 r, bytes32 s) public _valid_ {
        bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), chainId, address(this)));
        bytes32 structHash = keccak256(abi.encode(CLAIM_TYPEHASH, msg.sender, amount, fromChainId, fromWormhole, chainId, address(this), fromNonce));
        require(!usedHash[structHash], 'Wormhole.claim: replay');
        usedHash[structHash] = true;

        bytes32 digest = keccak256(abi.encodePacked('\x19\x01', domainSeparator, structHash));
        address signatory = ecrecover(digest, v, r, s);
        require(signatory == controller, 'Wormhole.claim: unauthorized');

        if (allowMintBurn) {
            IERC20(tokenAddress).mint(msg.sender, amount);
        } else {
            IERC20(tokenAddress).transfer(msg.sender, amount);
        }

        emit Claim(msg.sender, amount, fromChainId, fromWormhole, chainId, address(this), fromNonce, block.timestamp);
    }

}

interface IERC20 {
    function setController(address newController) external;
    function balanceOf(address account) external view returns (uint256);
    function approve(address account, uint256 amount) external returns (bool);
    function transfer(address to, uint256 amount) external returns (bool);
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function mint(address account, uint256 amount) external;
    function burn(address account, uint256 amount) external;
}

interface IWormhole {
    function migrationTimestamp() external view returns (uint256);
    function migrationDestination() external view returns (address);
    function allowMintBurn() external view returns (bool);
}
设置
{
  "compilationTarget": {
    "contracts/Wormhole.sol": "Wormhole"
  },
  "evmVersion": "istanbul",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"inputs":[{"internalType":"address","name":"tokenAddress_","type":"address"},{"internalType":"bool","name":"allowMintBurn_","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"migrationTimestamp","type":"uint256"},{"indexed":false,"internalType":"address","name":"source","type":"address"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"ApproveMigration","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fromChainId","type":"uint256"},{"indexed":false,"internalType":"address","name":"fromWormhole","type":"address"},{"indexed":false,"internalType":"uint256","name":"toChainId","type":"uint256"},{"indexed":false,"internalType":"address","name":"toWormhole","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"migrationTimestamp","type":"uint256"},{"indexed":false,"internalType":"address","name":"source","type":"address"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"ExecuteMigration","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fromChainId","type":"uint256"},{"indexed":false,"internalType":"address","name":"fromWormhole","type":"address"},{"indexed":false,"internalType":"uint256","name":"toChainId","type":"uint256"},{"indexed":false,"internalType":"address","name":"toWormhole","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"Freeze","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"migrationTimestamp","type":"uint256"},{"indexed":false,"internalType":"address","name":"source","type":"address"},{"indexed":false,"internalType":"address","name":"destination","type":"address"}],"name":"PrepareMigration","type":"event"},{"inputs":[],"name":"CLAIM_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allowMintBurn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"approveMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"chainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"fromChainId","type":"uint256"},{"internalType":"address","name":"fromWormhole","type":"address"},{"internalType":"uint256","name":"fromNonce","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"controller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"source","type":"address"}],"name":"executeMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"toChainId","type":"uint256"},{"internalType":"address","name":"toWormhole","type":"address"}],"name":"freeze","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isMigrated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"migrationDestination","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"migrationTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"destination","type":"address"},{"internalType":"uint256","name":"graceDays","type":"uint256"}],"name":"prepareMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newController","type":"address"}],"name":"setController","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"usedHash","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]