账户
0xfc...c4ac
0xfC...C4AC

0xfC...C4AC

$500
此合同的源代码已经过验证!
合同元数据
编译器
0.5.10+commit.5a6ea5b1
语言
Solidity
合同源代码
文件 1 的 1:CrossBlockchainSwap.sol
/**
 *Submitted for verification at Etherscan.io on 2019-08-23
*/

pragma solidity 0.5.10;

/**
 * @title SafeMath
 * @dev Unsigned math operations with safety checks that revert on error
 */
library SafeMath {
    /**
     * @dev Multiplies two unsigned integers, reverts on overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b);

        return c;
    }

    /**
     * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend).
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Adds two unsigned integers, reverts on overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a);

        return c;
    }

    /**
     * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo),
     * reverts when dividing by zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b != 0);
        return a % b;
    }
}

/**
 * Utility library of inline functions on addresses
 */
library Address {
    /**
     * Returns whether the target address is a contract
     * @dev This function will return false if invoked during the constructor of a contract,
     * as the code is not actually created until after the constructor finishes.
     * @param account address of the account to check
     * @return whether the target address is a contract
     */
    function isContract(address account) internal view returns (bool) {
        uint256 size;
        // XXX Currently there is no better way to check if there is a contract in an address
        // than to check the size of the code at that address.
        // See https://ethereum.stackexchange.com/a/14016/36603
        // for more details about how this works.
        // TODO Check this again before the Serenity release, because all addresses will be
        // contracts then.
        // solhint-disable-next-line no-inline-assembly
        assembly {size := extcodesize(account)}
        return size > 0;
    }
}

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using SafeMath for uint256;
    using Address for address;

    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require((value == 0) || (token.allowance(address(this), spender) == 0));
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).add(value);
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 newAllowance = token.allowance(address(this), spender).sub(value);
        callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must equal true).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves.

        // A Solidity high level call has three parts:
        //  1. The target address is checked to verify it contains contract code
        //  2. The call itself is made, and success asserted
        //  3. The return value is decoded, which in turn checks the size of the returned data.

        require(address(token).isContract());

        // solhint-disable-next-line avoid-low-level-calls
        (bool success, bytes memory returndata) = address(token).call(data);
        require(success);

        if (returndata.length > 0) {// Return data is optional
            require(abi.decode(returndata, (bool)));
        }
    }
}

/**
 * @title ERC20 interface
 * @dev see https://eips.ethereum.org/EIPS/eip-20
 */
interface IERC20 {
    function transfer(address to, uint256 value) external returns (bool);

    function approve(address spender, uint256 value) external returns (bool);

    function transferFrom(address from, address to, uint256 value) external returns (bool);

    function totalSupply() external view returns (uint256);

    function balanceOf(address who) external view returns (uint256);

    function allowance(address owner, address spender) external view returns (uint256);

    event Transfer(address indexed from, address indexed to, uint256 value);

    event Approval(address indexed owner, address indexed spender, uint256 value);
}

/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev The Ownable constructor sets the original `owner` of the contract to the sender
     * account.
     */
    constructor () internal {
        _owner = msg.sender;
        emit OwnershipTransferred(address(0), _owner);
    }

    /**
     * @return the address of the owner.
     */
    function owner() public view returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(isOwner());
        _;
    }

    /**
     * @return true if `msg.sender` is the owner of the contract.
     */
    function isOwner() public view returns (bool) {
        return msg.sender == _owner;
    }

    /**
     * @dev Allows the current owner to relinquish control of the contract.
     * It will not be possible to call the functions with the `onlyOwner`
     * modifier anymore.
     * @notice Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public onlyOwner {
        emit OwnershipTransferred(_owner, address(0));
        _owner = address(0);
    }

    /**
     * @dev Allows the current owner to transfer control of the contract to a newOwner.
     * @param newOwner The address to transfer ownership to.
     */
    function transferOwnership(address newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers control of the contract to a newOwner.
     * @param newOwner The address to transfer ownership to.
     */
    function _transferOwnership(address newOwner) internal {
        require(newOwner != address(0));
        emit OwnershipTransferred(_owner, newOwner);
        _owner = newOwner;
    }
}

/**
 * @title AssetsValue
 * @dev The contract which hold all tokens and ETH as a assets
 * Also should be responsible for the balance increasing/decreasing and validation
 */
contract AssetsValue {
    // using safe math calculation
    using SafeMath for uint256;

    // for being secure during transactions between users and contract gonna use SafeERC20 lib
    using SafeERC20 for IERC20;

    // The id for ETH asset is 0x0 address
    // the rest assets should have their token contract address
    address internal _ethAssetIdentificator = address(0);

    // Order details which is available by JAVA long number
    struct OrderDetails {
        // does the order has been deposited
        bool created;
        // the 0x0 for Ethereum and ERC contract address for tokens
        address asset;
        // tokens/eth amount
        uint256 amount;
        // the status (deposited/withdrawn)
        bool withdrawn;
        //Creation time
        uint256 initTimestamp;
    }

    // Each user has his own state and details
    struct User {
        // user exist validation bool
        bool exist;
        // contract order index
        uint256 index;
        // contract index (0, 1, 2 ...) => exchange order number (JAVA long number)
        mapping(uint256 => uint256) orderIdByIndex;
        // JAVA long number => order details
        mapping(uint256 => OrderDetails) orders;
    }

    // ETH wallet => Assets => value
    mapping(address => User) private _users;

    modifier orderIdNotExist(
        uint256 orderId,
        address user
    ) {
        require(_users[user].orders[orderId].created == false, "orderIdIsNotDeposited: user already deposit this orderId");
        _;
    }

    // Events
    event AssetDeposited(uint256 orderId, address indexed user, address indexed asset, uint256 amount);
    event AssetWithdrawal(uint256 orderId, address indexed user, address indexed asset, uint256 amount);

    // -----------------------------------------
    // EXTERNAL
    // -----------------------------------------

    function deposit(
        uint256 orderId
    ) public orderIdNotExist(orderId, msg.sender) payable {
        require(msg.value != 0, "deposit: user needs to transfer ETH for calling this method");

        _deposit(orderId, msg.sender, _ethAssetIdentificator, msg.value);
    }

    function deposit(
        uint256 orderId,
        uint256 amount,
        address token
    ) public orderIdNotExist(orderId, msg.sender) {
        require(token != address(0), "deposit: invalid token address");
        require(amount != 0, "deposit: user needs to fill transferable tokens amount for calling this method");

        IERC20(token).safeTransferFrom(msg.sender, address(this), amount);
        _deposit(orderId, msg.sender, token, amount);
    }

    function withdraw(
        uint256 orderId
    ) external {
        // validation of the user existion
        require(_doesUserExist(msg.sender) == true, "withdraw: the user is not active");

        // storing the order information (asset and amount)
        OrderDetails memory order = _getDepositedOrderDetails(orderId, msg.sender);
        address asset = order.asset;
        uint256 amount = order.amount;

        // order amount validation, it should not be zero
        require(order.withdrawn == false, "withdraw: this order Id has been already withdrawn or waiting for the swap");

        _withdrawOrderBalance(orderId, msg.sender);

        if (asset == _ethAssetIdentificator) {
            msg.sender.transfer(amount);
        } else {
            IERC20(asset).safeTransfer(msg.sender, amount);
        }

        emit AssetWithdrawal(orderId, msg.sender, asset, amount);
    }

    // -----------------------------------------
    // INTERNAL
    // -----------------------------------------

    function _deposit(
        uint256 orderId,
        address sender,
        address asset,
        uint256 amount
    ) internal {
        _activateIfUserIsNew(sender);
        _depositOrderBalance(orderId, sender, asset, amount);

        _users[sender].index += 1;

        emit AssetDeposited(orderId, sender, asset, amount);
    }

    function _doesUserExist(
        address user
    ) internal view returns (bool) {
        return _users[user].exist;
    }

    function _activateIfUserIsNew(
        address user
    ) internal returns (bool) {
        if (_doesUserExist(user) == false) {
            _users[user].exist = true;
        }
        return true;
    }

    function _getDepositedOrderDetails(
        uint256 orderId,
        address user
    ) internal view returns (OrderDetails memory order) {
        return _users[user].orders[orderId];
    }

    function _depositOrderBalance(
        uint256 orderId,
        address user,
        address asset,
        uint256 amount
    ) internal returns (bool) {
        _users[user].orderIdByIndex[_users[user].index] = orderId;
        _users[user].orders[orderId] = OrderDetails(true, asset, amount, false, block.timestamp);
        return true;
    }

    function _withdrawOrderBalance(
        uint256 orderId,
        address user
    ) internal returns (bool) {
        _users[user].orders[orderId].withdrawn = true;
        return true;
    }

    // -----------------------------------------
    // GETTERS
    // -----------------------------------------

    function doesUserExist(
        address user
    ) external view returns (bool) {
        return _doesUserExist(user);
    }

    function getUserFilledDeposits(
        address user
    ) external view returns (
        uint256[] memory orderIds,
        uint256[] memory amounts,
        uint256[] memory initTimestamps
    ) {
        // amount of deposits which user has been done
        uint256 depositsLength = _users[user].index;

        // init empty arrays which should been returned
        orderIds = new uint256[](depositsLength);
        amounts = new uint256[](depositsLength);
        initTimestamps = new uint256[](depositsLength);

        uint256 j = 0;
        for (uint256 i = 0; i <= depositsLength; i++) {
            uint256 orderId = _users[user].orderIdByIndex[i];
            if (_users[user].orders[orderId].created) {
                orderIds[j] = orderId;

                OrderDetails memory order = _users[user].orders[orderId];
                amounts[j] = order.amount;
                initTimestamps[j] = order.initTimestamp;
                j++;
            }
        }

        return (
        orderIds,
        amounts,
        initTimestamps
        );
    }

    function getUserDepositsAmount(
        address user
    ) external view returns (
        uint256
    ) {
        return _users[user].index;
    }

    function getDepositedOrderDetails(
        uint256 orderId,
        address user
    ) external view returns (
        bool created,
        address asset,
        uint256 amount,
        bool withdrawn
    ) {
        OrderDetails memory order = _getDepositedOrderDetails(orderId, user);
        return (
        order.created,
        order.asset,
        order.amount,
        order.withdrawn
        );
    }
}

/**
 * @title CrossBlockchainSwap
 * @dev Fully autonomous cross-blockchain swapping smart contract
 */
contract CrossBlockchainSwap is AssetsValue, Ownable {
    // swaps' state
    enum State {Empty, Filled, Redeemed, Refunded}

    // users can swap ETH and ERC tokens
    enum SwapType {ETH, Token}

    // the body of each swap
    struct Swap {
        uint256 initTimestamp;
        uint256 refundTimestamp;
        bytes32 secretHash;
        bytes32 secret;
        address initiator;
        address recipient;
        address asset;
        uint256 amount;
        uint256 orderId;
        State state;
    }

    struct Initiator {
        // contract order index
        uint256 index;
        // length of filled swaps
        uint256 filledSwaps;
        // index (0, 1, 2 ...) => swap hash
        mapping(uint256 => bytes32) swaps;
    }

    // mapping of swaps based on secret hash and swap info
    mapping(bytes32 => Swap) private _swaps;

    // the swaps data by initiator address
    mapping(address => Initiator) private _initiators;

    // min/max life limits for swap order
    // can be changed only by the contract owner
    struct SwapTimeLimits {
        uint256 min;
        uint256 max;
    }

    // By default, the contract has limits for swap orders lifetime
    // The swap order can be active from 10 minutes until 6 months
    SwapTimeLimits private _swapTimeLimits = SwapTimeLimits(10 minutes, 180 days);

    // -----------------------------------------
    // EVENTS
    // -----------------------------------------

    event Initiated(
        uint256 orderId,
        bytes32 secretHash,
        address indexed initiator,
        address indexed recipient,
        uint256 initTimestamp,
        uint256 refundTimestamp,
        address indexed asset,
        uint256 amount
    );

    event Redeemed(
        bytes32 secretHash,
        uint256 redeemTimestamp,
        bytes32 secret,
        address indexed redeemer
    );

    event Refunded(
        uint256 orderId,
        bytes32 secretHash,
        uint256 refundTime,
        address indexed refunder
    );

    // -----------------------------------------
    // MODIFIERS
    // -----------------------------------------

    modifier isNotInitiated(bytes32 secretHash) {
        require(_swaps[secretHash].state == State.Empty, "isNotInitiated: this secret hash was already used, please use another one");
        _;
    }

    modifier isRedeemable(bytes32 secret) {
        bytes32 secretHash = _hashTheSecret(secret);
        require(_swaps[secretHash].state == State.Filled, "isRedeemable: the swap with this secretHash does not exist or has been finished");
        uint256 refundTimestamp = _swaps[secretHash].refundTimestamp;
        require(refundTimestamp > block.timestamp, "isRedeemable: the redeem is closed for this swap");
        _;
    }

    modifier isRefundable(bytes32 secretHash, address refunder) {
        require(_swaps[secretHash].state == State.Filled, "isRefundable: the swap with this secretHash does not exist or has been finished");
        require(_swaps[secretHash].initiator == refunder, "isRefundable: only the initiator of the swap can call this method");
        uint256 refundTimestamp = _swaps[secretHash].refundTimestamp;
        require(block.timestamp >= refundTimestamp, "isRefundable: the refund is not available now");
        _;
    }

    // -----------------------------------------
    // FALLBACK
    // -----------------------------------------

    function() external payable {
        // reverts all fallback & payable transactions
        revert();
    }

    // -----------------------------------------
    // EXTERNAL
    // -----------------------------------------

    /**
     *  @dev If user wants to swap ERC token, before initiating the swap between that
     *  initiator need to call approve method from his tokens' smart contract,
     *  approving to it to spend the value1 amount of tokens
     *  @param secretHash the encoded secret which they discussed at offline (SHA256)
     *  @param refundTimestamp the period when the swap should be active
     *  it should be written in MINUTES
     */
    function initiate(
        uint256 orderId,
        bytes32 secretHash,
        address recipient,
        uint256 refundTimestamp
    ) public isNotInitiated(secretHash) {
        // validation that refund Timestamp more than exchange min limit and less then max limit
        _validateRefundTimestamp(refundTimestamp * 1 minutes);

        OrderDetails memory order = _getDepositedOrderDetails(orderId, msg.sender);

        // validation of the deposited order existing and non-zero amount
        require(order.created == true, "initiate: this order Id has not been created and deposited yet");
        require(order.withdrawn == false, "initiate: this order deposit has been withdrawn");
        require(order.amount != 0, "initiate: this order Id has been withdrawn, finished or waiting for the redeem");

        // withdrawing the balance of this orderId from sender deposites
        _withdrawOrderBalance(orderId, msg.sender);

        // swap asset details
        _swaps[secretHash].asset = order.asset;
        _swaps[secretHash].amount = order.amount;

        // swap status
        _swaps[secretHash].state = State.Filled;

        // swap clients
        _swaps[secretHash].initiator = msg.sender;
        _swaps[secretHash].recipient = recipient;
        _swaps[secretHash].secretHash = secretHash;
        _swaps[secretHash].orderId = orderId;

        // swap timestapms
        _swaps[secretHash].initTimestamp = block.timestamp;
        _swaps[secretHash].refundTimestamp = block.timestamp + (refundTimestamp * 1 minutes);

        // updating the initiator state
        Initiator storage initiator = _initiators[msg.sender];
        initiator.swaps[initiator.index] = secretHash;
        initiator.index++;
        initiator.filledSwaps++;

        emit Initiated(
            orderId,
            secretHash,
            msg.sender,
            recipient,
            block.timestamp,
            refundTimestamp,
            order.asset,
            order.amount
        );
    }

    /**
     *  @dev The participant of swap, who has the secret word and the secret hash can call this method
     *  and receive assets from contract.
     *  @param secret which both sides discussed before initialization
     */
    function redeem(
        bytes32 secret
    ) external isRedeemable(secret) {
        // storing the secret hash generated from secret
        bytes32 secretHash = _hashTheSecret(secret);

        // closing the state of this swap order
        _swaps[secretHash].state = State.Redeemed;

        // storing the recipient address
        address recipient = _swaps[secretHash].recipient;

        if (_getSwapType(secretHash) == SwapType.ETH) {
            // converting recipient address to payable address
            address payable payableReceiver = address(uint160(recipient));
            // transfer ETH to recipient wallet
            payableReceiver.transfer(_swaps[secretHash].amount);
        } else {
            // transfer tokens to recipient address
            IERC20(_swaps[secretHash].asset).safeTransfer(recipient, _swaps[secretHash].amount);
        }

        // saving the secret
        _swaps[secretHash].secret = secret;

        // decrease the filled swapss amount
        _initiators[_swaps[secretHash].initiator].filledSwaps--;

        emit Redeemed(
            secretHash,
            block.timestamp,
            secret,
            recipient
        );
    }

    /**
     *  @dev The initiator can get back his tokens until refundTimestamp comes,
     *  after that both sides cannot do anything with this swap
     *  @param secretHash the encoded secret which they discussed at offline (SHA256)
     */
    function refund(
        bytes32 secretHash
    ) public isRefundable(secretHash, msg.sender) {
        _swaps[secretHash].state = State.Refunded;
        _depositOrderBalance(_swaps[secretHash].orderId, msg.sender, _swaps[secretHash].asset, _swaps[secretHash].amount);

        // decrease the filled swapss amount
        _initiators[msg.sender].filledSwaps--;

        emit Refunded(
            _swaps[secretHash].orderId,
            secretHash,
            block.timestamp,
            msg.sender
        );
    }

    /**
     *  @dev The owner can change time limits for swap lifetime
     *  Amounts should be written in MINUTES
     */
    function changeSwapLifetimeLimits(
        uint256 newMin,
        uint256 newMax
    ) external onlyOwner {
        require(newMin != 0, "changeSwapLifetimeLimits: newMin and newMax should be bigger then 0");
        require(newMax >= newMin, "changeSwapLifetimeLimits: the newMax should be bigger then newMax");

        _swapTimeLimits = SwapTimeLimits(newMin * 1 minutes, newMax * 1 minutes);
    }

    // -----------------------------------------
    // INTERNAL
    // -----------------------------------------

    /**
     *  @dev Validating the period time of swap
     * It should be equal/bigger than 10 minutes and equal/less than 180 days
     */
    function _validateRefundTimestamp(
        uint256 refundTimestamp
    ) private view {
        require(refundTimestamp >= _swapTimeLimits.min, "_validateRefundTimestamp: the timestamp should be bigger than min swap lifetime");
        require(_swapTimeLimits.max >= refundTimestamp, "_validateRefundTimestamp: the timestamp should be smaller than max swap lifetime");
    }

    function _hashTheSecret(
        bytes32 secret
    ) private pure returns (bytes32) {
        return sha256(abi.encodePacked(secret));
    }

    function _getSwapType(
        bytes32 secretHash
    ) private view returns (SwapType tp) {
        if (_swaps[secretHash].asset == _ethAssetIdentificator) {
            return SwapType.ETH;
        } else {
            return SwapType.Token;
        }
    }

    // -----------------------------------------
    // GETTERS
    // -----------------------------------------

    /**
     *  @dev Get filled swaps order ids and amounts
     *  @return uint256[] of order ids
     *  @return uint256[] of amounts
     */
    function getUserFilledOrders(address user) external view returns (
        uint256[] memory amounts,
        uint256[] memory orderIds,
        uint256[] memory initTimestamps
    ) {
        uint256 swapsLength = _initiators[user].index;
        uint256 filledSwaps = _initiators[user].filledSwaps;

        orderIds = new uint256[](filledSwaps);
        amounts = new uint256[](filledSwaps);
        initTimestamps = new uint256[](filledSwaps);

        uint256 j = 0;
        for (uint256 i = 0; i <= swapsLength; i++) {
            Swap memory swap = _swaps[_initiators[user].swaps[i]];
            if (swap.state == State.Filled) {
                amounts[j] = swap.amount;
                orderIds[j] = swap.orderId;
                initTimestamps[j] = swap.initTimestamp;
                j++;
            }
        }

        return (
        orderIds,
        amounts,
        initTimestamps
        );
    }

    /**
     *  @dev Get limits of a lifetime for swap in minutes
     *  @return min lifetime
     *  @return max lifetime
     */
    function getSwapLifetimeLimits() public view returns (uint256, uint256) {
        return (
        _swapTimeLimits.min,
        _swapTimeLimits.max
        );
    }

    /**
     *  @dev Identification of the swap type with assets and value fields
     *  @param secretHash the encoded secret which they discussed at offline (SHA256)
     *  @return tp (type) of swap
     */
    function getSwapType(
        bytes32 secretHash
    ) public view returns (SwapType tp) {
        return _getSwapType(secretHash);
    }

    /**
     *  @dev Check the secret hash for existence, it can be used in UI for form validation
     *  @param secretHash the encoded secret which they discussed at offline (SHA256)
     *  @return state of this swap
     */
    function getSwapData(
        bytes32 secretHash
    ) external view returns (
        uint256,
        uint256,
        bytes32,
        bytes32,
        address,
        address,
        address,
        uint256,
        State state
    ) {
        Swap memory swap = _swaps[secretHash];
        return (
        swap.initTimestamp,
        swap.refundTimestamp,
        swap.secretHash,
        swap.secret,
        swap.initiator,
        swap.recipient,
        swap.asset,
        swap.amount,
        swap.state
        );
    }

}
设置
{
  "compilationTarget": {
    "CrossBlockchainSwap.sol": "CrossBlockchainSwap"
  },
  "evmVersion": "petersburg",
  "libraries": {},
  "optimizer": {
    "enabled": false,
    "runs": 200
  },
  "remappings": []
}
ABI
[{"constant":true,"inputs":[{"name":"secretHash","type":"bytes32"}],"name":"getSwapType","outputs":[{"name":"tp","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"orderId","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"orderId","type":"uint256"},{"name":"user","type":"address"}],"name":"getDepositedOrderDetails","outputs":[{"name":"created","type":"bool"},{"name":"asset","type":"address"},{"name":"amount","type":"uint256"},{"name":"withdrawn","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"getUserFilledDeposits","outputs":[{"name":"orderIds","type":"uint256[]"},{"name":"amounts","type":"uint256[]"},{"name":"initTimestamps","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"getUserFilledOrders","outputs":[{"name":"amounts","type":"uint256[]"},{"name":"orderIds","type":"uint256[]"},{"name":"initTimestamps","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getSwapLifetimeLimits","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"secretHash","type":"bytes32"}],"name":"refund","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"getUserDepositsAmount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"orderId","type":"uint256"},{"name":"secretHash","type":"bytes32"},{"name":"recipient","type":"address"},{"name":"refundTimestamp","type":"uint256"}],"name":"initiate","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"orderId","type":"uint256"},{"name":"amount","type":"uint256"},{"name":"token","type":"address"}],"name":"deposit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newMin","type":"uint256"},{"name":"newMax","type":"uint256"}],"name":"changeSwapLifetimeLimits","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"orderId","type":"uint256"}],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"doesUserExist","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"secret","type":"bytes32"}],"name":"redeem","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"secretHash","type":"bytes32"}],"name":"getSwapData","outputs":[{"name":"","type":"uint256"},{"name":"","type":"uint256"},{"name":"","type":"bytes32"},{"name":"","type":"bytes32"},{"name":"","type":"address"},{"name":"","type":"address"},{"name":"","type":"address"},{"name":"","type":"uint256"},{"name":"state","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"orderId","type":"uint256"},{"indexed":false,"name":"secretHash","type":"bytes32"},{"indexed":true,"name":"initiator","type":"address"},{"indexed":true,"name":"recipient","type":"address"},{"indexed":false,"name":"initTimestamp","type":"uint256"},{"indexed":false,"name":"refundTimestamp","type":"uint256"},{"indexed":true,"name":"asset","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Initiated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"secretHash","type":"bytes32"},{"indexed":false,"name":"redeemTimestamp","type":"uint256"},{"indexed":false,"name":"secret","type":"bytes32"},{"indexed":true,"name":"redeemer","type":"address"}],"name":"Redeemed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"orderId","type":"uint256"},{"indexed":false,"name":"secretHash","type":"bytes32"},{"indexed":false,"name":"refundTime","type":"uint256"},{"indexed":true,"name":"refunder","type":"address"}],"name":"Refunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"orderId","type":"uint256"},{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"asset","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"AssetDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"orderId","type":"uint256"},{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"asset","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"AssetWithdrawal","type":"event"}]