Created
January 25, 2020 01:24
-
-
Save publu/77c31c2351c643dc67be1716eb85756a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // File: installed_contracts/bytes/contracts/BytesLib.sol | |
| /* | |
| * @title Solidity Bytes Arrays Utils | |
| * @author Nick Johnson <[email protected]> | |
| * | |
| * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. | |
| * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. | |
| */ | |
| pragma solidity ^0.5.5; | |
| library BytesLib { | |
| function concat(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bytes memory) { | |
| bytes memory tempBytes; | |
| assembly { | |
| // Get a location of some free memory and store it in tempBytes as | |
| // Solidity does for memory variables. | |
| tempBytes := mload(0x40) | |
| // Store the length of the first bytes array at the beginning of | |
| // the memory for tempBytes. | |
| let length := mload(_preBytes) | |
| mstore(tempBytes, length) | |
| // Maintain a memory counter for the current write location in the | |
| // temp bytes array by adding the 32 bytes for the array length to | |
| // the starting location. | |
| let mc := add(tempBytes, 0x20) | |
| // Stop copying when the memory counter reaches the length of the | |
| // first bytes array. | |
| let end := add(mc, length) | |
| for { | |
| // Initialize a copy counter to the start of the _preBytes data, | |
| // 32 bytes into its memory. | |
| let cc := add(_preBytes, 0x20) | |
| } lt(mc, end) { | |
| // Increase both counters by 32 bytes each iteration. | |
| mc := add(mc, 0x20) | |
| cc := add(cc, 0x20) | |
| } { | |
| // Write the _preBytes data into the tempBytes memory 32 bytes | |
| // at a time. | |
| mstore(mc, mload(cc)) | |
| } | |
| // Add the length of _postBytes to the current length of tempBytes | |
| // and store it as the new length in the first 32 bytes of the | |
| // tempBytes memory. | |
| length := mload(_postBytes) | |
| mstore(tempBytes, add(length, mload(tempBytes))) | |
| // Move the memory counter back from a multiple of 0x20 to the | |
| // actual end of the _preBytes data. | |
| mc := end | |
| // Stop copying when the memory counter reaches the new combined | |
| // length of the arrays. | |
| end := add(mc, length) | |
| for { | |
| let cc := add(_postBytes, 0x20) | |
| } lt(mc, end) { | |
| mc := add(mc, 0x20) | |
| cc := add(cc, 0x20) | |
| } { | |
| mstore(mc, mload(cc)) | |
| } | |
| // Update the free-memory pointer by padding our last write location | |
| // to 32 bytes: add 31 bytes to the end of tempBytes to move to the | |
| // next 32 byte block, then round down to the nearest multiple of | |
| // 32. If the sum of the length of the two arrays is zero then add | |
| // one before rounding down to leave a blank 32 bytes (the length block with 0). | |
| mstore(0x40, and( | |
| add(add(end, iszero(add(length, mload(_preBytes)))), 31), | |
| not(31) // Round down to the nearest 32 bytes. | |
| )) | |
| } | |
| return tempBytes; | |
| } | |
| function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { | |
| assembly { | |
| // Read the first 32 bytes of _preBytes storage, which is the length | |
| // of the array. (We don't need to use the offset into the slot | |
| // because arrays use the entire slot.) | |
| 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) | |
| let newlength := add(slength, mlength) | |
| // slength can contain both the length and contents of the array | |
| // if length < 32 bytes so let's prepare for that | |
| // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage | |
| switch add(lt(slength, 32), lt(newlength, 32)) | |
| case 2 { | |
| // Since the new array still fits in the slot, we just need to | |
| // update the contents of the slot. | |
| // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length | |
| sstore( | |
| _preBytes_slot, | |
| // all the modifications to the slot are inside this | |
| // next block | |
| add( | |
| // we can just add to the slot contents because the | |
| // bytes we want to change are the LSBs | |
| fslot, | |
| add( | |
| mul( | |
| div( | |
| // load the bytes from memory | |
| mload(add(_postBytes, 0x20)), | |
| // zero all bytes to the right | |
| exp(0x100, sub(32, mlength)) | |
| ), | |
| // and now shift left the number of bytes to | |
| // leave space for the length in the slot | |
| exp(0x100, sub(32, newlength)) | |
| ), | |
| // increase length by the double of the memory | |
| // bytes length | |
| mul(mlength, 2) | |
| ) | |
| ) | |
| ) | |
| } | |
| case 1 { | |
| // The stored value fits in the slot, but the combined value | |
| // will exceed it. | |
| // get the keccak hash to get the contents of the array | |
| mstore(0x0, _preBytes_slot) | |
| let sc := add(keccak256(0x0, 0x20), div(slength, 32)) | |
| // save new length | |
| sstore(_preBytes_slot, add(mul(newlength, 2), 1)) | |
| // The contents of the _postBytes array start 32 bytes into | |
| // the structure. Our first read should obtain the `submod` | |
| // bytes that can fit into the unused space in the last word | |
| // of the stored array. To get this, we read 32 bytes starting | |
| // from `submod`, so the data we read overlaps with the array | |
| // contents by `submod` bytes. Masking the lowest-order | |
| // `submod` bytes allows us to add that value directly to the | |
| // stored value. | |
| let submod := sub(32, slength) | |
| let mc := add(_postBytes, submod) | |
| let end := add(_postBytes, mlength) | |
| let mask := sub(exp(0x100, submod), 1) | |
| sstore( | |
| sc, | |
| add( | |
| and( | |
| fslot, | |
| 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 | |
| ), | |
| and(mload(mc), mask) | |
| ) | |
| ) | |
| for { | |
| mc := add(mc, 0x20) | |
| sc := add(sc, 1) | |
| } lt(mc, end) { | |
| sc := add(sc, 1) | |
| mc := add(mc, 0x20) | |
| } { | |
| sstore(sc, mload(mc)) | |
| } | |
| mask := exp(0x100, sub(mc, end)) | |
| sstore(sc, mul(div(mload(mc), mask), mask)) | |
| } | |
| default { | |
| // get the keccak hash to get the contents of the array | |
| mstore(0x0, _preBytes_slot) | |
| // Start copying to the last used word of the stored array. | |
| let sc := add(keccak256(0x0, 0x20), div(slength, 32)) | |
| // save new length | |
| sstore(_preBytes_slot, add(mul(newlength, 2), 1)) | |
| // Copy over the first `submod` bytes of the new data as in | |
| // case 1 above. | |
| let slengthmod := mod(slength, 32) | |
| let mlengthmod := mod(mlength, 32) | |
| let submod := sub(32, slengthmod) | |
| let mc := add(_postBytes, submod) | |
| let end := add(_postBytes, mlength) | |
| let mask := sub(exp(0x100, submod), 1) | |
| sstore(sc, add(sload(sc), and(mload(mc), mask))) | |
| for { | |
| sc := add(sc, 1) | |
| mc := add(mc, 0x20) | |
| } lt(mc, end) { | |
| sc := add(sc, 1) | |
| mc := add(mc, 0x20) | |
| } { | |
| sstore(sc, mload(mc)) | |
| } | |
| mask := exp(0x100, sub(mc, end)) | |
| sstore(sc, mul(div(mload(mc), mask), mask)) | |
| } | |
| } | |
| } | |
| 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. | |
| 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; | |
| } | |
| function toAddress(bytes memory _bytes, uint _start) internal pure returns (address) { | |
| require(_bytes.length >= (_start + 20)); | |
| address tempAddress; | |
| assembly { | |
| tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) | |
| } | |
| return tempAddress; | |
| } | |
| function toUint(bytes memory _bytes, uint _start) internal pure returns (uint256) { | |
| require(_bytes.length >= (_start + 32)); | |
| uint256 tempUint; | |
| assembly { | |
| tempUint := mload(add(add(_bytes, 0x20), _start)) | |
| } | |
| return tempUint; | |
| } | |
| function toBytes32(bytes memory _bytes, uint _start) internal pure returns (bytes32) { | |
| require(_bytes.length >= (_start + 32)); | |
| bytes32 tempBytes32; | |
| assembly { | |
| tempBytes32 := mload(add(add(_bytes, 0x20), _start)) | |
| } | |
| return tempBytes32; | |
| } | |
| function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { | |
| bool success = true; | |
| assembly { | |
| let length := mload(_preBytes) | |
| // if lengths don't match the arrays are not equal | |
| switch eq(length, mload(_postBytes)) | |
| case 1 { | |
| // 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 | |
| let mc := add(_preBytes, 0x20) | |
| let end := add(mc, length) | |
| for { | |
| let cc := add(_postBytes, 0x20) | |
| // the next line is the loop condition: | |
| // while(uint(mc < end) + cb == 2) | |
| } eq(add(lt(mc, end), cb), 2) { | |
| mc := add(mc, 0x20) | |
| cc := add(cc, 0x20) | |
| } { | |
| // if any of these checks fails then arrays are not equal | |
| if iszero(eq(mload(mc), mload(cc))) { | |
| // unsuccess: | |
| success := 0 | |
| cb := 0 | |
| } | |
| } | |
| } | |
| default { | |
| // unsuccess: | |
| success := 0 | |
| } | |
| } | |
| return success; | |
| } | |
| 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) | |
| // Decode the length of the stored array like in concatStorage(). | |
| 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 { | |
| // slength can contain both the length and contents of the array | |
| // if length < 32 bytes so let's prepare for that | |
| // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage | |
| 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; | |
| } | |
| } | |
| // File: contracts/ArbValue.sol | |
| /* | |
| * Copyright 2019, Offchain Labs, Inc. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| // pragma solidity ^0.5.3; | |
| library ArbValue { | |
| using BytesLib for bytes; | |
| using ArbValue for Value; | |
| uint8 constant IntTypeCode = 0; | |
| uint8 constant CodePointCode = 1; | |
| uint8 constant HashOnlyTypeCode = 2; | |
| uint8 constant TupleTypeCode = 3; | |
| uint8 constant ValueTypeCount = TupleTypeCode + 9; | |
| function isTupleType(uint8 typeCode) private pure returns (bool) { | |
| return typeCode < ValueTypeCount && typeCode >= TupleTypeCode; | |
| } | |
| function typeLength(uint8 typeCode) private pure returns (uint8) { | |
| if (isTupleType(typeCode)) { | |
| return typeCode - TupleTypeCode; | |
| } else { | |
| return 1; | |
| } | |
| } | |
| function hashIntValue(uint256 val) public pure returns (bytes32) { | |
| return keccak256(abi.encodePacked(val)); | |
| } | |
| function hashCodePointBasicValue(uint8 opcode, bytes32 nextCodePoint) public pure returns (bytes32) { | |
| return keccak256(abi.encodePacked( | |
| CodePointCode, | |
| opcode, | |
| nextCodePoint | |
| )); | |
| } | |
| function hashCodePointImmediateValue(uint8 opcode, bytes32 immediateVal, bytes32 nextCodePoint) public pure returns (bytes32) { | |
| return keccak256(abi.encodePacked( | |
| CodePointCode, | |
| opcode, | |
| immediateVal, | |
| nextCodePoint | |
| )); | |
| } | |
| function hashEmptyTuple() public pure returns (bytes32) { | |
| bytes32[] memory hashes = new bytes32[](0); | |
| return keccak256(abi.encodePacked( | |
| uint8(TupleTypeCode), | |
| hashes | |
| )); | |
| } | |
| function hashTupleValue(Value[1] memory val) internal pure returns (bytes32) { | |
| Value[] memory vals = new Value[](val.length); | |
| for (uint i = 0; i < vals.length; i++) { | |
| vals[i] = val[i]; | |
| } | |
| return hashTupleValue(vals); | |
| } | |
| function hashTupleValue(Value[2] memory val) internal pure returns (bytes32) { | |
| Value[] memory vals = new Value[](val.length); | |
| for (uint i = 0; i < vals.length; i++) { | |
| vals[i] = val[i]; | |
| } | |
| return hashTupleValue(vals); | |
| } | |
| function hashTupleValue(Value[3] memory val) internal pure returns (bytes32) { | |
| Value[] memory vals = new Value[](val.length); | |
| for (uint i = 0; i < vals.length; i++) { | |
| vals[i] = val[i]; | |
| } | |
| return hashTupleValue(vals); | |
| } | |
| function hashTupleValue(Value[] memory val) private pure returns (bytes32) { | |
| require(val.length <= 8); | |
| bytes32[] memory hashes = new bytes32[](val.length); | |
| for (uint i = 0; i < hashes.length; i++) { | |
| HashOnlyValue memory hashVal = val[i].hash(); | |
| hashes[i] = hashVal.hash; | |
| } | |
| return keccak256(abi.encodePacked( | |
| uint8(TupleTypeCode + val.length), | |
| hashes | |
| )); | |
| } | |
| struct HashOnlyValue { | |
| bytes32 hash; | |
| } | |
| function deserialize_hash_only_value(bytes memory data, uint offset) internal pure returns(uint retCode, uint, HashOnlyValue memory) { | |
| bytes32 valHash = data.toBytes32(offset); | |
| offset += 32; | |
| return (0, offset, HashOnlyValue(valHash)); | |
| } | |
| struct Value { | |
| uint256 intVal; | |
| Value[] tupleVal; | |
| uint8 typeCode; | |
| } | |
| function typeCodeVal(Value memory val) internal pure returns (Value memory) { | |
| require(val.typeCode != 2); | |
| if (val.typeCode == 0) { | |
| return newIntValue(0); | |
| } else if (val.typeCode == 1) { | |
| return newIntValue(1); | |
| } else { | |
| return newIntValue(3); | |
| } | |
| } | |
| function valLength(Value memory val) internal pure returns (uint8) { | |
| return typeLength(val.typeCode); | |
| } | |
| function isInt(Value memory val) internal pure returns (bool) { | |
| return val.typeCode == IntTypeCode; | |
| } | |
| function isTuple(Value memory val) internal pure returns (bool) { | |
| return isTupleType(val.typeCode); | |
| } | |
| function hash(Value memory val) internal pure returns (HashOnlyValue memory) { | |
| require(val.typeCode < ValueTypeCount); | |
| if (val.typeCode == IntTypeCode) { | |
| return HashOnlyValue(hashIntValue(val.intVal)); | |
| } else if (val.typeCode == HashOnlyTypeCode) { | |
| return HashOnlyValue(bytes32(val.intVal)); | |
| } else if (val.typeCode >= TupleTypeCode && val.typeCode < ValueTypeCount) { | |
| return HashOnlyValue(hashTupleValue(val.tupleVal)); | |
| } else { | |
| assert(false); | |
| } | |
| } | |
| function newNoneValue() internal pure returns (Value memory) { | |
| return Value(0, new Value[](0), TupleTypeCode); | |
| } | |
| function newBooleanValue(bool val) internal pure returns (Value memory) { | |
| if (val) { | |
| return newIntValue(1); | |
| } else { | |
| return newIntValue(0); | |
| } | |
| } | |
| function newIntValue(uint256 _val) internal pure returns (Value memory) { | |
| return Value(_val, new Value[](0), IntTypeCode); | |
| } | |
| function isValidTupleSize(uint size) public pure returns (bool) { | |
| return size <= 8; | |
| } | |
| function newTupleValue(Value[] memory _val) internal pure returns (Value memory) { | |
| require(isValidTupleSize(_val.length)); | |
| return Value(0, _val, uint8(TupleTypeCode + _val.length)); | |
| } | |
| function newTupleHashValues(HashOnlyValue[] memory _val) internal pure returns (Value memory) { | |
| Value[] memory values = new Value[](_val.length); | |
| for (uint i = 0; i < _val.length; i++) { | |
| values[i] = newHashOnlyValue(_val[i].hash); | |
| } | |
| return newTupleValue(values); | |
| } | |
| function newRepeatedTuple(Value memory _val, uint8 _count) internal pure returns (Value memory) { | |
| Value[] memory values = new Value[](_count); | |
| for (uint i = 0; i < _count; i++) { | |
| values[i] = _val; | |
| } | |
| return newTupleValue(values); | |
| } | |
| function newHashOnlyValue(bytes32 _val) internal pure returns (Value memory) { | |
| return Value(uint256(_val), new Value[](0), HashOnlyTypeCode); | |
| } | |
| function deserialize_int(bytes memory data, uint offset) internal pure returns (uint, uint256) { | |
| uint256 intVal = data.toUint(offset); | |
| offset += 32; | |
| return (offset, intVal); | |
| } | |
| function deserialize_tuple(uint8 memberCount, bytes memory data, uint offset) internal pure returns (uint, uint, Value[] memory) { | |
| uint retVal; | |
| Value[] memory members = new Value[](memberCount); | |
| for (uint8 i = 0; i < memberCount; i++) { | |
| (retVal, offset, members[i]) = deserialize_value(data, offset); | |
| if (retVal != 0) { | |
| return (retVal, offset, members); | |
| } | |
| } | |
| return (0, offset, members); | |
| } | |
| function deserialize_value(bytes memory data, uint offset) internal pure returns(uint retCode, uint, Value memory) { | |
| uint8 valType = uint8(data[offset]); | |
| offset++; | |
| uint256 intVal; | |
| if (valType == IntTypeCode) { | |
| (offset, intVal) = deserialize_int(data, offset); | |
| return (0, offset, newIntValue(intVal)); | |
| } else if (valType == HashOnlyTypeCode) { | |
| (offset, intVal) = deserialize_int(data, offset); | |
| return (0, offset, newHashOnlyValue(bytes32(intVal))); | |
| } else if (valType >= TupleTypeCode && valType < ValueTypeCount) { | |
| uint8 tupLength = uint8(valType - TupleTypeCode); | |
| Value[] memory tupleVal; | |
| uint valid; | |
| (valid, offset, tupleVal) = deserialize_tuple(tupLength, data, offset); | |
| return (valid, offset, newTupleValue(tupleVal)); | |
| } | |
| return (10000 + uint(valType), 0, newIntValue(0)); | |
| } | |
| function deserialize_valid_value_hash(bytes memory data, uint offset) public pure returns(uint, bytes32) { | |
| uint valid; | |
| uint newOffset; | |
| Value memory value; | |
| (valid, newOffset, value) = deserialize_value(data, offset); | |
| require(valid == 0); | |
| return (newOffset, value.hash().hash); | |
| } | |
| function get_next_valid_value(bytes memory data, uint offset) public pure returns(uint, bytes memory) { | |
| uint valid; | |
| uint nextOffset; | |
| Value memory value; | |
| (valid, nextOffset, value) = deserialize_value(data, offset); | |
| require(valid == 0); | |
| return (nextOffset, data.slice(offset, nextOffset - offset)); | |
| } | |
| function deserialize_value_hash(bytes memory data) public pure returns (bytes32) { | |
| uint valid; | |
| uint offset = 0; | |
| Value memory value; | |
| (valid, offset, value) = deserialize_value(data, 0); | |
| require(valid == 0); | |
| return value.hash().hash; | |
| } | |
| } | |
| // File: contracts/ArbProtocol.sol | |
| /* | |
| * Copyright 2019, Offchain Labs, Inc. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| // pragma solidity ^0.5.3; | |
| library ArbProtocol { | |
| using ArbValue for ArbValue.Value; | |
| function appendInboxMessages( | |
| bytes32 _inboxHash, | |
| bytes32 _pendingMessages | |
| ) public pure returns (bytes32) { | |
| return ArbValue.hashTupleValue([ | |
| ArbValue.newIntValue(1), | |
| ArbValue.newHashOnlyValue(_inboxHash), | |
| ArbValue.newHashOnlyValue(_pendingMessages) | |
| ]); | |
| } | |
| function appendInboxPendingMessage( | |
| bytes32 _pendingMessages, | |
| bytes32 _newMessage | |
| ) public pure returns (bytes32) { | |
| return ArbValue.hashTupleValue([ | |
| ArbValue.newIntValue(0), | |
| ArbValue.newHashOnlyValue(_pendingMessages), | |
| ArbValue.newHashOnlyValue(_newMessage) | |
| ]); | |
| } | |
| function generateMessageStubHash( | |
| bytes32 _data, | |
| bytes21 _tokenType, | |
| uint256 _value, | |
| bytes32 _destination | |
| ) public pure returns (bytes32) { | |
| ArbValue.Value[] memory values = new ArbValue.Value[](4); | |
| values[0] = ArbValue.newHashOnlyValue(_data); | |
| values[1] = ArbValue.newIntValue(uint256(_destination)); | |
| values[2] = ArbValue.newIntValue(_value); | |
| values[3] = ArbValue.newIntValue(uint256(bytes32(_tokenType))); | |
| return ArbValue.newTupleValue(values).hash().hash; | |
| } | |
| function generateSentMessageHash( | |
| bytes32 _dest, | |
| bytes32 _data, | |
| bytes21 _tokenType, | |
| uint256 _value, | |
| bytes32 _sender | |
| ) public view returns (bytes32) { | |
| bytes32 txHash = keccak256(abi.encodePacked( | |
| _dest, | |
| _data, | |
| _value, | |
| _tokenType | |
| )); | |
| ArbValue.Value[] memory dataValues = new ArbValue.Value[](4); | |
| dataValues[0] = ArbValue.newHashOnlyValue(_data); | |
| dataValues[1] = ArbValue.newIntValue(block.timestamp); | |
| dataValues[2] = ArbValue.newIntValue(block.number); | |
| dataValues[3] = ArbValue.newIntValue(uint(txHash)); | |
| ArbValue.Value[] memory values = new ArbValue.Value[](4); | |
| values[0] = ArbValue.newTupleValue(dataValues); | |
| values[1] = ArbValue.newIntValue(uint256(_sender)); | |
| values[2] = ArbValue.newIntValue(_value); | |
| values[3] = ArbValue.newIntValue(uint256(bytes32(_tokenType))); | |
| return ArbValue.newTupleValue(values).hash().hash; | |
| } | |
| function generatePreconditionHash( | |
| bytes32 _beforeHash, | |
| uint64[2] memory _timeBounds, | |
| bytes32 _beforeInbox, | |
| bytes21[] memory _tokenTypes, | |
| uint256[] memory _beforeBalances | |
| ) public pure returns (bytes32) { | |
| return keccak256(abi.encodePacked( | |
| _beforeHash, | |
| _timeBounds[0], | |
| _timeBounds[1], | |
| _beforeInbox, | |
| _tokenTypes, | |
| _beforeBalances | |
| )); | |
| } | |
| function generateAssertionHash( | |
| bytes32 _afterHash, | |
| uint32 _numSteps, | |
| bytes32 _firstMessageHash, | |
| bytes32 _lastMessageHash, | |
| bytes32 _firstLogHash, | |
| bytes32 _lastLogHash, | |
| uint256[] memory _totalMessageValueAmounts | |
| ) public pure returns (bytes32) { | |
| return keccak256(abi.encodePacked( | |
| _afterHash, | |
| _numSteps, | |
| _firstMessageHash, | |
| _lastMessageHash, | |
| _firstLogHash, | |
| _lastLogHash, | |
| _totalMessageValueAmounts | |
| )); | |
| } | |
| // fields: | |
| // vmId | |
| // beforeHash | |
| // beforeInbox | |
| // afterHash | |
| // newInbox | |
| function unanimousAssertHash( | |
| bytes32[5] memory _fields, | |
| uint64[2] memory _timeBounds, | |
| bytes21[] memory _tokenTypes, | |
| bytes memory _messageData, | |
| uint16[] memory _messageTokenNum, | |
| uint256[] memory _messageAmount, | |
| bytes32[] memory _messageDestination | |
| ) public pure returns(bytes32) { | |
| return keccak256(abi.encodePacked( | |
| _fields, | |
| _timeBounds, | |
| _tokenTypes, | |
| _messageData, | |
| _messageTokenNum, | |
| _messageAmount, | |
| _messageDestination | |
| )); | |
| } | |
| function calculateBeforeValues( | |
| bytes21[] memory _tokenTypes, | |
| uint16[] memory _messageTokenNums, | |
| uint256[] memory _messageAmounts | |
| ) public pure returns(uint256[] memory) { | |
| uint256[] memory beforeBalances = new uint256[](_tokenTypes.length); | |
| for (uint i = 0; i < _messageTokenNums.length; i++) { | |
| if (_tokenTypes[_messageTokenNums[i]][20] == 0) { | |
| beforeBalances[_messageTokenNums[i]] += _messageAmounts[i]; | |
| } else { | |
| require(beforeBalances[_messageTokenNums[i]] == 0); | |
| require(_messageAmounts[i] != 0); | |
| beforeBalances[_messageTokenNums[i]] = _messageAmounts[i]; | |
| } | |
| } | |
| return beforeBalances; | |
| } | |
| function generateLastMessageHash( | |
| bytes21[] memory _tokenTypes, | |
| bytes memory _messageData, | |
| uint16[] memory _messageTokenNum, | |
| uint256[] memory _messageAmount, | |
| bytes32[] memory _messageDestination | |
| ) public pure returns (bytes32) { | |
| require(_messageAmount.length == _messageDestination.length); | |
| require(_messageAmount.length == _messageTokenNum.length); | |
| bytes32 hashVal = 0x00; | |
| uint256 offset = 0; | |
| bytes32 msgHash; | |
| for (uint i = 0; i < _messageAmount.length; i++) { | |
| (offset, msgHash) = ArbValue.deserialize_valid_value_hash(_messageData, offset); | |
| msgHash = generateMessageStubHash( | |
| msgHash, | |
| _tokenTypes[_messageTokenNum[i]], | |
| _messageAmount[i], | |
| _messageDestination[i] | |
| ); | |
| hashVal = keccak256(abi.encodePacked(hashVal, msgHash)); | |
| } | |
| } | |
| function generateLastMessageHashStub( | |
| bytes21[] memory _tokenTypes, | |
| bytes32[] memory _messageDataHashes, | |
| uint16[] memory _messageTokenNum, | |
| uint256[] memory _messageValueAmounts, | |
| bytes32[] memory _messageDestination | |
| ) public pure returns (bytes32) { | |
| require(_messageDataHashes.length == _messageTokenNum.length); | |
| require(_messageDataHashes.length == _messageValueAmounts.length); | |
| require(_messageDataHashes.length == _messageDestination.length); | |
| bytes32 hashVal = 0x00; | |
| bytes32 msgHash; | |
| for (uint i = 0; i < _messageDataHashes.length; i++) { | |
| msgHash = generateMessageStubHash( | |
| _messageDataHashes[i], | |
| _tokenTypes[_messageTokenNum[i]], | |
| _messageValueAmounts[i], | |
| _messageDestination[i] | |
| ); | |
| hashVal = keccak256(abi.encodePacked(hashVal, msgHash)); | |
| } | |
| return hashVal; | |
| } | |
| function parseSignature( | |
| bytes memory _signatures, | |
| uint _pos | |
| ) | |
| pure | |
| public | |
| returns (uint8 v, bytes32 r, bytes32 s) | |
| { | |
| uint offset = _pos * 65; | |
| // The signature format is a compact form of: | |
| // {bytes32 r}{bytes32 s}{uint8 v} | |
| // Compact means, uint8 is not padded to 32 bytes. | |
| assembly { // solium-disable-line security/no-inline-assembly | |
| r := mload(add(_signatures, add(32, offset))) | |
| s := mload(add(_signatures, add(64, offset))) | |
| // Here we are loading the last 32 bytes, including 31 bytes | |
| // of 's'. There is no 'mload8' to do this. | |
| // | |
| // 'byte' is not working due to the Solidity parser, so lets | |
| // use the second best option, 'and' | |
| v := and(mload(add(_signatures, add(65, offset))), 0xff) | |
| } | |
| if (v < 27) v += 27; | |
| require(v == 27 || v == 28); | |
| } | |
| /// @notice Counts the number of signatures in a signatures bytes array. Returns 0 if the length is invalid. | |
| /// @param _signatures The signatures bytes array | |
| /// @dev Signatures are 65 bytes long and are densely packed. | |
| function countSignatures( | |
| bytes memory _signatures | |
| ) | |
| pure | |
| public | |
| returns (uint) | |
| { | |
| return _signatures.length % 65 == 0 ? _signatures.length / 65 : 0; | |
| } | |
| /// @notice Recovers an array of addresses using a message hash and a signatures bytes array. | |
| /// @param _messageHash The signed message hash | |
| /// @param _signatures The signatures bytes array | |
| function recoverAddresses( | |
| bytes32 _messageHash, | |
| bytes memory _signatures | |
| ) | |
| pure | |
| public | |
| returns (address[] memory) | |
| { | |
| uint8 v; | |
| bytes32 r; | |
| bytes32 s; | |
| uint count = countSignatures(_signatures); | |
| address[] memory addresses = new address[](count); | |
| bytes memory prefix = "\x19Ethereum Signed Message:\n32"; | |
| bytes32 prefixedHash = keccak256(abi.encodePacked(prefix, _messageHash)); | |
| for (uint i = 0; i < count; i++) { | |
| (v, r, s) = parseSignature(_signatures, i); | |
| addresses[i] = ecrecover(prefixedHash, v, r, s); | |
| } | |
| return addresses; | |
| } | |
| } | |
| // File: contracts/IChallengeManager.sol | |
| /* | |
| * Copyright 2019, Offchain Labs, Inc. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| // pragma solidity ^0.5.3; | |
| interface IChallengeManager { | |
| function initiateChallenge( | |
| bytes32 vmId, | |
| address[2] calldata players, | |
| uint128[2] calldata escrows, | |
| uint32 challengePeriod, | |
| bytes32 challengeRoot | |
| ) external; | |
| } | |
| // File: openzeppelin-solidity/contracts/GSN/Context.sol | |
| // 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. | |
| */ | |
| 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; | |
| } | |
| } | |
| // File: openzeppelin-solidity/contracts/token/ERC20/IERC20.sol | |
| // pragma solidity ^0.5.0; | |
| /** | |
| * @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); | |
| } | |
| // File: openzeppelin-solidity/contracts/math/SafeMath.sol | |
| // pragma solidity ^0.5.0; | |
| /** | |
| * @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; | |
| } | |
| } | |
| // File: openzeppelin-solidity/contracts/token/ERC20/ERC20.sol | |
| // pragma solidity ^0.5.0; | |
| /** | |
| * @dev Implementation of the {IERC20} interface. | |
| * | |
| * This implementation is agnostic to the way tokens are created. This means | |
| * that a supply mechanism has to be added in a derived contract using {_mint}. | |
| * For a generic mechanism see {ERC20Mintable}. | |
| * | |
| * TIP: For a detailed writeup see our guide | |
| * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How | |
| * to implement supply mechanisms]. | |
| * | |
| * We have followed general OpenZeppelin guidelines: functions revert instead | |
| * of returning `false` on failure. This behavior is nonetheless conventional | |
| * and does not conflict with the expectations of ERC20 applications. | |
| * | |
| * Additionally, an {Approval} event is emitted on calls to {transferFrom}. | |
| * This allows applications to reconstruct the allowance for all accounts just | |
| * by listening to said events. Other implementations of the EIP may not emit | |
| * these events, as it isn't required by the specification. | |
| * | |
| * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} | |
| * functions have been added to mitigate the well-known issues around setting | |
| * allowances. See {IERC20-approve}. | |
| */ | |
| contract ERC20 is Context, IERC20 { | |
| using SafeMath for uint256; | |
| mapping (address => uint256) private _balances; | |
| mapping (address => mapping (address => uint256)) private _allowances; | |
| uint256 private _totalSupply; | |
| /** | |
| * @dev See {IERC20-totalSupply}. | |
| */ | |
| function totalSupply() public view returns (uint256) { | |
| return _totalSupply; | |
| } | |
| /** | |
| * @dev See {IERC20-balanceOf}. | |
| */ | |
| function balanceOf(address account) public view returns (uint256) { | |
| return _balances[account]; | |
| } | |
| /** | |
| * @dev See {IERC20-transfer}. | |
| * | |
| * Requirements: | |
| * | |
| * - `recipient` cannot be the zero address. | |
| * - the caller must have a balance of at least `amount`. | |
| */ | |
| function transfer(address recipient, uint256 amount) public returns (bool) { | |
| _transfer(_msgSender(), recipient, amount); | |
| return true; | |
| } | |
| /** | |
| * @dev See {IERC20-allowance}. | |
| */ | |
| function allowance(address owner, address spender) public view returns (uint256) { | |
| return _allowances[owner][spender]; | |
| } | |
| /** | |
| * @dev See {IERC20-approve}. | |
| * | |
| * Requirements: | |
| * | |
| * - `spender` cannot be the zero address. | |
| */ | |
| function approve(address spender, uint256 amount) public returns (bool) { | |
| _approve(_msgSender(), spender, amount); | |
| return true; | |
| } | |
| /** | |
| * @dev See {IERC20-transferFrom}. | |
| * | |
| * Emits an {Approval} event indicating the updated allowance. This is not | |
| * required by the EIP. See the note at the beginning of {ERC20}; | |
| * | |
| * Requirements: | |
| * - `sender` and `recipient` cannot be the zero address. | |
| * - `sender` must have a balance of at least `amount`. | |
| * - the caller must have allowance for `sender`'s tokens of at least | |
| * `amount`. | |
| */ | |
| function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { | |
| _transfer(sender, recipient, amount); | |
| _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); | |
| return true; | |
| } | |
| /** | |
| * @dev Atomically increases the allowance granted to `spender` by the caller. | |
| * | |
| * This is an alternative to {approve} that can be used as a mitigation for | |
| * problems described in {IERC20-approve}. | |
| * | |
| * Emits an {Approval} event indicating the updated allowance. | |
| * | |
| * Requirements: | |
| * | |
| * - `spender` cannot be the zero address. | |
| */ | |
| function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { | |
| _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); | |
| return true; | |
| } | |
| /** | |
| * @dev Atomically decreases the allowance granted to `spender` by the caller. | |
| * | |
| * This is an alternative to {approve} that can be used as a mitigation for | |
| * problems described in {IERC20-approve}. | |
| * | |
| * Emits an {Approval} event indicating the updated allowance. | |
| * | |
| * Requirements: | |
| * | |
| * - `spender` cannot be the zero address. | |
| * - `spender` must have allowance for the caller of at least | |
| * `subtractedValue`. | |
| */ | |
| function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { | |
| _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); | |
| return true; | |
| } | |
| /** | |
| * @dev Moves tokens `amount` from `sender` to `recipient`. | |
| * | |
| * This is internal function is equivalent to {transfer}, and can be used to | |
| * e.g. implement automatic token fees, slashing mechanisms, etc. | |
| * | |
| * Emits a {Transfer} event. | |
| * | |
| * Requirements: | |
| * | |
| * - `sender` cannot be the zero address. | |
| * - `recipient` cannot be the zero address. | |
| * - `sender` must have a balance of at least `amount`. | |
| */ | |
| function _transfer(address sender, address recipient, uint256 amount) internal { | |
| require(sender != address(0), "ERC20: transfer from the zero address"); | |
| require(recipient != address(0), "ERC20: transfer to the zero address"); | |
| _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); | |
| _balances[recipient] = _balances[recipient].add(amount); | |
| emit Transfer(sender, recipient, amount); | |
| } | |
| /** @dev Creates `amount` tokens and assigns them to `account`, increasing | |
| * the total supply. | |
| * | |
| * Emits a {Transfer} event with `from` set to the zero address. | |
| * | |
| * Requirements | |
| * | |
| * - `to` cannot be the zero address. | |
| */ | |
| function _mint(address account, uint256 amount) internal { | |
| require(account != address(0), "ERC20: mint to the zero address"); | |
| _totalSupply = _totalSupply.add(amount); | |
| _balances[account] = _balances[account].add(amount); | |
| emit Transfer(address(0), account, amount); | |
| } | |
| /** | |
| * @dev Destroys `amount` tokens from `account`, reducing the | |
| * total supply. | |
| * | |
| * Emits a {Transfer} event with `to` set to the zero address. | |
| * | |
| * Requirements | |
| * | |
| * - `account` cannot be the zero address. | |
| * - `account` must have at least `amount` tokens. | |
| */ | |
| function _burn(address account, uint256 amount) internal { | |
| require(account != address(0), "ERC20: burn from the zero address"); | |
| _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); | |
| _totalSupply = _totalSupply.sub(amount); | |
| emit Transfer(account, address(0), amount); | |
| } | |
| /** | |
| * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. | |
| * | |
| * This is internal function is equivalent to `approve`, and can be used to | |
| * e.g. set automatic allowances for certain subsystems, etc. | |
| * | |
| * Emits an {Approval} event. | |
| * | |
| * Requirements: | |
| * | |
| * - `owner` cannot be the zero address. | |
| * - `spender` cannot be the zero address. | |
| */ | |
| function _approve(address owner, address spender, uint256 amount) internal { | |
| require(owner != address(0), "ERC20: approve from the zero address"); | |
| require(spender != address(0), "ERC20: approve to the zero address"); | |
| _allowances[owner][spender] = amount; | |
| emit Approval(owner, spender, amount); | |
| } | |
| /** | |
| * @dev Destroys `amount` tokens from `account`.`amount` is then deducted | |
| * from the caller's allowance. | |
| * | |
| * See {_burn} and {_approve}. | |
| */ | |
| function _burnFrom(address account, uint256 amount) internal { | |
| _burn(account, amount); | |
| _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance")); | |
| } | |
| } | |
| // File: openzeppelin-solidity/contracts/introspection/IERC165.sol | |
| // pragma solidity ^0.5.0; | |
| /** | |
| * @dev Interface of the ERC165 standard, as defined in the | |
| * https://eips.ethereum.org/EIPS/eip-165[EIP]. | |
| * | |
| * Implementers can declare support of contract interfaces, which can then be | |
| * queried by others ({ERC165Checker}). | |
| * | |
| * For an implementation, see {ERC165}. | |
| */ | |
| interface IERC165 { | |
| /** | |
| * @dev Returns true if this contract implements the interface defined by | |
| * `interfaceId`. See the corresponding | |
| * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] | |
| * to learn more about how these ids are created. | |
| * | |
| * This function call must use less than 30 000 gas. | |
| */ | |
| function supportsInterface(bytes4 interfaceId) external view returns (bool); | |
| } | |
| // File: openzeppelin-solidity/contracts/token/ERC721/IERC721.sol | |
| // pragma solidity ^0.5.0; | |
| /** | |
| * @dev Required interface of an ERC721 compliant contract. | |
| */ | |
| contract IERC721 is IERC165 { | |
| event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); | |
| event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); | |
| event ApprovalForAll(address indexed owner, address indexed operator, bool approved); | |
| /** | |
| * @dev Returns the number of NFTs in `owner`'s account. | |
| */ | |
| function balanceOf(address owner) public view returns (uint256 balance); | |
| /** | |
| * @dev Returns the owner of the NFT specified by `tokenId`. | |
| */ | |
| function ownerOf(uint256 tokenId) public view returns (address owner); | |
| /** | |
| * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to | |
| * another (`to`). | |
| * | |
| * | |
| * | |
| * Requirements: | |
| * - `from`, `to` cannot be zero. | |
| * - `tokenId` must be owned by `from`. | |
| * - If the caller is not `from`, it must be have been allowed to move this | |
| * NFT by either {approve} or {setApprovalForAll}. | |
| */ | |
| function safeTransferFrom(address from, address to, uint256 tokenId) public; | |
| /** | |
| * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to | |
| * another (`to`). | |
| * | |
| * Requirements: | |
| * - If the caller is not `from`, it must be approved to move this NFT by | |
| * either {approve} or {setApprovalForAll}. | |
| */ | |
| function transferFrom(address from, address to, uint256 tokenId) public; | |
| function approve(address to, uint256 tokenId) public; | |
| function getApproved(uint256 tokenId) public view returns (address operator); | |
| function setApprovalForAll(address operator, bool _approved) public; | |
| function isApprovedForAll(address owner, address operator) public view returns (bool); | |
| function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public; | |
| } | |
| // File: openzeppelin-solidity/contracts/token/ERC721/IERC721Receiver.sol | |
| // pragma solidity ^0.5.0; | |
| /** | |
| * @title ERC721 token receiver interface | |
| * @dev Interface for any contract that wants to support safeTransfers | |
| * from ERC721 asset contracts. | |
| */ | |
| contract IERC721Receiver { | |
| /** | |
| * @notice Handle the receipt of an NFT | |
| * @dev The ERC721 smart contract calls this function on the recipient | |
| * after a {IERC721-safeTransferFrom}. This function MUST return the function selector, | |
| * otherwise the caller will revert the transaction. The selector to be | |
| * returned can be obtained as `this.onERC721Received.selector`. This | |
| * function MAY throw to revert and reject the transfer. | |
| * Note: the ERC721 contract address is always the message sender. | |
| * @param operator The address which called `safeTransferFrom` function | |
| * @param from The address which previously owned the token | |
| * @param tokenId The NFT identifier which is being transferred | |
| * @param data Additional data with no specified format | |
| * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` | |
| */ | |
| function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data) | |
| public returns (bytes4); | |
| } | |
| // File: openzeppelin-solidity/contracts/utils/Address.sol | |
| // pragma solidity ^0.5.5; | |
| /** | |
| * @dev Collection of functions related to the address type | |
| */ | |
| library Address { | |
| /** | |
| * @dev Returns true if `account` is a contract. | |
| * | |
| * 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 Converts an `address` into `address payable`. Note that this is | |
| * simply a type cast: the actual underlying value is not changed. | |
| * | |
| * _Available since v2.4.0._ | |
| */ | |
| function toPayable(address account) internal pure returns (address payable) { | |
| return address(uint160(account)); | |
| } | |
| /** | |
| * @dev Replacement for Solidity's `transfer`: sends `amount` wei to | |
| * `recipient`, forwarding all available gas and reverting on errors. | |
| * | |
| * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost | |
| * of certain opcodes, possibly making contracts go over the 2300 gas limit | |
| * imposed by `transfer`, making them unable to receive funds via | |
| * `transfer`. {sendValue} removes this limitation. | |
| * | |
| * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. | |
| * | |
| * IMPORTANT: because control is transferred to `recipient`, care must be | |
| * taken to not create reentrancy vulnerabilities. Consider using | |
| * {ReentrancyGuard} or the | |
| * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. | |
| * | |
| * _Available since v2.4.0._ | |
| */ | |
| function sendValue(address payable recipient, uint256 amount) internal { | |
| require(address(this).balance >= amount, "Address: insufficient balance"); | |
| // solhint-disable-next-line avoid-call-value | |
| (bool success, ) = recipient.call.value(amount)(""); | |
| require(success, "Address: unable to send value, recipient may have reverted"); | |
| } | |
| } | |
| // File: openzeppelin-solidity/contracts/drafts/Counters.sol | |
| // pragma solidity ^0.5.0; | |
| /** | |
| * @title Counters | |
| * @author Matt Condon (@shrugs) | |
| * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number | |
| * of elements in a mapping, issuing ERC721 ids, or counting request ids. | |
| * | |
| * Include with `using Counters for Counters.Counter;` | |
| * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath} | |
| * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never | |
| * directly accessed. | |
| */ | |
| library Counters { | |
| using SafeMath for uint256; | |
| struct Counter { | |
| // This variable should never be directly accessed by users of the library: interactions must be restricted to | |
| // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add | |
| // this feature: see https://github.com/ethereum/solidity/issues/4637 | |
| uint256 _value; // default: 0 | |
| } | |
| function current(Counter storage counter) internal view returns (uint256) { | |
| return counter._value; | |
| } | |
| function increment(Counter storage counter) internal { | |
| counter._value += 1; | |
| } | |
| function decrement(Counter storage counter) internal { | |
| counter._value = counter._value.sub(1); | |
| } | |
| } | |
| // File: openzeppelin-solidity/contracts/introspection/ERC165.sol | |
| // pragma solidity ^0.5.0; | |
| /** | |
| * @dev Implementation of the {IERC165} interface. | |
| * | |
| * Contracts may inherit from this and call {_registerInterface} to declare | |
| * their support of an interface. | |
| */ | |
| contract ERC165 is IERC165 { | |
| /* | |
| * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 | |
| */ | |
| bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; | |
| /** | |
| * @dev Mapping of interface ids to whether or not it's supported. | |
| */ | |
| mapping(bytes4 => bool) private _supportedInterfaces; | |
| constructor () internal { | |
| // Derived contracts need only register support for their own interfaces, | |
| // we register support for ERC165 itself here | |
| _registerInterface(_INTERFACE_ID_ERC165); | |
| } | |
| /** | |
| * @dev See {IERC165-supportsInterface}. | |
| * | |
| * Time complexity O(1), guaranteed to always use less than 30 000 gas. | |
| */ | |
| function supportsInterface(bytes4 interfaceId) external view returns (bool) { | |
| return _supportedInterfaces[interfaceId]; | |
| } | |
| /** | |
| * @dev Registers the contract as an implementer of the interface defined by | |
| * `interfaceId`. Support of the actual ERC165 interface is automatic and | |
| * registering its interface id is not required. | |
| * | |
| * See {IERC165-supportsInterface}. | |
| * | |
| * Requirements: | |
| * | |
| * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`). | |
| */ | |
| function _registerInterface(bytes4 interfaceId) internal { | |
| require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); | |
| _supportedInterfaces[interfaceId] = true; | |
| } | |
| } | |
| // File: openzeppelin-solidity/contracts/token/ERC721/ERC721.sol | |
| // pragma solidity ^0.5.0; | |
| /** | |
| * @title ERC721 Non-Fungible Token Standard basic implementation | |
| * @dev see https://eips.ethereum.org/EIPS/eip-721 | |
| */ | |
| contract ERC721 is Context, ERC165, IERC721 { | |
| using SafeMath for uint256; | |
| using Address for address; | |
| using Counters for Counters.Counter; | |
| // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` | |
| // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` | |
| bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; | |
| // Mapping from token ID to owner | |
| mapping (uint256 => address) private _tokenOwner; | |
| // Mapping from token ID to approved address | |
| mapping (uint256 => address) private _tokenApprovals; | |
| // Mapping from owner to number of owned token | |
| mapping (address => Counters.Counter) private _ownedTokensCount; | |
| // Mapping from owner to operator approvals | |
| mapping (address => mapping (address => bool)) private _operatorApprovals; | |
| /* | |
| * bytes4(keccak256('balanceOf(address)')) == 0x70a08231 | |
| * bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e | |
| * bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3 | |
| * bytes4(keccak256('getApproved(uint256)')) == 0x081812fc | |
| * bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465 | |
| * bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5 | |
| * bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd | |
| * bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e | |
| * bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde | |
| * | |
| * => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^ | |
| * 0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd | |
| */ | |
| bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; | |
| constructor () public { | |
| // register the supported interfaces to conform to ERC721 via ERC165 | |
| _registerInterface(_INTERFACE_ID_ERC721); | |
| } | |
| /** | |
| * @dev Gets the balance of the specified address. | |
| * @param owner address to query the balance of | |
| * @return uint256 representing the amount owned by the passed address | |
| */ | |
| function balanceOf(address owner) public view returns (uint256) { | |
| require(owner != address(0), "ERC721: balance query for the zero address"); | |
| return _ownedTokensCount[owner].current(); | |
| } | |
| /** | |
| * @dev Gets the owner of the specified token ID. | |
| * @param tokenId uint256 ID of the token to query the owner of | |
| * @return address currently marked as the owner of the given token ID | |
| */ | |
| function ownerOf(uint256 tokenId) public view returns (address) { | |
| address owner = _tokenOwner[tokenId]; | |
| require(owner != address(0), "ERC721: owner query for nonexistent token"); | |
| return owner; | |
| } | |
| /** | |
| * @dev Approves another address to transfer the given token ID | |
| * The zero address indicates there is no approved address. | |
| * There can only be one approved address per token at a given time. | |
| * Can only be called by the token owner or an approved operator. | |
| * @param to address to be approved for the given token ID | |
| * @param tokenId uint256 ID of the token to be approved | |
| */ | |
| function approve(address to, uint256 tokenId) public { | |
| address owner = ownerOf(tokenId); | |
| require(to != owner, "ERC721: approval to current owner"); | |
| require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()), | |
| "ERC721: approve caller is not owner nor approved for all" | |
| ); | |
| _tokenApprovals[tokenId] = to; | |
| emit Approval(owner, to, tokenId); | |
| } | |
| /** | |
| * @dev Gets the approved address for a token ID, or zero if no address set | |
| * Reverts if the token ID does not exist. | |
| * @param tokenId uint256 ID of the token to query the approval of | |
| * @return address currently approved for the given token ID | |
| */ | |
| function getApproved(uint256 tokenId) public view returns (address) { | |
| require(_exists(tokenId), "ERC721: approved query for nonexistent token"); | |
| return _tokenApprovals[tokenId]; | |
| } | |
| /** | |
| * @dev Sets or unsets the approval of a given operator | |
| * An operator is allowed to transfer all tokens of the sender on their behalf. | |
| * @param to operator address to set the approval | |
| * @param approved representing the status of the approval to be set | |
| */ | |
| function setApprovalForAll(address to, bool approved) public { | |
| require(to != _msgSender(), "ERC721: approve to caller"); | |
| _operatorApprovals[_msgSender()][to] = approved; | |
| emit ApprovalForAll(_msgSender(), to, approved); | |
| } | |
| /** | |
| * @dev Tells whether an operator is approved by a given owner. | |
| * @param owner owner address which you want to query the approval of | |
| * @param operator operator address which you want to query the approval of | |
| * @return bool whether the given operator is approved by the given owner | |
| */ | |
| function isApprovedForAll(address owner, address operator) public view returns (bool) { | |
| return _operatorApprovals[owner][operator]; | |
| } | |
| /** | |
| * @dev Transfers the ownership of a given token ID to another address. | |
| * Usage of this method is discouraged, use {safeTransferFrom} whenever possible. | |
| * Requires the msg.sender to be the owner, approved, or operator. | |
| * @param from current owner of the token | |
| * @param to address to receive the ownership of the given token ID | |
| * @param tokenId uint256 ID of the token to be transferred | |
| */ | |
| function transferFrom(address from, address to, uint256 tokenId) public { | |
| //solhint-disable-next-line max-line-length | |
| require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); | |
| _transferFrom(from, to, tokenId); | |
| } | |
| /** | |
| * @dev Safely transfers the ownership of a given token ID to another address | |
| * If the target address is a contract, it must implement {IERC721Receiver-onERC721Received}, | |
| * which is called upon a safe transfer, and return the magic value | |
| * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, | |
| * the transfer is reverted. | |
| * Requires the msg.sender to be the owner, approved, or operator | |
| * @param from current owner of the token | |
| * @param to address to receive the ownership of the given token ID | |
| * @param tokenId uint256 ID of the token to be transferred | |
| */ | |
| function safeTransferFrom(address from, address to, uint256 tokenId) public { | |
| safeTransferFrom(from, to, tokenId, ""); | |
| } | |
| /** | |
| * @dev Safely transfers the ownership of a given token ID to another address | |
| * If the target address is a contract, it must implement {IERC721Receiver-onERC721Received}, | |
| * which is called upon a safe transfer, and return the magic value | |
| * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, | |
| * the transfer is reverted. | |
| * Requires the _msgSender() to be the owner, approved, or operator | |
| * @param from current owner of the token | |
| * @param to address to receive the ownership of the given token ID | |
| * @param tokenId uint256 ID of the token to be transferred | |
| * @param _data bytes data to send along with a safe transfer check | |
| */ | |
| function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public { | |
| require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); | |
| _safeTransferFrom(from, to, tokenId, _data); | |
| } | |
| /** | |
| * @dev Safely transfers the ownership of a given token ID to another address | |
| * If the target address is a contract, it must implement `onERC721Received`, | |
| * which is called upon a safe transfer, and return the magic value | |
| * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, | |
| * the transfer is reverted. | |
| * Requires the msg.sender to be the owner, approved, or operator | |
| * @param from current owner of the token | |
| * @param to address to receive the ownership of the given token ID | |
| * @param tokenId uint256 ID of the token to be transferred | |
| * @param _data bytes data to send along with a safe transfer check | |
| */ | |
| function _safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) internal { | |
| _transferFrom(from, to, tokenId); | |
| require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); | |
| } | |
| /** | |
| * @dev Returns whether the specified token exists. | |
| * @param tokenId uint256 ID of the token to query the existence of | |
| * @return bool whether the token exists | |
| */ | |
| function _exists(uint256 tokenId) internal view returns (bool) { | |
| address owner = _tokenOwner[tokenId]; | |
| return owner != address(0); | |
| } | |
| /** | |
| * @dev Returns whether the given spender can transfer a given token ID. | |
| * @param spender address of the spender to query | |
| * @param tokenId uint256 ID of the token to be transferred | |
| * @return bool whether the msg.sender is approved for the given token ID, | |
| * is an operator of the owner, or is the owner of the token | |
| */ | |
| function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) { | |
| require(_exists(tokenId), "ERC721: operator query for nonexistent token"); | |
| address owner = ownerOf(tokenId); | |
| return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); | |
| } | |
| /** | |
| * @dev Internal function to safely mint a new token. | |
| * Reverts if the given token ID already exists. | |
| * If the target address is a contract, it must implement `onERC721Received`, | |
| * which is called upon a safe transfer, and return the magic value | |
| * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, | |
| * the transfer is reverted. | |
| * @param to The address that will own the minted token | |
| * @param tokenId uint256 ID of the token to be minted | |
| */ | |
| function _safeMint(address to, uint256 tokenId) internal { | |
| _safeMint(to, tokenId, ""); | |
| } | |
| /** | |
| * @dev Internal function to safely mint a new token. | |
| * Reverts if the given token ID already exists. | |
| * If the target address is a contract, it must implement `onERC721Received`, | |
| * which is called upon a safe transfer, and return the magic value | |
| * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, | |
| * the transfer is reverted. | |
| * @param to The address that will own the minted token | |
| * @param tokenId uint256 ID of the token to be minted | |
| * @param _data bytes data to send along with a safe transfer check | |
| */ | |
| function _safeMint(address to, uint256 tokenId, bytes memory _data) internal { | |
| _mint(to, tokenId); | |
| require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); | |
| } | |
| /** | |
| * @dev Internal function to mint a new token. | |
| * Reverts if the given token ID already exists. | |
| * @param to The address that will own the minted token | |
| * @param tokenId uint256 ID of the token to be minted | |
| */ | |
| function _mint(address to, uint256 tokenId) internal { | |
| require(to != address(0), "ERC721: mint to the zero address"); | |
| require(!_exists(tokenId), "ERC721: token already minted"); | |
| _tokenOwner[tokenId] = to; | |
| _ownedTokensCount[to].increment(); | |
| emit Transfer(address(0), to, tokenId); | |
| } | |
| /** | |
| * @dev Internal function to burn a specific token. | |
| * Reverts if the token does not exist. | |
| * Deprecated, use {_burn} instead. | |
| * @param owner owner of the token to burn | |
| * @param tokenId uint256 ID of the token being burned | |
| */ | |
| function _burn(address owner, uint256 tokenId) internal { | |
| require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own"); | |
| _clearApproval(tokenId); | |
| _ownedTokensCount[owner].decrement(); | |
| _tokenOwner[tokenId] = address(0); | |
| emit Transfer(owner, address(0), tokenId); | |
| } | |
| /** | |
| * @dev Internal function to burn a specific token. | |
| * Reverts if the token does not exist. | |
| * @param tokenId uint256 ID of the token being burned | |
| */ | |
| function _burn(uint256 tokenId) internal { | |
| _burn(ownerOf(tokenId), tokenId); | |
| } | |
| /** | |
| * @dev Internal function to transfer ownership of a given token ID to another address. | |
| * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. | |
| * @param from current owner of the token | |
| * @param to address to receive the ownership of the given token ID | |
| * @param tokenId uint256 ID of the token to be transferred | |
| */ | |
| function _transferFrom(address from, address to, uint256 tokenId) internal { | |
| require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); | |
| require(to != address(0), "ERC721: transfer to the zero address"); | |
| _clearApproval(tokenId); | |
| _ownedTokensCount[from].decrement(); | |
| _ownedTokensCount[to].increment(); | |
| _tokenOwner[tokenId] = to; | |
| emit Transfer(from, to, tokenId); | |
| } | |
| /** | |
| * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. | |
| * The call is not executed if the target address is not a contract. | |
| * | |
| * This function is deprecated. | |
| * @param from address representing the previous owner of the given token ID | |
| * @param to target address that will receive the tokens | |
| * @param tokenId uint256 ID of the token to be transferred | |
| * @param _data bytes optional data to send along with the call | |
| * @return bool whether the call correctly returned the expected magic value | |
| */ | |
| function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) | |
| internal returns (bool) | |
| { | |
| if (!to.isContract()) { | |
| return true; | |
| } | |
| bytes4 retval = IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data); | |
| return (retval == _ERC721_RECEIVED); | |
| } | |
| /** | |
| * @dev Private function to clear current approval of a given token ID. | |
| * @param tokenId uint256 ID of the token to be transferred | |
| */ | |
| function _clearApproval(uint256 tokenId) private { | |
| if (_tokenApprovals[tokenId] != address(0)) { | |
| _tokenApprovals[tokenId] = address(0); | |
| } | |
| } | |
| } | |
| // File: openzeppelin-solidity/contracts/ownership/Ownable.sol | |
| // pragma solidity ^0.5.0; | |
| /** | |
| * @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 { | |
| _owner = _msgSender(); | |
| emit OwnershipTransferred(address(0), _owner); | |
| } | |
| /** | |
| * @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; | |
| } | |
| } | |
| // File: contracts/ArbBalanceTracker.sol | |
| /* | |
| * Copyright 2019, Offchain Labs, Inc. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| // pragma solidity ^0.5.3; | |
| contract ArbBalanceTracker is Ownable, ERC20 { | |
| using SafeMath for uint256; | |
| struct NFTWallet { | |
| address contractAddress; | |
| mapping(uint256 => uint256) tokenIndex; | |
| uint256[] tokenList; | |
| } | |
| struct TokenWallet { | |
| address contractAddress; | |
| uint256 balance; | |
| } | |
| struct Wallet { | |
| mapping(address => uint256) tokenIndex; | |
| TokenWallet[] tokenList; | |
| mapping(address => uint256) nftWalletIndex; | |
| NFTWallet[] nftWalletList; | |
| } | |
| uint totalArbSupply; | |
| mapping(bytes32 => Wallet) wallets; | |
| function addNFTToken(bytes32 _user, address _tokenContract, uint256 _tokenId) internal { | |
| Wallet storage wallet = wallets[_user]; | |
| uint index = wallet.nftWalletIndex[_tokenContract]; | |
| if (index == 0) { | |
| index = wallet.nftWalletList.push(NFTWallet(_tokenContract, new uint256[](0))); | |
| wallet.nftWalletIndex[_tokenContract] = index; | |
| } | |
| NFTWallet storage nftWallet = wallet.nftWalletList[index - 1]; | |
| require(nftWallet.tokenIndex[_tokenId] == 0); | |
| nftWallet.tokenList.push(_tokenId); | |
| nftWallet.tokenIndex[_tokenId] = nftWallet.tokenList.length; | |
| } | |
| function addToken(bytes32 _user, address _tokenContract, uint256 _value) internal { | |
| if (_value == 0) { | |
| return; | |
| } | |
| Wallet storage wallet = wallets[_user]; | |
| uint index = wallet.tokenIndex[_tokenContract]; | |
| if (index == 0) { | |
| index = wallet.tokenList.push(TokenWallet(_tokenContract, 0)); | |
| wallet.tokenIndex[_tokenContract] = index; | |
| } | |
| TokenWallet storage tokenWallet = wallet.tokenList[index - 1]; | |
| tokenWallet.balance = tokenWallet.balance.add(_value); | |
| } | |
| function removeNFTToken(bytes32 _user, address _tokenContract, uint256 _tokenId) internal { | |
| Wallet storage wallet = wallets[_user]; | |
| uint walletIndex = wallet.nftWalletIndex[_tokenContract]; | |
| require(walletIndex != 0, "Wallet has no coins from given NFT contract"); | |
| NFTWallet storage nftWallet = wallet.nftWalletList[walletIndex - 1]; | |
| uint tokenIndex = nftWallet.tokenIndex[_tokenId]; | |
| require(tokenIndex != 0, "Wallet does not own specific NFT"); | |
| nftWallet.tokenIndex[nftWallet.tokenList[nftWallet.tokenList.length - 1]] = tokenIndex; | |
| nftWallet.tokenList[tokenIndex - 1] = nftWallet.tokenList[nftWallet.tokenList.length - 1]; | |
| delete nftWallet.tokenIndex[_tokenId]; | |
| nftWallet.tokenList.length = nftWallet.tokenList.length - 1; | |
| if (nftWallet.tokenList.length == 0) { | |
| wallet.nftWalletIndex[wallet.nftWalletList[wallet.nftWalletList.length - 1].contractAddress] = walletIndex; | |
| wallet.nftWalletList[walletIndex - 1] = wallet.nftWalletList[wallet.nftWalletList.length - 1]; | |
| delete wallet.nftWalletIndex[_tokenContract]; | |
| wallet.nftWalletList.length = wallet.nftWalletList.length - 1; | |
| } | |
| } | |
| function removeToken(bytes32 _user, address _tokenContract, uint256 _value) internal { | |
| if (_value == 0) { | |
| return; | |
| } | |
| Wallet storage wallet = wallets[_user]; | |
| uint walletIndex = wallet.tokenIndex[_tokenContract]; | |
| require(walletIndex != 0, "Wallet has no coins from given ERC20 contract"); | |
| TokenWallet storage tokenWallet = wallet.tokenList[walletIndex - 1]; | |
| require(_value <= tokenWallet.balance, "Wallet does not own enough ERC20 tokens"); | |
| tokenWallet.balance = tokenWallet.balance.sub(_value); | |
| if (tokenWallet.balance == 0) { | |
| wallet.tokenIndex[wallet.tokenList[wallet.tokenList.length - 1].contractAddress] = walletIndex; | |
| wallet.tokenList[walletIndex - 1] = wallet.tokenList[wallet.tokenList.length - 1]; | |
| delete wallet.tokenIndex[_tokenContract]; | |
| wallet.tokenList.length = wallet.tokenList.length - 1; | |
| } | |
| } | |
| function getTokenBalance(address _tokenContract, bytes32 _owner) public view returns (uint256) { | |
| Wallet storage wallet = wallets[_owner]; | |
| uint index = wallet.tokenIndex[_tokenContract]; | |
| if (index == 0) { | |
| return 0; | |
| } | |
| return wallet.tokenList[index - 1].balance; | |
| } | |
| function hasNFT(address _tokenContract, bytes32 _owner, uint256 _tokenId) public view returns (bool) { | |
| Wallet storage wallet = wallets[_owner]; | |
| uint index = wallet.nftWalletIndex[_tokenContract]; | |
| if (index == 0) { | |
| return false; | |
| } | |
| return wallet.nftWalletList[index - 1].tokenIndex[_tokenId] != 0; | |
| } | |
| function depositEth(bytes32 _destination) external payable { | |
| addToken(_destination, address(0), msg.value); | |
| } | |
| function withdrawEth(uint256 _value) external { | |
| removeToken(bytes32(bytes20(msg.sender)), address(0), _value); | |
| msg.sender.transfer(_value); | |
| } | |
| function depositERC20(address _tokenContract, uint256 _value) external { | |
| require(_tokenContract != address(this)); | |
| ERC20(_tokenContract).transferFrom(msg.sender, address(this), _value); | |
| addToken(bytes32(bytes20(msg.sender)), _tokenContract, _value); | |
| } | |
| function withdrawERC20(address _tokenContract, uint256 _value) external { | |
| require(_tokenContract != address(this)); | |
| removeToken(bytes32(bytes20(msg.sender)), _tokenContract, _value); | |
| ERC20(_tokenContract).transfer(msg.sender, _value); | |
| } | |
| function depositERC721(address _tokenContract, uint256 _tokenId) external { | |
| require(_tokenContract != address(this)); | |
| ERC721(_tokenContract).transferFrom(msg.sender, address(this), _tokenId); | |
| addNFTToken(bytes32(bytes20(msg.sender)), _tokenContract, _tokenId); | |
| } | |
| function withdrawERC721(address _tokenContract, uint256 _tokenId) external { | |
| require(_tokenContract != address(this)); | |
| removeNFTToken(bytes32(bytes20(msg.sender)), _tokenContract, _tokenId); | |
| ERC721(_tokenContract).safeTransferFrom(address(this), msg.sender, _tokenId); | |
| } | |
| function transferToken(bytes32 _from, bytes32 _to, address _tokenContract, uint256 _value) public onlyOwner { | |
| removeToken(_from, _tokenContract, _value); | |
| addToken(_to, _tokenContract, _value); | |
| } | |
| function transferNFT(bytes32 _from, bytes32 _to, address _tokenContract, uint256 _tokenId) public onlyOwner { | |
| removeNFTToken(_from, _tokenContract, _tokenId); | |
| addNFTToken(_to, _tokenContract, _tokenId); | |
| } | |
| function ownerRemoveToken(bytes32 _user, address _tokenContract, uint256 _value) public onlyOwner { | |
| removeToken(_user, _tokenContract, _value); | |
| } | |
| function hasFunds( | |
| bytes32 _vmId, | |
| bytes21[] memory _tokenTypes, | |
| uint256[] memory _amounts | |
| ) public { | |
| for (uint i = 0; i < _tokenTypes.length; i++) { | |
| if (_tokenTypes[i][20] == 0x01) { | |
| removeNFTToken( | |
| _vmId, | |
| address(bytes20(_tokenTypes[i])), | |
| _amounts[i] | |
| ); | |
| } else { | |
| removeToken( | |
| _vmId, | |
| address(bytes20(_tokenTypes[i])), | |
| _amounts[i] | |
| ); | |
| } | |
| } | |
| for (uint i = 0; i < _tokenTypes.length; i++) { | |
| if (_tokenTypes[i][20] == 0x01) { | |
| addNFTToken( | |
| _vmId, | |
| address(bytes20(_tokenTypes[i])), | |
| _amounts[i] | |
| ); | |
| } else { | |
| addToken( | |
| _vmId, | |
| address(bytes20(_tokenTypes[i])), | |
| _amounts[i] | |
| ); | |
| } | |
| } | |
| } | |
| function getTokenBalances(bytes32 _owner) external view returns (address[] memory, uint256[] memory) { | |
| Wallet storage wallet = wallets[_owner]; | |
| address[] memory addresses = new address[](wallet.tokenList.length); | |
| uint256[] memory values = new uint256[](addresses.length); | |
| for (uint i = 0; i < addresses.length; i++) { | |
| addresses[i] = wallet.tokenList[i].contractAddress; | |
| values[i] = wallet.tokenList[i].balance; | |
| } | |
| return (addresses, values); | |
| } | |
| function getNFTTokens(bytes32 _owner) external view returns (address[] memory, uint256[] memory) { | |
| Wallet storage wallet = wallets[_owner]; | |
| uint totalLength = 0; | |
| uint i; | |
| for (i = 0; i < wallet.nftWalletList.length; i++) { | |
| totalLength += wallet.nftWalletList[i].tokenList.length; | |
| } | |
| address[] memory addresses = new address[](totalLength); | |
| uint256[] memory tokens = new uint256[](totalLength); | |
| uint count = 0; | |
| for (i = 0; i < wallet.nftWalletList.length; i++) { | |
| NFTWallet storage nftWallet = wallet.nftWalletList[i]; | |
| for (uint j = 0; j < nftWallet.tokenList.length; j++) { | |
| addresses[count] = nftWallet.contractAddress; | |
| tokens[count] = nftWallet.tokenList[j]; | |
| count++; | |
| } | |
| } | |
| return (addresses, tokens); | |
| } | |
| } | |
| // File: contracts/MerkleLib.sol | |
| /* | |
| * Copyright 2019, Offchain Labs, Inc. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| // pragma solidity ^0.5.3; | |
| library MerkleLib { | |
| function generateAddressRoot(address[] memory _addresses) public pure returns (bytes32) { | |
| bytes32[] memory _hashes = new bytes32[](_addresses.length); | |
| for (uint i = 0; i < _addresses.length; i++) { | |
| _hashes[i] = bytes32(bytes20(_addresses[i])); | |
| } | |
| return generateRoot(_hashes); | |
| } | |
| function generateRoot(bytes32[] memory _hashes) public pure returns (bytes32) { | |
| while (_hashes.length > 1) { | |
| bytes32[] memory nextLayer = new bytes32[]((_hashes.length + 1) / 2); | |
| for (uint i = 0; i < nextLayer.length; i++) { | |
| if (2 * i + 1 < _hashes.length) { | |
| nextLayer[i] = keccak256(abi.encodePacked(_hashes[2 * i], _hashes[2 * i + 1])); | |
| } else { | |
| nextLayer[i] = _hashes[2 * i]; | |
| } | |
| } | |
| _hashes = nextLayer; | |
| } | |
| return _hashes[0]; | |
| } | |
| function verifyProof( | |
| bytes memory proof, | |
| bytes32 root, | |
| bytes32 hash, | |
| uint256 index | |
| ) public pure returns (bool) { | |
| // use the index to determine the node ordering | |
| // index ranges 1 to n | |
| bytes32 el; | |
| bytes32 h = hash; | |
| uint256 remaining; | |
| for (uint256 j = 32; j <= proof.length; j += 32) { | |
| assembly { | |
| el := mload(add(proof, j)) | |
| } | |
| // calculate remaining elements in proof | |
| remaining = (proof.length - j + 32) / 32; | |
| // we don't assume that the tree is padded to a power of 2 | |
| // if the index is odd then the proof will start with a hash at a higher | |
| // layer, so we have to adjust the index to be the index at that layer | |
| while (remaining > 0 && index % 2 == 1 && index > 2 ** remaining) { | |
| index = uint(index) / 2 + 1; | |
| } | |
| if (index % 2 == 0) { | |
| h = keccak256(abi.encodePacked(el, h)); | |
| index = index / 2; | |
| } else { | |
| h = keccak256(abi.encodePacked(h, el)); | |
| index = uint(index) / 2 + 1; | |
| } | |
| } | |
| return h == root; | |
| } | |
| } | |
| // File: contracts/VMTracker.sol | |
| /* | |
| * Copyright 2019, Offchain Labs, Inc. | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| // pragma solidity ^0.5.3; | |
| contract VMTracker is Ownable { | |
| using SafeMath for uint256; | |
| using BytesLib for bytes; | |
| event MessageDelivered( | |
| bytes32 indexed vmId, | |
| bytes32 destination, | |
| bytes21 tokenType, | |
| uint256 value, | |
| bytes data | |
| ); | |
| event VMCreated( | |
| bytes32 indexed vmId, | |
| uint32 _gracePeriod, | |
| uint128 _escrowRequired, | |
| address _escrowCurrency, | |
| uint32 _maxExecutionSteps, | |
| bytes32 _vmState, | |
| uint16 _challengeManagerNum, | |
| address _owner, | |
| address[] validators | |
| ); | |
| event PendingUnanimousAssertion ( | |
| bytes32 indexed vmId, | |
| bytes32 unanHash, | |
| uint64 sequenceNum | |
| ); | |
| event ConfirmedUnanimousAssertion ( | |
| bytes32 indexed vmId, | |
| uint64 sequenceNum | |
| ); | |
| event FinalizedUnanimousAssertion( | |
| bytes32 indexed vmId, | |
| bytes32 unanHash | |
| ); | |
| // fields: | |
| // beforeHash | |
| // beforeInbox | |
| // afterHash | |
| event PendingDisputableAssertion ( | |
| bytes32 indexed vmId, | |
| bytes32[3] fields, | |
| address asserter, | |
| uint64[2] timeBounds, | |
| bytes21[] tokenTypes, | |
| uint32 numSteps, | |
| bytes32 lastMessageHash, | |
| bytes32 logsAccHash, | |
| uint256[] amounts | |
| ); | |
| event ConfirmedDisputableAssertion( | |
| bytes32 indexed vmId, | |
| bytes32 newState, | |
| bytes32 logsAccHash | |
| ); | |
| enum VMState { | |
| Waiting, | |
| PendingAssertion, | |
| PendingUnanimous | |
| } | |
| struct VM { | |
| bytes32 machineHash; | |
| bytes32 pendingHash; // Lock pending and confirm asserts together | |
| bytes32 inboxHash; | |
| bytes32 pendingMessages; | |
| bytes32 validatorRoot; | |
| bytes32 exitAddress; | |
| bytes32 terminateAddress; | |
| address owner; | |
| address disputableAsserter; | |
| address currencyType; | |
| uint128 escrowRequired; | |
| uint64 deadline; | |
| uint64 sequenceNum; | |
| uint32 gracePeriod; | |
| uint32 maxExecutionSteps; | |
| uint16 validatorCount; | |
| uint16 challengeManagersNum; | |
| VMState state; | |
| bool inChallenge; | |
| mapping(address => uint256) validatorBalances; | |
| } | |
| bytes32 constant MACHINE_HALT_HASH = bytes32(0); | |
| bytes32 constant MACHINE_ERROR_HASH = bytes32(uint(1)); | |
| IChallengeManager[] challengeManagers; | |
| ArbBalanceTracker arbBalanceTracker; | |
| mapping(bytes32 => VM) vms; | |
| mapping(address => bool) acceptedCurrencies; | |
| constructor(address _balanceTrackerAddress) public { | |
| arbBalanceTracker = ArbBalanceTracker(_balanceTrackerAddress); | |
| acceptedCurrencies[address(0)] = true; | |
| } | |
| function addChallengeManager(IChallengeManager _challengeManager) onlyOwner public { | |
| challengeManagers.push(_challengeManager); | |
| } | |
| function withinTimeBounds(uint64[2] memory _timeBounds) public view returns (bool) { | |
| return block.number >= _timeBounds[0] && block.number <= _timeBounds[1]; | |
| } | |
| function _resetDeadline(VM storage _vm) internal { | |
| _vm.deadline = uint64(block.number) + _vm.gracePeriod; | |
| } | |
| function _acceptAssertion( | |
| bytes32 _vmId, | |
| bytes32 _afterHash, | |
| bytes21[] memory _tokenTypes, | |
| bytes memory _messageData, | |
| uint16[] memory _tokenTypeNum, | |
| uint256[] memory _amounts, | |
| bytes32[] memory _destinations | |
| ) internal { | |
| uint offset = 0; | |
| bytes memory msgData; | |
| VM storage vm = vms[_vmId]; | |
| for (uint i = 0; i < _amounts.length; i++) { | |
| (offset, msgData) = ArbValue.get_next_valid_value(_messageData, offset); | |
| _sendUnpaidMessage( | |
| _destinations[i], | |
| _tokenTypes[_tokenTypeNum[i]], | |
| _amounts[i], | |
| _vmId, | |
| msgData | |
| ); | |
| } | |
| vm.machineHash = _afterHash; | |
| vm.state = VMState.Waiting; | |
| if (vm.pendingMessages != ArbValue.hashEmptyTuple()) { | |
| vm.inboxHash = ArbProtocol.appendInboxMessages(vm.inboxHash, vm.pendingMessages); | |
| vm.pendingMessages = ArbValue.hashEmptyTuple(); | |
| } | |
| if (_afterHash == MACHINE_HALT_HASH) { | |
| _shutdownVM(_vmId); | |
| } | |
| } | |
| // fields | |
| // _vmId | |
| // _vmState | |
| // _createHash | |
| function createVm( | |
| bytes32[3] memory _fields, | |
| uint32 _gracePeriod, | |
| uint32 _maxExecutionSteps, | |
| uint16 _challengeManagerNum, | |
| uint128 _escrowRequired, | |
| address _escrowCurrency, | |
| address _owner, | |
| bytes memory _signatures | |
| ) public { | |
| require(_signatures.length / 65 < 2 ** 16, "Too many validators"); | |
| require(bytes32(bytes20(_fields[0])) != _fields[0], "Invalid vmId"); | |
| require(_challengeManagerNum < challengeManagers.length, "Invalid challenge manager num"); | |
| require(_escrowRequired > 0); | |
| require(acceptedCurrencies[_escrowCurrency], "Selected currency is not an accepted type"); | |
| address[] memory assertKeys = ArbProtocol.recoverAddresses(_fields[2], _signatures); | |
| require(_fields[2] == keccak256(abi.encodePacked( | |
| _gracePeriod, | |
| _escrowRequired, | |
| _escrowCurrency, | |
| _maxExecutionSteps, | |
| _fields[1], | |
| _challengeManagerNum, | |
| _owner, | |
| assertKeys | |
| )), "Create data incorrect"); | |
| for (uint i = 0; i < assertKeys.length; i++) { | |
| arbBalanceTracker.ownerRemoveToken( | |
| bytes32(bytes20(assertKeys[i])), | |
| _escrowCurrency, | |
| _escrowRequired | |
| ); | |
| } | |
| VM storage vm = vms[_fields[0]]; | |
| // Machine state | |
| vm.machineHash = _fields[1]; | |
| vm.inboxHash = ArbValue.hashEmptyTuple(); | |
| vm.pendingMessages = ArbValue.hashEmptyTuple(); | |
| vm.challengeManagersNum = _challengeManagerNum; | |
| vm.state = VMState.Waiting; | |
| vm.pendingHash = 0; | |
| // Validator options | |
| vm.validatorRoot = MerkleLib.generateAddressRoot(assertKeys); | |
| vm.validatorCount = uint16(assertKeys.length); | |
| vm.escrowRequired = _escrowRequired; | |
| vm.currencyType = _escrowCurrency; | |
| vm.owner = _owner; | |
| vm.gracePeriod = _gracePeriod; | |
| vm.maxExecutionSteps = _maxExecutionSteps; | |
| for (uint i = 0; i < assertKeys.length; i++) { | |
| vm.validatorBalances[assertKeys[i]] = _escrowRequired; | |
| } | |
| emit VMCreated( | |
| _fields[0], | |
| _gracePeriod, | |
| _escrowRequired, | |
| _escrowCurrency, | |
| _maxExecutionSteps, | |
| _fields[1], | |
| _challengeManagerNum, | |
| _owner, | |
| assertKeys | |
| ); | |
| } | |
| function _shutdownVM(bytes32 _vmId) private { | |
| // TODO: transfer all owned funds to halt address | |
| delete vms[_vmId]; | |
| } | |
| function ownerShutdown(bytes32 _vmId) external { | |
| VM storage vm = vms[_vmId]; | |
| require(msg.sender == vm.owner, "Only owner can shutdown the VM"); | |
| _shutdownVM(_vmId); | |
| } | |
| function sendMessage(bytes32 _destination, bytes21 _tokenType, uint256 _amount, bytes memory _data) public { | |
| _sendUnpaidMessage( | |
| _destination, | |
| _tokenType, | |
| _amount, | |
| bytes32(uint256(uint160(msg.sender))), | |
| _data | |
| ); | |
| } | |
| function sendEthMessage(bytes32 _destination, bytes memory _data) public payable { | |
| arbBalanceTracker.depositEth.value(msg.value)(_destination); | |
| _deliverMessage( | |
| _destination, | |
| bytes21(0), | |
| msg.value, | |
| bytes32(uint256(uint160(msg.sender))), | |
| _data | |
| ); | |
| } | |
| function _sendUnpaidMessage(bytes32 _destination, bytes21 _tokenType, uint256 _value, bytes32 _sender, bytes memory _data) internal { | |
| if(_tokenType[20] == 0x01) { | |
| arbBalanceTracker.transferNFT(_sender, _destination, address(bytes20(_tokenType)), _value); | |
| } else { | |
| arbBalanceTracker.transferToken(_sender, _destination, address(bytes20(_tokenType)), _value); | |
| } | |
| _deliverMessage(_destination, _tokenType, _value, _sender, _data); | |
| } | |
| function _deliverMessage(bytes32 _destination, bytes21 _tokenType, uint256 _value, bytes32 _sender, bytes memory _data) internal { | |
| if (bytes32(bytes20(_destination)) != _destination) { | |
| VM storage vm = vms[_destination]; | |
| bytes32 messageHash = ArbProtocol.generateSentMessageHash( | |
| _destination, | |
| ArbValue.deserialize_value_hash(_data), | |
| _tokenType, | |
| _value, | |
| _sender | |
| ); | |
| vm.pendingMessages = ArbProtocol.appendInboxPendingMessage( | |
| vm.pendingMessages, | |
| messageHash | |
| ); | |
| } | |
| emit MessageDelivered( | |
| _destination, | |
| _sender, | |
| _tokenType, | |
| _value, | |
| _data | |
| ); | |
| } | |
| function _cancelCurrentState(VM storage vm) internal { | |
| if (vm.state != VMState.Waiting) { | |
| require(block.number <= vm.deadline); | |
| } | |
| if (vm.state == VMState.PendingAssertion) { | |
| // If there is a pending disputable assertion, cancel it | |
| vm.validatorBalances[vm.disputableAsserter] = vm.validatorBalances[vm.disputableAsserter].add(vm.escrowRequired); | |
| } | |
| } | |
| struct finalizedUnanimousAssertData { | |
| bytes32 vmId; | |
| bytes32 afterHash; | |
| bytes32 newInbox; | |
| uint64[2] timeBounds; | |
| bytes21[] tokenTypes; | |
| bytes messageData; | |
| uint16[] messageTokenNum; | |
| uint256[] messageAmount; | |
| bytes32[] messageDestination; | |
| bytes32 logsAccHash; | |
| bytes signatures; | |
| } | |
| function finalizedUnanimousAssert( | |
| bytes32 _vmId, | |
| bytes32 _afterHash, | |
| bytes32 _newInbox, | |
| uint64[2] memory _timeBounds, | |
| bytes21[] memory _tokenTypes, | |
| bytes memory _messageData, | |
| uint16[] memory _messageTokenNum, | |
| uint256[] memory _messageAmount, | |
| bytes32[] memory _messageDestination, | |
| bytes32 _logsAccHash, | |
| bytes memory _signatures | |
| ) public { | |
| _finalizedUnanimousAssert(finalizedUnanimousAssertData( | |
| _vmId, | |
| _afterHash, | |
| _newInbox, | |
| _timeBounds, | |
| _tokenTypes, | |
| _messageData, | |
| _messageTokenNum, | |
| _messageAmount, | |
| _messageDestination, | |
| _logsAccHash, | |
| _signatures | |
| )); | |
| } | |
| function _finalizedUnanimousAssert(finalizedUnanimousAssertData memory data) internal { | |
| VM storage vm = vms[data.vmId]; | |
| require(vm.machineHash != MACHINE_HALT_HASH); | |
| require(withinTimeBounds(data.timeBounds), "Precondition: not within time bounds"); | |
| bytes32 unanHash = keccak256(abi.encodePacked( | |
| data.vmId, | |
| keccak256(abi.encodePacked( | |
| keccak256(abi.encodePacked( | |
| data.newInbox, | |
| data.afterHash, | |
| data.messageData, | |
| data.messageDestination | |
| )), | |
| data.timeBounds, | |
| vm.machineHash, | |
| vm.inboxHash, | |
| data.tokenTypes, | |
| data.messageTokenNum, | |
| data.messageAmount | |
| )), | |
| data.logsAccHash | |
| )); | |
| require(MerkleLib.generateAddressRoot( | |
| ArbProtocol.recoverAddresses(unanHash, data.signatures) | |
| ) == vm.validatorRoot, "Validator signatures don't match"); | |
| _cancelCurrentState(vm); | |
| vm.state = VMState.Waiting; | |
| vm.inboxHash = data.newInbox; | |
| _acceptAssertion( | |
| data.vmId, | |
| data.afterHash, | |
| data.tokenTypes, | |
| data.messageData, | |
| data.messageTokenNum, | |
| data.messageAmount, | |
| data.messageDestination | |
| ); | |
| emit FinalizedUnanimousAssertion( | |
| data.vmId, | |
| unanHash | |
| ); | |
| } | |
| function pendingUnanimousAssert( | |
| bytes32 _vmId, | |
| bytes32 _unanRest, | |
| uint64[2] memory _timeBounds, | |
| bytes21[] memory _tokenTypes, | |
| uint16[] memory _messageTokenNum, | |
| uint256[] memory _messageAmount, | |
| uint64 _sequenceNum, | |
| bytes32 _logsAccHash, | |
| bytes memory _signatures | |
| ) public { | |
| VM storage vm = vms[_vmId]; | |
| require(withinTimeBounds(_timeBounds), "Precondition: not within time bounds"); | |
| require(vm.machineHash != MACHINE_HALT_HASH); | |
| bytes32 unanHash = keccak256(abi.encodePacked( | |
| _vmId, | |
| keccak256(abi.encodePacked( | |
| _unanRest, | |
| _timeBounds, | |
| vm.machineHash, | |
| vm.inboxHash, | |
| _tokenTypes, | |
| _messageTokenNum, | |
| _messageAmount, | |
| _sequenceNum | |
| )), | |
| _logsAccHash | |
| )); | |
| require(MerkleLib.generateAddressRoot( | |
| ArbProtocol.recoverAddresses(unanHash, _signatures) | |
| ) == vm.validatorRoot, "Validator signatures don't match"); | |
| if (vm.state == VMState.PendingUnanimous) { | |
| require(_sequenceNum > vm.sequenceNum); | |
| } | |
| arbBalanceTracker.hasFunds( | |
| _vmId, | |
| _tokenTypes, | |
| ArbProtocol.calculateBeforeValues( | |
| _tokenTypes, | |
| _messageTokenNum, | |
| _messageAmount | |
| ) | |
| ); | |
| _cancelCurrentState(vm); | |
| _resetDeadline(vm); | |
| vm.state = VMState.PendingUnanimous; | |
| vm.sequenceNum = _sequenceNum; | |
| vm.pendingHash = keccak256(abi.encodePacked( | |
| _tokenTypes, | |
| _messageTokenNum, | |
| _messageAmount, | |
| _unanRest | |
| )); | |
| emit PendingUnanimousAssertion( | |
| _vmId, | |
| unanHash, | |
| _sequenceNum | |
| ); | |
| } | |
| function ConfirmUnanimousAsserted( | |
| bytes32 _vmId, | |
| bytes32 _afterHash, | |
| bytes32 _newInbox, | |
| bytes21[] memory _tokenTypes, | |
| bytes memory _messageData, | |
| uint16[] memory _messageTokenNum, | |
| uint256[] memory _messageAmount, | |
| bytes32[] memory _messageDestination | |
| ) public { | |
| VM storage vm = vms[_vmId]; | |
| require(vm.state == VMState.PendingUnanimous); | |
| require(block.number > vm.deadline); | |
| require(vm.pendingHash == keccak256(abi.encodePacked( | |
| _tokenTypes, | |
| _messageTokenNum, | |
| _messageAmount, | |
| keccak256(abi.encodePacked( | |
| _newInbox, | |
| _afterHash, | |
| _messageData, | |
| _messageDestination | |
| )) | |
| ))); | |
| vm.inboxHash = _newInbox; | |
| _acceptAssertion( | |
| _vmId, | |
| _afterHash, | |
| _tokenTypes, | |
| _messageData, | |
| _messageTokenNum, | |
| _messageAmount, | |
| _messageDestination | |
| ); | |
| emit ConfirmedUnanimousAssertion( | |
| _vmId, | |
| vm.sequenceNum | |
| ); | |
| } | |
| struct PendingDisputableAssertData { | |
| bytes32 vmId; | |
| bytes32 beforeHash; | |
| bytes32 beforeInbox; | |
| bytes32 afterHash; | |
| bytes32 logsAccHash; | |
| uint32 numSteps; | |
| uint64[2] timeBounds; | |
| bytes21[] tokenTypes; | |
| bytes32[] messageDataHash; | |
| uint16[] messageTokenNum; | |
| uint256[] msgAmount; | |
| bytes32[] msgDestination; | |
| } | |
| // fields: | |
| // _vmId | |
| // _beforeHash | |
| // _beforeInbox | |
| // _afterHash | |
| // _logsAccHash | |
| function pendingDisputableAssert( | |
| bytes32[5] memory _fields, | |
| uint32 _numSteps, | |
| uint64[2] memory timeBounds, | |
| bytes21[] memory _tokenTypes, | |
| bytes32[] memory _messageDataHash, | |
| uint16[] memory _messageTokenNum, | |
| uint256[] memory _msgAmount, | |
| bytes32[] memory _msgDestination | |
| ) public { | |
| return _pendingDisputableAssert(PendingDisputableAssertData( | |
| _fields[0], | |
| _fields[1], | |
| _fields[2], | |
| _fields[3], | |
| _fields[4], | |
| _numSteps, | |
| timeBounds, | |
| _tokenTypes, | |
| _messageDataHash, | |
| _messageTokenNum, | |
| _msgAmount, | |
| _msgDestination | |
| )); | |
| } | |
| function _pendingDisputableAssert(PendingDisputableAssertData memory _data) internal { | |
| VM storage vm = vms[_data.vmId]; | |
| require(vm.state == VMState.Waiting, "Can only disputable assert from waiting state"); | |
| require(vm.machineHash != MACHINE_HALT_HASH && vm.machineHash != MACHINE_ERROR_HASH); | |
| require(!vm.inChallenge); | |
| require(vm.escrowRequired <= vm.validatorBalances[msg.sender], "Validator does not have required escrow"); | |
| require(_data.numSteps <= vm.maxExecutionSteps, "Tried to execute too many steps"); | |
| require(withinTimeBounds(_data.timeBounds), "Precondition: not within time bounds"); | |
| require(_data.beforeHash == vm.machineHash, "Precondition: state hash does not match"); | |
| require( | |
| _data.beforeInbox == vm.inboxHash || | |
| _data.beforeInbox == ArbProtocol.appendInboxMessages(vm.inboxHash, vm.pendingMessages) | |
| , "Precondition: inbox does not match"); | |
| uint256[] memory beforeBalances = ArbProtocol.calculateBeforeValues( | |
| _data.tokenTypes, | |
| _data.messageTokenNum, | |
| _data.msgAmount | |
| ); | |
| arbBalanceTracker.hasFunds( | |
| _data.vmId, | |
| _data.tokenTypes, | |
| beforeBalances | |
| ); | |
| _resetDeadline(vm); | |
| bytes32 lastMessageHash = ArbProtocol.generateLastMessageHashStub( | |
| _data.tokenTypes, | |
| _data.messageDataHash, | |
| _data.messageTokenNum, | |
| _data.msgAmount, | |
| _data.msgDestination | |
| ); | |
| vm.pendingHash = keccak256(abi.encodePacked( | |
| ArbProtocol.generatePreconditionHash( | |
| _data.beforeHash, | |
| _data.timeBounds, | |
| _data.beforeInbox, | |
| _data.tokenTypes, | |
| beforeBalances | |
| ), | |
| ArbProtocol.generateAssertionHash( | |
| _data.afterHash, | |
| _data.numSteps, | |
| 0x00, | |
| lastMessageHash, | |
| 0x00, | |
| _data.logsAccHash, | |
| beforeBalances | |
| ) | |
| )); | |
| vm.validatorBalances[msg.sender] = vm.validatorBalances[msg.sender].sub(vm.escrowRequired); | |
| vm.disputableAsserter = msg.sender; | |
| vm.state = VMState.PendingAssertion; | |
| emit PendingDisputableAssertion( | |
| _data.vmId, | |
| [_data.beforeHash, _data.beforeInbox, _data.afterHash], | |
| msg.sender, | |
| _data.timeBounds, | |
| _data.tokenTypes, | |
| _data.numSteps, | |
| lastMessageHash, | |
| _data.logsAccHash, | |
| beforeBalances | |
| ); | |
| } | |
| // fields: | |
| // _vmId | |
| // _preconditionHash | |
| // _afterHash | |
| // _logsAccHash | |
| struct confirmDisputableAssertedData{ | |
| bytes32 vmId; | |
| bytes32 preconditionHash; | |
| bytes32 afterHash; | |
| uint32 numSteps; | |
| bytes21[] tokenTypes; | |
| bytes messageData; | |
| uint16[] messageTokenNums; | |
| uint256[] messageAmounts; | |
| bytes32[] messageDestination; | |
| bytes32 logsAccHash; | |
| } | |
| function confirmDisputableAsserted( | |
| bytes32 _vmId, | |
| bytes32 _preconditionHash, | |
| bytes32 _afterHash, | |
| uint32 _numSteps, | |
| bytes21[] memory _tokenTypes, | |
| bytes memory _messageData, | |
| uint16[] memory _messageTokenNums, | |
| uint256[] memory _messageAmounts, | |
| bytes32[] memory _messageDestination, | |
| bytes32 _logsAccHash | |
| ) public { | |
| return _confirmDisputableAsserted(confirmDisputableAssertedData( | |
| _vmId, | |
| _preconditionHash, | |
| _afterHash, | |
| _numSteps, | |
| _tokenTypes, | |
| _messageData, | |
| _messageTokenNums, | |
| _messageAmounts, | |
| _messageDestination, | |
| _logsAccHash | |
| )); | |
| } | |
| function _confirmDisputableAsserted(confirmDisputableAssertedData memory _data) internal { | |
| VM storage vm = vms[_data.vmId]; | |
| require(vm.state == VMState.PendingAssertion, "VM does not have assertion pending"); | |
| require(block.number > vm.deadline, "Assertion is still pending challenge"); | |
| require( | |
| keccak256(abi.encodePacked( | |
| _data.preconditionHash, | |
| ArbProtocol.generateAssertionHash( | |
| _data.afterHash, | |
| _data.numSteps, | |
| 0x00, | |
| ArbProtocol.generateLastMessageHash( | |
| _data.tokenTypes, | |
| _data.messageData, | |
| _data.messageTokenNums, | |
| _data.messageAmounts, | |
| _data.messageDestination | |
| ), | |
| 0x00, | |
| _data.logsAccHash, | |
| ArbProtocol.calculateBeforeValues( | |
| _data.tokenTypes, | |
| _data.messageTokenNums, | |
| _data.messageAmounts | |
| ) | |
| ) | |
| )) == vm.pendingHash, | |
| "Precondition and assertion do not match pending assertion" | |
| ); | |
| vm.validatorBalances[vm.disputableAsserter] = vm.validatorBalances[vm.disputableAsserter].add(vm.escrowRequired); | |
| _acceptAssertion( | |
| _data.vmId, | |
| _data.afterHash, | |
| _data.tokenTypes, | |
| _data.messageData, | |
| _data.messageTokenNums, | |
| _data.messageAmounts, | |
| _data.messageDestination | |
| ); | |
| emit ConfirmedDisputableAssertion( | |
| _data.vmId, | |
| _data.afterHash, | |
| _data.logsAccHash | |
| ); | |
| } | |
| event InitiatedChallenge( | |
| bytes32 indexed vmId, | |
| address challenger | |
| ); | |
| // fields | |
| // _vmId | |
| // _assertionHash | |
| function initiateChallenge( | |
| bytes32 _vmId, | |
| bytes32 _assertPreHash | |
| ) public { | |
| VM storage vm = vms[_vmId]; | |
| require(msg.sender != vm.disputableAsserter, "Challenge was created by asserter"); | |
| require(block.number <= vm.deadline, "Challenge did not come before deadline"); | |
| require(vm.state == VMState.PendingAssertion, "Assertion must be pending to initiate challenge"); | |
| require(vm.escrowRequired <= vm.validatorBalances[msg.sender], "Challenger did not have enough escrowed"); | |
| require( | |
| _assertPreHash | |
| == vm.pendingHash, | |
| "Precondition and assertion do not match pending assertion" | |
| ); | |
| vm.validatorBalances[msg.sender] = vm.validatorBalances[msg.sender].sub(vm.escrowRequired); | |
| vm.pendingHash = 0; | |
| vm.state = VMState.Waiting; | |
| vm.inChallenge = true; | |
| challengeManagers[vm.challengeManagersNum].initiateChallenge( | |
| _vmId, | |
| [vm.disputableAsserter, msg.sender], | |
| [vm.escrowRequired, vm.escrowRequired], | |
| vm.gracePeriod, | |
| _assertPreHash | |
| ); | |
| emit InitiatedChallenge( | |
| _vmId, | |
| msg.sender | |
| ); | |
| } | |
| function completeChallenge(bytes32 _vmId, address[2] calldata _players, uint128[2] calldata _rewards) external { | |
| VM storage vm = vms[_vmId]; | |
| require(msg.sender == address(challengeManagers[vm.challengeManagersNum])); | |
| require(vm.inChallenge); | |
| vm.inChallenge = false; | |
| vm.validatorBalances[_players[0]] = vm.validatorBalances[_players[0]].add(_rewards[0]); | |
| vm.validatorBalances[_players[1]] = vm.validatorBalances[_players[1]].add(_rewards[1]); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment