pragma solidity ^0.5.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with GSN meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
* Refer from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/GSN/Context.sol
*/
contract Context {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
constructor () internal { }
// solhint-disable-previous-line no-empty-blocks
function _msgSender() internal view returns (address payable) {
return msg.sender;
}
function _msgData() internal view returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor () internal {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Returns true if the caller is the current owner.
*/
function isOwner() public view returns (bool) {
return _msgSender() == _owner;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: 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 Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public onlyOwner {
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
*/
function _transferOwnership(address newOwner) internal {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
/**
* @dev Wrappers over decoding and deserialization operation from bytes into bassic types in Solidity for PolyNetwork cross chain utility.
*
* Decode into basic types in Solidity from bytes easily. It's designed to be used
* for PolyNetwork cross chain application, and the decoding rules on Ethereum chain
* and the encoding rule on other chains should be consistent, and . Here we
* follow the underlying deserialization rule with implementation found here:
* https://github.com/polynetwork/poly/blob/master/common/zero_copy_source.go
*
* Using this library instead of the unchecked serialization method can help reduce
* the risk of serious bugs and handfule, so it's recommended to use it.
*
* Please note that risk can be minimized, yet not eliminated.
*/
library ZeroCopySource {
/* @notice Read next byte as boolean type starting at offset from buff
* @param buff Source bytes array
* @param offset The position from where we read the boolean value
* @return The the read boolean value and new offset
*/
function NextBool(bytes memory buff, uint256 offset) internal pure returns(bool, uint256) {
require(offset + 1 <= buff.length && offset < offset + 1, "Offset exceeds limit");
// byte === bytes1
byte v;
assembly{
v := mload(add(add(buff, 0x20), offset))
}
bool value;
if (v == 0x01) {
value = true;
} else if (v == 0x00) {
value = false;
} else {
revert("NextBool value error");
}
return (value, offset + 1);
}
/* @notice Read next byte starting at offset from buff
* @param buff Source bytes array
* @param offset The position from where we read the byte value
* @return The read byte value and new offset
*/
function NextByte(bytes memory buff, uint256 offset) internal pure returns (byte, uint256) {
require(offset + 1 <= buff.length && offset < offset + 1, "NextByte, Offset exceeds maximum");
byte v;
assembly{
v := mload(add(add(buff, 0x20), offset))
}
return (v, offset + 1);
}
/* @notice Read next byte as uint8 starting at offset from buff
* @param buff Source bytes array
* @param offset The position from where we read the byte value
* @return The read uint8 value and new offset
*/
function NextUint8(bytes memory buff, uint256 offset) internal pure returns (uint8, uint256) {
require(offset + 1 <= buff.length && offset < offset + 1, "NextUint8, Offset exceeds maximum");
uint8 v;
assembly{
let tmpbytes := mload(0x40)
let bvalue := mload(add(add(buff, 0x20), offset))
mstore8(tmpbytes, byte(0, bvalue))
mstore(0x40, add(tmpbytes, 0x01))
v := mload(sub(tmpbytes, 0x1f))
}
return (v, offset + 1);
}
/* @notice Read next two bytes as uint16 type starting from offset
* @param buff Source bytes array
* @param offset The position from where we read the uint16 value
* @return The read uint16 value and updated offset
*/
function NextUint16(bytes memory buff, uint256 offset) internal pure returns (uint16, uint256) {
require(offset + 2 <= buff.length && offset < offset + 2, "NextUint16, offset exceeds maximum");
uint16 v;
assembly {
let tmpbytes := mload(0x40)
let bvalue := mload(add(add(buff, 0x20), offset))
mstore8(tmpbytes, byte(0x01, bvalue))
mstore8(add(tmpbytes, 0x01), byte(0, bvalue))
mstore(0x40, add(tmpbytes, 0x02))
v := mload(sub(tmpbytes, 0x1e))
}
return (v, offset + 2);
}
/* @notice Read next four bytes as uint32 type starting from offset
* @param buff Source bytes array
* @param offset The position from where we read the uint32 value
* @return The read uint32 value and updated offset
*/
function NextUint32(bytes memory buff, uint256 offset) internal pure returns (uint32, uint256) {
require(offset + 4 <= buff.length && offset < offset + 4, "NextUint32, offset exceeds maximum");
uint32 v;
assembly {
let tmpbytes := mload(0x40)
let byteLen := 0x04
for {
let tindex := 0x00
let bindex := sub(byteLen, 0x01)
let bvalue := mload(add(add(buff, 0x20), offset))
} lt(tindex, byteLen) {
tindex := add(tindex, 0x01)
bindex := sub(bindex, 0x01)
}{
mstore8(add(tmpbytes, tindex), byte(bindex, bvalue))
}
mstore(0x40, add(tmpbytes, byteLen))
v := mload(sub(tmpbytes, sub(0x20, byteLen)))
}
return (v, offset + 4);
}
/* @notice Read next eight bytes as uint64 type starting from offset
* @param buff Source bytes array
* @param offset The position from where we read the uint64 value
* @return The read uint64 value and updated offset
*/
function NextUint64(bytes memory buff, uint256 offset) internal pure returns (uint64, uint256) {
require(offset + 8 <= buff.length && offset < offset + 8, "NextUint64, offset exceeds maximum");
uint64 v;
assembly {
let tmpbytes := mload(0x40)
let byteLen := 0x08
for {
let tindex := 0x00
let bindex := sub(byteLen, 0x01)
let bvalue := mload(add(add(buff, 0x20), offset))
} lt(tindex, byteLen) {
tindex := add(tindex, 0x01)
bindex := sub(bindex, 0x01)
}{
mstore8(add(tmpbytes, tindex), byte(bindex, bvalue))
}
mstore(0x40, add(tmpbytes, byteLen))
v := mload(sub(tmpbytes, sub(0x20, byteLen)))
}
return (v, offset + 8);
}
/* @notice Read next 32 bytes as uint256 type starting from offset,
there are limits considering the numerical limits in multi-chain
* @param buff Source bytes array
* @param offset The position from where we read the uint256 value
* @return The read uint256 value and updated offset
*/
function NextUint255(bytes memory buff, uint256 offset) internal pure returns (uint256, uint256) {
require(offset + 32 <= buff.length && offset < offset + 32, "NextUint255, offset exceeds maximum");
uint256 v;
assembly {
let tmpbytes := mload(0x40)
let byteLen := 0x20
for {
let tindex := 0x00
let bindex := sub(byteLen, 0x01)
let bvalue := mload(add(add(buff, 0x20), offset))
} lt(tindex, byteLen) {
tindex := add(tindex, 0x01)
bindex := sub(bindex, 0x01)
}{
mstore8(add(tmpbytes, tindex), byte(bindex, bvalue))
}
mstore(0x40, add(tmpbytes, byteLen))
v := mload(tmpbytes)
}
require(v <= 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "Value exceeds the range");
return (v, offset + 32);
}
/* @notice Read next variable bytes starting from offset,
the decoding rule coming from multi-chain
* @param buff Source bytes array
* @param offset The position from where we read the bytes value
* @return The read variable bytes array value and updated offset
*/
function NextVarBytes(bytes memory buff, uint256 offset) internal pure returns(bytes memory, uint256) {
uint len;
(len, offset) = NextVarUint(buff, offset);
require(offset + len <= buff.length && offset < offset + len, "NextVarBytes, offset exceeds maximum");
bytes memory tempBytes;
assembly{
switch iszero(len)
case 0 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
let lengthmod := and(len, 31)
// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, len)
for {
// The multiplication in the next line has the same exact purpose
// as the one above.
let cc := add(add(add(buff, lengthmod), mul(0x20, iszero(lengthmod))), offset)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, len)
//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)
mstore(0x40, add(tempBytes, 0x20))
}
}
return (tempBytes, offset + len);
}
/* @notice Read next 32 bytes starting from offset,
* @param buff Source bytes array
* @param offset The position from where we read the bytes value
* @return The read bytes32 value and updated offset
*/
function NextHash(bytes memory buff, uint256 offset) internal pure returns (bytes32 , uint256) {
require(offset + 32 <= buff.length && offset < offset + 32, "NextHash, offset exceeds maximum");
bytes32 v;
assembly {
v := mload(add(buff, add(offset, 0x20)))
}
return (v, offset + 32);
}
/* @notice Read next 20 bytes starting from offset,
* @param buff Source bytes array
* @param offset The position from where we read the bytes value
* @return The read bytes20 value and updated offset
*/
function NextBytes20(bytes memory buff, uint256 offset) internal pure returns (bytes20 , uint256) {
require(offset + 20 <= buff.length && offset < offset + 20, "NextBytes20, offset exceeds maximum");
bytes20 v;
assembly {
v := mload(add(buff, add(offset, 0x20)))
}
return (v, offset + 20);
}
function NextVarUint(bytes memory buff, uint256 offset) internal pure returns(uint, uint256) {
byte v;
(v, offset) = NextByte(buff, offset);
uint value;
if (v == 0xFD) {
// return NextUint16(buff, offset);
(value, offset) = NextUint16(buff, offset);
require(value >= 0xFD && value <= 0xFFFF, "NextUint16, value outside range");
return (value, offset);
} else if (v == 0xFE) {
// return NextUint32(buff, offset);
(value, offset) = NextUint32(buff, offset);
require(value > 0xFFFF && value <= 0xFFFFFFFF, "NextVarUint, value outside range");
return (value, offset);
} else if (v == 0xFF) {
// return NextUint64(buff, offset);
(value, offset) = NextUint64(buff, offset);
require(value > 0xFFFFFFFF, "NextVarUint, value outside range");
return (value, offset);
} else{
// return (uint8(v), offset);
value = uint8(v);
require(value < 0xFD, "NextVarUint, value outside range");
return (value, offset);
}
}
}
/**
* @dev Wrappers over encoding and serialization operation into bytes from bassic types in Solidity for PolyNetwork cross chain utility.
*
* Encode basic types in Solidity into bytes easily. It's designed to be used
* for PolyNetwork cross chain application, and the encoding rules on Ethereum chain
* and the decoding rules on other chains should be consistent. Here we
* follow the underlying serialization rule with implementation found here:
* https://github.com/polynetwork/poly/blob/master/common/zero_copy_sink.go
*
* Using this library instead of the unchecked serialization method can help reduce
* the risk of serious bugs and handfule, so it's recommended to use it.
*
* Please note that risk can be minimized, yet not eliminated.
*/
library ZeroCopySink {
/* @notice Convert boolean value into bytes
* @param b The boolean value
* @return Converted bytes array
*/
function WriteBool(bool b) internal pure returns (bytes memory) {
bytes memory buff;
assembly{
buff := mload(0x40)
mstore(buff, 1)
switch iszero(b)
case 1 {
mstore(add(buff, 0x20), shl(248, 0x00))
// mstore8(add(buff, 0x20), 0x00)
}
default {
mstore(add(buff, 0x20), shl(248, 0x01))
// mstore8(add(buff, 0x20), 0x01)
}
mstore(0x40, add(buff, 0x21))
}
return buff;
}
/* @notice Convert byte value into bytes
* @param b The byte value
* @return Converted bytes array
*/
function WriteByte(byte b) internal pure returns (bytes memory) {
return WriteUint8(uint8(b));
}
/* @notice Convert uint8 value into bytes
* @param v The uint8 value
* @return Converted bytes array
*/
function WriteUint8(uint8 v) internal pure returns (bytes memory) {
bytes memory buff;
assembly{
buff := mload(0x40)
mstore(buff, 1)
mstore(add(buff, 0x20), shl(248, v))
// mstore(add(buff, 0x20), byte(0x1f, v))
mstore(0x40, add(buff, 0x21))
}
return buff;
}
/* @notice Convert uint16 value into bytes
* @param v The uint16 value
* @return Converted bytes array
*/
function WriteUint16(uint16 v) internal pure returns (bytes memory) {
bytes memory buff;
assembly{
buff := mload(0x40)
let byteLen := 0x02
mstore(buff, byteLen)
for {
let mindex := 0x00
let vindex := 0x1f
} lt(mindex, byteLen) {
mindex := add(mindex, 0x01)
vindex := sub(vindex, 0x01)
}{
mstore8(add(add(buff, 0x20), mindex), byte(vindex, v))
}
mstore(0x40, add(buff, 0x22))
}
return buff;
}
/* @notice Convert uint32 value into bytes
* @param v The uint32 value
* @return Converted bytes array
*/
function WriteUint32(uint32 v) internal pure returns(bytes memory) {
bytes memory buff;
assembly{
buff := mload(0x40)
let byteLen := 0x04
mstore(buff, byteLen)
for {
let mindex := 0x00
let vindex := 0x1f
} lt(mindex, byteLen) {
mindex := add(mindex, 0x01)
vindex := sub(vindex, 0x01)
}{
mstore8(add(add(buff, 0x20), mindex), byte(vindex, v))
}
mstore(0x40, add(buff, 0x24))
}
return buff;
}
/* @notice Convert uint64 value into bytes
* @param v The uint64 value
* @return Converted bytes array
*/
function WriteUint64(uint64 v) internal pure returns(bytes memory) {
bytes memory buff;
assembly{
buff := mload(0x40)
let byteLen := 0x08
mstore(buff, byteLen)
for {
let mindex := 0x00
let vindex := 0x1f
} lt(mindex, byteLen) {
mindex := add(mindex, 0x01)
vindex := sub(vindex, 0x01)
}{
mstore8(add(add(buff, 0x20), mindex), byte(vindex, v))
}
mstore(0x40, add(buff, 0x28))
}
return buff;
}
/* @notice Convert limited uint256 value into bytes
* @param v The uint256 value
* @return Converted bytes array
*/
function WriteUint255(uint256 v) internal pure returns (bytes memory) {
require(v <= 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "Value exceeds uint255 range");
bytes memory buff;
assembly{
buff := mload(0x40)
let byteLen := 0x20
mstore(buff, byteLen)
for {
let mindex := 0x00
let vindex := 0x1f
} lt(mindex, byteLen) {
mindex := add(mindex, 0x01)
vindex := sub(vindex, 0x01)
}{
mstore8(add(add(buff, 0x20), mindex), byte(vindex, v))
}
mstore(0x40, add(buff, 0x40))
}
return buff;
}
/* @notice Encode bytes format data into bytes
* @param data The bytes array data
* @return Encoded bytes array
*/
function WriteVarBytes(bytes memory data) internal pure returns (bytes memory) {
uint64 l = uint64(data.length);
return abi.encodePacked(WriteVarUint(l), data);
}
function WriteVarUint(uint64 v) internal pure returns (bytes memory) {
if (v < 0xFD){
return WriteUint8(uint8(v));
} else if (v <= 0xFFFF) {
return abi.encodePacked(WriteByte(0xFD), WriteUint16(uint16(v)));
} else if (v <= 0xFFFFFFFF) {
return abi.encodePacked(WriteByte(0xFE), WriteUint32(uint32(v)));
} else {
return abi.encodePacked(WriteByte(0xFF), WriteUint64(uint64(v)));
}
}
}
library Utils {
/* @notice Convert the bytes array to bytes32 type, the bytes array length must be 32
* @param _bs Source bytes array
* @return bytes32
*/
function bytesToBytes32(bytes memory _bs) internal pure returns (bytes32 value) {
require(_bs.length == 32, "bytes length is not 32.");
assembly {
// load 32 bytes from memory starting from position _bs + 0x20 since the first 0x20 bytes stores _bs length
value := mload(add(_bs, 0x20))
}
}
/* @notice Convert bytes to uint256
* @param _b Source bytes should have length of 32
* @return uint256
*/
function bytesToUint256(bytes memory _bs) internal pure returns (uint256 value) {
require(_bs.length == 32, "bytes length is not 32.");
assembly {
// load 32 bytes from memory starting from position _bs + 32
value := mload(add(_bs, 0x20))
}
require(value <= 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "Value exceeds the range");
}
/* @notice Convert uint256 to bytes
* @param _b uint256 that needs to be converted
* @return bytes
*/
function uint256ToBytes(uint256 _value) internal pure returns (bytes memory bs) {
require(_value <= 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, "Value exceeds the range");
assembly {
// Get a location of some free memory and store it in result as
// Solidity does for memory variables.
bs := mload(0x40)
// Put 0x20 at the first word, the length of bytes for uint256 value
mstore(bs, 0x20)
//In the next word, put value in bytes format to the next 32 bytes
mstore(add(bs, 0x20), _value)
// Update the free-memory pointer by padding our last write location to 32 bytes
mstore(0x40, add(bs, 0x40))
}
}
/* @notice Convert bytes to address
* @param _bs Source bytes: bytes length must be 20
* @return Converted address from source bytes
*/
function bytesToAddress(bytes memory _bs) internal pure returns (address addr)
{
require(_bs.length == 20, "bytes length does not match address");
assembly {
// for _bs, first word store _bs.length, second word store _bs.value
// load 32 bytes from mem[_bs+20], convert it into Uint160, meaning we take last 20 bytes as addr (address).
addr := mload(add(_bs, 0x14))
}
}
/* @notice Convert address to bytes
* @param _addr Address need to be converted
* @return Converted bytes from address
*/
function addressToBytes(address _addr) internal pure returns (bytes memory bs){
assembly {
// Get a location of some free memory and store it in result as
// Solidity does for memory variables.
bs := mload(0x40)
// Put 20 (address byte length) at the first word, the length of bytes for uint256 value
mstore(bs, 0x14)
// logical shift left _a by 12 bytes, change _a from right-aligned to left-aligned
mstore(add(bs, 0x20), shl(96, _addr))
// Update the free-memory pointer by padding our last write location to 32 bytes
mstore(0x40, add(bs, 0x40))
}
}
/* @notice Do hash leaf as the multi-chain does
* @param _data Data in bytes format
* @return Hashed value in bytes32 format
*/
function hashLeaf(bytes memory _data) internal pure returns (bytes32 result) {
result = sha256(abi.encodePacked(byte(0x0), _data));
}
/* @notice Do hash children as the multi-chain does
* @param _l Left node
* @param _r Right node
* @return Hashed value in bytes32 format
*/
function hashChildren(bytes32 _l, bytes32 _r) internal pure returns (bytes32 result) {
result = sha256(abi.encodePacked(bytes1(0x01), _l, _r));
}
/* @notice Compare if two bytes are equal, which are in storage and memory, seperately
Refer from https://github.com/summa-tx/bitcoin-spv/blob/master/solidity/contracts/BytesLib.sol#L368
* @param _preBytes The bytes stored in storage
* @param _postBytes The bytes stored in memory
* @return Bool type indicating if they are equal
*/
function equalStorage(bytes storage _preBytes, bytes memory _postBytes) internal view returns (bool) {
bool success = true;
assembly {
// we know _preBytes_offset is 0
let fslot := sload(_preBytes_slot)
// Arrays of 31 bytes or less have an even value in their slot,
// while longer arrays have an odd value. The actual length is
// the slot divided by two for odd values, and the lowest order
// byte divided by two for even values.
// If the slot is even, bitwise and the slot with 255 and divide by
// two to get the length. If the slot is odd, bitwise and the slot
// with -1 and divide by two.
let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2)
let mlength := mload(_postBytes)
// if lengths don't match the arrays are not equal
switch eq(slength, mlength)
case 1 {
// fslot can contain both the length and contents of the array
// if slength < 32 bytes so let's prepare for that
// v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage
// slength != 0
if iszero(iszero(slength)) {
switch lt(slength, 32)
case 1 {
// blank the last byte which is the length
fslot := mul(div(fslot, 0x100), 0x100)
if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) {
// unsuccess:
success := 0
}
}
default {
// cb is a circuit breaker in the for loop since there's
// no said feature for inline assembly loops
// cb = 1 - don't breaker
// cb = 0 - break
let cb := 1
// get the keccak hash to get the contents of the array
mstore(0x0, _preBytes_slot)
let sc := keccak256(0x0, 0x20)
let mc := add(_postBytes, 0x20)
let end := add(mc, mlength)
// the next line is the loop condition:
// while(uint(mc < end) + cb == 2)
for {} eq(add(lt(mc, end), cb), 2) {
sc := add(sc, 1)
mc := add(mc, 0x20)
} {
if iszero(eq(sload(sc), mload(mc))) {
// unsuccess:
success := 0
cb := 0
}
}
}
}
}
default {
// unsuccess:
success := 0
}
}
return success;
}
/* @notice Slice the _bytes from _start index till the result has length of _length
Refer from https://github.com/summa-tx/bitcoin-spv/blob/master/solidity/contracts/BytesLib.sol#L246
* @param _bytes The original bytes needs to be sliced
* @param _start The index of _bytes for the start of sliced bytes
* @param _length The index of _bytes for the end of sliced bytes
* @return The sliced bytes
*/
function slice(
bytes memory _bytes,
uint _start,
uint _length
)
internal
pure
returns (bytes memory)
{
require(_bytes.length >= (_start + _length));
bytes memory tempBytes;
assembly {
switch iszero(_length)
case 0 {
// Get a location of some free memory and store it in tempBytes as
// Solidity does for memory variables.
tempBytes := mload(0x40)
// The first word of the slice result is potentially a partial
// word read from the original array. To read it, we calculate
// the length of that partial word and start copying that many
// bytes into the array. The first word we copy will start with
// data we don't care about, but the last `lengthmod` bytes will
// land at the beginning of the contents of the new array. When
// we're done copying, we overwrite the full first word with
// the actual length of the slice.
// lengthmod <= _length % 32
let lengthmod := and(_length, 31)
// The multiplication in the next line is necessary
// because when slicing multiples of 32 bytes (lengthmod == 0)
// the following copy loop was copying the origin's length
// and then ending prematurely not copying everything it should.
let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end := add(mc, _length)
for {
// The multiplication in the next line has the same exact purpose
// as the one above.
let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc := add(mc, 0x20)
cc := add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, _length)
//update free-memory pointer
//allocating the array padded to 32 bytes like the compiler does now
mstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length array
default {
tempBytes := mload(0x40)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
/* @notice Check if the elements number of _signers within _keepers array is no less than _m
* @param _keepers The array consists of serveral address
* @param _signers Some specific addresses to be looked into
* @param _m The number requirement paramter
* @return True means containment, false meansdo do not contain.
*/
function containMAddresses(address[] memory _keepers, address[] memory _signers, uint _m) internal pure returns (bool){
uint m = 0;
for(uint i = 0; i < _signers.length; i++){
for (uint j = 0; j < _keepers.length; j++) {
if (_signers[i] == _keepers[j]) {
m++;
// delete _keepers[j];
_keepers[j] = 0x7777777777777777777777777777777777777777;
}
}
}
return m >= _m;
}
/* @notice TODO
* @param key
* @return
*/
function compressMCPubKey(bytes memory key) internal pure returns (bytes memory newkey) {
require(key.length >= 67, "key lenggh is too short");
newkey = slice(key, 0, 35);
if (uint8(key[66]) % 2 == 0){
newkey[2] = byte(0x02);
} else {
newkey[2] = byte(0x03);
}
return newkey;
}
/**
* @dev Returns true if `account` is a contract.
* Refer from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Address.sol#L18
*
* This test is non-exhaustive, and there may be false-negatives: during the
* execution of a contract's constructor, its address will be reported as
* not containing a contract.
*
* IMPORTANT: It is unsafe to assume that an address for which this
* function returns false is an externally-owned account (EOA) and not a
* contract.
*/
function isContract(address account) internal view returns (bool) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) }
return (codehash != 0x0 && codehash != accountHash);
}
}
/**
* @dev Interface of the ERC20 standard as defined in the EIP. Does not include
* the optional functions; to access them see {ERC20Detailed}.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*
* _Available since v2.4.0._
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot 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-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b != 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*
* _Available since v2.4.0._
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
/**
* @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;
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'
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
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 not be false).
* @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.
// solhint-disable-next-line max-line-length
require(Utils.isContract(address(token)), "SafeERC20: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
/**
* @dev Interface of the EthCrossChainManager contract for business contract like LockProxy to request cross chain transaction
*/
interface IEthCrossChainManager {
function crossChain(uint64 _toChainId, bytes calldata _toContract, bytes calldata _method, bytes calldata _txData) external returns (bool);
}
/**
* @dev Interface of the EthCrossChainManagerProxy for business contract like LockProxy to obtain the reliable EthCrossChainManager contract hash.
*/
interface IEthCrossChainManagerProxy {
function getEthCrossChainManager() external view returns (address);
}
interface IBridgeAsset {
function mint(address to, uint256 amount) external;
function burnFrom(address account, uint256 amount) external;
}
contract LockProxy is Ownable {
using SafeMath for uint;
using SafeERC20 for IERC20;
struct TxArgs {
bytes toAssetHash;
bytes toAddress;
uint256 amount;
}
address public managerProxyContract;
mapping(uint64 => bytes) public proxyHashMap;
mapping(address => mapping(uint64 => bytes)) public assetHashMap;
mapping(address => bool) safeTransfer;
event SetManagerProxyEvent(address manager);
event BindProxyEvent(uint64 toChainId, bytes targetProxyHash);
event BindAssetEvent(address fromAssetHash, uint64 toChainId, bytes targetProxyHash, uint initialAmount);
event UnlockEvent(address toAssetHash, address toAddress, uint256 amount);
event LockEvent(address fromAssetHash, address fromAddress, uint64 toChainId, bytes toAssetHash, bytes toAddress, uint256 amount);
modifier onlyManagerContract() {
IEthCrossChainManagerProxy ieccmp = IEthCrossChainManagerProxy(managerProxyContract);
require(_msgSender() == ieccmp.getEthCrossChainManager(), "msgSender is not EthCrossChainManagerContract");
_;
}
function setManagerProxy(address ethCCMProxyAddr) onlyOwner public {
managerProxyContract = ethCCMProxyAddr;
emit SetManagerProxyEvent(managerProxyContract);
}
function bindProxyHash(uint64 toChainId, bytes memory targetProxyHash) onlyOwner public returns (bool) {
proxyHashMap[toChainId] = targetProxyHash;
emit BindProxyEvent(toChainId, targetProxyHash);
return true;
}
function bindAssetHash(address fromAssetHash, uint64 toChainId, bytes memory toAssetHash) onlyOwner public returns (bool) {
assetHashMap[fromAssetHash][toChainId] = toAssetHash;
emit BindAssetEvent(fromAssetHash, toChainId, toAssetHash, getBalanceFor(fromAssetHash));
return true;
}
/* @notice This function is meant to be invoked by the user,
* a certin amount teokens will be locked in the proxy contract the invoker/msg.sender immediately.
* Then the same amount of tokens will be unloked from target chain proxy contract at the target chain with chainId later.
* @param fromAssetHash The asset address in current chain, uniformly named as `fromAssetHash`
* @param toChainId The target chain id
*
* @param toAddress The address in bytes format to receive same amount of tokens in target chain
* @param amount The amount of tokens to be crossed from ethereum to the chain with chainId
*/
function lock(address fromAssetHash, uint64 toChainId, bytes memory toAddress, uint256 amount) public payable returns (bool) {
require(amount != 0, "amount cannot be zero!");
require(_burn(fromAssetHash, amount), "burn asset from fromAddress failed!");
bytes memory toAssetHash = assetHashMap[fromAssetHash][toChainId];
require(toAssetHash.length != 0, "empty illegal toAssetHash");
TxArgs memory txArgs = TxArgs({
toAssetHash: toAssetHash,
toAddress: toAddress,
amount: amount
});
bytes memory txData = _serializeTxArgs(txArgs);
IEthCrossChainManagerProxy eccmp = IEthCrossChainManagerProxy(managerProxyContract);
address eccmAddr = eccmp.getEthCrossChainManager();
IEthCrossChainManager eccm = IEthCrossChainManager(eccmAddr);
bytes memory toProxyHash = proxyHashMap[toChainId];
require(toProxyHash.length != 0, "empty illegal toProxyHash");
require(eccm.crossChain(toChainId, toProxyHash, "unlock", txData), "EthCrossChainManager crossChain executed error!");
emit LockEvent(fromAssetHash, _msgSender(), toChainId, toAssetHash, toAddress, amount);
return true;
}
// /* @notice This function is meant to be invoked by the ETH crosschain management contract,
// * then mint a certin amount of tokens to the designated address since a certain amount
// * was burnt from the source chain invoker.
// * @param argsBs The argument bytes recevied by the ethereum lock proxy contract, need to be deserialized.
// * based on the way of serialization in the source chain proxy contract.
// * @param fromContractAddr The source chain contract address
// * @param fromChainId The source chain id
// */
function unlock(bytes memory argsBs, bytes memory fromContractAddr, uint64 fromChainId) onlyManagerContract public returns (bool) {
TxArgs memory args = _deserializeTxArgs(argsBs);
require(fromContractAddr.length != 0, "from proxy contract address cannot be empty");
require(Utils.equalStorage(proxyHashMap[fromChainId], fromContractAddr), "From Proxy contract address error!");
require(args.toAssetHash.length != 0, "toAssetHash cannot be empty");
address toAssetHash = Utils.bytesToAddress(args.toAssetHash);
require(args.toAddress.length != 0, "toAddress cannot be empty");
address toAddress = Utils.bytesToAddress(args.toAddress);
require(_mint(toAssetHash, toAddress, args.amount), "mint asset to toAddress failed!");
emit UnlockEvent(toAssetHash, toAddress, args.amount);
return true;
}
function getBalanceFor(address fromAssetHash) public view returns (uint256) {
if (fromAssetHash == address(0)) {
// return address(this).balance; // this expression would result in error: Failed to decode output: Error: insufficient data for uint256 type
address selfAddr = address(this);
return selfAddr.balance;
} else {
IERC20 erc20Token = IERC20(fromAssetHash);
return erc20Token.balanceOf(address(this));
}
}
function _burn(address fromAssetHash, uint256 amount) internal returns (bool) {
if (fromAssetHash == address(0)) {
// fromAssetHash === address(0) denotes user choose to lock ether
// passively check if the received msg.value equals amount
require(msg.value != 0, "transferred ether cannot be zero!");
require(msg.value == amount, "transferred ether is not equal to amount!");
} else {
// make sure lockproxy contract will decline any received ether
require(msg.value == 0, "there should be no ether transfer!");
// actively transfer amount of asset from msg.sender to lock_proxy contract
require(_burnERC20(fromAssetHash, _msgSender(), address(this), amount), "burn erc20 asset from msgSender failed!");
}
return true;
}
function _mint(address toAssetHash, address toAddress, uint256 amount) internal returns (bool) {
if (toAssetHash == address(0x0000000000000000000000000000000000000000)) {
// toAssetHash === address(0) denotes contract needs to unlock ether to toAddress
// convert toAddress from 'address' type to 'address payable' type, then actively transfer ether
address(uint160(toAddress)).transfer(amount);
} else {
// actively transfer amount of asset from lock_proxy contract to toAddress
require(_mintERC20(toAssetHash, toAddress, amount), "mint erc20 asset to toAddress failed!");
}
return true;
}
function _burnERC20(address fromAssetHash, address fromAddress, address toAddress, uint256 amount) internal returns (bool) {
IBridgeAsset erc20Token = IBridgeAsset(fromAssetHash);
erc20Token.burnFrom(fromAddress, amount);
return true;
}
function _mintERC20(address toAssetHash, address toAddress, uint256 amount) internal returns (bool) {
IBridgeAsset erc20Token = IBridgeAsset(toAssetHash);
erc20Token.mint(toAddress, amount);
return true;
}
function _serializeTxArgs(TxArgs memory args) internal pure returns (bytes memory) {
bytes memory buff;
buff = abi.encodePacked(
ZeroCopySink.WriteVarBytes(args.toAssetHash),
ZeroCopySink.WriteVarBytes(args.toAddress),
ZeroCopySink.WriteUint255(args.amount)
);
return buff;
}
function _deserializeTxArgs(bytes memory valueBs) internal pure returns (TxArgs memory) {
TxArgs memory args;
uint256 off = 0;
(args.toAssetHash, off) = ZeroCopySource.NextVarBytes(valueBs, off);
(args.toAddress, off) = ZeroCopySource.NextVarBytes(valueBs, off);
(args.amount, off) = ZeroCopySource.NextUint255(valueBs, off);
return args;
}
}
{
"compilationTarget": {
"LockProxy.sol": "LockProxy"
},
"evmVersion": "istanbul",
"libraries": {},
"optimizer": {
"enabled": false,
"runs": 200
},
"remappings": []
}
[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"fromAssetHash","type":"address"},{"indexed":false,"internalType":"uint64","name":"toChainId","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"targetProxyHash","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"initialAmount","type":"uint256"}],"name":"BindAssetEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"toChainId","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"targetProxyHash","type":"bytes"}],"name":"BindProxyEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"fromAssetHash","type":"address"},{"indexed":false,"internalType":"address","name":"fromAddress","type":"address"},{"indexed":false,"internalType":"uint64","name":"toChainId","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"toAssetHash","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"toAddress","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LockEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"manager","type":"address"}],"name":"SetManagerProxyEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"toAssetHash","type":"address"},{"indexed":false,"internalType":"address","name":"toAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"UnlockEvent","type":"event"},{"constant":true,"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint64","name":"","type":"uint64"}],"name":"assetHashMap","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"fromAssetHash","type":"address"},{"internalType":"uint64","name":"toChainId","type":"uint64"},{"internalType":"bytes","name":"toAssetHash","type":"bytes"}],"name":"bindAssetHash","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint64","name":"toChainId","type":"uint64"},{"internalType":"bytes","name":"targetProxyHash","type":"bytes"}],"name":"bindProxyHash","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"fromAssetHash","type":"address"}],"name":"getBalanceFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"fromAssetHash","type":"address"},{"internalType":"uint64","name":"toChainId","type":"uint64"},{"internalType":"bytes","name":"toAddress","type":"bytes"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"lock","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"managerProxyContract","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"proxyHashMap","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"renounceOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"ethCCMProxyAddr","type":"address"}],"name":"setManagerProxy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes","name":"argsBs","type":"bytes"},{"internalType":"bytes","name":"fromContractAddr","type":"bytes"},{"internalType":"uint64","name":"fromChainId","type":"uint64"}],"name":"unlock","outputs":[{"internalType":"bool","name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"}]