Skip to content

Instantly share code, notes, and snippets.

@publu
Created January 25, 2020 01:24
Show Gist options
  • Select an option

  • Save publu/77c31c2351c643dc67be1716eb85756a to your computer and use it in GitHub Desktop.

Select an option

Save publu/77c31c2351c643dc67be1716eb85756a to your computer and use it in GitHub Desktop.
// 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