Overview
ETH Balance
0.238 ETH
Eth Value
$487.77 (@ $2,049.46/ETH)Latest 25 from a total of 212 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Start Standard E... | 16205549 | 1180 days ago | IN | 0.014 ETH | 0.00555464 | ||||
| Start Standard E... | 16205529 | 1180 days ago | IN | 0.014 ETH | 0.00555447 | ||||
| Start Standard E... | 16205387 | 1180 days ago | IN | 0.014 ETH | 0.00583843 | ||||
| Start Standard E... | 16205339 | 1180 days ago | IN | 0.014 ETH | 0.00555447 | ||||
| Start Standard E... | 16205264 | 1180 days ago | IN | 0.014 ETH | 0.00588156 | ||||
| Start Standard E... | 16205224 | 1180 days ago | IN | 0.014 ETH | 0.00608354 | ||||
| Start Standard E... | 16128465 | 1191 days ago | IN | 0.014 ETH | 0.02254805 | ||||
| Start Standard E... | 16125660 | 1191 days ago | IN | 0.014 ETH | 0.00562493 | ||||
| Start Standard E... | 16125658 | 1191 days ago | IN | 0.014 ETH | 0.00631138 | ||||
| Start Standard E... | 14990177 | 1361 days ago | IN | 0.014 ETH | 0.01276413 | ||||
| Start Standard E... | 14569929 | 1429 days ago | IN | 0.014 ETH | 0.01152624 | ||||
| Start Standard E... | 14561559 | 1430 days ago | IN | 0.014 ETH | 0.01132623 | ||||
| Start Standard E... | 13718481 | 1561 days ago | IN | 0.014 ETH | 0.04839735 | ||||
| Start Standard E... | 13679419 | 1568 days ago | IN | 0.014 ETH | 0.05402125 | ||||
| Start Standard E... | 13255883 | 1634 days ago | IN | 0.014 ETH | 0.01153372 | ||||
| Start Standard E... | 13249303 | 1635 days ago | IN | 0.014 ETH | 0.01633296 | ||||
| Start Standard E... | 12805239 | 1704 days ago | IN | 0.014 ETH | 0.00255123 | ||||
| Start Standard E... | 12761842 | 1711 days ago | IN | 0.014 ETH | 0.00172584 | ||||
| Start Standard E... | 12733223 | 1715 days ago | IN | 0.014 ETH | 0.0023358 | ||||
| Start Standard E... | 12733223 | 1715 days ago | IN | 0.014 ETH | 0.00472246 | ||||
| Start Standard E... | 12708692 | 1719 days ago | IN | 0.014 ETH | 0.00444675 | ||||
| Start Standard E... | 12695578 | 1721 days ago | IN | 0.014 ETH | 0.00326471 | ||||
| Start Standard E... | 12672706 | 1725 days ago | IN | 0.014 ETH | 0.00212653 | ||||
| Start Standard E... | 12572832 | 1740 days ago | IN | 0.014 ETH | 0.0058094 | ||||
| Start Standard E... | 12566166 | 1741 days ago | IN | 0.014 ETH | 0.00576323 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Transfer | 16191258 | 1182 days ago | 0.014 ETH | ||||
| - | 14632612 | 1419 days ago | 0.014 ETH | ||||
| - | 14632612 | 1419 days ago | 0.014 ETH | ||||
| - | 14615354 | 1422 days ago | 0.014 ETH | ||||
| - | 13809623 | 1547 days ago | 0.014 ETH | ||||
| - | 13305134 | 1626 days ago | 0.014 ETH | ||||
| - | 13305134 | 1626 days ago | 0.014 ETH | ||||
| - | 12856592 | 1696 days ago | 0.014 ETH | ||||
| - | 12786708 | 1707 days ago | 0.014 ETH | ||||
| - | 12786674 | 1707 days ago | 0.014 ETH | ||||
| - | 12783928 | 1707 days ago | 0.014 ETH | ||||
| - | 12783928 | 1707 days ago | 0.014 ETH | ||||
| - | 12783928 | 1707 days ago | 0.014 ETH | ||||
| - | 12782801 | 1708 days ago | 0.014 ETH | ||||
| - | 12595783 | 1737 days ago | 0.014 ETH | ||||
| - | 12595783 | 1737 days ago | 0.014 ETH | ||||
| - | 12542005 | 1745 days ago | 0.014 ETH | ||||
| - | 12495883 | 1752 days ago | 0.014 ETH | ||||
| - | 12310722 | 1781 days ago | 0.014 ETH | ||||
| - | 12310716 | 1781 days ago | 0.014 ETH | ||||
| - | 12158154 | 1804 days ago | 0.014 ETH | ||||
| - | 12158154 | 1804 days ago | 0.014 ETH | ||||
| - | 12158154 | 1804 days ago | 0.014 ETH | ||||
| - | 12158154 | 1804 days ago | 0.014 ETH | ||||
| - | 12158154 | 1804 days ago | 0.014 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract contains unverified libraries: PaymentStartStandardExit, PaymentChallengeStandardExit, PaymentProcessStandardExit, PaymentStartInFlightExit, PaymentPiggybackInFlightExit, PaymentChallengeIFENotCanonical, PaymentChallengeIFEInputSpent, PaymentChallengeIFEOutputSpent, PaymentDeleteInFlightExit, PaymentProcessInFlightExit
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
PaymentExitGame
Compiler Version
v0.5.11+commit.c082d0b4
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "./PaymentExitGameArgs.sol";
import "./routers/PaymentStandardExitRouter.sol";
import "./routers/PaymentInFlightExitRouter.sol";
import "../utils/ExitId.sol";
import "../registries/SpendingConditionRegistry.sol";
import "../../framework/interfaces/IExitProcessor.sol";
import "../../framework/PlasmaFramework.sol";
import "../../utils/OnlyFromAddress.sol";
/**
* @notice The exit game contract implementation for Payment Transaction
*/
contract PaymentExitGame is IExitProcessor, OnlyFromAddress, PaymentStandardExitRouter, PaymentInFlightExitRouter {
PlasmaFramework private plasmaFramework;
/**
* @dev use struct PaymentExitGameArgs to avoid stack too deep compilation error.
*/
constructor(PaymentExitGameArgs.Args memory args)
public
PaymentStandardExitRouter(args)
PaymentInFlightExitRouter(args)
{
plasmaFramework = args.framework;
// makes sure that the spending condition has already renounced ownership
require(args.spendingConditionRegistry.owner() == address(0), "Spending condition registry ownership needs to be renounced");
}
/**
* @notice Callback processes exit function for the PlasmaFramework to call
* @param exitId The exit ID
* @param token Token (ERC20 address or address(0) for ETH) of the exiting output
*/
function processExit(uint160 exitId, uint256, address token) external onlyFrom(address(plasmaFramework)) {
if (ExitId.isStandardExit(exitId)) {
PaymentStandardExitRouter.processStandardExit(exitId, token);
} else {
PaymentInFlightExitRouter.processInFlightExit(exitId, token);
}
}
/**
* @notice Helper function to compute the standard exit ID
*/
function getStandardExitId(bool _isDeposit, bytes memory _txBytes, uint256 _utxoPos)
public
pure
returns (uint160)
{
PosLib.Position memory utxoPos = PosLib.decode(_utxoPos);
return ExitId.getStandardExitId(_isDeposit, _txBytes, utxoPos);
}
/**
* @notice Helper function to compute the in-flight exit ID
*/
function getInFlightExitId(bytes memory _txBytes)
public
pure
returns (uint160)
{
return ExitId.getInFlightExitId(_txBytes);
}
}pragma solidity >=0.4.21 <0.6.0;
contract Migrations {
address public owner;
uint public last_completed_migration;
constructor() public {
owner = msg.sender;
}
modifier restricted() {
if (msg.sender == owner) _;
}
function setCompleted(uint completed) public restricted {
last_completed_migration = completed;
}
function upgrade(address new_address) public restricted {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}pragma solidity 0.5.11;
// Import contracts from third party Solidity libraries to make them available in tests.
import "openzeppelin-solidity/contracts/token/ERC20/ERC20Mintable.sol";
contract Import {
// dummy empty contract to allow the compiler not always trying to re-compile this file.
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../src/utils/FailFastReentrancyGuard.sol";
import "../../src/framework/PlasmaFramework.sol";
contract FailFastReentrancyGuardAttacker is FailFastReentrancyGuard {
PlasmaFramework private framework;
event RemoteCallFailed();
constructor(PlasmaFramework plasmaFramework) public {
framework = plasmaFramework;
}
function guardedLocal() public nonReentrant(framework) {
guardedLocal();
}
function guardedRemote() external nonReentrant(framework) {
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = address(this).call(abi.encodeWithSignature("guardedRemote()"));
if (!success) {
emit RemoteCallFailed();
}
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
contract FallbackFunctionFailAttacker {
function () external payable {
revert("fail on fallback function");
}
}pragma solidity 0.5.11;
contract OutOfGasFallbackAttacker {
function () external payable {
while (true) {}
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../src/exits/interfaces/ISpendingCondition.sol";
contract SpendingConditionMock is ISpendingCondition {
bool internal expectedResult;
bool internal shouldRevert;
Args internal expectedArgs;
string constant internal REVERT_MESSAGE = "Test spending condition reverts";
struct Args {
bytes inputTx;
uint256 utxoPos;
bytes spendingTx;
uint16 inputIndex;
bytes witness;
}
/** mock what would "verify()" returns */
function mockResult(bool result) public {
expectedResult = result;
}
/** when called, the spending condition would always revert on purpose */
function mockRevert() public {
shouldRevert = true;
}
/** provide the expected args, it would check with the value called for "verify()" */
function shouldVerifyArgumentEquals(Args memory args) public {
expectedArgs = args;
}
/** override */
function verify(
bytes calldata inputTx,
uint256 utxoPos,
bytes calldata spendingTx,
uint16 inputIndex,
bytes calldata witness
)
external
view
returns (bool)
{
if (shouldRevert) {
revert(REVERT_MESSAGE);
}
// only run the check when "shouldVerifyArgumentEqauals" is called
if (expectedArgs.inputTx.length > 0) {
require(keccak256(expectedArgs.inputTx) == keccak256(inputTx), "input tx not as expected");
require(expectedArgs.utxoPos == utxoPos, "utxoPos not as expected");
require(keccak256(expectedArgs.spendingTx) == keccak256(spendingTx), "spending tx not as expected");
require(expectedArgs.inputIndex == inputIndex, "input index not as expected");
require(keccak256(expectedArgs.witness) == keccak256(witness), "witness not as expected");
}
return expectedResult;
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../src/framework/PlasmaFramework.sol";
import "../../src/utils/PosLib.sol";
import "../../src/framework/models/BlockModel.sol";
contract SpyPlasmaFrameworkForExitGame is PlasmaFramework {
using PosLib for PosLib.Position;
uint256 public enqueuedCount = 0;
mapping (uint256 => BlockModel.Block) public blocks;
event EnqueueTriggered(
uint256 vaultId,
address token,
uint64 exitableAt,
uint256 txPos,
uint256 exitId,
address exitProcessor
);
/** This spy contract set the authority and maintainer both to the caller */
constructor(uint256 _minExitPeriod, uint256 _initialImmuneVaults, uint256 _initialImmuneExitGames)
public
PlasmaFramework(_minExitPeriod, _initialImmuneVaults, _initialImmuneExitGames, msg.sender, msg.sender)
{
}
/** override for test */
function enqueue(
uint256 _vaultId,
address _token,
uint64 _exitableAt,
PosLib.Position calldata _txPos,
uint160 _exitId,
IExitProcessor _exitProcessor
)
external
returns (uint256)
{
enqueuedCount += 1;
emit EnqueueTriggered(
_vaultId,
_token,
_exitableAt,
_txPos.getTxPositionForExitPriority(),
_exitId,
address(_exitProcessor)
);
return enqueuedCount;
}
/**
Custom test helpers
*/
function setBlock(uint256 _blockNum, bytes32 _root, uint256 _timestamp) external {
blocks[_blockNum] = BlockModel.Block(_root, _timestamp);
}
function setOutputFinalized(bytes32 _outputId, uint160 _exitId) external {
outputsFinalizations[_outputId] = _exitId;
}
/**
* Override to remove check that block exists
*/
function isDeposit(uint256 blockNum) public view returns (bool) {
return blockNum % childBlockInterval != 0;
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../src/exits/interfaces/IStateTransitionVerifier.sol";
contract StateTransitionVerifierMock is IStateTransitionVerifier {
bool public expectedResult;
bool public shouldRevert;
Args public expectedArgs;
struct Args {
bytes inFlightTx;
bytes[] inputTxs;
uint16[] outputIndexOfInputTxs;
}
/** mock what "isCorrectStateTransition()" returns */
function mockResult(bool result) public {
expectedResult = result;
}
/** when called, the "isCorrectStateTransition" function reverts on purpose */
function mockRevert() public {
shouldRevert = true;
}
/** provide the expected args, it would check with the value called for "verify()" */
function shouldVerifyArgumentEquals(Args memory args)
public
{
expectedArgs = args;
}
function isCorrectStateTransition(
bytes calldata inFlightTx,
bytes[] calldata inputTxs,
uint16[] calldata outputIndexOfInputTxs
)
external
view
returns (bool)
{
if (shouldRevert) {
revert("Failing on purpose");
}
// only run the check when "shouldVerifyArgumentEqauals" is called
if (expectedArgs.inFlightTx.length > 0) {
require(keccak256(inFlightTx) == keccak256(expectedArgs.inFlightTx), "in-flight tx is not as expected");
require(inputTxs.length == expectedArgs.inputTxs.length, "input txs array length mismatches expected data");
for (uint i = 0; i < expectedArgs.inputTxs.length; i++) {
require(keccak256(inputTxs[i]) == keccak256(expectedArgs.inputTxs[i]), "input tx is not as expected");
}
require(outputIndexOfInputTxs.length == expectedArgs.outputIndexOfInputTxs.length, "outputIndex array length mismatches expected data");
for (uint i = 0; i < expectedArgs.outputIndexOfInputTxs.length; i++) {
require(outputIndexOfInputTxs[i] == expectedArgs.outputIndexOfInputTxs[i], "output index of input txs is not as expected");
}
}
return expectedResult;
}
}pragma solidity 0.5.11;
import "../../../src/vaults/Erc20Vault.sol";
import "../../../src/framework/PlasmaFramework.sol";
contract SpyErc20VaultForExitGame is Erc20Vault {
event Erc20WithdrawCalled(
address target,
address token,
uint256 amount
);
constructor(PlasmaFramework _framework) public Erc20Vault(_framework) {}
/** override for test */
function withdraw(address payable _target, address _token, uint256 _amount) external {
emit Erc20WithdrawCalled(_target, _token, _amount);
}
}pragma solidity 0.5.11;
import "../../../src/vaults/EthVault.sol";
import "../../../src/framework/PlasmaFramework.sol";
contract SpyEthVaultForExitGame is EthVault {
uint256 public constant SAFE_GAS_STIPEND = 2300;
event EthWithdrawCalled(
address target,
uint256 amount
);
constructor(PlasmaFramework _framework) public EthVault(_framework, SAFE_GAS_STIPEND) {}
/** override for test */
function withdraw(address payable _target, uint256 _amount) external {
emit EthWithdrawCalled(_target, _amount);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../../src/exits/payment/PaymentInFlightExitModelUtils.sol";
import { PaymentExitDataModel as ExitModel } from "../../../src/exits/payment/PaymentExitDataModel.sol";
contract PaymentInFlightExitModelUtilsMock {
ExitModel.InFlightExit public ife;
constructor(uint256 exitMap, uint64 exitStartTimestamp) public {
ife.exitMap = exitMap;
ife.exitStartTimestamp = exitStartTimestamp;
}
/** Helper functions */
function setWithdrawData(
string memory target,
uint16 index,
ExitModel.WithdrawData memory data
)
public
{
if (stringEquals(target, "inputs")) {
ife.inputs[index] = data;
} else if (stringEquals(target, "outputs")) {
ife.outputs[index] = data;
} else {
revert("target should be either inputs or outputs");
}
}
function stringEquals(string memory a, string memory b) private pure returns (bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}
/** Wrapper functions */
function isInputEmpty(uint16 index)
external
view
returns (bool)
{
return PaymentInFlightExitModelUtils.isInputEmpty(ife, index);
}
function isOutputEmpty(uint16 index)
external
view
returns (bool)
{
return PaymentInFlightExitModelUtils.isOutputEmpty(ife, index);
}
function isInputPiggybacked(uint16 index)
external
view
returns (bool)
{
return PaymentInFlightExitModelUtils.isInputPiggybacked(ife, index);
}
function isOutputPiggybacked(uint16 index)
external
view
returns (bool)
{
return PaymentInFlightExitModelUtils.isOutputPiggybacked(ife, index);
}
function setInputPiggybacked(uint16 index)
external
{
PaymentInFlightExitModelUtils.setInputPiggybacked(ife, index);
}
function clearInputPiggybacked(uint16 index)
external
{
PaymentInFlightExitModelUtils.clearInputPiggybacked(ife, index);
}
function setOutputPiggybacked(uint16 index)
external
{
PaymentInFlightExitModelUtils.setOutputPiggybacked(ife, index);
}
function clearOutputPiggybacked(uint16 index)
external
{
PaymentInFlightExitModelUtils.clearOutputPiggybacked(ife, index);
}
function isInFirstPhase(uint256 minExitPeriod)
external
view
returns (bool)
{
return PaymentInFlightExitModelUtils.isInFirstPhase(ife, minExitPeriod);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../../../src/exits/payment/PaymentExitDataModel.sol";
import "../../../../src/exits/payment/routers/PaymentInFlightExitRouter.sol";
import "../../../../src/exits/payment/routers/PaymentInFlightExitRouterArgs.sol";
import "../../../../src/framework/PlasmaFramework.sol";
import "../../../../src/exits/interfaces/IStateTransitionVerifier.sol";
import "../../../../src/exits/payment/PaymentInFlightExitModelUtils.sol";
import "../../../../src/utils/FailFastReentrancyGuard.sol";
contract PaymentInFlightExitRouterMock is FailFastReentrancyGuard, PaymentInFlightExitRouter {
using PaymentInFlightExitModelUtils for PaymentExitDataModel.InFlightExit;
PlasmaFramework public framework;
PaymentInFlightExitRouterArgs.StartExitArgs private startIfeArgs;
PaymentInFlightExitRouterArgs.PiggybackInFlightExitOnInputArgs private piggybackInputArgs;
PaymentInFlightExitRouterArgs.PiggybackInFlightExitOnOutputArgs private piggybackOutputArgs;
PaymentInFlightExitRouterArgs.ChallengeCanonicityArgs private challengeCanonicityArgs;
PaymentInFlightExitRouterArgs.ChallengeInputSpentArgs private challengeInputSpentArgs;
PaymentInFlightExitRouterArgs.ChallengeOutputSpent private challengeOutputSpentArgs;
constructor(PaymentExitGameArgs.Args memory args)
public
PaymentInFlightExitRouter(args)
{
framework = args.framework;
}
/** override and calls processInFlightExit for test */
function processExit(uint160 exitId, uint256, address ercContract) external {
PaymentInFlightExitRouter.processInFlightExit(exitId, ercContract);
}
function setInFlightExit(uint160 exitId, PaymentExitDataModel.InFlightExit memory exit) public {
PaymentExitDataModel.InFlightExit storage ife = inFlightExitMap.exits[exitId];
ife.isCanonical = exit.isCanonical;
ife.exitStartTimestamp = exit.exitStartTimestamp;
ife.exitMap = exit.exitMap;
ife.position = exit.position;
ife.bondOwner = exit.bondOwner;
ife.bondSize = exit.bondSize;
ife.oldestCompetitorPosition = exit.oldestCompetitorPosition;
for (uint i = 0; i < exit.inputs.length; i++) {
ife.inputs[i] = exit.inputs[i];
}
for (uint i = 0; i < exit.outputs.length; i++) {
ife.outputs[i] = exit.outputs[i];
}
}
function getInFlightExitInput(uint160 exitId, uint16 inputIndex) public view returns (PaymentExitDataModel.WithdrawData memory) {
return inFlightExitMap.exits[exitId].inputs[inputIndex];
}
function setInFlightExitInputPiggybacked(uint160 exitId, uint16 inputIndex) public payable {
inFlightExitMap.exits[exitId].setInputPiggybacked(inputIndex);
}
function setInFlightExitOutputPiggybacked(uint160 exitId, uint16 outputIndex) public payable {
inFlightExitMap.exits[exitId].setOutputPiggybacked(outputIndex);
}
function getInFlightExitOutput(uint160 exitId, uint16 outputIndex) public view returns (PaymentExitDataModel.WithdrawData memory) {
return inFlightExitMap.exits[exitId].outputs[outputIndex];
}
/** calls the flagOutputFinalized function on behalf of the exit game */
function proxyFlagOutputFinalized(bytes32 outputId, uint160 exitId) public {
framework.flagOutputFinalized(outputId, exitId);
}
/**
* This function helps test reentrant by making this function itself the first call with 'nonReentrant' protection
* So all other PaymentExitGame functions that is protected by 'nonReentrant' too would fail as it would be considered as re-entrancy
*/
function testNonReentrant(string memory testTarget) public nonReentrant(framework) {
if (stringEquals(testTarget, "startInFlightExit")) {
PaymentInFlightExitRouter.startInFlightExit(startIfeArgs);
} else if (stringEquals(testTarget, "piggybackInFlightExitOnInput")) {
PaymentInFlightExitRouter.piggybackInFlightExitOnInput(piggybackInputArgs);
} else if (stringEquals(testTarget, "piggybackInFlightExitOnOutput")) {
PaymentInFlightExitRouter.piggybackInFlightExitOnOutput(piggybackOutputArgs);
} else if (stringEquals(testTarget, "challengeInFlightExitNotCanonical")) {
PaymentInFlightExitRouter.challengeInFlightExitNotCanonical(challengeCanonicityArgs);
} else if (stringEquals(testTarget, "respondToNonCanonicalChallenge")) {
PaymentInFlightExitRouter.respondToNonCanonicalChallenge(bytes(""), 0, bytes(""));
} else if (stringEquals(testTarget, "challengeInFlightExitInputSpent")) {
PaymentInFlightExitRouter.challengeInFlightExitInputSpent(challengeInputSpentArgs);
} else if (stringEquals(testTarget, "challengeInFlightExitOutputSpent")) {
PaymentInFlightExitRouter.challengeInFlightExitOutputSpent(challengeOutputSpentArgs);
} else if (stringEquals(testTarget, "deleteNonPiggybackedInFlightExit")) {
PaymentInFlightExitRouter.deleteNonPiggybackedInFlightExit(uint160(0));
}
revert("non defined function");
}
/** empty function that accepts ETH to fund the contract as test setup */
function depositFundForTest() public payable {}
function stringEquals(string memory a, string memory b) private pure returns (bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../../../src/exits/payment/PaymentExitGameArgs.sol";
import "../../../../src/exits/payment/routers/PaymentStandardExitRouter.sol";
import "../../../../src/exits/payment/routers/PaymentStandardExitRouterArgs.sol";
import "../../../../src/framework/PlasmaFramework.sol";
contract PaymentStandardExitRouterMock is PaymentStandardExitRouter {
PlasmaFramework private framework;
PaymentStandardExitRouterArgs.StartStandardExitArgs private startStandardExitArgs;
PaymentStandardExitRouterArgs.ChallengeStandardExitArgs private challengeStandardExitArgs;
constructor(PaymentExitGameArgs.Args memory args)
public
PaymentStandardExitRouter(args)
{
framework = args.framework;
}
/** override and calls processStandardExit for test */
function processExit(uint160 exitId, uint256, address ercContract) external {
PaymentStandardExitRouter.processStandardExit(exitId, ercContract);
}
/** helper functions for testing */
function setExit(uint160 exitId, PaymentExitDataModel.StandardExit memory exitData) public {
PaymentStandardExitRouter.standardExitMap.exits[exitId] = exitData;
}
function proxyFlagOutputFinalized(bytes32 outputId, uint160 exitId) public {
framework.flagOutputFinalized(outputId, exitId);
}
function depositFundForTest() public payable {}
/**
* This function helps test reentrant by making this function itself the first call with 'nonReentrant' protection
* So all other PaymentExitGame functions that is protected by 'nonReentrant' too would fail as it would be considered as re-entrancy
*/
function testNonReentrant(string memory testTarget) public nonReentrant(framework) {
if (stringEquals(testTarget, "startStandardExit")) {
PaymentStandardExitRouter.startStandardExit(startStandardExitArgs);
} else if (stringEquals(testTarget, "challengeStandardExit")) {
PaymentStandardExitRouter.challengeStandardExit(challengeStandardExitArgs);
}
}
function stringEquals(string memory a, string memory b) private pure returns (bool) {
return keccak256(abi.encodePacked(a)) == keccak256(abi.encodePacked(b));
}
}pragma solidity 0.5.11;
import "../../../src/utils/PosLib.sol";
import "../../../src/exits/utils/ExitId.sol";
contract ExitIdWrapper {
function isStandardExit(uint160 _exitId) public pure returns (bool) {
return ExitId.isStandardExit(_exitId);
}
function getStandardExitId(bool _isDeposit, bytes memory _txBytes, uint256 _utxoPos)
public
pure
returns (uint160)
{
PosLib.Position memory utxoPos = PosLib.decode(_utxoPos);
return ExitId.getStandardExitId(_isDeposit, _txBytes, utxoPos);
}
function getInFlightExitId(bytes memory _txBytes)
public
pure
returns (uint160)
{
return ExitId.getInFlightExitId(_txBytes);
}
}pragma solidity 0.5.11;
import "../../../src/exits/utils/ExitableTimestamp.sol";
contract ExitableTimestampWrapper {
using ExitableTimestamp for ExitableTimestamp.Calculator;
ExitableTimestamp.Calculator internal calculator;
constructor(uint256 _minExitPeriod) public {
calculator = ExitableTimestamp.Calculator(_minExitPeriod);
}
function calculateDepositTxOutputExitableTimestamp(
uint256 _now
)
public
view
returns (uint64)
{
return calculator.calculateDepositTxOutputExitableTimestamp(_now);
}
function calculateTxExitableTimestamp(
uint256 _now,
uint256 _blockTimestamp
)
public
view
returns (uint64)
{
return calculator.calculateTxExitableTimestamp(_now, _blockTimestamp);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../../src/exits/utils/MoreVpFinalization.sol";
contract MoreVpFinalizationWrapper {
function isStandardFinalized(
PlasmaFramework framework,
bytes memory txBytes,
uint256 txPos,
bytes memory inclusionProof
)
public
view
returns (bool)
{
return MoreVpFinalization.isStandardFinalized(
framework,
txBytes,
PosLib.decode(txPos),
inclusionProof
);
}
function isProtocolFinalized(
PlasmaFramework framework,
bytes memory txBytes
)
public
view
returns (bool)
{
return MoreVpFinalization.isProtocolFinalized(
framework,
txBytes
);
}
}pragma solidity 0.5.11;
import "../../../src/exits/utils/OutputId.sol";
contract OutputIdWrapper {
function computeDepositOutputId(
bytes memory _txBytes,
uint8 _outputIndex,
uint256 _utxoPosValue
)
public
pure
returns (bytes32)
{
return OutputId.computeDepositOutputId(_txBytes, _outputIndex, _utxoPosValue);
}
function computeNormalOutputId(
bytes memory _txBytes,
uint8 _outputIndex
)
public
pure
returns (bytes32)
{
return OutputId.computeNormalOutputId(_txBytes, _outputIndex);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../src/framework/BlockController.sol";
contract BlockControllerMock is BlockController {
address private maintainer;
constructor(
uint256 interval,
uint256 minExitPeriod,
uint256 initialImmuneVaults,
address authority
)
public
BlockController(
interval,
minExitPeriod,
initialImmuneVaults,
authority
)
{
maintainer = msg.sender;
}
/**
* override to make it non-abstract contract
* this mock file set the user that deploys the contract as maintainer to simplify the test.
*/
function getMaintainer() public view returns (address) {
return maintainer;
}
function setBlock(uint256 _blockNum, bytes32 _root, uint256 _timestamp) external {
blocks[_blockNum] = BlockModel.Block(_root, _timestamp);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "./registries/ExitGameRegistryMock.sol";
import "../../src/framework/ExitGameController.sol";
import "../../src/framework/interfaces/IExitProcessor.sol";
import "../../src/vaults/Erc20Vault.sol";
import "../../src/vaults/EthVault.sol";
import "../../src/utils/PosLib.sol";
contract DummyExitGame is IExitProcessor {
uint256 public priorityFromEnqueue;
ExitGameRegistryMock public exitGameRegistry;
ExitGameController public exitGameController;
EthVault public ethVault;
Erc20Vault public erc20Vault;
event ExitFinalizedFromDummyExitGame (
uint256 indexed exitId,
uint256 vaultId,
address ercContract
);
// override ExitProcessor interface
function processExit(uint160 exitId, uint256 vaultId, address ercContract) public {
emit ExitFinalizedFromDummyExitGame(exitId, vaultId, ercContract);
}
// setter function only for test, not a real Exit Game function
function setExitGameRegistry(address _contract) public {
exitGameRegistry = ExitGameRegistryMock(_contract);
}
function checkOnlyFromNonQuarantinedExitGame() public view returns (bool) {
return exitGameRegistry.checkOnlyFromNonQuarantinedExitGame();
}
// setter function only for test, not a real Exit Game function
function setExitGameController(address _contract) public {
exitGameController = ExitGameController(_contract);
}
function enqueue(uint256 vaultId, address token, uint64 exitableAt, uint256 txPos, uint160 exitId, IExitProcessor exitProcessor)
public
{
priorityFromEnqueue = exitGameController.enqueue(vaultId, token, exitableAt, PosLib.decode(txPos), exitId, exitProcessor);
}
function proxyBatchFlagOutputsFinalized(bytes32[] memory outputIds, uint160 exitId) public {
exitGameController.batchFlagOutputsFinalized(outputIds, exitId);
}
function proxyFlagOutputFinalized(bytes32 outputId, uint160 exitId) public {
exitGameController.flagOutputFinalized(outputId, exitId);
}
// setter function only for test, not a real Exit Game function
function setEthVault(EthVault vault) public {
ethVault = vault;
}
function proxyEthWithdraw(address payable target, uint256 amount) public {
ethVault.withdraw(target, amount);
}
// setter function only for test, not a real Exit Game function
function setErc20Vault(Erc20Vault vault) public {
erc20Vault = vault;
}
function proxyErc20Withdraw(address payable target, address token, uint256 amount) public {
erc20Vault.withdraw(target, token, amount);
}
}pragma solidity 0.5.11;
import "./registries/VaultRegistryMock.sol";
import "../../src/framework/BlockController.sol";
contract DummyVault {
VaultRegistryMock internal vaultRegistry;
BlockController internal blockController;
// setter function only for test, not a real Vault function
function setVaultRegistry(address _contract) public {
vaultRegistry = VaultRegistryMock(_contract);
}
function checkOnlyFromNonQuarantinedVault() public view returns (bool) {
return vaultRegistry.checkOnlyFromNonQuarantinedVault();
}
// setter function only for test, not a real Vault function
function setBlockController(address _contract) public {
blockController = BlockController(_contract);
}
function submitDepositBlock(bytes32 _blockRoot) public {
blockController.submitDepositBlock(_blockRoot);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../src/framework/ExitGameController.sol";
contract ExitGameControllerMock is ExitGameController {
address private maintainer;
constructor(uint256 _minExitPeriod, uint256 _initialImmuneExitGames)
public
ExitGameController(_minExitPeriod, _initialImmuneExitGames)
{
maintainer = msg.sender;
}
/**
* override to make it non-abstract contract
* this mock file set the user that deploys the contract as maintainer to simplify the test.
*/
function getMaintainer() public view returns (address) {
return maintainer;
}
}pragma solidity 0.5.11;
import "../../src/framework/Protocol.sol";
contract ProtocolWrapper {
// solhint-disable-next-line func-name-mixedcase
function MVP() public pure returns (uint8) {
return Protocol.MVP();
}
// solhint-disable-next-line func-name-mixedcase
function MORE_VP() public pure returns (uint8) {
return Protocol.MORE_VP();
}
function isValidProtocol(uint8 protocol) public pure returns (bool) {
return Protocol.isValidProtocol(protocol);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../src/framework/ExitGameController.sol";
import "../../src/framework/interfaces/IExitProcessor.sol";
contract ReentrancyExitGame is IExitProcessor {
ExitGameController public exitGameController;
uint256 public vaultId;
address public testToken;
uint256 public reentryMaxExitToProcess;
constructor(ExitGameController _controller, uint256 _vaultId, address _token, uint256 _reentryMaxExitToProcess) public {
exitGameController = _controller;
vaultId = _vaultId;
testToken = _token;
reentryMaxExitToProcess = _reentryMaxExitToProcess;
}
// override ExitProcessor interface
// This would call the processExits back to mimic reentracy attack
function processExit(uint160, uint256, address) public {
exitGameController.processExits(vaultId, testToken, 0, reentryMaxExitToProcess);
}
function enqueue(uint256 _vaultId, address _token, uint64 _exitableAt, uint256 _txPos, uint160 _exitId, IExitProcessor _exitProcessor)
public
{
exitGameController.enqueue(_vaultId, _token, _exitableAt, PosLib.decode(_txPos), _exitId, _exitProcessor);
}
}pragma solidity 0.5.11;
import "../../../src/framework/registries/ExitGameRegistry.sol";
contract ExitGameRegistryMock is ExitGameRegistry {
address private maintainer;
constructor (uint256 _minExitPeriod, uint256 _initialImmuneExitGames)
public
ExitGameRegistry(_minExitPeriod, _initialImmuneExitGames)
{
}
/** override to make it non-abstract contract */
function getMaintainer() public view returns (address) {
return maintainer;
}
/** test helper function */
function setMaintainer(address maintainerToSet) public {
maintainer = maintainerToSet;
}
function checkOnlyFromNonQuarantinedExitGame() public onlyFromNonQuarantinedExitGame view returns (bool) {
return true;
}
}pragma solidity 0.5.11;
import "../../../src/framework/registries/VaultRegistry.sol";
contract VaultRegistryMock is VaultRegistry {
address private maintainer;
constructor (uint256 _minExitPeriod, uint256 _initialImmuneVaults)
public
VaultRegistry(_minExitPeriod, _initialImmuneVaults)
{
}
/** override to make it non-abstract contract */
function getMaintainer() public view returns (address) {
return maintainer;
}
/** test helper function */
function setMaintainer(address maintainerToSet) public {
maintainer = maintainerToSet;
}
function checkOnlyFromNonQuarantinedVault() public onlyFromNonQuarantinedVault view returns (bool) {
return true;
}
}pragma solidity 0.5.11;
import "../../../src/framework/utils/ExitPriority.sol";
import "../../../src/utils/PosLib.sol";
contract ExitPriorityWrapper {
function computePriority(uint64 exitableAt, uint256 txPos, uint160 exitId) public pure returns (uint256) {
return ExitPriority.computePriority(exitableAt, PosLib.decode(txPos), exitId);
}
function parseExitableAt(uint256 priority) public pure returns (uint64) {
return ExitPriority.parseExitableAt(priority);
}
function parseExitId(uint256 priority) public pure returns (uint160) {
return ExitPriority.parseExitId(priority);
}
}pragma solidity 0.5.11;
import "../../../src/framework/utils/PriorityQueue.sol";
contract PriorityQueueLoadTest is PriorityQueue {
/**
* Helper function to inject heap data. It only appends batch of data to the end of array used as heap.
* The client using this should make sure the data is in the order of an valid heap.
*/
function setHeapData(uint256[] calldata heapList) external {
for (uint i = 0; i < heapList.length; i++) {
PriorityQueue.queue.heapList.push(heapList[i]);
}
PriorityQueue.queue.currentSize += heapList.length;
}
}pragma solidity ^0.5.0;
import "../../src/framework/utils/PriorityQueue.sol";
/**
* @title PriorityQueue
* @dev Min-heap priority queue implementation.
*/
contract PriorityQueueTest{
/*
* Events
*/
event DelMin(uint256 val);
/*
* Storage
*/
PriorityQueue public queue;
/*
* Public functions
*/
constructor()
public
{
queue = new PriorityQueue();
}
/**
* @dev Inserts an element into the queue. Does not perform deduplication.
*/
function insert(uint256 _element)
public
{
queue.insert(_element);
}
/**
* @dev Overrides the default implementation, by simply emitting an even on deletion, so that the result is testable.
* @return The smallest element in the priority queue.
*/
function delMin()
public
returns (uint256 value)
{
value = queue.delMin();
emit DelMin(value);
}
/*
* Read-only functions
*/
/**
* @dev Returns the top element of the heap.
* @return The smallest element in the priority queue.
*/
function getMin()
public
view
returns (uint256)
{
return queue.getMin();
}
function currentSize()
external
view
returns (uint256)
{
return queue.currentSize();
}
}pragma solidity ^0.5.0;
import "../../src/utils/RLPReader.sol";
/**
* @title RLPTest
* @dev Contract for testing RLP decoding.
*/
contract RLPTest {
function eight(bytes memory tx_bytes)
public
pure
returns (uint256, address, address)
{
RLPReader.RLPItem[] memory txList = RLPReader.toList(RLPReader.toRlpItem(tx_bytes));
return (
RLPReader.toUint(txList[5]),
RLPReader.toAddress(txList[6]),
RLPReader.toAddress(txList[7])
);
}
function eleven(bytes memory tx_bytes)
public
pure
returns (uint256, address, address, address)
{
RLPReader.RLPItem[] memory txList = RLPReader.toList(RLPReader.toRlpItem(tx_bytes));
return (
RLPReader.toUint(txList[7]),
RLPReader.toAddress(txList[8]),
RLPReader.toAddress(txList[9]),
RLPReader.toAddress(txList[10])
);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../src/transactions/FungibleTokenOutputModel.sol";
contract FungibleTokenOutputWrapper {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
function decodeOutput(bytes memory encodedOutput)
public
pure
returns (FungibleTokenOutputModel.Output memory)
{
GenericTransaction.Output memory genericOutput = GenericTransaction.decodeOutput(encodedOutput.toRlpItem());
return FungibleTokenOutputModel.decodeOutput(genericOutput);
}
function getOutput(bytes memory transaction, uint16 outputIndex)
public
pure
returns (FungibleTokenOutputModel.Output memory)
{
GenericTransaction.Transaction memory genericTx = GenericTransaction.decode(transaction);
return FungibleTokenOutputModel.getOutput(genericTx, outputIndex);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../src/transactions/GenericTransaction.sol";
contract GenericTransactionWrapper {
function decode(bytes memory transaction) public pure returns (GenericTransaction.Transaction memory) {
return GenericTransaction.decode(transaction);
}
function getOutput(bytes memory transaction, uint16 outputIndex) public pure returns (GenericTransaction.Output memory) {
GenericTransaction.Transaction memory genericTx = GenericTransaction.decode(transaction);
return GenericTransaction.getOutput(genericTx, outputIndex);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../src/transactions/PaymentTransactionModel.sol";
contract PaymentTransactionModelMock {
using RLPReader for bytes;
function decode(bytes memory transaction) public pure returns (PaymentTransactionModel.Transaction memory) {
return PaymentTransactionModel.decode(transaction);
}
function getOutputOwner(uint256 outputType, address owner, address token, uint256 amount) public pure returns (address payable) {
FungibleTokenOutputModel.Output memory output = FungibleTokenOutputModel.Output({
outputType: outputType,
outputGuard: bytes20(owner),
token: token,
amount: amount
});
return PaymentTransactionModel.getOutputOwner(output);
}
function getOutput(bytes memory transaction, uint16 outputIndex) public pure returns (FungibleTokenOutputModel.Output memory) {
PaymentTransactionModel.Transaction memory decodedTx = PaymentTransactionModel.decode(transaction);
return PaymentTransactionModel.getOutput(decodedTx, outputIndex);
}
}pragma solidity 0.5.11;
import "../../../src/transactions/eip712Libs/PaymentEip712Lib.sol";
import "../../../src/transactions/PaymentTransactionModel.sol";
contract PaymentEip712LibMock {
function hashTx(address _verifyingContract, bytes memory _rlpTx)
public
pure
returns (bytes32)
{
PaymentEip712Lib.Constants memory eip712 = PaymentEip712Lib.initConstants(_verifyingContract);
return PaymentEip712Lib.hashTx(eip712, PaymentTransactionModel.decode(_rlpTx));
}
}pragma solidity 0.5.11;
import "../../src/utils/Bits.sol";
contract BitsWrapper {
function setBit(uint _self, uint8 _index) public pure returns (uint)
{
return Bits.setBit(_self, _index);
}
function clearBit(uint _self, uint8 _index) public pure returns (uint)
{
return Bits.clearBit(_self, _index);
}
/**
* @dev It makes sense to expose just `bitSet` to be able to test both of Bits `getBit` and `bitSet`
*/
function bitSet(uint _self, uint8 _index) public pure returns (bool)
{
return Bits.bitSet(_self, _index);
}
}pragma solidity 0.5.11;
import "../../src/exits/utils/BondSize.sol";
contract BondSizeMock {
using BondSize for BondSize.Params;
BondSize.Params public bond;
constructor (uint128 initialBondSize, uint16 lowerBoundDivisor, uint16 upperBoundMultiplier) public {
bond = BondSize.buildParams(initialBondSize, lowerBoundDivisor, upperBoundMultiplier);
}
function bondSize() public view returns (uint128) {
return bond.bondSize();
}
function updateBondSize(uint128 newBondSize) public {
bond.updateBondSize(newBondSize);
}
}pragma solidity 0.5.11;
import "../../src/utils/Merkle.sol";
contract MerkleWrapper {
function checkMembership(bytes memory leaf, uint256 index, bytes32 rootHash, bytes memory proof)
public
pure
returns (bool)
{
return Merkle.checkMembership(leaf, index, rootHash, proof);
}
}pragma solidity 0.5.11;
import "../../src/utils/OnlyWithValue.sol";
contract OnlyWithValueMock is OnlyWithValue {
event OnlyWithValuePassed();
function checkOnlyWithValue(uint256 _value) public payable onlyWithValue(_value) {
emit OnlyWithValuePassed();
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../src/utils/PosLib.sol";
contract PosLibWrapper {
using PosLib for PosLib.Position;
function toStrictTxPos(PosLib.Position memory pos)
public
pure
returns (PosLib.Position memory)
{
return pos.toStrictTxPos();
}
function getTxPositionForExitPriority(PosLib.Position memory pos)
public
pure
returns (uint256)
{
return pos.getTxPositionForExitPriority();
}
function encode(PosLib.Position memory pos) public pure returns (uint256) {
return pos.encode();
}
function decode(uint256 pos) public pure returns (PosLib.Position memory) {
return PosLib.decode(pos);
}
}pragma solidity 0.5.11;
import "../../src/framework/utils/Quarantine.sol";
contract QuarantineMock {
using Quarantine for Quarantine.Data;
Quarantine.Data internal _quarantine;
constructor(uint256 _period, uint256 _initialImmuneCount)
public
{
_quarantine.quarantinePeriod = _period;
_quarantine.immunitiesRemaining = _initialImmuneCount;
}
function quarantineContract(address _contractAddress) public {
_quarantine.quarantine(_contractAddress);
}
function isQuarantined(address _contractAddress) public view returns (bool) {
return _quarantine.isQuarantined(_contractAddress);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../src/utils/RLPReader.sol";
contract RLPMock {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
uint8 constant internal WORD_SIZE = 32;
function decodeBytes32(bytes memory _data) public pure returns (bytes32) {
return _data.toRlpItem().toBytes32();
}
function decodeAddress(bytes memory _data) public pure returns (address) {
return _data.toRlpItem().toAddress();
}
function decodeBytes20(bytes memory _data) public pure returns (bytes20) {
return bytes20(_data.toRlpItem().toAddress());
}
function decodeBytes(bytes memory _data) public pure returns (bytes memory) {
return toBytes(_data.toRlpItem());
}
function decodeUint(bytes memory _data) public pure returns (uint) {
return _data.toRlpItem().toUint();
}
function decodeInt(bytes memory _data) public pure returns (int) {
return int(_data.toRlpItem().toUint());
}
function decodeString(bytes memory _data) public pure returns (string memory) {
return string(toBytes(_data.toRlpItem()));
}
function decodeList(bytes memory _data) public pure returns (bytes[] memory) {
RLPReader.RLPItem[] memory items = _data.toRlpItem().toList();
bytes[] memory result = new bytes[](items.length);
for (uint i = 0; i < items.length; i++) {
result[i] = toRlpBytes(items[i]);
}
return result;
}
function toBytes(RLPReader.RLPItem memory item) internal pure returns (bytes memory) {
require(item.len > 0, "Item length must be > 0");
(uint256 itemLen, uint256 offset) = RLPReader.decodeLengthAndOffset(item.memPtr);
require(itemLen == item.len, "Decoded RLP length is invalid");
uint len = itemLen - offset;
bytes memory result = new bytes(len);
uint destPtr;
// solhint-disable-next-line no-inline-assembly
assembly {
destPtr := add(0x20, result)
}
copyUnsafe(item.memPtr + offset, destPtr, len);
return result;
}
function copyUnsafe(uint src, uint dest, uint len) private pure {
if (len == 0) return;
uint remainingLength = len;
// copy as many word sizes as possible
for (uint i = WORD_SIZE; len >= i; i += WORD_SIZE) {
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(dest, mload(src))
}
src += WORD_SIZE;
dest += WORD_SIZE;
remainingLength -= WORD_SIZE;
require(remainingLength < len, "Remaining length not less than original length");
}
// left over bytes. Mask is used to remove unwanted bytes from the word
uint mask = 256 ** (WORD_SIZE - remainingLength) - 1;
// solhint-disable-next-line no-inline-assembly
assembly {
let srcpart := and(mload(src), not(mask)) // zero out src
let destpart := and(mload(dest), mask) // retrieve the bytes
mstore(dest, or(destpart, srcpart))
}
}
function toRlpBytes(RLPReader.RLPItem memory item) internal pure returns (bytes memory) {
bytes memory result = new bytes(item.len);
if (result.length == 0) return result;
uint resultPtr;
// solhint-disable-next-line no-inline-assembly
assembly {
resultPtr := add(0x20, result)
}
copyUnsafe(item.memPtr, resultPtr, item.len);
return result;
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../src/utils/RLPReader.sol";
contract RLPMockSecurity {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
function decodeBytes32(bytes memory _data) public pure returns (bytes32) {
return bytes32(_data.toRlpItem().toUint());
}
function decodeBytes20(bytes memory _data) public pure returns (bytes20) {
return bytes20(_data.toRlpItem().toAddress());
}
function decodeUint(bytes memory _data) public pure returns (uint) {
return _data.toRlpItem().toUint();
}
function decodeList(bytes memory _data) public pure returns (RLPReader.RLPItem[] memory) {
return _data.toRlpItem().toList();
}
}pragma solidity 0.5.11;
import "../../src/utils/SafeEthTransfer.sol";
contract SafeEthTransferMock {
bool public transferResult;
function transferRevertOnError(address payable receiver, uint256 amount, uint256 gasStipend)
public
{
SafeEthTransfer.transferRevertOnError(receiver, amount, gasStipend);
}
function transferReturnResult(address payable receiver, uint256 amount, uint256 gasStipend)
public
{
transferResult = SafeEthTransfer.transferReturnResult(receiver, amount, gasStipend);
}
/** helper function to pre-fund the contract to test */
function setupInitialFundToTestTransfer() external payable {}
}pragma solidity 0.5.11;
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
// A 'NonCompliantERC20' token is one that uses an old version of the ERC20 standard,
// as described here https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
// Basically, this version does not return anything from `transfer` and `transferFrom`,
// whereas most modern implementions of ERC20 return a boolean to indicate success or failure.
contract NonCompliantERC20 {
using SafeMath for uint256;
mapping (address => uint256) private balances;
mapping (address => mapping (address => uint256)) private allowances;
uint256 private totalSupply;
constructor(uint256 _initialAmount) public {
balances[msg.sender] = _initialAmount;
totalSupply = _initialAmount;
}
function balanceOf(address _account) public view returns (uint256) {
return balances[_account];
}
function transfer(address _to, uint _value) public {
balances[msg.sender] = balances[msg.sender].sub(_value);
balances[_to] = balances[_to].add(_value);
}
function transferFrom(address _from, address _to, uint _value) public {
uint256 _allowance = allowances[_from][msg.sender];
balances[_to] = balances[_to].add(_value);
balances[_from] = balances[_from].sub(_value);
allowances[_from][msg.sender] = _allowance.sub(_value);
}
function approve(address _spender, uint _value) public {
allowances[msg.sender][_spender] = _value;
}
function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
return allowances[_owner][_spender];
}
}pragma solidity 0.5.11;
import "openzeppelin-solidity/contracts/cryptography/ECDSA.sol";
import "../interfaces/ISpendingCondition.sol";
import "../utils/OutputId.sol";
import "../../framework/PlasmaFramework.sol";
import "../../transactions/FungibleTokenOutputModel.sol";
import "../../transactions/GenericTransaction.sol";
import "../../transactions/PaymentTransactionModel.sol";
import "../../transactions/eip712Libs/PaymentEip712Lib.sol";
import "../../utils/PosLib.sol";
contract FeeClaimOutputToPaymentTxCondition is ISpendingCondition {
using PaymentEip712Lib for PaymentEip712Lib.Constants;
using PosLib for PosLib.Position;
uint256 public feeTxType;
uint256 public feeClaimOutputType;
uint256 public paymentTxType;
PaymentEip712Lib.Constants internal eip712;
constructor(
PlasmaFramework _framework,
uint256 _feeTxType,
uint256 _feeClaimOutputType,
uint256 _paymentTxType
)
public
{
eip712 = PaymentEip712Lib.initConstants(address(_framework));
feeTxType = _feeTxType;
feeClaimOutputType = _feeClaimOutputType;
paymentTxType = _paymentTxType;
}
/**
* @dev This implementation checks signature for spending fee claim output. It should be signed with the owner signature.
* The fee claim output that is spendable follows Fungible Token Output format.
* @param feeTxBytes Encoded fee transaction
* @param utxoPos Position of the fee utxo
* @param paymentTxBytes Payment transaction (in bytes) that spends the fee claim output
* @param inputIndex Input index of the payment tx that points to the fee claim output
* @param signature Signature of the owner of fee claiming output
*/
function verify(
bytes calldata feeTxBytes,
uint256 utxoPos,
bytes calldata paymentTxBytes,
uint16 inputIndex,
bytes calldata signature
)
external
view
returns (bool)
{
PosLib.Position memory decodedUtxoPos = PosLib.decode(utxoPos);
require(decodedUtxoPos.outputIndex == 0, "Fee claim output must be the first output of fee tx");
GenericTransaction.Transaction memory feeTx = GenericTransaction.decode(feeTxBytes);
FungibleTokenOutputModel.Output memory feeClaimOutput = FungibleTokenOutputModel.getOutput(feeTx, decodedUtxoPos.outputIndex);
require(feeTx.txType == feeTxType, "Unexpected tx type for fee transaction");
require(feeClaimOutput.outputType == feeClaimOutputType, "Unexpected output type for fee claim output");
PaymentTransactionModel.Transaction memory paymentTx = PaymentTransactionModel.decode(paymentTxBytes);
require(paymentTx.txType == paymentTxType, "Unexpected tx type for payment transaction");
require(
paymentTx.inputs[inputIndex] == bytes32(decodedUtxoPos.encode()),
"Payment tx points to the incorrect output UTXO position of the fee claim output"
);
address owner = address(feeClaimOutput.outputGuard);
address signer = ECDSA.recover(eip712.hashTx(paymentTx), signature);
require(signer != address(0), "Failed to recover the signer from the signature");
require(owner == signer, "Tx is not signed correctly");
return true;
}
}pragma solidity 0.5.11;
/**
* It is an empty contract by design. We only want to be able to register the tx type to the framework.
* For simplicity, a fee claiming tx does not have the ability to exit directly.
* It should be first spend to a Payment tx and then exit the fund from Payment tx.
*/
contract FeeExitGame {
}pragma solidity 0.5.11;
/**
* @notice Interface of the spending condition
* @dev For the interface design and discussion, see the following GH issue
* https://github.com/omisego/plasma-contracts/issues/214
*/
interface ISpendingCondition {
/**
* @notice Verifies the spending condition
* @param inputTx Encoded input transaction, in bytes
* @param utxoPos Position of the utxo
* @param spendingTx Spending transaction, in bytes
* @param inputIndex The input index of the spending tx that points to the output
* @param witness The witness data of the spending condition
*/
function verify(
bytes calldata inputTx,
uint256 utxoPos,
bytes calldata spendingTx,
uint16 inputIndex,
bytes calldata witness
) external view returns (bool);
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
interface IStateTransitionVerifier {
/**
* @notice Verifies state transition logic
* @param txBytes The transaction that does the state transition to verify
* @param inputTxs Input transaction to the transaction to verify
* @param outputIndexOfInputTxs Output index of the input txs that the transaction input points to
*/
function isCorrectStateTransition(
bytes calldata txBytes,
bytes[] calldata inputTxs,
uint16[] calldata outputIndexOfInputTxs
)
external
view
returns (bool);
}pragma solidity 0.5.11;
/**
* @notice Model library for PaymentExit
*/
library PaymentExitDataModel {
uint8 constant public MAX_INPUT_NUM = 4;
uint8 constant public MAX_OUTPUT_NUM = 4;
/**
* @dev Exit model for a standard exit
* @param exitable Boolean that defines whether exit is possible. Used by the challenge game to flag the result.
* @param utxoPos The UTXO position of the transaction's exiting output
* @param outputId The output identifier, in OutputId format
* @param exitTarget The address to which the exit withdraws funds
* @param amount The amount of funds to withdraw with this exit
* @param bondSize The size of the bond put up for this exit to start, and which is used to cover the cost of challenges
*/
struct StandardExit {
bool exitable;
uint256 utxoPos;
bytes32 outputId;
address payable exitTarget;
uint256 amount;
uint256 bondSize;
}
/**
* @dev Mapping of (exitId => StandardExit) that stores all standard exit data
*/
struct StandardExitMap {
mapping (uint160 => PaymentExitDataModel.StandardExit) exits;
}
/**
* @dev The necessary data needed for processExit for in-flight exit inputs/outputs
*/
struct WithdrawData {
bytes32 outputId;
address payable exitTarget;
address token;
uint256 amount;
uint256 piggybackBondSize;
}
/**
* @dev Exit model for an in-flight exit
* @param isCanonical A boolean that defines whether the exit is canonical
* A canonical exit withdraws the outputs while a non-canonical exit withdraws the inputs
* @param exitStartTimestamp Timestamp for the start of the exit
* @param exitMap A bitmap that stores piggyback flags
* @param position The position of the youngest input of the in-flight exit transaction
* @param inputs Fixed-size array of data required to withdraw inputs (if undefined, the default value is empty)
* @param outputs Fixed-size array of data required to withdraw outputs (if undefined, the default value is empty)
* @param bondOwner Recipient of the bond, when the in-flight exit is processed
* @param bondSize The size of the bond put up for this exit to start. Used to cover the cost of challenges.
* @param oldestCompetitorPosition The position of the oldest competing transaction
* The exiting transaction is only canonical if all competing transactions are younger.
*/
struct InFlightExit {
// Canonicity is assumed at start, and can be challenged and set to `false` after start
// Response to non-canonical challenge can set it back to `true`
bool isCanonical;
uint64 exitStartTimestamp;
/**
* exit map Stores piggybacks and finalized exits
* right most 0 ~ MAX_INPUT bits is flagged when input is piggybacked
* right most MAX_INPUT ~ MAX_INPUT + MAX_OUTPUT bits is flagged when output is piggybacked
*/
uint256 exitMap;
uint256 position;
WithdrawData[MAX_INPUT_NUM] inputs;
WithdrawData[MAX_OUTPUT_NUM] outputs;
address payable bondOwner;
uint256 bondSize;
uint256 oldestCompetitorPosition;
}
/**
* @dev Mapping of (exitId => InFlightExit) that stores all in-flight exit data
*/
struct InFlightExitMap {
mapping (uint160 => PaymentExitDataModel.InFlightExit) exits;
}
}pragma solidity 0.5.11;
import "../registries/SpendingConditionRegistry.sol";
import "../interfaces/IStateTransitionVerifier.sol";
import "../../framework/PlasmaFramework.sol";
library PaymentExitGameArgs {
/**
* @param framework The Plasma framework
* @param ethVaultId Vault id for EthVault
* @param erc20VaultId Vault id for the Erc20Vault
* @param spendingConditionRegistry the spendingConditionRegistry that can provide spending condition implementation by types
* @param stateTransitionVerifier state transition verifier predicate contract that checks the transaction correctness
* @param supportTxType the tx type of this exit game is using
* @param safeGasStipend a gas amount limit when transferring Eth to protect from attack with draining gas
*/
struct Args {
PlasmaFramework framework;
uint256 ethVaultId;
uint256 erc20VaultId;
SpendingConditionRegistry spendingConditionRegistry;
IStateTransitionVerifier stateTransitionVerifier;
uint256 supportTxType;
uint256 safeGasStipend;
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../utils/Bits.sol";
import "../../transactions/PaymentTransactionModel.sol";
import { PaymentExitDataModel as ExitModel } from "./PaymentExitDataModel.sol";
library PaymentInFlightExitModelUtils {
using Bits for uint256;
function isInputEmpty(ExitModel.InFlightExit memory ife, uint16 index)
internal
pure
returns (bool)
{
require(index < PaymentTransactionModel.MAX_INPUT_NUM(), "Invalid input index");
return isEmptyWithdrawData(ife.inputs[index]);
}
function isOutputEmpty(ExitModel.InFlightExit memory ife, uint16 index)
internal
pure
returns (bool)
{
require(index < PaymentTransactionModel.MAX_OUTPUT_NUM(), "Invalid output index");
return isEmptyWithdrawData(ife.outputs[index]);
}
function isInputPiggybacked(ExitModel.InFlightExit memory ife, uint16 index)
internal
pure
returns (bool)
{
require(index < PaymentTransactionModel.MAX_INPUT_NUM(), "Invalid input index");
return ife.exitMap.bitSet(uint8(index));
}
function isOutputPiggybacked(ExitModel.InFlightExit memory ife, uint16 index)
internal
pure
returns (bool)
{
require(index < PaymentTransactionModel.MAX_OUTPUT_NUM(), "Invalid output index");
uint8 indexInExitMap = uint8(index + PaymentTransactionModel.MAX_INPUT_NUM());
return ife.exitMap.bitSet(indexInExitMap);
}
function setInputPiggybacked(ExitModel.InFlightExit storage ife, uint16 index)
internal
{
require(index < PaymentTransactionModel.MAX_INPUT_NUM(), "Invalid input index");
ife.exitMap = ife.exitMap.setBit(uint8(index));
}
function clearInputPiggybacked(ExitModel.InFlightExit storage ife, uint16 index)
internal
{
require(index < PaymentTransactionModel.MAX_INPUT_NUM(), "Invalid input index");
ife.exitMap = ife.exitMap.clearBit(uint8(index));
}
function setOutputPiggybacked(ExitModel.InFlightExit storage ife, uint16 index)
internal
{
require(index < PaymentTransactionModel.MAX_OUTPUT_NUM(), "Invalid output index");
uint8 indexInExitMap = uint8(index + PaymentTransactionModel.MAX_INPUT_NUM());
ife.exitMap = ife.exitMap.setBit(indexInExitMap);
}
function clearOutputPiggybacked(ExitModel.InFlightExit storage ife, uint16 index)
internal
{
require(index < PaymentTransactionModel.MAX_OUTPUT_NUM(), "Invalid output index");
uint8 indexInExitMap = uint8(index + PaymentTransactionModel.MAX_INPUT_NUM());
ife.exitMap = ife.exitMap.clearBit(indexInExitMap);
}
function isInFirstPhase(ExitModel.InFlightExit memory ife, uint256 minExitPeriod)
internal
view
returns (bool)
{
uint256 periodTime = minExitPeriod / 2;
return ((block.timestamp - ife.exitStartTimestamp) / periodTime) < 1;
}
function isEmptyWithdrawData(ExitModel.WithdrawData memory data) private pure returns (bool) {
return data.outputId == bytes32("") &&
data.exitTarget == address(0) &&
data.token == address(0) &&
data.amount == 0 &&
data.piggybackBondSize == 0;
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../interfaces/IStateTransitionVerifier.sol";
import "../payment/PaymentExitDataModel.sol";
import "../../transactions/FungibleTokenOutputModel.sol";
import "../../transactions/PaymentTransactionModel.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
/**
* @notice Verifies state transitions for payment transaction
* @dev For payment transaction to be valid, the state transition should check that the sum of the inputs is larger than the sum of the outputs
*/
contract PaymentTransactionStateTransitionVerifier {
using SafeMath for uint256;
/**
* @dev For payment transaction to be valid, the state transition should check that the sum of the inputs is larger than the sum of the outputs
*/
function isCorrectStateTransition(
bytes calldata txBytes,
bytes[] calldata inputTxs,
uint16[] calldata outputIndexOfInputTxs
)
external
pure
returns (bool)
{
if (inputTxs.length != outputIndexOfInputTxs.length) {
return false;
}
FungibleTokenOutputModel.Output[] memory inputs = new FungibleTokenOutputModel.Output[](inputTxs.length);
for (uint i = 0; i < inputTxs.length; i++) {
uint16 outputIndex = outputIndexOfInputTxs[i];
FungibleTokenOutputModel.Output memory output = FungibleTokenOutputModel.getOutput(
GenericTransaction.decode(inputTxs[i]),
outputIndex
);
inputs[i] = output;
}
PaymentTransactionModel.Transaction memory transaction = PaymentTransactionModel.decode(txBytes);
FungibleTokenOutputModel.Output[] memory outputs = new FungibleTokenOutputModel.Output[](transaction.outputs.length);
for (uint i = 0; i < transaction.outputs.length; i++) {
outputs[i] = FungibleTokenOutputModel.Output(
transaction.outputs[i].outputType,
transaction.outputs[i].outputGuard,
transaction.outputs[i].token,
transaction.outputs[i].amount
);
}
return _isCorrectStateTransition(inputs, outputs);
}
function _isCorrectStateTransition(
FungibleTokenOutputModel.Output[] memory inputs,
FungibleTokenOutputModel.Output[] memory outputs
)
private
pure
returns (bool)
{
bool correctTransition = true;
uint i = 0;
while (correctTransition && i < outputs.length) {
address token = outputs[i].token;
FungibleTokenOutputModel.Output[] memory inputsForToken = filterWithToken(inputs, token);
FungibleTokenOutputModel.Output[] memory outputsForToken = filterWithToken(outputs, token);
correctTransition = isCorrectSpend(inputsForToken, outputsForToken);
i += 1;
}
return correctTransition;
}
function filterWithToken(
FungibleTokenOutputModel.Output[] memory outputs,
address token
)
private
pure
returns (FungibleTokenOutputModel.Output[] memory)
{
// Required for calculating the size of the filtered array
uint256 arraySize = 0;
for (uint i = 0; i < outputs.length; ++i) {
if (outputs[i].token == token) {
arraySize += 1;
}
}
FungibleTokenOutputModel.Output[] memory outputsWithToken = new FungibleTokenOutputModel.Output[](arraySize);
uint j = 0;
for (uint i = 0; i < outputs.length; ++i) {
if (outputs[i].token == token) {
outputsWithToken[j] = outputs[i];
j += 1;
}
}
return outputsWithToken;
}
function isCorrectSpend(
FungibleTokenOutputModel.Output[] memory inputs,
FungibleTokenOutputModel.Output[] memory outputs
)
internal
pure
returns (bool)
{
uint256 amountIn = sumAmounts(inputs);
uint256 amountOut = sumAmounts(outputs);
return amountIn >= amountOut;
}
function sumAmounts(FungibleTokenOutputModel.Output[] memory outputs) private pure returns (uint256) {
uint256 amount = 0;
for (uint i = 0; i < outputs.length; i++) {
amount = amount.add(outputs[i].amount);
}
return amount;
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../PaymentExitDataModel.sol";
import "../PaymentInFlightExitModelUtils.sol";
import "../routers/PaymentInFlightExitRouterArgs.sol";
import "../../interfaces/ISpendingCondition.sol";
import "../../registries/SpendingConditionRegistry.sol";
import "../../utils/ExitId.sol";
import "../../utils/OutputId.sol";
import "../../utils/MoreVpFinalization.sol";
import "../../../utils/Merkle.sol";
import "../../../utils/SafeEthTransfer.sol";
import "../../../utils/PosLib.sol";
import "../../../framework/PlasmaFramework.sol";
import "../../../transactions/PaymentTransactionModel.sol";
import "../../../transactions/GenericTransaction.sol";
library PaymentChallengeIFEInputSpent {
using PosLib for PosLib.Position;
using PaymentInFlightExitModelUtils for PaymentExitDataModel.InFlightExit;
struct Controller {
PlasmaFramework framework;
SpendingConditionRegistry spendingConditionRegistry;
uint256 safeGasStipend;
}
event InFlightExitInputBlocked(
address indexed challenger,
bytes32 indexed txHash,
uint16 inputIndex
);
/**
* @dev Data to be passed around helper functions
*/
struct ChallengeIFEData {
Controller controller;
PaymentInFlightExitRouterArgs.ChallengeInputSpentArgs args;
PaymentExitDataModel.InFlightExit ife;
}
/**
* @notice Function that builds the controller struct
* @return Controller struct of PaymentChallengeIFEInputSpent
*/
function buildController(
PlasmaFramework framework,
SpendingConditionRegistry spendingConditionRegistry,
uint256 safeGasStipend
)
public
pure
returns (Controller memory)
{
return Controller({
framework: framework,
spendingConditionRegistry: spendingConditionRegistry,
safeGasStipend: safeGasStipend
});
}
/**
* @notice Main logic implementation for 'challengeInFlightExitInputSpent'
* @dev emits InFlightExitInputBlocked event on success
* @param self The controller struct
* @param inFlightExitMap The storage of all in-flight exit data
* @param args Arguments of 'challengeInFlightExitInputSpent' function from client
*/
function run(
Controller memory self,
PaymentExitDataModel.InFlightExitMap storage inFlightExitMap,
PaymentInFlightExitRouterArgs.ChallengeInputSpentArgs memory args
)
public
{
require(args.senderData == keccak256(abi.encodePacked(msg.sender)), "Incorrect senderData");
uint160 exitId = ExitId.getInFlightExitId(args.inFlightTx);
PaymentExitDataModel.InFlightExit storage ife = inFlightExitMap.exits[exitId];
require(ife.exitStartTimestamp != 0, "In-flight exit does not exist");
require(ife.isInputPiggybacked(args.inFlightTxInputIndex), "The indexed input has not been piggybacked");
require(
keccak256(args.inFlightTx) != keccak256(args.challengingTx),
"The challenging transaction is the same as the in-flight transaction"
);
ChallengeIFEData memory data = ChallengeIFEData({
controller: self,
args: args,
ife: inFlightExitMap.exits[exitId]
});
verifySpentInputEqualsIFEInput(data);
verifyChallengingTransactionProtocolFinalized(data);
verifySpendingCondition(data);
// Remove the input from the piggyback map
ife.clearInputPiggybacked(args.inFlightTxInputIndex);
uint256 piggybackBondSize = ife.inputs[args.inFlightTxInputIndex].piggybackBondSize;
SafeEthTransfer.transferRevertOnError(msg.sender, piggybackBondSize, self.safeGasStipend);
emit InFlightExitInputBlocked(msg.sender, keccak256(args.inFlightTx), args.inFlightTxInputIndex);
}
function verifySpentInputEqualsIFEInput(ChallengeIFEData memory data) private view {
bytes32 ifeInputOutputId = data.ife.inputs[data.args.inFlightTxInputIndex].outputId;
PosLib.Position memory utxoPos = PosLib.decode(data.args.inputUtxoPos);
bytes32 challengingTxInputOutputId = data.controller.framework.isDeposit(utxoPos.blockNum)
? OutputId.computeDepositOutputId(data.args.inputTx, utxoPos.outputIndex, utxoPos.encode())
: OutputId.computeNormalOutputId(data.args.inputTx, utxoPos.outputIndex);
require(ifeInputOutputId == challengingTxInputOutputId, "Spent input is not the same as piggybacked input");
}
function verifyChallengingTransactionProtocolFinalized(ChallengeIFEData memory data)
private
view
{
bool isProtocolFinalized = MoreVpFinalization.isProtocolFinalized(
data.controller.framework,
data.args.challengingTx
);
// MoreVP protocol finalization would only return false only when tx does not exists.
// Should fail already in early stages (eg. decode)
assert(isProtocolFinalized);
}
function verifySpendingCondition(ChallengeIFEData memory data) private view {
GenericTransaction.Transaction memory challengingTx = GenericTransaction.decode(data.args.challengingTx);
GenericTransaction.Transaction memory inputTx = GenericTransaction.decode(data.args.inputTx);
PosLib.Position memory utxoPos = PosLib.decode(data.args.inputUtxoPos);
GenericTransaction.Output memory output = GenericTransaction.getOutput(inputTx, utxoPos.outputIndex);
ISpendingCondition condition = data.controller.spendingConditionRegistry.spendingConditions(
output.outputType, challengingTx.txType
);
require(address(condition) != address(0), "Spending condition contract not found");
bool isSpent = condition.verify(
data.args.inputTx,
data.args.inputUtxoPos,
data.args.challengingTx,
data.args.challengingTxInputIndex,
data.args.challengingTxWitness
);
require(isSpent, "Spending condition failed");
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../PaymentExitDataModel.sol";
import "../PaymentInFlightExitModelUtils.sol";
import "../routers/PaymentInFlightExitRouterArgs.sol";
import "../../interfaces/ISpendingCondition.sol";
import "../../registries/SpendingConditionRegistry.sol";
import "../../utils/ExitId.sol";
import "../../utils/OutputId.sol";
import "../../utils/MoreVpFinalization.sol";
import "../../../utils/PosLib.sol";
import "../../../utils/Merkle.sol";
import "../../../framework/PlasmaFramework.sol";
import "../../../transactions/PaymentTransactionModel.sol";
import "../../../transactions/GenericTransaction.sol";
library PaymentChallengeIFENotCanonical {
using PosLib for PosLib.Position;
using PaymentInFlightExitModelUtils for PaymentExitDataModel.InFlightExit;
/**
* @dev supportedTxType Allows reuse of code in different Payment Tx versions
*/
struct Controller {
PlasmaFramework framework;
SpendingConditionRegistry spendingConditionRegistry;
uint256 supportedTxType;
}
event InFlightExitChallenged(
address indexed challenger,
bytes32 indexed txHash,
uint256 challengeTxPosition
);
event InFlightExitChallengeResponded(
address indexed challenger,
bytes32 indexed txHash,
uint256 challengeTxPosition
);
/**
* @notice Function that builds the controller struct
* @return Controller struct of PaymentChallengeIFENotCanonical
*/
function buildController(
PlasmaFramework framework,
SpendingConditionRegistry spendingConditionRegistry,
uint256 supportedTxType
)
public
pure
returns (Controller memory)
{
return Controller({
framework: framework,
spendingConditionRegistry: spendingConditionRegistry,
supportedTxType: supportedTxType
});
}
/**
* @notice Main logic implementation for 'challengeInFlightExitNotCanonical'
* @dev emits InFlightExitChallenged event on success
* @param self The controller struct
* @param inFlightExitMap The storage of all in-flight exit data
* @param args Arguments of 'challengeInFlightExitNotCanonical' function from client
*/
function challenge(
Controller memory self,
PaymentExitDataModel.InFlightExitMap storage inFlightExitMap,
PaymentInFlightExitRouterArgs.ChallengeCanonicityArgs memory args
)
public
{
uint160 exitId = ExitId.getInFlightExitId(args.inFlightTx);
PaymentExitDataModel.InFlightExit storage ife = inFlightExitMap.exits[exitId];
require(args.inFlightTxInputIndex < ife.inputs.length, "Input index out of bounds");
require(ife.exitStartTimestamp != 0, "In-flight exit does not exist");
require(ife.isInFirstPhase(self.framework.minExitPeriod()),
"Canonicity challenge phase for this exit has ended");
require(
keccak256(args.inFlightTx) != keccak256(args.competingTx),
"The competitor transaction is the same as transaction in-flight"
);
PosLib.Position memory inputUtxoPos = PosLib.decode(args.inputUtxoPos);
bytes32 outputId;
if (self.framework.isDeposit(inputUtxoPos.blockNum)) {
outputId = OutputId.computeDepositOutputId(args.inputTx, inputUtxoPos.outputIndex, args.inputUtxoPos);
} else {
outputId = OutputId.computeNormalOutputId(args.inputTx, inputUtxoPos.outputIndex);
}
require(outputId == ife.inputs[args.inFlightTxInputIndex].outputId,
"Provided inputs data does not point to the same outputId from the in-flight exit");
GenericTransaction.Output memory output = GenericTransaction.getOutput(
GenericTransaction.decode(args.inputTx),
inputUtxoPos.outputIndex
);
ISpendingCondition condition = self.spendingConditionRegistry.spendingConditions(
output.outputType, self.supportedTxType
);
require(address(condition) != address(0), "Spending condition contract not found");
bool isSpentByCompetingTx = condition.verify(
args.inputTx,
args.inputUtxoPos,
args.competingTx,
args.competingTxInputIndex,
args.competingTxWitness
);
require(isSpentByCompetingTx, "Competing input spending condition is not met");
// Determine the position of the competing transaction
uint256 competitorPosition = verifyCompetingTxFinalizedInThePosition(self, args);
require(
ife.oldestCompetitorPosition == 0 || ife.oldestCompetitorPosition > competitorPosition,
"Competing transaction is not older than already known competitor"
);
ife.oldestCompetitorPosition = competitorPosition;
ife.bondOwner = msg.sender;
// Set a flag so that only the inputs are exitable, unless a response is received.
ife.isCanonical = false;
emit InFlightExitChallenged(msg.sender, keccak256(args.inFlightTx), competitorPosition);
}
/**
* @notice Main logic implementation for 'respondToNonCanonicalChallenge'
* @dev emits InFlightExitChallengeResponded event on success
* @param self The controller struct
* @param inFlightExitMap The storage of all in-flight exit data
* @param inFlightTx The in-flight tx, in RLP-encoded bytes
* @param inFlightTxPos The UTXO position of the in-flight exit. Should hardcode 0 for the outputIndex.
* @param inFlightTxInclusionProof Inclusion proof for the in-flight tx
*/
function respond(
Controller memory self,
PaymentExitDataModel.InFlightExitMap storage inFlightExitMap,
bytes memory inFlightTx,
uint256 inFlightTxPos,
bytes memory inFlightTxInclusionProof
)
public
{
uint160 exitId = ExitId.getInFlightExitId(inFlightTx);
PaymentExitDataModel.InFlightExit storage ife = inFlightExitMap.exits[exitId];
require(ife.exitStartTimestamp != 0, "In-flight exit does not exist");
require(inFlightTxPos > 0, "In-flight transaction position must not be 0");
require(
ife.oldestCompetitorPosition > inFlightTxPos,
"In-flight transaction must be older than competitors to respond to non-canonical challenge");
PosLib.Position memory txPos = PosLib.decode(inFlightTxPos);
(bytes32 root, ) = self.framework.blocks(txPos.blockNum);
require(root != bytes32(""), "Failed to get the block root hash of the tx position");
verifyPositionOfTransactionIncludedInBlock(
inFlightTx, txPos, root, inFlightTxInclusionProof
);
ife.oldestCompetitorPosition = inFlightTxPos;
ife.isCanonical = true;
ife.bondOwner = msg.sender;
emit InFlightExitChallengeResponded(msg.sender, keccak256(inFlightTx), inFlightTxPos);
}
function verifyPositionOfTransactionIncludedInBlock(
bytes memory txbytes,
PosLib.Position memory txPos,
bytes32 root,
bytes memory inclusionProof
)
private
pure
{
require(txPos.outputIndex == 0, "Output index of txPos has to be 0");
require(
Merkle.checkMembership(txbytes, txPos.txIndex, root, inclusionProof),
"Transaction is not included in block of Plasma chain"
);
}
function verifyCompetingTxFinalizedInThePosition(
Controller memory self,
PaymentInFlightExitRouterArgs.ChallengeCanonicityArgs memory args
)
private
view
returns (uint256)
{
// default to infinite low priority position
uint256 competitorPosition = ~uint256(0);
if (args.competingTxPos == 0) {
bool isProtocolFinalized = MoreVpFinalization.isProtocolFinalized(
self.framework,
args.competingTx
);
// MoreVP protocol finalization would only return false only when tx does not exists.
// Should fail already in early stages (eg. decode)
assert(isProtocolFinalized);
} else {
PosLib.Position memory competingTxPos = PosLib.decode(args.competingTxPos);
require(competingTxPos.outputIndex == 0, "OutputIndex of competingTxPos should be 0");
bool isStandardFinalized = MoreVpFinalization.isStandardFinalized(
self.framework,
args.competingTx,
competingTxPos,
args.competingTxInclusionProof
);
require(isStandardFinalized, "Competing tx is not standard finalized with the given tx position");
competitorPosition = args.competingTxPos;
}
return competitorPosition;
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../PaymentExitDataModel.sol";
import "../PaymentInFlightExitModelUtils.sol";
import "../routers/PaymentInFlightExitRouterArgs.sol";
import "../../interfaces/ISpendingCondition.sol";
import "../../registries/SpendingConditionRegistry.sol";
import "../../utils/ExitId.sol";
import "../../utils/MoreVpFinalization.sol";
import "../../../utils/Merkle.sol";
import "../../../utils/SafeEthTransfer.sol";
import "../../../utils/PosLib.sol";
import "../../../transactions/GenericTransaction.sol";
import "../../../framework/PlasmaFramework.sol";
import "../../../transactions/PaymentTransactionModel.sol";
library PaymentChallengeIFEOutputSpent {
using PosLib for PosLib.Position;
using PaymentInFlightExitModelUtils for PaymentExitDataModel.InFlightExit;
struct Controller {
PlasmaFramework framework;
SpendingConditionRegistry spendingConditionRegistry;
uint256 safeGasStipend;
}
event InFlightExitOutputBlocked(
address indexed challenger,
bytes32 indexed txHash,
uint16 outputIndex
);
/**
* @notice Main logic implementation for 'challengeInFlightExitOutputSpent'
* @dev emits InFlightExitOutputBlocked event on success
* @param controller The controller struct
* @param inFlightExitMap The storage of all in-flight exit data
* @param args Arguments of 'challengeInFlightExitOutputSpent' function from client
*/
function run(
Controller memory controller,
PaymentExitDataModel.InFlightExitMap storage inFlightExitMap,
PaymentInFlightExitRouterArgs.ChallengeOutputSpent memory args
)
public
{
require(args.senderData == keccak256(abi.encodePacked(msg.sender)), "Incorrect senderData");
uint160 exitId = ExitId.getInFlightExitId(args.inFlightTx);
PaymentExitDataModel.InFlightExit storage ife = inFlightExitMap.exits[exitId];
require(ife.exitStartTimestamp != 0, "In-flight exit does not exist");
PosLib.Position memory utxoPos = PosLib.decode(args.outputUtxoPos);
uint16 outputIndex = utxoPos.outputIndex;
require(
ife.isOutputPiggybacked(outputIndex),
"Output is not piggybacked"
);
verifyInFlightTransactionStandardFinalized(controller, args);
verifyChallengingTransactionProtocolFinalized(controller, args);
verifyChallengingTransactionSpendsOutput(controller, args);
ife.clearOutputPiggybacked(outputIndex);
uint256 piggybackBondSize = ife.outputs[outputIndex].piggybackBondSize;
SafeEthTransfer.transferRevertOnError(msg.sender, piggybackBondSize, controller.safeGasStipend);
emit InFlightExitOutputBlocked(msg.sender, keccak256(args.inFlightTx), outputIndex);
}
function verifyInFlightTransactionStandardFinalized(
Controller memory controller,
PaymentInFlightExitRouterArgs.ChallengeOutputSpent memory args
)
private
view
{
PosLib.Position memory utxoPos = PosLib.decode(args.outputUtxoPos);
bool isStandardFinalized = MoreVpFinalization.isStandardFinalized(
controller.framework,
args.inFlightTx,
utxoPos.toStrictTxPos(),
args.inFlightTxInclusionProof
);
require(isStandardFinalized, "In-flight transaction must be standard finalized (included in Plasma) to be able to spend");
}
function verifyChallengingTransactionProtocolFinalized(
Controller memory controller,
PaymentInFlightExitRouterArgs.ChallengeOutputSpent memory args
)
private
view
{
bool isProtocolFinalized = MoreVpFinalization.isProtocolFinalized(
controller.framework,
args.challengingTx
);
// MoreVP protocol finalization would only return false only when tx does not exists.
// Should fail already in early stages (eg. decode)
assert(isProtocolFinalized);
}
function verifyChallengingTransactionSpendsOutput(
Controller memory controller,
PaymentInFlightExitRouterArgs.ChallengeOutputSpent memory args
)
private
view
{
PosLib.Position memory utxoPos = PosLib.decode(args.outputUtxoPos);
GenericTransaction.Transaction memory challengingTx = GenericTransaction.decode(args.challengingTx);
GenericTransaction.Transaction memory ifeTx = GenericTransaction.decode(args.inFlightTx);
GenericTransaction.Output memory ifeTxOutput = GenericTransaction.getOutput(ifeTx, utxoPos.outputIndex);
ISpendingCondition condition = controller.spendingConditionRegistry.spendingConditions(
ifeTxOutput.outputType,
challengingTx.txType
);
require(address(condition) != address(0), "Spending condition contract not found");
bool isSpentBySpendingTx = condition.verify(
args.inFlightTx,
utxoPos.encode(),
args.challengingTx,
args.challengingTxInputIndex,
args.challengingTxWitness
);
require(isSpentBySpendingTx, "Challenging transaction does not spend the output");
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../PaymentExitDataModel.sol";
import "../routers/PaymentStandardExitRouterArgs.sol";
import "../../interfaces/ISpendingCondition.sol";
import "../../registries/SpendingConditionRegistry.sol";
import "../../utils/MoreVpFinalization.sol";
import "../../utils/OutputId.sol";
import "../../../vaults/EthVault.sol";
import "../../../vaults/Erc20Vault.sol";
import "../../../framework/PlasmaFramework.sol";
import "../../../framework/Protocol.sol";
import "../../../utils/SafeEthTransfer.sol";
import "../../../utils/PosLib.sol";
import "../../../transactions/PaymentTransactionModel.sol";
import "../../../transactions/GenericTransaction.sol";
library PaymentChallengeStandardExit {
using PosLib for PosLib.Position;
using PaymentTransactionModel for PaymentTransactionModel.Transaction;
struct Controller {
PlasmaFramework framework;
SpendingConditionRegistry spendingConditionRegistry;
uint256 safeGasStipend;
}
event ExitChallenged(
uint256 indexed utxoPos
);
/**
* @dev Data to be passed around helper functions
*/
struct ChallengeStandardExitData {
Controller controller;
PaymentStandardExitRouterArgs.ChallengeStandardExitArgs args;
PaymentExitDataModel.StandardExit exitData;
uint256 challengeTxType;
}
/**
* @notice Function that builds the controller struct
* @return Controller struct of PaymentChallengeStandardExit
*/
function buildController(
PlasmaFramework framework,
SpendingConditionRegistry spendingConditionRegistry,
uint256 safeGasStipend
)
public
pure
returns (Controller memory)
{
return Controller({
framework: framework,
spendingConditionRegistry: spendingConditionRegistry,
safeGasStipend: safeGasStipend
});
}
/**
* @notice Main logic function to challenge standard exit
* @dev emits ExitChallenged event on success
* @param self The controller struct
* @param exitMap The storage of all standard exit data
* @param args Arguments of challenge standard exit function from client
*/
function run(
Controller memory self,
PaymentExitDataModel.StandardExitMap storage exitMap,
PaymentStandardExitRouterArgs.ChallengeStandardExitArgs memory args
)
public
{
require(args.senderData == keccak256(abi.encodePacked(msg.sender)), "Incorrect senderData");
GenericTransaction.Transaction memory challengeTx = GenericTransaction.decode(args.challengeTx);
ChallengeStandardExitData memory data = ChallengeStandardExitData({
controller: self,
args: args,
exitData: exitMap.exits[args.exitId],
challengeTxType: challengeTx.txType
});
verifyChallengeExitExists(data);
verifyChallengeTxProtocolFinalized(data);
verifySpendingCondition(data);
exitMap.exits[args.exitId].exitable = false;
SafeEthTransfer.transferRevertOnError(msg.sender, data.exitData.bondSize, self.safeGasStipend);
emit ExitChallenged(data.exitData.utxoPos);
}
function verifyChallengeExitExists(ChallengeStandardExitData memory data) private pure {
require(data.exitData.exitable == true, "The exit does not exist");
}
function verifyChallengeTxProtocolFinalized(ChallengeStandardExitData memory data) private view {
bool isProtocolFinalized = MoreVpFinalization.isProtocolFinalized(data.controller.framework, data.args.challengeTx);
// MoreVP protocol finalization would only return false only when tx does not exists.
// Should fail already in early stages (eg. decode)
assert(isProtocolFinalized);
}
function verifySpendingCondition(ChallengeStandardExitData memory data) private view {
PaymentStandardExitRouterArgs.ChallengeStandardExitArgs memory args = data.args;
PosLib.Position memory utxoPos = PosLib.decode(data.exitData.utxoPos);
FungibleTokenOutputModel.Output memory output = PaymentTransactionModel
.decode(args.exitingTx)
.getOutput(utxoPos.outputIndex);
ISpendingCondition condition = data.controller.spendingConditionRegistry.spendingConditions(
output.outputType, data.challengeTxType
);
require(address(condition) != address(0), "Spending condition contract not found");
bytes32 outputId = data.controller.framework.isDeposit(utxoPos.blockNum)
? OutputId.computeDepositOutputId(args.exitingTx, utxoPos.outputIndex, utxoPos.encode())
: OutputId.computeNormalOutputId(args.exitingTx, utxoPos.outputIndex);
require(outputId == data.exitData.outputId, "Invalid exiting tx causing outputId mismatch");
bool isSpentByChallengeTx = condition.verify(
args.exitingTx,
utxoPos.encode(),
args.challengeTx,
args.inputIndex,
args.witness
);
require(isSpentByChallengeTx, "Spending condition failed");
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../PaymentExitDataModel.sol";
import "../PaymentInFlightExitModelUtils.sol";
import "../../../utils/SafeEthTransfer.sol";
import "../../../transactions/PaymentTransactionModel.sol";
library PaymentDeleteInFlightExit {
using PaymentInFlightExitModelUtils for PaymentExitDataModel.InFlightExit;
struct Controller {
uint256 minExitPeriod;
uint256 safeGasStipend;
}
event InFlightExitDeleted(
uint160 indexed exitId
);
/**
* @notice Main logic function to delete the non piggybacked in-flight exit
* @param exitId The exitId of the standard exit
*/
function run(
Controller memory self,
PaymentExitDataModel.InFlightExitMap storage exitMap,
uint160 exitId
)
public
{
PaymentExitDataModel.InFlightExit memory ife = exitMap.exits[exitId];
require(ife.exitStartTimestamp != 0, "In-flight exit does not exist");
require(!ife.isInFirstPhase(self.minExitPeriod), "Cannot delete in-flight exit still in first phase");
require(!isPiggybacked(ife), "The in-flight exit is already piggybacked");
delete exitMap.exits[exitId];
SafeEthTransfer.transferRevertOnError(ife.bondOwner, ife.bondSize, self.safeGasStipend);
emit InFlightExitDeleted(exitId);
}
function isPiggybacked(ExitModel.InFlightExit memory ife)
private
pure
returns (bool)
{
for (uint16 i = 0; i < PaymentTransactionModel.MAX_INPUT_NUM(); i++) {
if (ife.isInputPiggybacked(i)) {
return true;
}
}
for (uint16 i = 0; i < PaymentTransactionModel.MAX_OUTPUT_NUM(); i++) {
if (ife.isOutputPiggybacked(i)) {
return true;
}
}
return false;
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../PaymentExitDataModel.sol";
import "../PaymentInFlightExitModelUtils.sol";
import "../routers/PaymentInFlightExitRouterArgs.sol";
import "../../utils/ExitableTimestamp.sol";
import "../../utils/ExitId.sol";
import "../../../framework/PlasmaFramework.sol";
import "../../../framework/interfaces/IExitProcessor.sol";
import "../../../transactions/PaymentTransactionModel.sol";
import "../../../utils/PosLib.sol";
library PaymentPiggybackInFlightExit {
using PosLib for PosLib.Position;
using ExitableTimestamp for ExitableTimestamp.Calculator;
using PaymentInFlightExitModelUtils for PaymentExitDataModel.InFlightExit;
struct Controller {
PlasmaFramework framework;
ExitableTimestamp.Calculator exitableTimestampCalculator;
IExitProcessor exitProcessor;
uint256 minExitPeriod;
uint256 ethVaultId;
uint256 erc20VaultId;
}
event InFlightExitInputPiggybacked(
address indexed exitTarget,
bytes32 indexed txHash,
uint16 inputIndex
);
event InFlightExitOutputPiggybacked(
address indexed exitTarget,
bytes32 indexed txHash,
uint16 outputIndex
);
/**
* @notice Function that builds the controller struct
* @return Controller struct of PaymentPiggybackInFlightExit
*/
function buildController(
PlasmaFramework framework,
IExitProcessor exitProcessor,
uint256 ethVaultId,
uint256 erc20VaultId
)
public
view
returns (Controller memory)
{
return Controller({
framework: framework,
exitableTimestampCalculator: ExitableTimestamp.Calculator(framework.minExitPeriod()),
exitProcessor: exitProcessor,
minExitPeriod: framework.minExitPeriod(),
ethVaultId: ethVaultId,
erc20VaultId: erc20VaultId
});
}
/**
* @notice The main controller logic for 'piggybackInFlightExitOnInput'
* @dev emits InFlightExitInputPiggybacked event on success
* @param self The controller struct
* @param inFlightExitMap The storage of all in-flight exit data
* @param args Arguments of 'piggybackInFlightExitOnInput' function from client
*/
function piggybackInput(
Controller memory self,
PaymentExitDataModel.InFlightExitMap storage inFlightExitMap,
PaymentInFlightExitRouterArgs.PiggybackInFlightExitOnInputArgs memory args
)
public
{
uint160 exitId = ExitId.getInFlightExitId(args.inFlightTx);
PaymentExitDataModel.InFlightExit storage exit = inFlightExitMap.exits[exitId];
require(exit.exitStartTimestamp != 0, "No in-flight exit to piggyback on");
require(exit.isInFirstPhase(self.minExitPeriod), "Piggyback is possible only in the first phase of the exit period");
require(!exit.isInputEmpty(args.inputIndex), "Indexed input is empty");
require(!exit.isInputPiggybacked(args.inputIndex), "Indexed input already piggybacked");
PaymentExitDataModel.WithdrawData storage withdrawData = exit.inputs[args.inputIndex];
require(withdrawData.exitTarget == msg.sender, "Can be called only by the exit target");
withdrawData.piggybackBondSize = msg.value;
if (isFirstPiggybackOfTheToken(exit, withdrawData.token)) {
enqueue(self, withdrawData.token, PosLib.decode(exit.position), exitId);
}
exit.setInputPiggybacked(args.inputIndex);
emit InFlightExitInputPiggybacked(msg.sender, keccak256(args.inFlightTx), args.inputIndex);
}
/**
* @notice The main controller logic for 'piggybackInFlightExitOnOutput'
* @dev emits InFlightExitOutputPiggybacked event on success
* @param self The controller struct
* @param inFlightExitMap The storage of all in-flight exit data
* @param args Arguments of 'piggybackInFlightExitOnOutput' function from client
*/
function piggybackOutput(
Controller memory self,
PaymentExitDataModel.InFlightExitMap storage inFlightExitMap,
PaymentInFlightExitRouterArgs.PiggybackInFlightExitOnOutputArgs memory args
)
public
{
uint160 exitId = ExitId.getInFlightExitId(args.inFlightTx);
PaymentExitDataModel.InFlightExit storage exit = inFlightExitMap.exits[exitId];
require(exit.exitStartTimestamp != 0, "No in-flight exit to piggyback on");
require(exit.isInFirstPhase(self.minExitPeriod), "Piggyback is possible only in the first phase of the exit period");
require(!exit.isOutputEmpty(args.outputIndex), "Indexed output is empty");
require(!exit.isOutputPiggybacked(args.outputIndex), "Indexed output already piggybacked");
PaymentExitDataModel.WithdrawData storage withdrawData = exit.outputs[args.outputIndex];
require(withdrawData.exitTarget == msg.sender, "Can be called only by the exit target");
withdrawData.piggybackBondSize = msg.value;
if (isFirstPiggybackOfTheToken(exit, withdrawData.token)) {
enqueue(self, withdrawData.token, PosLib.decode(exit.position), exitId);
}
exit.setOutputPiggybacked(args.outputIndex);
emit InFlightExitOutputPiggybacked(msg.sender, keccak256(args.inFlightTx), args.outputIndex);
}
function enqueue(
Controller memory controller,
address token,
PosLib.Position memory utxoPos,
uint160 exitId
)
private
{
(, uint256 blockTimestamp) = controller.framework.blocks(utxoPos.blockNum);
require(blockTimestamp != 0, "There is no block for the exit position to enqueue");
uint64 exitableAt = controller.exitableTimestampCalculator.calculateTxExitableTimestamp(now, blockTimestamp);
uint256 vaultId;
if (token == address(0)) {
vaultId = controller.ethVaultId;
} else {
vaultId = controller.erc20VaultId;
}
controller.framework.enqueue(vaultId, token, exitableAt, utxoPos.toStrictTxPos(), exitId, controller.exitProcessor);
}
function isFirstPiggybackOfTheToken(ExitModel.InFlightExit memory ife, address token)
private
pure
returns (bool)
{
for (uint i = 0; i < PaymentTransactionModel.MAX_INPUT_NUM(); i++) {
if (ife.isInputPiggybacked(uint16(i)) && ife.inputs[i].token == token) {
return false;
}
}
for (uint i = 0; i < PaymentTransactionModel.MAX_OUTPUT_NUM(); i++) {
if (ife.isOutputPiggybacked(uint16(i)) && ife.outputs[i].token == token) {
return false;
}
}
return true;
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../PaymentExitDataModel.sol";
import "../PaymentInFlightExitModelUtils.sol";
import "../../../framework/PlasmaFramework.sol";
import "../../../transactions/PaymentTransactionModel.sol";
import "../../../utils/SafeEthTransfer.sol";
import "../../../vaults/EthVault.sol";
import "../../../vaults/Erc20Vault.sol";
library PaymentProcessInFlightExit {
using PaymentInFlightExitModelUtils for PaymentExitDataModel.InFlightExit;
struct Controller {
PlasmaFramework framework;
EthVault ethVault;
Erc20Vault erc20Vault;
uint256 safeGasStipend;
}
event InFlightExitOmitted(
uint160 indexed exitId,
address token
);
event InFlightExitOutputWithdrawn(
uint160 indexed exitId,
uint16 outputIndex
);
event InFlightExitInputWithdrawn(
uint160 indexed exitId,
uint16 inputIndex
);
event InFlightBondReturnFailed(
address indexed receiver,
uint256 amount
);
/**
* @notice Main logic function to process in-flight exit
* @dev emits InFlightExitOmitted event if the exit is omitted
* @dev emits InFlightBondReturnFailed event if failed to pay out bond. Would continue to process the exit.
* @dev emits InFlightExitInputWithdrawn event if the input of IFE is withdrawn successfully
* @dev emits InFlightExitOutputWithdrawn event if the output of IFE is withdrawn successfully
* @param self The controller struct
* @param exitMap The storage of all in-flight exit data
* @param exitId The exitId of the in-flight exit
* @param token The ERC20 token address of the exit; uses address(0) to represent ETH
*/
function run(
Controller memory self,
PaymentExitDataModel.InFlightExitMap storage exitMap,
uint160 exitId,
address token
)
public
{
PaymentExitDataModel.InFlightExit storage exit = exitMap.exits[exitId];
if (exit.exitStartTimestamp == 0) {
emit InFlightExitOmitted(exitId, token);
return;
}
/* To prevent a double spend, it is needed to know if an output can be exited.
* An output can not be exited if:
* - it is finalized by a standard exit
* - it is finalized by an in-flight exit as input of a non-canonical transaction
* - it is blocked from exiting, because it is an input of a canonical transaction
* that exited from one of it's outputs
* - it is finalized by an in-flight exit as an output of a canonical transaction
* - it is an output of a transaction for which at least one of its inputs is already finalized
*
* Hence, Plasma Framework stores each output with an exit id that finalized it.
* When transaction is marked as canonical but any of it's input was finalized by
* other exit, it is not allowed to exit from the transaction's outputs.
* In that case exit from an unspent input is possible.
* When all inputs of a transaction that is marked as canonical are either not finalized or finalized
* by the same exit (which means they were marked as finalized when processing the same exit for a different token),
* only exit from outputs is possible.
*
* See: https://github.com/omisego/plasma-contracts/issues/102#issuecomment-495809967 for more details
*/
if (!exit.isCanonical || isAnyInputFinalizedByOtherExit(self.framework, exit, exitId)) {
for (uint16 i = 0; i < exit.inputs.length; i++) {
PaymentExitDataModel.WithdrawData memory withdrawal = exit.inputs[i];
if (shouldWithdrawInput(self, exit, withdrawal, token, i)) {
withdrawFromVault(self, withdrawal);
emit InFlightExitInputWithdrawn(exitId, i);
}
}
flagOutputsWhenNonCanonical(self.framework, exit, token, exitId);
} else {
for (uint16 i = 0; i < exit.outputs.length; i++) {
PaymentExitDataModel.WithdrawData memory withdrawal = exit.outputs[i];
if (shouldWithdrawOutput(self, exit, withdrawal, token, i)) {
withdrawFromVault(self, withdrawal);
emit InFlightExitOutputWithdrawn(exitId, i);
}
}
flagOutputsWhenCanonical(self.framework, exit, token, exitId);
}
returnInputPiggybackBonds(self, exit, token);
returnOutputPiggybackBonds(self, exit, token);
clearPiggybackInputFlag(exit, token);
clearPiggybackOutputFlag(exit, token);
if (allPiggybacksCleared(exit)) {
bool success = SafeEthTransfer.transferReturnResult(
exit.bondOwner, exit.bondSize, self.safeGasStipend
);
// we do not want to block a queue if bond return is unsuccessful
if (!success) {
emit InFlightBondReturnFailed(exit.bondOwner, exit.bondSize);
}
delete exitMap.exits[exitId];
}
}
function isAnyInputFinalizedByOtherExit(
PlasmaFramework framework,
PaymentExitDataModel.InFlightExit memory exit,
uint160 exitId
)
private
view
returns (bool)
{
uint256 nonEmptyInputIndex;
for (uint16 i = 0; i < exit.inputs.length; i++) {
if (!exit.isInputEmpty(i)) {
nonEmptyInputIndex++;
}
}
bytes32[] memory outputIdsOfInputs = new bytes32[](nonEmptyInputIndex);
nonEmptyInputIndex = 0;
for (uint16 i = 0; i < exit.inputs.length; i++) {
if (!exit.isInputEmpty(i)) {
outputIdsOfInputs[nonEmptyInputIndex] = exit.inputs[i].outputId;
nonEmptyInputIndex++;
}
}
return framework.isAnyInputFinalizedByOtherExit(outputIdsOfInputs, exitId);
}
function shouldWithdrawInput(
Controller memory controller,
PaymentExitDataModel.InFlightExit memory exit,
PaymentExitDataModel.WithdrawData memory withdrawal,
address token,
uint16 index
)
private
view
returns (bool)
{
return withdrawal.token == token &&
exit.isInputPiggybacked(index) &&
!controller.framework.isOutputFinalized(withdrawal.outputId);
}
function shouldWithdrawOutput(
Controller memory controller,
PaymentExitDataModel.InFlightExit memory exit,
PaymentExitDataModel.WithdrawData memory withdrawal,
address token,
uint16 index
)
private
view
returns (bool)
{
return withdrawal.token == token &&
exit.isOutputPiggybacked(index) &&
!controller.framework.isOutputFinalized(withdrawal.outputId);
}
function withdrawFromVault(
Controller memory self,
PaymentExitDataModel.WithdrawData memory withdrawal
)
private
{
if (withdrawal.token == address(0)) {
self.ethVault.withdraw(withdrawal.exitTarget, withdrawal.amount);
} else {
self.erc20Vault.withdraw(withdrawal.exitTarget, withdrawal.token, withdrawal.amount);
}
}
function flagOutputsWhenNonCanonical(
PlasmaFramework framework,
PaymentExitDataModel.InFlightExit memory exit,
address token,
uint160 exitId
)
private
{
uint256 piggybackedInputNumOfTheToken;
for (uint16 i = 0; i < exit.inputs.length; i++) {
if (exit.inputs[i].token == token && exit.isInputPiggybacked(i)) {
piggybackedInputNumOfTheToken++;
}
}
bytes32[] memory outputIdsToFlag = new bytes32[](piggybackedInputNumOfTheToken);
uint indexForOutputIds = 0;
for (uint16 i = 0; i < exit.inputs.length; i++) {
if (exit.inputs[i].token == token && exit.isInputPiggybacked(i)) {
outputIdsToFlag[indexForOutputIds] = exit.inputs[i].outputId;
indexForOutputIds++;
}
}
framework.batchFlagOutputsFinalized(outputIdsToFlag, exitId);
}
function flagOutputsWhenCanonical(
PlasmaFramework framework,
PaymentExitDataModel.InFlightExit memory exit,
address token,
uint160 exitId
)
private
{
uint256 inputNumOfTheToken;
for (uint16 i = 0; i < exit.inputs.length; i++) {
if (!exit.isInputEmpty(i)) {
inputNumOfTheToken++;
}
}
uint256 piggybackedOutputNumOfTheToken;
for (uint16 i = 0; i < exit.outputs.length; i++) {
if (exit.outputs[i].token == token && exit.isOutputPiggybacked(i)) {
piggybackedOutputNumOfTheToken++;
}
}
bytes32[] memory outputIdsToFlag = new bytes32[](inputNumOfTheToken + piggybackedOutputNumOfTheToken);
uint indexForOutputIds = 0;
for (uint16 i = 0; i < exit.inputs.length; i++) {
if (!exit.isInputEmpty(i)) {
outputIdsToFlag[indexForOutputIds] = exit.inputs[i].outputId;
indexForOutputIds++;
}
}
for (uint16 i = 0; i < exit.outputs.length; i++) {
if (exit.outputs[i].token == token && exit.isOutputPiggybacked(i)) {
outputIdsToFlag[indexForOutputIds] = exit.outputs[i].outputId;
indexForOutputIds++;
}
}
framework.batchFlagOutputsFinalized(outputIdsToFlag, exitId);
}
function returnInputPiggybackBonds(
Controller memory self,
PaymentExitDataModel.InFlightExit storage exit,
address token
)
private
{
for (uint16 i = 0; i < exit.inputs.length; i++) {
PaymentExitDataModel.WithdrawData memory withdrawal = exit.inputs[i];
// If the input has been challenged, isInputPiggybacked() will return false
if (token == withdrawal.token && exit.isInputPiggybacked(i)) {
bool success = SafeEthTransfer.transferReturnResult(
withdrawal.exitTarget, withdrawal.piggybackBondSize, self.safeGasStipend
);
// we do not want to block a queue if bond return is unsuccessful
if (!success) {
emit InFlightBondReturnFailed(withdrawal.exitTarget, withdrawal.piggybackBondSize);
}
}
}
}
function returnOutputPiggybackBonds(
Controller memory self,
PaymentExitDataModel.InFlightExit storage exit,
address token
)
private
{
for (uint16 i = 0; i < exit.outputs.length; i++) {
PaymentExitDataModel.WithdrawData memory withdrawal = exit.outputs[i];
// If the output has been challenged, isOutputPiggybacked() will return false
if (token == withdrawal.token && exit.isOutputPiggybacked(i)) {
bool success = SafeEthTransfer.transferReturnResult(
withdrawal.exitTarget, withdrawal.piggybackBondSize, self.safeGasStipend
);
// we do not want to block a queue if bond return is unsuccessful
if (!success) {
emit InFlightBondReturnFailed(withdrawal.exitTarget, withdrawal.piggybackBondSize);
}
}
}
}
function clearPiggybackInputFlag(
PaymentExitDataModel.InFlightExit storage exit,
address token
)
private
{
for (uint16 i = 0; i < exit.inputs.length; i++) {
if (token == exit.inputs[i].token) {
exit.clearInputPiggybacked(i);
}
}
}
function clearPiggybackOutputFlag(
PaymentExitDataModel.InFlightExit storage exit,
address token
)
private
{
for (uint16 i = 0; i < exit.outputs.length; i++) {
if (token == exit.outputs[i].token) {
exit.clearOutputPiggybacked(i);
}
}
}
function allPiggybacksCleared(PaymentExitDataModel.InFlightExit memory exit) private pure returns (bool) {
for (uint16 i = 0; i < exit.inputs.length; i++) {
if (exit.isInputPiggybacked(i))
return false;
}
for (uint16 i = 0; i < exit.outputs.length; i++) {
if (exit.isOutputPiggybacked(i))
return false;
}
return true;
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../PaymentExitDataModel.sol";
import "../routers/PaymentStandardExitRouterArgs.sol";
import "../../../framework/PlasmaFramework.sol";
import "../../../utils/SafeEthTransfer.sol";
import "../../../vaults/EthVault.sol";
import "../../../vaults/Erc20Vault.sol";
library PaymentProcessStandardExit {
struct Controller {
PlasmaFramework framework;
EthVault ethVault;
Erc20Vault erc20Vault;
uint256 safeGasStipend;
}
event ExitOmitted(
uint160 indexed exitId
);
event ExitFinalized(
uint160 indexed exitId
);
event BondReturnFailed(
address indexed receiver,
uint256 amount
);
/**
* @notice Main logic function to process standard exit
* @dev emits ExitOmitted event if the exit is omitted
* @dev emits ExitFinalized event if the exit is processed and funds are withdrawn
* @param self The controller struct
* @param exitMap The storage of all standard exit data
* @param exitId The exitId of the standard exit
* @param token The ERC20 token address of the exit. Uses address(0) to represent ETH.
*/
function run(
Controller memory self,
PaymentExitDataModel.StandardExitMap storage exitMap,
uint160 exitId,
address token
)
public
{
PaymentExitDataModel.StandardExit memory exit = exitMap.exits[exitId];
if (!exit.exitable || self.framework.isOutputFinalized(exit.outputId)) {
emit ExitOmitted(exitId);
delete exitMap.exits[exitId];
return;
}
self.framework.flagOutputFinalized(exit.outputId, exitId);
// we do not want to block a queue if bond return is unsuccessful
bool success = SafeEthTransfer.transferReturnResult(exit.exitTarget, exit.bondSize, self.safeGasStipend);
if (!success) {
emit BondReturnFailed(exit.exitTarget, exit.bondSize);
}
if (token == address(0)) {
self.ethVault.withdraw(exit.exitTarget, exit.amount);
} else {
self.erc20Vault.withdraw(exit.exitTarget, token, exit.amount);
}
delete exitMap.exits[exitId];
emit ExitFinalized(exitId);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../PaymentExitDataModel.sol";
import "../PaymentInFlightExitModelUtils.sol";
import "../routers/PaymentInFlightExitRouterArgs.sol";
import "../../interfaces/ISpendingCondition.sol";
import "../../interfaces/IStateTransitionVerifier.sol";
import "../../registries/SpendingConditionRegistry.sol";
import "../../utils/ExitableTimestamp.sol";
import "../../utils/ExitId.sol";
import "../../utils/OutputId.sol";
import "../../utils/MoreVpFinalization.sol";
import "../../../utils/PosLib.sol";
import "../../../utils/Merkle.sol";
import "../../../framework/PlasmaFramework.sol";
import "../../../transactions/PaymentTransactionModel.sol";
import "../../../transactions/GenericTransaction.sol";
library PaymentStartInFlightExit {
using ExitableTimestamp for ExitableTimestamp.Calculator;
using PosLib for PosLib.Position;
using PaymentInFlightExitModelUtils for PaymentExitDataModel.InFlightExit;
using PaymentTransactionModel for PaymentTransactionModel.Transaction;
/**
* @dev supportedTxType enables code reuse in different Payment Tx versions
*/
struct Controller {
PlasmaFramework framework;
ExitableTimestamp.Calculator exitTimestampCalculator;
SpendingConditionRegistry spendingConditionRegistry;
IStateTransitionVerifier transitionVerifier;
uint256 supportedTxType;
}
event InFlightExitStarted(
address indexed initiator,
bytes32 indexed txHash
);
/**
* @dev data to be passed around start in-flight exit helper functions
* @param controller the Controller struct of this library
* @param exitId ID of the exit
* @param inFlightTxRaw In-flight transaction as bytes
* @param inFlightTx Decoded in-flight transaction
* @param inFlightTxHash Hash of in-flight transaction
* @param inputTxs Input transactions as bytes
* @param inputUtxosPos Postions of input utxos coded as integers
* @param inputTxsInclusionProofs Merkle proofs for input transactions
* @param inFlightTxWitnesses Witnesses for in-flight transactions
* @param outputIds Output IDs for input transactions.
*/
struct StartExitData {
Controller controller;
uint160 exitId;
bytes inFlightTxRaw;
PaymentTransactionModel.Transaction inFlightTx;
bytes32 inFlightTxHash;
bytes[] inputTxs;
PosLib.Position[] inputUtxosPos;
bytes[] inputTxsInclusionProofs;
bytes[] inFlightTxWitnesses;
bytes32[] outputIds;
}
/**
* @notice Function that builds the controller struct
* @return Controller struct of PaymentStartInFlightExit
*/
function buildController(
PlasmaFramework framework,
SpendingConditionRegistry spendingConditionRegistry,
IStateTransitionVerifier transitionVerifier,
uint256 supportedTxType
)
public
view
returns (Controller memory)
{
return Controller({
framework: framework,
exitTimestampCalculator: ExitableTimestamp.Calculator(framework.minExitPeriod()),
spendingConditionRegistry: spendingConditionRegistry,
transitionVerifier: transitionVerifier,
supportedTxType: supportedTxType
});
}
/**
* @notice Main logic function to start in-flight exit
* @dev emits InFlightExitStarted event on success
* @param self The controller struct
* @param inFlightExitMap The storage of all in-flight exit data
* @param args Arguments of start in-flight exit function from client
*/
function run(
Controller memory self,
PaymentExitDataModel.InFlightExitMap storage inFlightExitMap,
PaymentInFlightExitRouterArgs.StartExitArgs memory args
)
public
{
StartExitData memory startExitData = createStartExitData(self, args);
verifyStart(startExitData, inFlightExitMap);
startExit(startExitData, inFlightExitMap);
emit InFlightExitStarted(msg.sender, startExitData.inFlightTxHash);
}
function createStartExitData(
Controller memory controller,
PaymentInFlightExitRouterArgs.StartExitArgs memory args
)
private
view
returns (StartExitData memory)
{
StartExitData memory exitData;
exitData.controller = controller;
exitData.exitId = ExitId.getInFlightExitId(args.inFlightTx);
exitData.inFlightTxRaw = args.inFlightTx;
exitData.inFlightTx = PaymentTransactionModel.decode(args.inFlightTx);
exitData.inFlightTxHash = keccak256(args.inFlightTx);
exitData.inputTxs = args.inputTxs;
exitData.inputUtxosPos = decodeInputTxsPositions(args.inputUtxosPos);
exitData.inputTxsInclusionProofs = args.inputTxsInclusionProofs;
exitData.inFlightTxWitnesses = args.inFlightTxWitnesses;
exitData.outputIds = getOutputIds(controller, exitData.inputTxs, exitData.inputUtxosPos);
return exitData;
}
function decodeInputTxsPositions(uint256[] memory inputUtxosPos) private pure returns (PosLib.Position[] memory) {
require(inputUtxosPos.length <= PaymentTransactionModel.MAX_INPUT_NUM(), "Too many transactions provided");
PosLib.Position[] memory utxosPos = new PosLib.Position[](inputUtxosPos.length);
for (uint i = 0; i < inputUtxosPos.length; i++) {
utxosPos[i] = PosLib.decode(inputUtxosPos[i]);
}
return utxosPos;
}
function getOutputIds(Controller memory controller, bytes[] memory inputTxs, PosLib.Position[] memory utxoPos)
private
view
returns (bytes32[] memory)
{
require(inputTxs.length == utxoPos.length, "Number of input transactions does not match number of provided input utxos positions");
bytes32[] memory outputIds = new bytes32[](inputTxs.length);
for (uint i = 0; i < inputTxs.length; i++) {
bool isDepositTx = controller.framework.isDeposit(utxoPos[i].blockNum);
outputIds[i] = isDepositTx
? OutputId.computeDepositOutputId(inputTxs[i], utxoPos[i].outputIndex, utxoPos[i].encode())
: OutputId.computeNormalOutputId(inputTxs[i], utxoPos[i].outputIndex);
}
return outputIds;
}
function verifyStart(
StartExitData memory exitData,
PaymentExitDataModel.InFlightExitMap storage inFlightExitMap
)
private
view
{
verifyExitNotStarted(exitData.exitId, inFlightExitMap);
verifyInFlightTxType(exitData);
verifyNumberOfInputsMatchesNumberOfInFlightTransactionInputs(exitData);
verifyNoInputSpentMoreThanOnce(exitData.inFlightTx);
verifyInputTransactionIsStandardFinalized(exitData);
verifyInputsSpent(exitData);
verifyStateTransition(exitData);
}
function verifyExitNotStarted(
uint160 exitId,
PaymentExitDataModel.InFlightExitMap storage inFlightExitMap
)
private
view
{
PaymentExitDataModel.InFlightExit storage exit = inFlightExitMap.exits[exitId];
require(exit.exitStartTimestamp == 0, "There is an active in-flight exit from this transaction");
}
function verifyInFlightTxType(StartExitData memory exitData) private pure {
require(exitData.inFlightTx.txType == exitData.controller.supportedTxType, "Unsupported transaction type of the exit game");
}
function verifyNumberOfInputsMatchesNumberOfInFlightTransactionInputs(StartExitData memory exitData) private pure {
require(exitData.inputTxs.length != 0, "In-flight transaction must have inputs");
require(
exitData.inputTxs.length == exitData.inFlightTx.inputs.length,
"Number of input transactions does not match number of in-flight transaction inputs"
);
require(
exitData.inputTxsInclusionProofs.length == exitData.inFlightTx.inputs.length,
"Number of input transactions inclusion proofs does not match the number of in-flight transaction inputs"
);
require(
exitData.inFlightTxWitnesses.length == exitData.inFlightTx.inputs.length,
"Number of input transaction witnesses does not match the number of in-flight transaction inputs"
);
}
function verifyNoInputSpentMoreThanOnce(PaymentTransactionModel.Transaction memory inFlightTx) private pure {
if (inFlightTx.inputs.length > 1) {
for (uint i = 0; i < inFlightTx.inputs.length; i++) {
for (uint j = i + 1; j < inFlightTx.inputs.length; j++) {
require(inFlightTx.inputs[i] != inFlightTx.inputs[j], "In-flight transaction must have unique inputs");
}
}
}
}
function verifyInputTransactionIsStandardFinalized(StartExitData memory exitData) private view {
for (uint i = 0; i < exitData.inputTxs.length; i++) {
bool isStandardFinalized = MoreVpFinalization.isStandardFinalized(
exitData.controller.framework,
exitData.inputTxs[i],
exitData.inputUtxosPos[i].toStrictTxPos(),
exitData.inputTxsInclusionProofs[i]
);
require(isStandardFinalized, "Input transaction is not standard finalized");
}
}
function verifyInputsSpent(StartExitData memory exitData) private view {
for (uint16 i = 0; i < exitData.inputTxs.length; i++) {
uint16 outputIndex = exitData.inputUtxosPos[i].outputIndex;
GenericTransaction.Output memory output = GenericTransaction.getOutput(
GenericTransaction.decode(exitData.inputTxs[i]),
outputIndex
);
ISpendingCondition condition = exitData.controller.spendingConditionRegistry.spendingConditions(
output.outputType, exitData.controller.supportedTxType
);
require(address(condition) != address(0), "Spending condition contract not found");
bool isSpentByInFlightTx = condition.verify(
exitData.inputTxs[i],
exitData.inputUtxosPos[i].encode(),
exitData.inFlightTxRaw,
i,
exitData.inFlightTxWitnesses[i]
);
require(isSpentByInFlightTx, "Spending condition failed");
}
}
function verifyStateTransition(StartExitData memory exitData) private view {
uint16[] memory outputIndexForInputTxs = new uint16[](exitData.inputTxs.length);
for (uint i = 0; i < exitData.inFlightTx.inputs.length; i++) {
outputIndexForInputTxs[i] = exitData.inputUtxosPos[i].outputIndex;
}
require(
exitData.controller.transitionVerifier.isCorrectStateTransition(exitData.inFlightTxRaw, exitData.inputTxs, outputIndexForInputTxs),
"Invalid state transition"
);
}
function startExit(
StartExitData memory startExitData,
PaymentExitDataModel.InFlightExitMap storage inFlightExitMap
)
private
{
PaymentExitDataModel.InFlightExit storage ife = inFlightExitMap.exits[startExitData.exitId];
ife.isCanonical = true;
ife.bondOwner = msg.sender;
ife.bondSize = msg.value;
ife.position = getYoungestInputUtxoPosition(startExitData.inputUtxosPos);
ife.exitStartTimestamp = uint64(block.timestamp);
setInFlightExitInputs(ife, startExitData);
setInFlightExitOutputs(ife, startExitData);
}
function getYoungestInputUtxoPosition(PosLib.Position[] memory inputUtxosPos) private pure returns (uint256) {
uint256 youngest = inputUtxosPos[0].encode();
for (uint i = 1; i < inputUtxosPos.length; i++) {
uint256 encodedUtxoPos = inputUtxosPos[i].encode();
if (encodedUtxoPos > youngest) {
youngest = encodedUtxoPos;
}
}
return youngest;
}
function setInFlightExitInputs(
PaymentExitDataModel.InFlightExit storage ife,
StartExitData memory exitData
)
private
{
for (uint i = 0; i < exitData.inputTxs.length; i++) {
uint16 outputIndex = exitData.inputUtxosPos[i].outputIndex;
FungibleTokenOutputModel.Output memory output = FungibleTokenOutputModel.getOutput(
GenericTransaction.decode(exitData.inputTxs[i]),
outputIndex
);
ife.inputs[i].outputId = exitData.outputIds[i];
ife.inputs[i].exitTarget = address(uint160(output.outputGuard));
ife.inputs[i].token = output.token;
ife.inputs[i].amount = output.amount;
}
}
function setInFlightExitOutputs(
PaymentExitDataModel.InFlightExit storage ife,
StartExitData memory exitData
)
private
{
for (uint16 i = 0; i < exitData.inFlightTx.outputs.length; i++) {
// deposit transaction can't be in-flight exited
bytes32 outputId = OutputId.computeNormalOutputId(exitData.inFlightTxRaw, i);
FungibleTokenOutputModel.Output memory output = exitData.inFlightTx.getOutput(i);
ife.outputs[i].outputId = outputId;
ife.outputs[i].exitTarget = address(uint160(output.outputGuard));
ife.outputs[i].token = output.token;
ife.outputs[i].amount = output.amount;
}
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../PaymentExitDataModel.sol";
import "../routers/PaymentStandardExitRouterArgs.sol";
import "../../utils/ExitableTimestamp.sol";
import "../../utils/ExitId.sol";
import "../../utils/OutputId.sol";
import "../../utils/MoreVpFinalization.sol";
import "../../../transactions/PaymentTransactionModel.sol";
import "../../../utils/PosLib.sol";
import "../../../framework/PlasmaFramework.sol";
import "../../utils/ExitableTimestamp.sol";
library PaymentStartStandardExit {
using ExitableTimestamp for ExitableTimestamp.Calculator;
using PosLib for PosLib.Position;
using PaymentTransactionModel for PaymentTransactionModel.Transaction;
struct Controller {
IExitProcessor exitProcessor;
PlasmaFramework framework;
ExitableTimestamp.Calculator exitableTimestampCalculator;
uint256 ethVaultId;
uint256 erc20VaultId;
uint256 supportedTxType;
}
/**
* @dev Data to be passed around startStandardExit helper functions
*/
struct StartStandardExitData {
Controller controller;
PaymentStandardExitRouterArgs.StartStandardExitArgs args;
PosLib.Position utxoPos;
PaymentTransactionModel.Transaction outputTx;
FungibleTokenOutputModel.Output output;
uint160 exitId;
bool isTxDeposit;
uint256 txBlockTimeStamp;
bytes32 outputId;
}
event ExitStarted(
address indexed owner,
uint160 exitId
);
/**
* @notice Function that builds the controller struct
* @return Controller struct of PaymentStartStandardExit
*/
function buildController(
IExitProcessor exitProcessor,
PlasmaFramework framework,
uint256 ethVaultId,
uint256 erc20VaultId,
uint256 supportedTxType
)
public
view
returns (Controller memory)
{
return Controller({
exitProcessor: exitProcessor,
framework: framework,
exitableTimestampCalculator: ExitableTimestamp.Calculator(framework.minExitPeriod()),
ethVaultId: ethVaultId,
erc20VaultId: erc20VaultId,
supportedTxType: supportedTxType
});
}
/**
* @notice Main logic function to start standard exit
* @dev emits ExitStarted event on success
* @param self The controller struct
* @param exitMap The storage of all standard exit data
* @param args Arguments of start standard exit function from client
*/
function run(
Controller memory self,
PaymentExitDataModel.StandardExitMap storage exitMap,
PaymentStandardExitRouterArgs.StartStandardExitArgs memory args
)
public
{
StartStandardExitData memory data = setupStartStandardExitData(self, args);
verifyStartStandardExitData(self, data, exitMap);
saveStandardExitData(data, exitMap);
enqueueStandardExit(data);
emit ExitStarted(msg.sender, data.exitId);
}
function setupStartStandardExitData(
Controller memory controller,
PaymentStandardExitRouterArgs.StartStandardExitArgs memory args
)
private
view
returns (StartStandardExitData memory)
{
PosLib.Position memory utxoPos = PosLib.decode(args.utxoPos);
PaymentTransactionModel.Transaction memory outputTx = PaymentTransactionModel.decode(args.rlpOutputTx);
FungibleTokenOutputModel.Output memory output = outputTx.getOutput(utxoPos.outputIndex);
bool isTxDeposit = controller.framework.isDeposit(utxoPos.blockNum);
uint160 exitId = ExitId.getStandardExitId(isTxDeposit, args.rlpOutputTx, utxoPos);
(, uint256 blockTimestamp) = controller.framework.blocks(utxoPos.blockNum);
bytes32 outputId = isTxDeposit
? OutputId.computeDepositOutputId(args.rlpOutputTx, utxoPos.outputIndex, utxoPos.encode())
: OutputId.computeNormalOutputId(args.rlpOutputTx, utxoPos.outputIndex);
return StartStandardExitData({
controller: controller,
args: args,
utxoPos: utxoPos,
outputTx: outputTx,
output: output,
exitId: exitId,
isTxDeposit: isTxDeposit,
txBlockTimeStamp: blockTimestamp,
outputId: outputId
});
}
function verifyStartStandardExitData(
Controller memory self,
StartStandardExitData memory data,
PaymentExitDataModel.StandardExitMap storage exitMap
)
private
view
{
require(data.outputTx.txType == data.controller.supportedTxType, "Unsupported transaction type of the exit game");
require(data.txBlockTimeStamp != 0, "There is no block for the position");
require(PaymentTransactionModel.getOutputOwner(data.output) == msg.sender, "Only output owner can start an exit");
require(isStandardFinalized(data), "The transaction must be standard finalized");
PaymentExitDataModel.StandardExit memory exit = exitMap.exits[data.exitId];
require(exit.amount == 0, "Exit has already started");
require(self.framework.isOutputFinalized(data.outputId) == false, "Output is already spent");
}
function isStandardFinalized(StartStandardExitData memory data)
private
view
returns (bool)
{
return MoreVpFinalization.isStandardFinalized(
data.controller.framework,
data.args.rlpOutputTx,
data.utxoPos.toStrictTxPos(),
data.args.outputTxInclusionProof
);
}
function saveStandardExitData(
StartStandardExitData memory data,
PaymentExitDataModel.StandardExitMap storage exitMap
)
private
{
exitMap.exits[data.exitId] = PaymentExitDataModel.StandardExit({
exitable: true,
utxoPos: data.utxoPos.encode(),
outputId: data.outputId,
exitTarget: msg.sender,
amount: data.output.amount,
bondSize: msg.value
});
}
function enqueueStandardExit(StartStandardExitData memory data) private {
uint64 exitableAt;
ExitableTimestamp.Calculator memory exitableTimestampCalculator = data.controller.exitableTimestampCalculator;
if (data.isTxDeposit){
exitableAt = exitableTimestampCalculator.calculateDepositTxOutputExitableTimestamp(block.timestamp);
} else {
exitableAt = exitableTimestampCalculator.calculateTxExitableTimestamp(block.timestamp, data.txBlockTimeStamp);
}
uint256 vaultId;
if (data.output.token == address(0)) {
vaultId = data.controller.ethVaultId;
} else {
vaultId = data.controller.erc20VaultId;
}
data.controller.framework.enqueue(
vaultId, data.output.token, exitableAt, data.utxoPos.toStrictTxPos(),
data.exitId, data.controller.exitProcessor
);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "./PaymentInFlightExitRouterArgs.sol";
import "../PaymentExitDataModel.sol";
import "../PaymentExitGameArgs.sol";
import "../controllers/PaymentStartInFlightExit.sol";
import "../controllers/PaymentPiggybackInFlightExit.sol";
import "../controllers/PaymentChallengeIFENotCanonical.sol";
import "../controllers/PaymentChallengeIFEInputSpent.sol";
import "../controllers/PaymentChallengeIFEOutputSpent.sol";
import "../controllers/PaymentDeleteInFlightExit.sol";
import "../controllers/PaymentProcessInFlightExit.sol";
import "../../registries/SpendingConditionRegistry.sol";
import "../../interfaces/IStateTransitionVerifier.sol";
import "../../utils/BondSize.sol";
import "../../../utils/FailFastReentrancyGuard.sol";
import "../../../utils/OnlyFromAddress.sol";
import "../../../utils/OnlyWithValue.sol";
import "../../../framework/PlasmaFramework.sol";
import "../../../framework/interfaces/IExitProcessor.sol";
contract PaymentInFlightExitRouter is
IExitProcessor,
OnlyFromAddress,
OnlyWithValue,
FailFastReentrancyGuard
{
using PaymentStartInFlightExit for PaymentStartInFlightExit.Controller;
using PaymentPiggybackInFlightExit for PaymentPiggybackInFlightExit.Controller;
using PaymentChallengeIFENotCanonical for PaymentChallengeIFENotCanonical.Controller;
using PaymentChallengeIFEInputSpent for PaymentChallengeIFEInputSpent.Controller;
using PaymentChallengeIFEOutputSpent for PaymentChallengeIFEOutputSpent.Controller;
using PaymentDeleteInFlightExit for PaymentDeleteInFlightExit.Controller;
using PaymentProcessInFlightExit for PaymentProcessInFlightExit.Controller;
using BondSize for BondSize.Params;
// Initial IFE bond size = 185000 (gas cost of challenge) * 20 gwei (current fast gas price) * 10 (safety margin)
uint128 public constant INITIAL_IFE_BOND_SIZE = 37000000000000000 wei;
// Initial piggyback bond size = 140000 (gas cost of challenge) * 20 gwei (current fast gas price) * 10 (safety margin)
uint128 public constant INITIAL_PB_BOND_SIZE = 28000000000000000 wei;
// Each bond size upgrade can increase to a maximum of 200% or decrease to 50% of the current bond
uint16 public constant BOND_LOWER_BOUND_DIVISOR = 2;
uint16 public constant BOND_UPPER_BOUND_MULTIPLIER = 2;
PaymentExitDataModel.InFlightExitMap internal inFlightExitMap;
PaymentStartInFlightExit.Controller internal startInFlightExitController;
PaymentPiggybackInFlightExit.Controller internal piggybackInFlightExitController;
PaymentChallengeIFENotCanonical.Controller internal challengeCanonicityController;
PaymentChallengeIFEInputSpent.Controller internal challengeInputSpentController;
PaymentChallengeIFEOutputSpent.Controller internal challengeOutputSpentController;
PaymentDeleteInFlightExit.Controller internal deleteNonPiggybackIFEController;
PaymentProcessInFlightExit.Controller internal processInflightExitController;
BondSize.Params internal startIFEBond;
BondSize.Params internal piggybackBond;
PlasmaFramework private framework;
event IFEBondUpdated(uint128 bondSize);
event PiggybackBondUpdated(uint128 bondSize);
event InFlightExitStarted(
address indexed initiator,
bytes32 indexed txHash
);
event InFlightExitInputPiggybacked(
address indexed exitTarget,
bytes32 indexed txHash,
uint16 inputIndex
);
event InFlightExitOmitted(
uint160 indexed exitId,
address token
);
event InFlightBondReturnFailed(
address indexed receiver,
uint256 amount
);
event InFlightExitOutputWithdrawn(
uint160 indexed exitId,
uint16 outputIndex
);
event InFlightExitInputWithdrawn(
uint160 indexed exitId,
uint16 inputIndex
);
event InFlightExitOutputPiggybacked(
address indexed exitTarget,
bytes32 indexed txHash,
uint16 outputIndex
);
event InFlightExitChallenged(
address indexed challenger,
bytes32 indexed txHash,
uint256 challengeTxPosition
);
event InFlightExitChallengeResponded(
address indexed challenger,
bytes32 indexed txHash,
uint256 challengeTxPosition
);
event InFlightExitInputBlocked(
address indexed challenger,
bytes32 indexed txHash,
uint16 inputIndex
);
event InFlightExitOutputBlocked(
address indexed challenger,
bytes32 indexed txHash,
uint16 outputIndex
);
event InFlightExitDeleted(
uint160 indexed exitId
);
constructor(PaymentExitGameArgs.Args memory args)
public
{
framework = args.framework;
EthVault ethVault = EthVault(args.framework.vaults(args.ethVaultId));
require(address(ethVault) != address(0), "Invalid ETH vault");
Erc20Vault erc20Vault = Erc20Vault(args.framework.vaults(args.erc20VaultId));
require(address(erc20Vault) != address(0), "Invalid ERC20 vault");
startInFlightExitController = PaymentStartInFlightExit.buildController(
args.framework,
args.spendingConditionRegistry,
args.stateTransitionVerifier,
args.supportTxType
);
piggybackInFlightExitController = PaymentPiggybackInFlightExit.buildController(
args.framework,
this,
args.ethVaultId,
args.erc20VaultId
);
challengeCanonicityController = PaymentChallengeIFENotCanonical.buildController(
args.framework,
args.spendingConditionRegistry,
args.supportTxType
);
challengeInputSpentController = PaymentChallengeIFEInputSpent.buildController(
args.framework,
args.spendingConditionRegistry,
args.safeGasStipend
);
challengeOutputSpentController = PaymentChallengeIFEOutputSpent.Controller(
args.framework,
args.spendingConditionRegistry,
args.safeGasStipend
);
deleteNonPiggybackIFEController = PaymentDeleteInFlightExit.Controller({
minExitPeriod: args.framework.minExitPeriod(),
safeGasStipend: args.safeGasStipend
});
processInflightExitController = PaymentProcessInFlightExit.Controller({
framework: args.framework,
ethVault: ethVault,
erc20Vault: erc20Vault,
safeGasStipend: args.safeGasStipend
});
startIFEBond = BondSize.buildParams(INITIAL_IFE_BOND_SIZE, BOND_LOWER_BOUND_DIVISOR, BOND_UPPER_BOUND_MULTIPLIER);
piggybackBond = BondSize.buildParams(INITIAL_PB_BOND_SIZE, BOND_LOWER_BOUND_DIVISOR, BOND_UPPER_BOUND_MULTIPLIER);
}
/**
* @notice Getter functions to retrieve in-flight exit data of the PaymentExitGame
* @param exitIds The exit IDs of the in-flight exits
*/
function inFlightExits(uint160[] calldata exitIds) external view returns (PaymentExitDataModel.InFlightExit[] memory) {
PaymentExitDataModel.InFlightExit[] memory exits = new PaymentExitDataModel.InFlightExit[](exitIds.length);
for (uint i = 0; i < exitIds.length; i++) {
uint160 exitId = exitIds[i];
exits[i] = inFlightExitMap.exits[exitId];
}
return exits;
}
/**
* @notice Starts withdrawal from a transaction that may be in-flight
* @param args Input argument data to challenge (see also struct 'StartExitArgs')
*/
function startInFlightExit(PaymentInFlightExitRouterArgs.StartExitArgs memory args)
public
payable
nonReentrant(framework)
onlyWithValue(startIFEBondSize())
{
startInFlightExitController.run(inFlightExitMap, args);
}
/**
* @notice Piggyback on an input of an in-flight exiting tx. Processed only if the in-flight exit is non-canonical.
* @param args Input argument data to piggyback (see also struct 'PiggybackInFlightExitOnInputArgs')
*/
function piggybackInFlightExitOnInput(
PaymentInFlightExitRouterArgs.PiggybackInFlightExitOnInputArgs memory args
)
public
payable
nonReentrant(framework)
onlyWithValue(piggybackBondSize())
{
piggybackInFlightExitController.piggybackInput(inFlightExitMap, args);
}
/**
* @notice Piggyback on an output of an in-flight exiting tx. Processed only if the in-flight exit is canonical.
* @param args Input argument data to piggyback (see also struct 'PiggybackInFlightExitOnOutputArgs')
*/
function piggybackInFlightExitOnOutput(
PaymentInFlightExitRouterArgs.PiggybackInFlightExitOnOutputArgs memory args
)
public
payable
nonReentrant(framework)
onlyWithValue(piggybackBondSize())
{
piggybackInFlightExitController.piggybackOutput(inFlightExitMap, args);
}
/**
* @notice Challenges an in-flight exit to be non-canonical
* @param args Input argument data to challenge (see also struct 'ChallengeCanonicityArgs')
*/
function challengeInFlightExitNotCanonical(PaymentInFlightExitRouterArgs.ChallengeCanonicityArgs memory args)
public
nonReentrant(framework)
{
challengeCanonicityController.challenge(inFlightExitMap, args);
}
/**
* @notice Respond to a non-canonical challenge by providing its position and by proving its correctness
* @param inFlightTx The RLP-encoded in-flight transaction
* @param inFlightTxPos The position of the in-flight exiting transaction. The output index within the position is unused and should be set to 0
* @param inFlightTxInclusionProof Proof that the in-flight exiting transaction is included in a Plasma block
*/
function respondToNonCanonicalChallenge(
bytes memory inFlightTx,
uint256 inFlightTxPos,
bytes memory inFlightTxInclusionProof
)
public
nonReentrant(framework)
{
challengeCanonicityController.respond(inFlightExitMap, inFlightTx, inFlightTxPos, inFlightTxInclusionProof);
}
/**
* @notice Challenges an exit from in-flight transaction input
* @param args Argument data to challenge (see also struct 'ChallengeInputSpentArgs')
*/
function challengeInFlightExitInputSpent(PaymentInFlightExitRouterArgs.ChallengeInputSpentArgs memory args)
public
nonReentrant(framework)
{
challengeInputSpentController.run(inFlightExitMap, args);
}
/**
* @notice Challenges an exit from in-flight transaction output
* @param args Argument data to challenge (see also struct 'ChallengeOutputSpent')
*/
function challengeInFlightExitOutputSpent(PaymentInFlightExitRouterArgs.ChallengeOutputSpent memory args)
public
nonReentrant(framework)
{
challengeOutputSpentController.run(inFlightExitMap, args);
}
/**
* @notice Deletes in-flight exit if the first phase has passed and not being piggybacked
* @dev Since IFE is enqueued during piggyback, a non-piggybacked IFE means that it will never be processed.
* This means that the IFE bond will never be returned.
* see: https://github.com/omisego/plasma-contracts/issues/440
* @param exitId The exitId of the in-flight exit
*/
function deleteNonPiggybackedInFlightExit(uint160 exitId) public nonReentrant(framework) {
deleteNonPiggybackIFEController.run(inFlightExitMap, exitId);
}
/**
* @notice Process in-flight exit
* @dev This function is designed to be called in the main processExit function, thus, using internal
* @param exitId The in-flight exit ID
* @param token The token (in erc20 address or address(0) for ETH) of the exiting output
*/
function processInFlightExit(uint160 exitId, address token) internal {
processInflightExitController.run(inFlightExitMap, exitId, token);
}
/**
* @notice Retrieves the in-flight exit bond size
*/
function startIFEBondSize() public view returns (uint128) {
return startIFEBond.bondSize();
}
/**
* @notice Updates the in-flight exit bond size, taking two days to become effective.
* @param newBondSize The new bond size
*/
function updateStartIFEBondSize(uint128 newBondSize) public onlyFrom(framework.getMaintainer()) {
startIFEBond.updateBondSize(newBondSize);
emit IFEBondUpdated(newBondSize);
}
/**
* @notice Retrieves the piggyback bond size
*/
function piggybackBondSize() public view returns (uint128) {
return piggybackBond.bondSize();
}
/**
* @notice Updates the piggyback bond size, taking two days to become effective
* @param newBondSize The new bond size
*/
function updatePiggybackBondSize(uint128 newBondSize) public onlyFrom(framework.getMaintainer()) {
piggybackBond.updateBondSize(newBondSize);
emit PiggybackBondUpdated(newBondSize);
}
}pragma solidity 0.5.11;
library PaymentInFlightExitRouterArgs {
/**
* @notice Wraps arguments for startInFlightExit.
* @param inFlightTx RLP encoded in-flight transaction.
* @param inputTxs Transactions that created the inputs to the in-flight transaction. In the same order as in-flight transaction inputs.
* @param inputUtxosPos Utxos that represent in-flight transaction inputs. In the same order as input transactions.
* @param inputTxsInclusionProofs Merkle proofs that show the input-creating transactions are valid. In the same order as input transactions.
* @param inFlightTxWitnesses Witnesses for in-flight transaction. In the same order as input transactions.
*/
struct StartExitArgs {
bytes inFlightTx;
bytes[] inputTxs;
uint256[] inputUtxosPos;
bytes[] inputTxsInclusionProofs;
bytes[] inFlightTxWitnesses;
}
/**
* @notice Wraps arguments for piggybacking on in-flight transaction input exit
* @param inFlightTx RLP-encoded in-flight transaction
* @param inputIndex Index of the input to piggyback on
*/
struct PiggybackInFlightExitOnInputArgs {
bytes inFlightTx;
uint16 inputIndex;
}
/**
* @notice Wraps arguments for piggybacking on in-flight transaction output exit
* @param inFlightTx RLP-encoded in-flight transaction
* @param outputIndex Index of the output to piggyback on
*/
struct PiggybackInFlightExitOnOutputArgs {
bytes inFlightTx;
uint16 outputIndex;
}
/**
* @notice Wraps arguments for challenging non-canonical in-flight exits
* @param inputTx Transaction that created the input shared by the in-flight transaction and its competitor
* @param inputUtxoPos Position of input utxo
* @param inFlightTx RLP-encoded in-flight transaction
* @param inFlightTxInputIndex Index of the shared input in the in-flight transaction
* @param competingTx RLP-encoded competing transaction
* @param competingTxInputIndex Index of shared input in competing transaction
* @param competingTxPos (Optional) Position of competing transaction in the chain, if included. OutputIndex of the position should be 0.
* @param competingTxInclusionProof (Optional) Merkle proofs showing that the competing transaction was contained in chain
* @param competingTxWitness Witness for competing transaction
*/
struct ChallengeCanonicityArgs {
bytes inputTx;
uint256 inputUtxoPos;
bytes inFlightTx;
uint16 inFlightTxInputIndex;
bytes competingTx;
uint16 competingTxInputIndex;
uint256 competingTxPos;
bytes competingTxInclusionProof;
bytes competingTxWitness;
}
/**
* @notice Wraps arguments for challenging in-flight exit input spent
* @param inFlightTx RLP-encoded in-flight transaction
* @param inFlightTxInputIndex Index of spent input
* @param challengingTx RLP-encoded challenging transaction
* @param challengingTxInputIndex Index of spent input in a challenging transaction
* @param challengingTxWitness Witness for challenging transactions
* @param inputTx RLP-encoded input transaction
* @param inputUtxoPos UTXO position of input transaction's output
* @param senderData A keccak256 hash of the sender's address
*/
struct ChallengeInputSpentArgs {
bytes inFlightTx;
uint16 inFlightTxInputIndex;
bytes challengingTx;
uint16 challengingTxInputIndex;
bytes challengingTxWitness;
bytes inputTx;
uint256 inputUtxoPos;
bytes32 senderData;
}
/**
* @notice Wraps arguments for challenging in-flight transaction output exit
* @param inFlightTx RLP-encoded in-flight transaction
* @param inFlightTxInclusionProof Proof that an in-flight transaction is included in Plasma
* @param outputUtxoPos UTXO position of challenged output
* @param challengingTx RLP-encoded challenging transaction
* @param challengingTxInputIndex Input index of challenged output in a challenging transaction
* @param challengingTxWitness Witness for challenging transaction
* @param senderData A keccak256 hash of the sender's address
*/
struct ChallengeOutputSpent {
bytes inFlightTx;
bytes inFlightTxInclusionProof;
uint256 outputUtxoPos;
bytes challengingTx;
uint16 challengingTxInputIndex;
bytes challengingTxWitness;
bytes32 senderData;
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "./PaymentStandardExitRouterArgs.sol";
import "../PaymentExitGameArgs.sol";
import "../PaymentExitDataModel.sol";
import "../controllers/PaymentStartStandardExit.sol";
import "../controllers/PaymentProcessStandardExit.sol";
import "../controllers/PaymentChallengeStandardExit.sol";
import "../../registries/SpendingConditionRegistry.sol";
import "../../utils/BondSize.sol";
import "../../../vaults/EthVault.sol";
import "../../../vaults/Erc20Vault.sol";
import "../../../framework/PlasmaFramework.sol";
import "../../../framework/interfaces/IExitProcessor.sol";
import "../../../utils/OnlyWithValue.sol";
import "../../../utils/OnlyFromAddress.sol";
import "../../../utils/FailFastReentrancyGuard.sol";
contract PaymentStandardExitRouter is
IExitProcessor,
OnlyFromAddress,
OnlyWithValue,
FailFastReentrancyGuard
{
using PaymentStartStandardExit for PaymentStartStandardExit.Controller;
using PaymentChallengeStandardExit for PaymentChallengeStandardExit.Controller;
using PaymentProcessStandardExit for PaymentProcessStandardExit.Controller;
using BondSize for BondSize.Params;
// Initial bond size = 70000 (gas cost of challenge) * 20 gwei (current fast gas price) * 10 (safety margin)
uint128 public constant INITIAL_BOND_SIZE = 14000000000000000 wei;
// Each bond size upgrade can either at most increase to 200% or decrease to 50% of current bond
uint16 public constant BOND_LOWER_BOUND_DIVISOR = 2;
uint16 public constant BOND_UPPER_BOUND_MULTIPLIER = 2;
PaymentExitDataModel.StandardExitMap internal standardExitMap;
PaymentStartStandardExit.Controller internal startStandardExitController;
PaymentProcessStandardExit.Controller internal processStandardExitController;
PaymentChallengeStandardExit.Controller internal challengeStandardExitController;
BondSize.Params internal startStandardExitBond;
PlasmaFramework private framework;
event StandardExitBondUpdated(uint128 bondSize);
event ExitStarted(
address indexed owner,
uint160 exitId
);
event ExitChallenged(
uint256 indexed utxoPos
);
event ExitOmitted(
uint160 indexed exitId
);
event ExitFinalized(
uint160 indexed exitId
);
event BondReturnFailed(
address indexed receiver,
uint256 amount
);
constructor(PaymentExitGameArgs.Args memory args)
public
{
framework = args.framework;
EthVault ethVault = EthVault(args.framework.vaults(args.ethVaultId));
require(address(ethVault) != address(0), "Invalid ETH vault");
Erc20Vault erc20Vault = Erc20Vault(args.framework.vaults(args.erc20VaultId));
require(address(erc20Vault) != address(0), "Invalid ERC20 vault");
startStandardExitController = PaymentStartStandardExit.buildController(
this,
args.framework,
args.ethVaultId,
args.erc20VaultId,
args.supportTxType
);
challengeStandardExitController = PaymentChallengeStandardExit.buildController(
args.framework,
args.spendingConditionRegistry,
args.safeGasStipend
);
processStandardExitController = PaymentProcessStandardExit.Controller(
args.framework, ethVault, erc20Vault, args.safeGasStipend
);
startStandardExitBond = BondSize.buildParams(INITIAL_BOND_SIZE, BOND_LOWER_BOUND_DIVISOR, BOND_UPPER_BOUND_MULTIPLIER);
}
/**
* @notice Getter retrieves standard exit data of the PaymentExitGame
* @param exitIds Exit IDs of the standard exits
*/
function standardExits(uint160[] calldata exitIds) external view returns (PaymentExitDataModel.StandardExit[] memory) {
PaymentExitDataModel.StandardExit[] memory exits = new PaymentExitDataModel.StandardExit[](exitIds.length);
for (uint i = 0; i < exitIds.length; i++){
uint160 exitId = exitIds[i];
exits[i] = standardExitMap.exits[exitId];
}
return exits;
}
/**
* @notice Retrieves the standard exit bond size
*/
function startStandardExitBondSize() public view returns (uint128) {
return startStandardExitBond.bondSize();
}
/**
* @notice Updates the standard exit bond size, taking two days to become effective
* @param newBondSize The new bond size
*/
function updateStartStandardExitBondSize(uint128 newBondSize) public onlyFrom(framework.getMaintainer()) {
startStandardExitBond.updateBondSize(newBondSize);
emit StandardExitBondUpdated(newBondSize);
}
/**
* @notice Starts a standard exit of a given output, using output-age priority
*/
function startStandardExit(
PaymentStandardExitRouterArgs.StartStandardExitArgs memory args
)
public
payable
nonReentrant(framework)
onlyWithValue(startStandardExitBondSize())
{
startStandardExitController.run(standardExitMap, args);
}
/**
* @notice Challenge a standard exit by showing the exiting output was spent
*/
function challengeStandardExit(PaymentStandardExitRouterArgs.ChallengeStandardExitArgs memory args)
public
nonReentrant(framework)
{
challengeStandardExitController.run(standardExitMap, args);
}
/**
* @notice Process standard exit
* @dev This function is designed to be called in the main processExit function, using internal
* @param exitId The standard exit ID
* @param token The token (in erc20 address or address(0) for ETH) of the exiting output
*/
function processStandardExit(uint160 exitId, address token) internal {
processStandardExitController.run(standardExitMap, exitId, token);
}
}pragma solidity 0.5.11;
library PaymentStandardExitRouterArgs {
/**
* @notice Wraps arguments for startStandardExit
* @param utxoPos Position of the exiting output
* @param rlpOutputTx The RLP-encoded transaction that creates the exiting output
* @param outputTxInclusionProof A Merkle proof showing that the transaction was included
*/
struct StartStandardExitArgs {
uint256 utxoPos;
bytes rlpOutputTx;
bytes outputTxInclusionProof;
}
/**
* @notice Input args data for challengeStandardExit
* @param exitId Identifier of the standard exit to challenge
* @param exitingTx RLP-encoded transaction that creates the exiting output
* @param challengeTx RLP-encoded transaction that spends the exiting output
* @param inputIndex Input of the challenging tx, corresponding to the exiting output
* @param witness Witness data that proves the exiting output is spent
* @param senderData A keccak256 hash of the sender's address
*/
struct ChallengeStandardExitArgs {
uint160 exitId;
bytes exitingTx;
bytes challengeTx;
uint16 inputIndex;
bytes witness;
bytes32 senderData;
}
}pragma solidity 0.5.11;
import "openzeppelin-solidity/contracts/cryptography/ECDSA.sol";
import "../../interfaces/ISpendingCondition.sol";
import "../../../utils/PosLib.sol";
import "../../../transactions/PaymentTransactionModel.sol";
import "../../../transactions/eip712Libs/PaymentEip712Lib.sol";
contract PaymentOutputToPaymentTxCondition is ISpendingCondition {
using PaymentEip712Lib for PaymentEip712Lib.Constants;
using PosLib for PosLib.Position;
using PaymentTransactionModel for PaymentTransactionModel.Transaction;
uint256 internal supportInputTxType;
uint256 internal supportSpendingTxType;
PaymentEip712Lib.Constants internal eip712;
/**
* @dev This is designed to be re-useable for all versions of payment transaction, so that
* inputTxType and spendingTxType of the payment output is injected instead
*/
constructor(address framework, uint256 inputTxType, uint256 spendingTxType) public {
eip712 = PaymentEip712Lib.initConstants(framework);
supportInputTxType = inputTxType;
supportSpendingTxType = spendingTxType;
}
/**
* @notice Verifies the spending condition
* @param inputTxBytes Encoded input transaction, in bytes
* @param utxoPos Position of the utxo
* @param spendingTxBytes Spending transaction, in bytes
* @param inputIndex Input index of the spending tx that points to the output
* @param signature Signature of the output owner
*/
function verify(
bytes calldata inputTxBytes,
uint256 utxoPos,
bytes calldata spendingTxBytes,
uint16 inputIndex,
bytes calldata signature
)
external
view
returns (bool)
{
PaymentTransactionModel.Transaction memory inputTx = PaymentTransactionModel.decode(inputTxBytes);
require(inputTx.txType == supportInputTxType, "Input tx is an unsupported payment tx type");
PaymentTransactionModel.Transaction memory spendingTx = PaymentTransactionModel.decode(spendingTxBytes);
require(spendingTx.txType == supportSpendingTxType, "The spending tx is an unsupported payment tx type");
require(
spendingTx.inputs[inputIndex] == bytes32(utxoPos),
"Spending tx points to the incorrect output UTXO position"
);
PosLib.Position memory decodedUtxoPos = PosLib.decode(utxoPos);
address owner = PaymentTransactionModel.getOutputOwner(inputTx.getOutput(decodedUtxoPos.outputIndex));
address signer = ECDSA.recover(eip712.hashTx(spendingTx), signature);
require(signer != address(0), "Failed to recover the signer from the signature");
require(owner == signer, "Tx is not signed correctly");
return true;
}
}pragma solidity 0.5.11;
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "openzeppelin-solidity/contracts/utils/Address.sol";
import "../interfaces/ISpendingCondition.sol";
/**
* @title SpendingConditionRegistry
* @notice The registry contracts of the spending condition
* @dev This is designed to renounce ownership before injecting the registry contract to ExitGame contracts
* After registering all the essential condition contracts, the owner should renounce its ownership to
* ensure no further conditions are registered for an ExitGame contract.
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/ownership/Ownable.sol#L55
*/
contract SpendingConditionRegistry is Ownable {
// mapping of hash(outputType, spendingTxTpye) => ISpendingCondition
mapping(bytes32 => ISpendingCondition) internal _spendingConditions;
function spendingConditions(uint256 outputType, uint256 spendingTxType) public view returns (ISpendingCondition) {
bytes32 key = keccak256(abi.encode(outputType, spendingTxType));
return _spendingConditions[key];
}
/**
* @notice Register the spending condition contract
* @param outputType The output type of the spending condition
* @param spendingTxType Spending tx type of the spending condition
* @param condition The spending condition contract
*/
function registerSpendingCondition(uint256 outputType, uint256 spendingTxType, ISpendingCondition condition)
public
onlyOwner
{
require(outputType != 0, "Registration not possible with output type 0");
require(spendingTxType != 0, "Registration not possible with spending tx type 0");
require(Address.isContract(address(condition)), "Registration not possible with a non-contract address");
bytes32 key = keccak256(abi.encode(outputType, spendingTxType));
require(address(_spendingConditions[key]) == address(0), "The (output type, spending tx type) pair is already registered");
_spendingConditions[key] = condition;
}
}pragma solidity 0.5.11;
/**
* @notice Stores an updateable bond size
* @dev Bond design details at https://github.com/omisego/research/issues/107#issuecomment-525267486
* @dev Security depends on the min/max value, which can be updated to compare to the current bond size, plus the waiting period
* Min/max value of the next bond size prevents the possibility to set bond size too low or too high, which risks breaking the system
* Waiting period ensures that a user does not get an unexpected bond without notice.
*/
library BondSize {
uint64 constant public WAITING_PERIOD = 2 days;
/**
* @dev Struct is designed to be packed into two 32-bytes storage slots
* @param previousBondSize The bond size prior to upgrade, which should remain the same until the waiting period completes
* @param updatedBondSize The bond size to use once the waiting period completes
* @param effectiveUpdateTime A timestamp for the end of the waiting period, when the updated bond size is implemented
* @param lowerBoundDivisor The divisor that checks the lower bound for an update. Each update cannot be lower than (current bond / lowerBoundDivisor)
* @param upperBoundMultiplier The multiplier that checks the upper bound for an update. Each update cannot be larger than (current bond * upperBoundMultiplier)
*/
struct Params {
uint128 previousBondSize;
uint128 updatedBondSize;
uint128 effectiveUpdateTime;
uint16 lowerBoundDivisor;
uint16 upperBoundMultiplier;
}
function buildParams(uint128 initialBondSize, uint16 lowerBoundDivisor, uint16 upperBoundMultiplier)
internal
pure
returns (Params memory)
{
// Set the initial value far into the future
uint128 initialEffectiveUpdateTime = 2 ** 63;
return Params({
previousBondSize: initialBondSize,
updatedBondSize: 0,
effectiveUpdateTime: initialEffectiveUpdateTime,
lowerBoundDivisor: lowerBoundDivisor,
upperBoundMultiplier: upperBoundMultiplier
});
}
/**
* @notice Updates the bond size
* @dev The new bond size value updates once the two day waiting period completes
* @param newBondSize The new bond size
*/
function updateBondSize(Params storage self, uint128 newBondSize) internal {
validateBondSize(self, newBondSize);
if (self.updatedBondSize != 0 && now >= self.effectiveUpdateTime) {
self.previousBondSize = self.updatedBondSize;
}
self.updatedBondSize = newBondSize;
self.effectiveUpdateTime = uint64(now) + WAITING_PERIOD;
}
/**
* @notice Returns the current bond size
*/
function bondSize(Params memory self) internal view returns (uint128) {
if (now < self.effectiveUpdateTime) {
return self.previousBondSize;
} else {
return self.updatedBondSize;
}
}
function validateBondSize(Params memory self, uint128 newBondSize) private view {
uint128 currentBondSize = bondSize(self);
require(newBondSize > 0, "Bond size cannot be zero");
require(newBondSize >= currentBondSize / self.lowerBoundDivisor, "Bond size is too low");
require(uint256(newBondSize) <= uint256(currentBondSize) * self.upperBoundMultiplier, "Bond size is too high");
}
}pragma solidity 0.5.11;
import "../../utils/Bits.sol";
import "../../utils/PosLib.sol";
library ExitId {
using PosLib for PosLib.Position;
using Bits for uint160;
using Bits for uint256;
/**
* @notice Checks whether exitId is a standard exit ID
*/
function isStandardExit(uint160 _exitId) internal pure returns (bool) {
return _exitId.getBit(151) == 0;
}
/**
* @notice Given transaction bytes and UTXO position, returns its exit ID
* @dev Computation of a deposit ID is different to any other tx because txBytes of a deposit tx can be a non-unique value
* @notice Output index must be within range 0 - 255
* @param _isDeposit Defines whether the tx for the exitId is a deposit tx
* @param _txBytes Transaction bytes
* @param _utxoPos UTXO position of the exiting output
* @return _standardExitId Unique ID of the standard exit
* Anatomy of returned value, most significant bits first:
* 8-bits - output index
* 1-bit - in-flight flag (0 for standard exit)
* 151-bits - hash(tx) or hash(tx|utxo) for deposit
*/
function getStandardExitId(
bool _isDeposit,
bytes memory _txBytes,
PosLib.Position memory _utxoPos
)
internal
pure
returns (uint160)
{
if (_isDeposit) {
bytes32 hashData = keccak256(abi.encodePacked(_txBytes, _utxoPos.encode()));
return _computeStandardExitId(hashData, _utxoPos.outputIndex);
}
return _computeStandardExitId(keccak256(_txBytes), _utxoPos.outputIndex);
}
/**
* @notice Given transaction bytes, returns in-flight exit ID
* @param _txBytes Transaction bytes
* @return Unique in-flight exit ID
*/
function getInFlightExitId(bytes memory _txBytes) internal pure returns (uint160) {
return uint160((uint256(keccak256(_txBytes)) >> 105).setBit(151));
}
function _computeStandardExitId(bytes32 _txhash, uint16 _outputIndex)
private
pure
returns (uint160)
{
uint256 exitId = (uint256(_txhash) >> 105) | (uint256(_outputIndex) << 152);
uint160 croppedExitId = uint160(exitId);
require(uint256(croppedExitId) == exitId, "ExitId overflows");
return croppedExitId;
}
}pragma solidity 0.5.11;
import "openzeppelin-solidity/contracts/math/Math.sol";
library ExitableTimestamp {
struct Calculator {
uint256 minExitPeriod;
}
/**
* @notice Calculates the exitable timestamp for a mined transaction
* @dev This is the main function when asking for exitable timestamp in most cases.
* The only exception is to calculate the exitable timestamp for a deposit output in standard exit.
* Should use the function 'calculateDepositTxOutputExitableTimestamp' for that case.
*/
function calculateTxExitableTimestamp(
Calculator memory _calculator,
uint256 _now,
uint256 _blockTimestamp
)
internal
pure
returns (uint64)
{
return uint64(Math.max(_blockTimestamp + (_calculator.minExitPeriod * 2), _now + _calculator.minExitPeriod));
}
/**
* @notice Calculates the exitable timestamp for deposit transaction output for standard exit
* @dev This function should only be used in standard exit for calculating exitable timestamp of a deposit output.
* For in-fight exit, the priority of a input tx which is a deposit tx should still be using the another function 'calculateTxExitableTimestamp'.
* See discussion here: https://git.io/Je4N5
* Reason of deposit output has different exitable timestamp: https://git.io/JecCV
*/
function calculateDepositTxOutputExitableTimestamp(
Calculator memory _calculator,
uint256 _now
)
internal
pure
returns (uint64)
{
return uint64(_now + _calculator.minExitPeriod);
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../../framework/PlasmaFramework.sol";
import "../../framework/Protocol.sol";
import "../../utils/Merkle.sol";
import "../../utils/PosLib.sol";
import "../../transactions/GenericTransaction.sol";
/**
* @notice Library to check finalization for MoreVP protocol
* @dev This library assumes that the tx is of the GenericTransaction format
*/
library MoreVpFinalization {
using PosLib for PosLib.Position;
/**
* @notice Checks whether a transaction is "standard finalized".
* For MoreVP, it means the transaction should be included in a plasma block.
*/
function isStandardFinalized(
PlasmaFramework framework,
bytes memory txBytes,
PosLib.Position memory txPos,
bytes memory inclusionProof
)
internal
view
returns (bool)
{
require(txPos.outputIndex == 0, "Invalid transaction position");
GenericTransaction.Transaction memory genericTx = GenericTransaction.decode(txBytes);
uint8 protocol = framework.protocols(genericTx.txType);
require(protocol == Protocol.MORE_VP(), "MoreVpFinalization: not a MoreVP protocol tx");
(bytes32 root,) = framework.blocks(txPos.blockNum);
require(root != bytes32(""), "Failed to get the root hash of the block num");
return Merkle.checkMembership(
txBytes, txPos.txIndex, root, inclusionProof
);
}
/**
* @notice Checks whether a transaction is "protocol finalized"
* For MoreVP, since it allows in-flight tx, so only checks for the existence of the transaction
*/
function isProtocolFinalized(
PlasmaFramework framework,
bytes memory txBytes
)
internal
view
returns (bool)
{
if (txBytes.length == 0) {
return false;
}
GenericTransaction.Transaction memory genericTx = GenericTransaction.decode(txBytes);
uint8 protocol = framework.protocols(genericTx.txType);
require(protocol == Protocol.MORE_VP(), "MoreVpFinalization: not a MoreVP protocol tx");
return true;
}
}pragma solidity 0.5.11;
library OutputId {
/**
* @notice Computes the output ID for a deposit tx
* @dev Deposit tx bytes might not be unique because all inputs are empty
* Two deposits with the same output value would result in the same tx bytes
* As a result, we need to hash with utxoPos to ensure uniqueness
* @param _txBytes Transaction bytes
* @param _outputIndex Output index of the output
* @param _utxoPosValue (Optional) UTXO position of the deposit output
*/
function computeDepositOutputId(bytes memory _txBytes, uint256 _outputIndex, uint256 _utxoPosValue)
internal
pure
returns(bytes32)
{
return keccak256(abi.encodePacked(_txBytes, _outputIndex, _utxoPosValue));
}
/**
* @notice Computes the output ID for normal (non-deposit) tx
* @dev Since txBytes for non-deposit tx is unique, directly hash the txBytes with outputIndex
* @param _txBytes Transaction bytes
* @param _outputIndex Output index of the output
*/
function computeNormalOutputId(bytes memory _txBytes, uint256 _outputIndex)
internal
pure
returns(bytes32)
{
return keccak256(abi.encodePacked(_txBytes, _outputIndex));
}
}pragma solidity 0.5.11;
import "./models/BlockModel.sol";
import "./registries/VaultRegistry.sol";
import "../utils/OnlyFromAddress.sol";
/**
* @notice Controls the logic and functions for block submissions in PlasmaFramework
* @dev There are two types of blocks: child block and deposit block
* Each child block has an interval of 'childBlockInterval'
* The interval is preserved for deposits. Each deposit results in one deposit block.
* For instance, a child block would be in block 1000 and the next deposit would result in block 1001.
*
* Only the authority address can perform a block submission.
* Details on limitations for the authority address can be found here: https://github.com/omisego/elixir-omg#managing-the-operator-address
*/
contract BlockController is OnlyFromAddress, VaultRegistry {
address public authority;
uint256 public childBlockInterval;
uint256 public nextChildBlock;
uint256 public nextDeposit;
bool public isChildChainActivated;
mapping (uint256 => BlockModel.Block) public blocks; // block number => Block data
event BlockSubmitted(
uint256 blknum
);
event ChildChainActivated(
address authority
);
constructor(
uint256 _interval,
uint256 _minExitPeriod,
uint256 _initialImmuneVaults,
address _authority
)
public
VaultRegistry(_minExitPeriod, _initialImmuneVaults)
{
authority = _authority;
childBlockInterval = _interval;
nextChildBlock = childBlockInterval;
nextDeposit = 1;
isChildChainActivated = false;
}
/**
* @notice Activates the child chain so that child chain can start to submit child blocks to root chain
* @notice Can only be called once by the authority.
* @notice Sets isChildChainActivated to true and emits the ChildChainActivated event.
* @dev This is a preserved action for authority account to start its nonce with 1.
* Child chain rely ethereum nonce to protect re-org: https://git.io/JecDG
* see discussion: https://git.io/JenaT, https://git.io/JecDO
*/
function activateChildChain() external onlyFrom(authority) {
require(isChildChainActivated == false, "Child chain already activated");
isChildChainActivated = true;
emit ChildChainActivated(authority);
}
/**
* @notice Allows the authority to submit the Merkle root of a Plasma block
* @dev emit BlockSubmitted event
* @dev Block number jumps 'childBlockInterval' per submission
* @dev See discussion in https://github.com/omisego/plasma-contracts/issues/233
* @param _blockRoot Merkle root of the Plasma block
*/
function submitBlock(bytes32 _blockRoot) external onlyFrom(authority) {
require(isChildChainActivated == true, "Child chain has not been activated by authority address yet");
uint256 submittedBlockNumber = nextChildBlock;
blocks[submittedBlockNumber] = BlockModel.Block({
root: _blockRoot,
timestamp: block.timestamp
});
nextChildBlock += childBlockInterval;
nextDeposit = 1;
emit BlockSubmitted(submittedBlockNumber);
}
/**
* @notice Submits a block for deposit
* @dev Block number adds 1 per submission; it's possible to have at most 'childBlockInterval' deposit blocks between two child chain blocks
* @param _blockRoot Merkle root of the Plasma block
* @return The deposit block number
*/
function submitDepositBlock(bytes32 _blockRoot) public onlyFromNonQuarantinedVault returns (uint256) {
require(isChildChainActivated == true, "Child chain has not been activated by authority address yet");
require(nextDeposit < childBlockInterval, "Exceeded limit of deposits per child block interval");
uint256 blknum = nextDepositBlock();
blocks[blknum] = BlockModel.Block({
root : _blockRoot,
timestamp : block.timestamp
});
nextDeposit++;
return blknum;
}
function nextDepositBlock() public view returns (uint256) {
return nextChildBlock - childBlockInterval + nextDeposit;
}
function isDeposit(uint256 blockNum) public view returns (bool) {
require(blocks[blockNum].timestamp != 0, "Block does not exist");
return blockNum % childBlockInterval != 0;
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "./interfaces/IExitProcessor.sol";
import "./registries/ExitGameRegistry.sol";
import "./utils/PriorityQueue.sol";
import "./utils/ExitPriority.sol";
import "../utils/PosLib.sol";
/**
* @notice Controls the logic and functions for ExitGame to interact with the PlasmaFramework
* Plasma M(ore)VP relies on exit priority to secure the user from invalid transactions
* The priority queue ensures the exit is processed with the exit priority
* For details, see the Plasma MVP spec: https://ethresear.ch/t/minimal-viable-plasma/426
*/
contract ExitGameController is ExitGameRegistry {
// exit hashed (priority, vault id, token) => IExitProcessor
mapping (bytes32 => IExitProcessor) public delegations;
// hashed (vault id, token) => PriorityQueue
mapping (bytes32 => PriorityQueue) public exitsQueues;
// outputId => exitId
mapping (bytes32 => uint160) public outputsFinalizations;
bool private mutex = false;
event ExitQueueAdded(
uint256 vaultId,
address token
);
event ProcessedExitsNum(
uint256 processedNum,
uint256 vaultId,
address token
);
event ExitQueued(
uint160 indexed exitId,
uint256 priority
);
constructor(uint256 _minExitPeriod, uint256 _initialImmuneExitGames)
public
ExitGameRegistry(_minExitPeriod, _initialImmuneExitGames)
{
}
/**
* @dev Prevents reentrant calls by using a mutex.
*/
modifier nonReentrant() {
require(!mutex, "Reentrant call");
mutex = true;
_;
assert(mutex);
mutex = false;
}
/**
* @notice Activates non reentrancy mode
* Guards against reentering into publicly accessible code that modifies state related to exits
* @dev Accessible only from non quarantined exit games, uses a mutex
*/
function activateNonReentrant() external onlyFromNonQuarantinedExitGame() {
require(!mutex, "Reentrant call");
mutex = true;
}
/**
* @notice Deactivates non reentrancy mode
* @dev Accessible only from non quarantined exit games, uses a mutex
*/
function deactivateNonReentrant() external onlyFromNonQuarantinedExitGame() {
assert(mutex);
mutex = false;
}
/**
* @notice Checks if the queue for a specified token was created
* @param vaultId ID of the vault that handles the token
* @param token Address of the token
* @return bool Defines whether the queue for a token was created
*/
function hasExitQueue(uint256 vaultId, address token) public view returns (bool) {
bytes32 key = exitQueueKey(vaultId, token);
return hasExitQueue(key);
}
/**
* @notice Adds queue to the Plasma framework
* @dev The queue is created as a new contract instance
* @param vaultId ID of the vault
* @param token Address of the token
*/
function addExitQueue(uint256 vaultId, address token) external {
require(vaultId != 0, "Vault ID must not be 0");
bytes32 key = exitQueueKey(vaultId, token);
require(!hasExitQueue(key), "Exit queue exists");
exitsQueues[key] = new PriorityQueue();
emit ExitQueueAdded(vaultId, token);
}
/**
* @notice Enqueue exits from exit game contracts is a function that places the exit into the
* priority queue to enforce the priority of exit during 'processExits'
* @dev emits ExitQueued event, which can be used to back trace the priority inside the queue
* @dev Caller of this function should add "pragma experimental ABIEncoderV2;" on top of file
* @dev Priority (exitableAt, txPos, exitId) must be unique per queue. Do not enqueue when the same priority is already in the queue.
* @param vaultId Vault ID of the vault that stores exiting funds
* @param token Token for the exit
* @param exitableAt The earliest time a specified exit can be processed
* @param txPos Transaction position for the exit priority. For SE it should be the exit tx, for IFE it should be the youngest input tx position.
* @param exitId ID used by the exit processor contract to determine how to process the exit
* @param exitProcessor The exit processor contract, called during "processExits"
* @return A unique priority number computed for the exit
*/
function enqueue(
uint256 vaultId,
address token,
uint64 exitableAt,
PosLib.Position calldata txPos,
uint160 exitId,
IExitProcessor exitProcessor
)
external
onlyFromNonQuarantinedExitGame
returns (uint256)
{
bytes32 key = exitQueueKey(vaultId, token);
require(hasExitQueue(key), "The queue for the (vaultId, token) pair is not yet added to the Plasma framework");
PriorityQueue queue = exitsQueues[key];
uint256 priority = ExitPriority.computePriority(exitableAt, txPos, exitId);
queue.insert(priority);
bytes32 delegationKey = getDelegationKey(priority, vaultId, token);
require(address(delegations[delegationKey]) == address(0), "The same priority is already enqueued");
delegations[delegationKey] = exitProcessor;
emit ExitQueued(exitId, priority);
return priority;
}
/**
* @notice Processes any exits that have completed the challenge period. Exits are processed according to the exit priority.
* @dev Emits ProcessedExitsNum event
* @param vaultId Vault ID of the vault that stores exiting funds
* @param token The token type to process
* @param topExitId Unique identifier for prioritizing the first exit to process. Set to zero to skip this check.
* @param maxExitsToProcess Maximum number of exits to process
* @return Total number of processed exits
*/
function processExits(uint256 vaultId, address token, uint160 topExitId, uint256 maxExitsToProcess) external nonReentrant {
bytes32 key = exitQueueKey(vaultId, token);
require(hasExitQueue(key), "The token is not yet added to the Plasma framework");
PriorityQueue queue = exitsQueues[key];
require(queue.currentSize() > 0, "Exit queue is empty");
uint256 uniquePriority = queue.getMin();
uint160 exitId = ExitPriority.parseExitId(uniquePriority);
require(topExitId == 0 || exitId == topExitId,
"Top exit ID of the queue is different to the one specified");
bytes32 delegationKey = getDelegationKey(uniquePriority, vaultId, token);
IExitProcessor processor = delegations[delegationKey];
uint256 processedNum = 0;
while (processedNum < maxExitsToProcess && ExitPriority.parseExitableAt(uniquePriority) < block.timestamp) {
delete delegations[delegationKey];
queue.delMin();
processedNum++;
processor.processExit(exitId, vaultId, token);
if (queue.currentSize() == 0) {
break;
}
uniquePriority = queue.getMin();
delegationKey = getDelegationKey(uniquePriority, vaultId, token);
exitId = ExitPriority.parseExitId(uniquePriority);
processor = delegations[delegationKey];
}
emit ProcessedExitsNum(processedNum, vaultId, token);
}
/**
* @notice Checks whether any of the output with the given outputIds is already spent
* @param _outputIds Output IDs to check
*/
function isAnyInputFinalizedByOtherExit(bytes32[] calldata _outputIds, uint160 exitId) external view returns (bool) {
for (uint i = 0; i < _outputIds.length; i++) {
uint160 finalizedExitId = outputsFinalizations[_outputIds[i]];
if (finalizedExitId != 0 && finalizedExitId != exitId) {
return true;
}
}
return false;
}
/**
* @notice Batch flags already spent outputs (only not already spent)
* @param outputIds Output IDs to flag
*/
function batchFlagOutputsFinalized(bytes32[] calldata outputIds, uint160 exitId) external onlyFromNonQuarantinedExitGame {
for (uint i = 0; i < outputIds.length; i++) {
require(outputIds[i] != bytes32(""), "Should not flag with empty outputId");
if (outputsFinalizations[outputIds[i]] == 0) {
outputsFinalizations[outputIds[i]] = exitId;
}
}
}
/**
* @notice Flags a single output as spent if it is not flagged already
* @param outputId The output ID to flag as spent
*/
function flagOutputFinalized(bytes32 outputId, uint160 exitId) external onlyFromNonQuarantinedExitGame {
require(outputId != bytes32(""), "Should not flag with empty outputId");
if (outputsFinalizations[outputId] == 0) {
outputsFinalizations[outputId] = exitId;
}
}
/**
* @notice Checks whether output with a given outputId is finalized
* @param outputId Output ID to check
*/
function isOutputFinalized(bytes32 outputId) external view returns (bool) {
return outputsFinalizations[outputId] != 0;
}
function getNextExit(uint256 vaultId, address token) external view returns (uint256) {
bytes32 key = exitQueueKey(vaultId, token);
return exitsQueues[key].getMin();
}
function exitQueueKey(uint256 vaultId, address token) private pure returns (bytes32) {
return keccak256(abi.encodePacked(vaultId, token));
}
function hasExitQueue(bytes32 queueKey) private view returns (bool) {
return address(exitsQueues[queueKey]) != address(0);
}
function getDelegationKey(uint256 priority, uint256 vaultId, address token) private pure returns (bytes32) {
return keccak256(abi.encodePacked(priority, vaultId, token));
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "./BlockController.sol";
import "./ExitGameController.sol";
import "./registries/VaultRegistry.sol";
import "./registries/ExitGameRegistry.sol";
contract PlasmaFramework is VaultRegistry, ExitGameRegistry, ExitGameController, BlockController {
uint256 public constant CHILD_BLOCK_INTERVAL = 1000;
/**
* The minimum finalization period is the Plasma guarantee that all exits are safe provided the user takes action within the specified time period
* When the child chain is rogue, user should start their exit and challenge any invalid exit within this period
* An exit can be processed/finalized after minimum two finalization periods from its inclusion position, unless it is an exit for a deposit,
* which would use one finalization period, instead of two
*
* For the Abstract Layer Design, OmiseGO also uses some multitude of this period to update its framework
* See also ExitGameRegistry.sol, VaultRegistry.sol, and Vault.sol for more information on the update waiting time (the quarantined period)
*
* MVP: https://ethresear.ch/t/minimal-viable-plasma/426
* MoreVP: https://github.com/omisego/elixir-omg/blob/master/docs/morevp.md#timeline
* Special period for deposit: https://git.io/JecCV
*/
uint256 public minExitPeriod;
address private maintainer;
string public version;
constructor(
uint256 _minExitPeriod,
uint256 _initialImmuneVaults,
uint256 _initialImmuneExitGames,
address _authority,
address _maintainer
)
public
BlockController(CHILD_BLOCK_INTERVAL, _minExitPeriod, _initialImmuneVaults, _authority)
ExitGameController(_minExitPeriod, _initialImmuneExitGames)
{
minExitPeriod = _minExitPeriod;
maintainer = _maintainer;
}
function getMaintainer() public view returns (address) {
return maintainer;
}
/**
* @notice Gets the semantic version of the current deployed contracts
*/
function getVersion() external view returns (string memory) {
return version;
}
/**
* @notice Sets the semantic version of the current deployed contracts
* @param _version is semver string
*/
function setVersion(string memory _version) public onlyFrom(getMaintainer()) {
version = _version;
}
}pragma solidity 0.5.11;
/**
* @notice Protocols for the PlasmaFramework
*/
library Protocol {
uint8 constant internal MVP_VALUE = 1;
uint8 constant internal MORE_VP_VALUE = 2;
// solhint-disable-next-line func-name-mixedcase
function MVP() internal pure returns (uint8) {
return MVP_VALUE;
}
// solhint-disable-next-line func-name-mixedcase
function MORE_VP() internal pure returns (uint8) {
return MORE_VP_VALUE;
}
function isValidProtocol(uint8 protocol) internal pure returns (bool) {
return protocol == MVP_VALUE || protocol == MORE_VP_VALUE;
}
}pragma solidity 0.5.11;
/**
* @dev An interface that allows custom logic to process exits for different requirements.
* This interface is used to dispatch to each custom processor when 'processExits' is called on PlasmaFramework.
*/
interface IExitProcessor {
/**
* @dev Function interface for processing exits.
* @param exitId Unique ID for exit per tx type
* @param vaultId ID of the vault that funds the exit
* @param token Address of the token contract
*/
function processExit(uint160 exitId, uint256 vaultId, address token) external;
}pragma solidity 0.5.11;
library BlockModel {
/**
* @notice Block data structure that is stored in the contract
* @param root The Merkle root block hash of the Plasma blocks
* @param timestamp The timestamp, in seconds, when the block is saved
*/
struct Block {
bytes32 root;
uint256 timestamp;
}
}pragma solidity 0.5.11;
import "openzeppelin-solidity/contracts/utils/Address.sol";
import "../Protocol.sol";
import "../utils/Quarantine.sol";
import "../../utils/OnlyFromAddress.sol";
contract ExitGameRegistry is OnlyFromAddress {
using Quarantine for Quarantine.Data;
mapping(uint256 => address) private _exitGames; // txType => exit game contract address
mapping(address => uint256) private _exitGameToTxType; // exit game contract address => tx type
mapping(uint256 => uint8) private _protocols; // tx type => protocol (MVP/MORE_VP)
Quarantine.Data private _exitGameQuarantine;
event ExitGameRegistered(
uint256 txType,
address exitGameAddress,
uint8 protocol
);
/**
* @dev It takes at least 3 * minExitPeriod before each new exit game contract is able to start protecting existing transactions
* see: https://github.com/omisego/plasma-contracts/issues/172
* https://github.com/omisego/plasma-contracts/issues/197
*/
constructor (uint256 _minExitPeriod, uint256 _initialImmuneExitGames)
public
{
_exitGameQuarantine.quarantinePeriod = 4 * _minExitPeriod;
_exitGameQuarantine.immunitiesRemaining = _initialImmuneExitGames;
}
/**
* @notice A modifier to verify that the call is from a non-quarantined exit game
*/
modifier onlyFromNonQuarantinedExitGame() {
require(_exitGameToTxType[msg.sender] != 0, "The call is not from a registered exit game contract");
require(!_exitGameQuarantine.isQuarantined(msg.sender), "ExitGame is quarantined");
_;
}
/**
* @notice interface to get the 'maintainer' address.
* @dev see discussion here: https://git.io/Je8is
*/
function getMaintainer() public view returns (address);
/**
* @notice Checks whether the contract is safe to use and is not under quarantine
* @dev Exposes information about exit games quarantine
* @param _contract Address of the exit game contract
* @return boolean Whether the contract is safe to use and is not under quarantine
*/
function isExitGameSafeToUse(address _contract) public view returns (bool) {
return _exitGameToTxType[_contract] != 0 && !_exitGameQuarantine.isQuarantined(_contract);
}
/**
* @notice Registers an exit game within the PlasmaFramework. Only the maintainer can call the function.
* @dev Emits ExitGameRegistered event to notify clients
* @param _txType The tx type where the exit game wants to register
* @param _contract Address of the exit game contract
* @param _protocol The transaction protocol, either 1 for MVP or 2 for MoreVP
*/
function registerExitGame(uint256 _txType, address _contract, uint8 _protocol) public onlyFrom(getMaintainer()) {
require(_txType != 0, "Should not register with tx type 0");
require(Address.isContract(_contract), "Should not register with a non-contract address");
require(_exitGames[_txType] == address(0), "The tx type is already registered");
require(_exitGameToTxType[_contract] == 0, "The exit game contract is already registered");
require(Protocol.isValidProtocol(_protocol), "Invalid protocol value");
_exitGames[_txType] = _contract;
_exitGameToTxType[_contract] = _txType;
_protocols[_txType] = _protocol;
_exitGameQuarantine.quarantine(_contract);
emit ExitGameRegistered(_txType, _contract, _protocol);
}
/**
* @notice Public getter for retrieving protocol with tx type
*/
function protocols(uint256 _txType) public view returns (uint8) {
return _protocols[_txType];
}
/**
* @notice Public getter for retrieving exit game address with tx type
*/
function exitGames(uint256 _txType) public view returns (address) {
return _exitGames[_txType];
}
/**
* @notice Public getter for retrieving tx type with exit game address
*/
function exitGameToTxType(address _exitGame) public view returns (uint256) {
return _exitGameToTxType[_exitGame];
}
}pragma solidity 0.5.11;
import "openzeppelin-solidity/contracts/utils/Address.sol";
import "../utils/Quarantine.sol";
import "../../utils/OnlyFromAddress.sol";
contract VaultRegistry is OnlyFromAddress {
using Quarantine for Quarantine.Data;
mapping(uint256 => address) private _vaults; // vault id => vault address
mapping(address => uint256) private _vaultToId; // vault address => vault id
Quarantine.Data private _vaultQuarantine;
event VaultRegistered(
uint256 vaultId,
address vaultAddress
);
/**
* @dev It takes at least 2 minExitPeriod for each new vault contract to start.
* This is to protect deposit transactions already in mempool,
* and also make sure user only needs to SE within first week when invalid vault is registered.
* see: https://github.com/omisego/plasma-contracts/issues/412
* https://github.com/omisego/plasma-contracts/issues/173
*/
constructor(uint256 _minExitPeriod, uint256 _initialImmuneVaults)
public
{
_vaultQuarantine.quarantinePeriod = 2 * _minExitPeriod;
_vaultQuarantine.immunitiesRemaining = _initialImmuneVaults;
}
/**
* @notice interface to get the 'maintainer' address.
* @dev see discussion here: https://git.io/Je8is
*/
function getMaintainer() public view returns (address);
/**
* @notice A modifier to check that the call is from a non-quarantined vault
*/
modifier onlyFromNonQuarantinedVault() {
require(_vaultToId[msg.sender] > 0, "The call is not from a registered vault");
require(!_vaultQuarantine.isQuarantined(msg.sender), "Vault is quarantined");
_;
}
/**
* @notice Register a vault within the PlasmaFramework. Only a maintainer can make the call.
* @dev emits VaultRegistered event to notify clients
* @param _vaultId The ID for the vault contract to register
* @param _vaultAddress Address of the vault contract
*/
function registerVault(uint256 _vaultId, address _vaultAddress) public onlyFrom(getMaintainer()) {
require(_vaultId != 0, "Should not register with vault ID 0");
require(Address.isContract(_vaultAddress), "Should not register with a non-contract address");
require(_vaults[_vaultId] == address(0), "The vault ID is already registered");
require(_vaultToId[_vaultAddress] == 0, "The vault contract is already registered");
_vaults[_vaultId] = _vaultAddress;
_vaultToId[_vaultAddress] = _vaultId;
_vaultQuarantine.quarantine(_vaultAddress);
emit VaultRegistered(_vaultId, _vaultAddress);
}
/**
* @notice Public getter for retrieving vault address with vault ID
*/
function vaults(uint256 _vaultId) public view returns (address) {
return _vaults[_vaultId];
}
/**
* @notice Public getter for retrieving vault ID with vault address
*/
function vaultToId(address _vaultAddress) public view returns (uint256) {
return _vaultToId[_vaultAddress];
}
}pragma solidity 0.5.11;
import "../../utils/PosLib.sol";
library ExitPriority {
using PosLib for PosLib.Position;
/**
* @dev Returns an exit priority for a given UTXO position and a unique ID.
* The priority for Plasma M(ore)VP protocol is a combination of 'exitableAt' and 'txPos'.
* Since 'exitableAt' only provides granularity of block, add 'txPos' to provide priority for a transaction.
* @notice Detailed explanation on field lengths can be found at https://github.com/omisego/plasma-contracts/pull/303#discussion_r328850572
* @param exitId Unique exit identifier
* @return An exit priority
* Anatomy of returned value, most significant bits first
* 42 bits - timestamp in seconds (exitable_at); we can represent dates until year 141431
* 54 bits - blocknum * 10^5 + txindex; 54 bits represent all transactions for 85 years. Be aware that child chain block number jumps with the interval of CHILD_BLOCK_INTERVAL, which would be 1000 in production.
* 160 bits - exit id
*/
function computePriority(uint64 exitableAt, PosLib.Position memory txPos, uint160 exitId)
internal
pure
returns (uint256)
{
return (uint256(exitableAt) << 214) | (txPos.getTxPositionForExitPriority() << 160) | uint256(exitId);
}
function parseExitableAt(uint256 priority) internal pure returns (uint64) {
return uint64(priority >> 214);
}
function parseExitId(uint256 priority) internal pure returns (uint160) {
// Exit ID uses only 160 least significant bits
return uint160(priority);
}
}pragma solidity 0.5.11;
import "../../utils/OnlyFromAddress.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
/**
* @title PriorityQueue
* @dev Min-heap priority queue implementation
*/
contract PriorityQueue is OnlyFromAddress {
using SafeMath for uint256;
struct Queue {
uint256[] heapList;
uint256 currentSize;
}
Queue public queue;
address public framework;
constructor() public {
queue.heapList = [0];
queue.currentSize = 0;
// it is expected that this should be called by PlasmaFramework
// and only PlasmaFramework contract can add things to the queue
framework = msg.sender;
}
/**
* @notice Gets num of elements in the queue
*/
function currentSize() external view returns (uint256) {
return queue.currentSize;
}
/**
* @notice Gets all elements in the queue
*/
function heapList() external view returns (uint256[] memory) {
return queue.heapList;
}
/**
* @notice Inserts an element into the queue by the framework
* @dev Does not perform deduplication
*/
function insert(uint256 _element) external onlyFrom(framework) {
queue.heapList.push(_element);
queue.currentSize = queue.currentSize.add(1);
percUp(queue, queue.currentSize);
}
/**
* @notice Deletes the smallest element from the queue by the framework
* @dev Fails when queue is empty
* @return The smallest element in the priority queue
*/
function delMin() external onlyFrom(framework) returns (uint256) {
require(queue.currentSize > 0, "Queue is empty");
uint256 retVal = queue.heapList[1];
queue.heapList[1] = queue.heapList[queue.currentSize];
delete queue.heapList[queue.currentSize];
queue.currentSize = queue.currentSize.sub(1);
percDown(queue, 1);
queue.heapList.length = queue.heapList.length.sub(1);
return retVal;
}
/**
* @notice Returns the smallest element from the queue
* @dev Fails when queue is empty
* @return The smallest element in the priority queue
*/
function getMin() external view returns (uint256) {
require(queue.currentSize > 0, "Queue is empty");
return queue.heapList[1];
}
function percUp(Queue storage self, uint256 pointer) private {
uint256 i = pointer;
uint256 j = i;
uint256 newVal = self.heapList[i];
while (newVal < self.heapList[i.div(2)]) {
self.heapList[i] = self.heapList[i.div(2)];
i = i.div(2);
}
if (i != j) {
self.heapList[i] = newVal;
}
}
function percDown(Queue storage self, uint256 pointer) private {
uint256 i = pointer;
uint256 j = i;
uint256 newVal = self.heapList[i];
uint256 mc = minChild(self, i);
while (mc <= self.currentSize && newVal > self.heapList[mc]) {
self.heapList[i] = self.heapList[mc];
i = mc;
mc = minChild(self, i);
}
if (i != j) {
self.heapList[i] = newVal;
}
}
function minChild(Queue storage self, uint256 i) private view returns (uint256) {
if (i.mul(2).add(1) > self.currentSize) {
return i.mul(2);
} else {
if (self.heapList[i.mul(2)] < self.heapList[i.mul(2).add(1)]) {
return i.mul(2);
} else {
return i.mul(2).add(1);
}
}
}
}pragma solidity 0.5.11;
/**
* @notice Provides a way to quarantine (disable) contracts for a specified period of time
* @dev The immunitiesRemaining member allows deployment to the platform with some
* pre-verified contracts that don't get quarantined
*/
library Quarantine {
struct Data {
mapping(address => uint256) store;
uint256 quarantinePeriod;
uint256 immunitiesRemaining;
}
/**
* @notice Checks whether a contract is quarantined
*/
function isQuarantined(Data storage _self, address _contractAddress) internal view returns (bool) {
return block.timestamp < _self.store[_contractAddress];
}
/**
* @notice Places a contract into quarantine
* @param _contractAddress The address of the contract
*/
function quarantine(Data storage _self, address _contractAddress) internal {
require(_contractAddress != address(0), "An empty address cannot be quarantined");
require(_self.store[_contractAddress] == 0, "The contract is already quarantined");
if (_self.immunitiesRemaining == 0) {
_self.store[_contractAddress] = block.timestamp + _self.quarantinePeriod;
} else {
_self.immunitiesRemaining--;
}
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "./GenericTransaction.sol";
import "../utils/RLPReader.sol";
/**
* @notice Data structure and its decode function for ouputs of fungible token transactions
*/
library FungibleTokenOutputModel {
using RLPReader for RLPReader.RLPItem;
struct Output {
uint256 outputType;
bytes20 outputGuard;
address token;
uint256 amount;
}
/**
* @notice Given a GenericTransaction.Output, decodes the `data` field.
* The data field is an RLP list that must satisfy the following conditions:
* - It must have 3 elements: [`outputGuard`, `token`, `amount`]
* - `outputGuard` is a 20 byte long array
* - `token` is a 20 byte long array
* - `amount` must be an integer value with no leading zeros. It may not be zero.
* @param genericOutput A GenericTransaction.Output
* @return A fully decoded FungibleTokenOutputModel.Output struct
*/
function decodeOutput(GenericTransaction.Output memory genericOutput)
internal
pure
returns (Output memory)
{
RLPReader.RLPItem[] memory dataList = genericOutput.data.toList();
require(dataList.length == 3, "Output data must have 3 items");
Output memory outputData = Output({
outputType: genericOutput.outputType,
outputGuard: bytes20(dataList[0].toAddress()),
token: dataList[1].toAddress(),
amount: dataList[2].toUint()
});
require(outputData.amount != 0, "Output amount must not be 0");
require(outputData.outputGuard != bytes20(0), "Output outputGuard must not be 0");
return outputData;
}
/**
* @dev Decodes and returns the output at a specific index in the transaction
*/
function getOutput(GenericTransaction.Transaction memory transaction, uint16 outputIndex)
internal
pure
returns
(Output memory)
{
require(outputIndex < transaction.outputs.length, "Output index out of bounds");
return decodeOutput(transaction.outputs[outputIndex]);
}
}pragma solidity 0.5.11;
import "../utils/RLPReader.sol";
/**
* @title GenericTransaction
* @notice GenericTransaction is a generic transaction format that makes few assumptions about the
* content of the transaction. A transaction must satisy the following requirements:
* - It must be a list of 5 items: [txType, inputs, outputs, txData, metaData]
* - `txType` must be a uint not equal to zero
* - inputs must be a list of RLP items.
* - outputs must be a list of `Output`s
* - an `Output` is a list of 2 items: [outputType, data]
* - `Output.outputType` must be a uint not equal to zero
* - `Output.data` is an RLP item. It can be a list.
* - no assumptions are made about `txData`. Note that `txData` can be a list.
* - `metaData` must be 32 bytes long.
*/
library GenericTransaction {
uint8 constant private TX_NUM_ITEMS = 5;
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
struct Transaction {
uint256 txType;
RLPReader.RLPItem[] inputs;
Output[] outputs;
RLPReader.RLPItem txData;
bytes32 metaData;
}
struct Output {
uint256 outputType;
RLPReader.RLPItem data;
}
/**
* @dev Decodes an RLP encoded transaction into the generic format.
*/
function decode(bytes memory transaction) internal pure returns (Transaction memory) {
RLPReader.RLPItem[] memory rlpTx = transaction.toRlpItem().toList();
require(rlpTx.length == TX_NUM_ITEMS, "Invalid encoding of transaction");
uint256 txType = rlpTx[0].toUint();
require(txType > 0, "Transaction type must not be 0");
RLPReader.RLPItem[] memory outputList = rlpTx[2].toList();
Output[] memory outputs = new Output[](outputList.length);
for (uint i = 0; i < outputList.length; i++) {
outputs[i] = decodeOutput(outputList[i]);
}
bytes32 metaData = rlpTx[4].toBytes32();
return Transaction({
txType: txType,
inputs: rlpTx[1].toList(),
outputs: outputs,
txData: rlpTx[3],
metaData: metaData
});
}
/**
* @dev Returns the output at a specific index in the transaction
*/
function getOutput(Transaction memory transaction, uint16 outputIndex)
internal
pure
returns (Output memory)
{
require(outputIndex < transaction.outputs.length, "Output index out of bounds");
return transaction.outputs[outputIndex];
}
/**
* @dev Decodes an RLPItem to an output
*/
function decodeOutput(RLPReader.RLPItem memory encodedOutput)
internal
pure
returns (Output memory)
{
RLPReader.RLPItem[] memory rlpList = encodedOutput.toList();
require(rlpList.length == 2, "Output must have 2 items");
Output memory output = Output({
outputType: rlpList[0].toUint(),
data: rlpList[1]
});
require(output.outputType != 0, "Output type must not be 0");
return output;
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "./FungibleTokenOutputModel.sol";
import "../utils/RLPReader.sol";
/**
* @notice Data structure and its decode function for Payment transaction
*/
library PaymentTransactionModel {
using RLPReader for bytes;
using RLPReader for RLPReader.RLPItem;
uint8 constant private _MAX_INPUT_NUM = 4;
uint8 constant private _MAX_OUTPUT_NUM = 4;
uint8 constant private ENCODED_LENGTH = 4;
// solhint-disable-next-line func-name-mixedcase
function MAX_INPUT_NUM() internal pure returns (uint8) {
return _MAX_INPUT_NUM;
}
// solhint-disable-next-line func-name-mixedcase
function MAX_OUTPUT_NUM() internal pure returns (uint8) {
return _MAX_OUTPUT_NUM;
}
struct Transaction {
uint256 txType;
bytes32[] inputs;
FungibleTokenOutputModel.Output[] outputs;
uint256 txData;
bytes32 metaData;
}
/**
* @notice Decodes a encoded byte array into a PaymentTransaction
* The following rules about the rlp-encoded transaction are enforced:
* - `txType` must be an integer value with no leading zeros
* - `inputs` is an list of 0 to 4 elements
* - Each `input` is a 32 byte long array
* - An `input` may not be all zeros
* - `outputs` is an list of 0 to 4 elements
* - Each `output` is a list of 2 elements: [`outputType`, `data`]
* - `output.outputType` must be an integer value with no leading zeros
* - See FungibleTokenOutputModel for deatils on `output.data` encoding.
* - An `output` may not be null; A null output is one whose amount is zero
* @param _tx An RLP-encoded transaction
* @return A decoded PaymentTransaction struct
*/
function decode(bytes memory _tx) internal pure returns (PaymentTransactionModel.Transaction memory) {
return fromGeneric(GenericTransaction.decode(_tx));
}
/**
* @notice Converts a GenericTransaction to a PaymentTransaction
* @param genericTx A GenericTransaction.Transaction struct
* @return A PaymentTransaction.Transaction struct
*/
function fromGeneric(GenericTransaction.Transaction memory genericTx)
internal
pure
returns (PaymentTransactionModel.Transaction memory)
{
require(genericTx.inputs.length <= _MAX_INPUT_NUM, "Transaction inputs num exceeds limit");
require(genericTx.outputs.length != 0, "Transaction cannot have 0 outputs");
require(genericTx.outputs.length <= _MAX_OUTPUT_NUM, "Transaction outputs num exceeds limit");
bytes32[] memory inputs = new bytes32[](genericTx.inputs.length);
for (uint i = 0; i < genericTx.inputs.length; i++) {
bytes32 input = genericTx.inputs[i].toBytes32();
require(uint256(input) != 0, "Null input not allowed");
inputs[i] = input;
}
FungibleTokenOutputModel.Output[] memory outputs = new FungibleTokenOutputModel.Output[](genericTx.outputs.length);
for (uint i = 0; i < genericTx.outputs.length; i++) {
outputs[i] = FungibleTokenOutputModel.decodeOutput(genericTx.outputs[i]);
}
// txData is unused, it must be 0
require(genericTx.txData.toUint() == 0, "txData must be 0");
return Transaction({
txType: genericTx.txType,
inputs: inputs,
outputs: outputs,
txData: 0,
metaData: genericTx.metaData
});
}
/**
* @notice Retrieve the 'owner' from the output, assuming the
* 'outputGuard' field directly holds the owner's address
*/
function getOutputOwner(FungibleTokenOutputModel.Output memory output) internal pure returns (address payable) {
return address(uint160(output.outputGuard));
}
/**
* @notice Gets output at provided index
*
*/
function getOutput(Transaction memory transaction, uint16 outputIndex) internal pure returns (FungibleTokenOutputModel.Output memory) {
require(outputIndex < transaction.outputs.length, "Output index out of bounds");
return transaction.outputs[outputIndex];
}
}pragma solidity 0.5.11;
pragma experimental ABIEncoderV2;
import "../PaymentTransactionModel.sol";
import "../../utils/PosLib.sol";
/**
* @title PaymentEip712Lib
* @notice Utilities for hashing structural data for PaymentTransaction (see EIP-712)
*
* @dev EIP712: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md
* We rely on the contract address to protect against replay attacks instead of using chain ID
* For more information, see https://github.com/omisego/plasma-contracts/issues/98#issuecomment-490792098
*/
library PaymentEip712Lib {
using PosLib for PosLib.Position;
bytes2 constant internal EIP191_PREFIX = "\x19\x01";
bytes32 constant internal EIP712_DOMAIN_HASH = keccak256(
"EIP712Domain(string name,string version,address verifyingContract,bytes32 salt)"
);
bytes32 constant internal TX_TYPE_HASH = keccak256(
"Transaction(uint256 txType,Input input0,Input input1,Input input2,Input input3,Output output0,Output output1,Output output2,Output output3,uint256 txData,bytes32 metadata)Input(uint256 blknum,uint256 txindex,uint256 oindex)Output(uint256 outputType,bytes20 outputGuard,address currency,uint256 amount)"
);
bytes32 constant internal INPUT_TYPE_HASH = keccak256("Input(uint256 blknum,uint256 txindex,uint256 oindex)");
bytes32 constant internal OUTPUT_TYPE_HASH = keccak256("Output(uint256 outputType,bytes20 outputGuard,address currency,uint256 amount)");
bytes32 constant internal SALT = 0xfad5c7f626d80f9256ef01929f3beb96e058b8b4b0e3fe52d84f054c0e2a7a83;
bytes32 constant internal EMPTY_INPUT_HASH = keccak256(abi.encode(INPUT_TYPE_HASH, 0, 0, 0));
bytes32 constant internal EMPTY_OUTPUT_HASH = keccak256(abi.encode(OUTPUT_TYPE_HASH, 0, bytes20(0x0), address(0x0), 0));
struct Constants {
// solhint-disable-next-line var-name-mixedcase
bytes32 DOMAIN_SEPARATOR;
}
function initConstants(address _verifyingContract) internal pure returns (Constants memory) {
// solhint-disable-next-line var-name-mixedcase
bytes32 DOMAIN_SEPARATOR = keccak256(abi.encode(
EIP712_DOMAIN_HASH,
keccak256("OMG Network"),
keccak256("1"),
address(_verifyingContract),
SALT
));
return Constants({
DOMAIN_SEPARATOR: DOMAIN_SEPARATOR
});
}
// The 'encode(domainSeparator, message)' of the EIP712 specification
// See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#specification
function hashTx(Constants memory _eip712, PaymentTransactionModel.Transaction memory _tx)
internal
pure
returns (bytes32)
{
return keccak256(abi.encodePacked(
EIP191_PREFIX,
_eip712.DOMAIN_SEPARATOR,
_hashTx(_tx)
));
}
// The 'hashStruct(message)' function of transaction
// See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-hashstruct
function _hashTx(PaymentTransactionModel.Transaction memory _tx)
private
pure
returns (bytes32)
{
// Pad empty value to input array
bytes32[] memory inputs = new bytes32[](PaymentTransactionModel.MAX_INPUT_NUM());
for (uint i = 0; i < _tx.inputs.length; i++) {
inputs[i] = _tx.inputs[i];
}
// Pad empty value to output array
FungibleTokenOutputModel.Output[] memory outputs = new FungibleTokenOutputModel.Output[](PaymentTransactionModel.MAX_OUTPUT_NUM());
for (uint i = 0; i < _tx.outputs.length; i++) {
outputs[i] = _tx.outputs[i];
}
return keccak256(abi.encode(
TX_TYPE_HASH,
_tx.txType,
_hashInput(inputs[0]),
_hashInput(inputs[1]),
_hashInput(inputs[2]),
_hashInput(inputs[3]),
_hashOutput(outputs[0]),
_hashOutput(outputs[1]),
_hashOutput(outputs[2]),
_hashOutput(outputs[3]),
_tx.txData,
_tx.metaData
));
}
function _hashInput(bytes32 _input) private pure returns (bytes32) {
uint256 inputUtxoValue = uint256(_input);
if (inputUtxoValue == 0) {
return EMPTY_INPUT_HASH;
}
PosLib.Position memory utxo = PosLib.decode(inputUtxoValue);
return keccak256(abi.encode(
INPUT_TYPE_HASH,
utxo.blockNum,
utxo.txIndex,
uint256(utxo.outputIndex)
));
}
function _hashOutput(FungibleTokenOutputModel.Output memory _output)
private
pure
returns (bytes32)
{
if (_output.amount == 0) {
return EMPTY_OUTPUT_HASH;
}
return keccak256(abi.encode(
OUTPUT_TYPE_HASH,
_output.outputType,
_output.outputGuard,
_output.token,
_output.amount
));
}
}pragma solidity 0.5.11;
/**
* @title Bits
* @dev Operations on individual bits of a word
*/
library Bits {
/*
* Storage
*/
uint constant internal ONE = uint(1);
/*
* Internal functions
*/
/**
* @dev Sets the bit at the given '_index' in '_self' to '1'
* @param _self Uint to modify
* @param _index Index of the bit to set
* @return The modified value
*/
function setBit(uint _self, uint8 _index)
internal
pure
returns (uint)
{
return _self | ONE << _index;
}
/**
* @dev Sets the bit at the given '_index' in '_self' to '0'
* @param _self Uint to modify
* @param _index Index of the bit to set
* @return The modified value
*/
function clearBit(uint _self, uint8 _index)
internal
pure
returns (uint)
{
return _self & ~(ONE << _index);
}
/**
* @dev Returns the bit at the given '_index' in '_self'
* @param _self Uint to check
* @param _index Index of the bit to retrieve
* @return The value of the bit at '_index'
*/
function getBit(uint _self, uint8 _index)
internal
pure
returns (uint8)
{
return uint8(_self >> _index & 1);
}
/**
* @dev Checks if the bit at the given '_index' in '_self' is '1'
* @param _self Uint to check
* @param _index Index of the bit to check
* @return True, if the bit is '0'; otherwise, False
*/
function bitSet(uint _self, uint8 _index)
internal
pure
returns (bool)
{
return getBit(_self, _index) == 1;
}
}pragma solidity 0.5.11;
import "../framework/ExitGameController.sol";
/**
* @notice Reentrancy guard that fails immediately when a reentrace occurs
* Works on multi-contracts level by activating and deactivating a reentrancy guard kept in plasma framework's state
*/
contract FailFastReentrancyGuard {
/**
* @dev Prevents reentrant calls by using a mutex.
*/
modifier nonReentrant(ExitGameController exitGameController) {
exitGameController.activateNonReentrant();
_;
exitGameController.deactivateNonReentrant();
}
}pragma solidity 0.5.11;
/**
* @title Merkle
* @dev Library for working with Merkle trees
*/
library Merkle {
byte private constant LEAF_SALT = 0x00;
byte private constant NODE_SALT = 0x01;
/**
* @notice Checks that a leaf hash is contained in a root hash
* @param leaf Leaf hash to verify
* @param index Position of the leaf hash in the Merkle tree
* @param rootHash Root of the Merkle tree
* @param proof A Merkle proof demonstrating membership of the leaf hash
* @return True, if the leaf hash is in the Merkle tree; otherwise, False
*/
function checkMembership(bytes memory leaf, uint256 index, bytes32 rootHash, bytes memory proof)
internal
pure
returns (bool)
{
require(proof.length != 0, "Merkle proof must not be empty");
require(proof.length % 32 == 0, "Length of Merkle proof must be a multiple of 32");
// see https://github.com/omisego/plasma-contracts/issues/546
require(index < 2**(proof.length/32), "Index does not match the length of the proof");
bytes32 proofElement;
bytes32 computedHash = keccak256(abi.encodePacked(LEAF_SALT, leaf));
uint256 j = index;
// Note: We're skipping the first 32 bytes of `proof`, which holds the size of the dynamically sized `bytes`
for (uint256 i = 32; i <= proof.length; i += 32) {
// solhint-disable-next-line no-inline-assembly
assembly {
proofElement := mload(add(proof, i))
}
if (j % 2 == 0) {
computedHash = keccak256(abi.encodePacked(NODE_SALT, computedHash, proofElement));
} else {
computedHash = keccak256(abi.encodePacked(NODE_SALT, proofElement, computedHash));
}
j = j / 2;
}
return computedHash == rootHash;
}
}pragma solidity 0.5.11;
contract OnlyFromAddress {
modifier onlyFrom(address caller) {
require(msg.sender == caller, "Caller address is unauthorized");
_;
}
}pragma solidity 0.5.11;
contract OnlyWithValue {
modifier onlyWithValue(uint256 _value) {
require(msg.value == _value, "Input value must match msg.value");
_;
}
}pragma solidity 0.5.11;
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
/**
* @dev UTXO position = (blknum * BLOCK_OFFSET + txIndex * TX_OFFSET + outputIndex).
* TX position = (blknum * BLOCK_OFFSET + txIndex * TX_OFFSET)
*/
library PosLib {
struct Position {
uint64 blockNum;
uint16 txIndex;
uint16 outputIndex;
}
uint256 constant internal BLOCK_OFFSET = 1000000000;
uint256 constant internal TX_OFFSET = 10000;
uint256 constant internal MAX_OUTPUT_INDEX = TX_OFFSET - 1;
// since we are using merkle tree of depth 16, max tx index size is 2^16 - 1
uint256 constant internal MAX_TX_INDEX = 2 ** 16 - 1;
// in ExitPriority, only 54 bits are reserved for both blockNum and txIndex
uint256 constant internal MAX_BLOCK_NUM = ((2 ** 54 - 1) - MAX_TX_INDEX) / (BLOCK_OFFSET / TX_OFFSET);
/**
* @notice Returns transaction position which is an utxo position of zero index output
* @param pos UTXO position of the output
* @return Position of a transaction
*/
function toStrictTxPos(Position memory pos)
internal
pure
returns (Position memory)
{
return Position(pos.blockNum, pos.txIndex, 0);
}
/**
* @notice Used for calculating exit priority
* @param pos UTXO position for the output
* @return Identifier of the transaction
*/
function getTxPositionForExitPriority(Position memory pos)
internal
pure
returns (uint256)
{
return encode(pos) / TX_OFFSET;
}
/**
* @notice Encodes a position
* @param pos Position
* @return Position encoded as an integer
*/
function encode(Position memory pos) internal pure returns (uint256) {
require(pos.outputIndex <= MAX_OUTPUT_INDEX, "Invalid output index");
require(pos.blockNum <= MAX_BLOCK_NUM, "Invalid block number");
return pos.blockNum * BLOCK_OFFSET + pos.txIndex * TX_OFFSET + pos.outputIndex;
}
/**
* @notice Decodes a position from an integer value
* @param pos Encoded position
* @return Position
*/
function decode(uint256 pos) internal pure returns (Position memory) {
uint256 blockNum = pos / BLOCK_OFFSET;
uint256 txIndex = (pos % BLOCK_OFFSET) / TX_OFFSET;
uint16 outputIndex = uint16(pos % TX_OFFSET);
require(blockNum <= MAX_BLOCK_NUM, "blockNum exceeds max size allowed in PlasmaFramework");
require(txIndex <= MAX_TX_INDEX, "txIndex exceeds the size of uint16");
return Position(uint64(blockNum), uint16(txIndex), outputIndex);
}
}/** * @author Hamdi Allam [email protected] * @notice RLP decoding library forked from https://github.com/hamdiallam/Solidity-RLP * @dev Some changes that were made to the library are: * - Added more test cases from https://github.com/ethereum/tests/tree/master/RLPTests * - Created more custom invalid test cases * - Added more checks to ensure the decoder reads within bounds of the input length * - Moved utility functions necessary to run some of the tests to the RLPMock.sol */ pragma solidity 0.5.11; library RLPReader { uint8 constant internal STRING_SHORT_START = 0x80; uint8 constant internal STRING_LONG_START = 0xb8; uint8 constant internal LIST_SHORT_START = 0xc0; uint8 constant internal LIST_LONG_START = 0xf8; uint8 constant internal MAX_SHORT_LEN = 55; uint8 constant internal WORD_SIZE = 32; struct RLPItem { uint256 len; uint256 memPtr; } /** * @notice Convert a dynamic bytes array into an RLPItem * @param item RLP encoded bytes * @return An RLPItem */ function toRlpItem(bytes memory item) internal pure returns (RLPItem memory) { uint256 memPtr; // solhint-disable-next-line no-inline-assembly assembly { memPtr := add(item, 0x20) } return RLPItem(item.length, memPtr); } /** * @notice Convert a dynamic bytes array into a list of RLPItems * @param item RLP encoded list in bytes * @return A list of RLPItems */ function toList(RLPItem memory item) internal pure returns (RLPItem[] memory) { require(isList(item), "Item is not a list"); (uint256 listLength, uint256 offset) = decodeLengthAndOffset(item.memPtr); require(listLength == item.len, "Decoded RLP length for list is invalid"); uint256 items = countEncodedItems(item); RLPItem[] memory result = new RLPItem[](items); uint256 dataMemPtr = item.memPtr + offset; uint256 dataLen; for (uint256 i = 0; i < items; i++) { (dataLen, ) = decodeLengthAndOffset(dataMemPtr); result[i] = RLPItem(dataLen, dataMemPtr); dataMemPtr = dataMemPtr + dataLen; } return result; } /** * @notice Check whether the RLPItem is either a list * @param item RLP encoded list in bytes * @return A boolean whether the RLPItem is a list */ function isList(RLPItem memory item) internal pure returns (bool) { if (item.len == 0) return false; uint8 byte0; uint256 memPtr = item.memPtr; // solhint-disable-next-line no-inline-assembly assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < LIST_SHORT_START) return false; return true; } /** * @notice Create an address from a RLPItem * @dev Function is not a standard RLP decoding function and it used to decode to the Solidity native address type * @param item RLPItem */ function toAddress(RLPItem memory item) internal pure returns (address) { require(item.len == 21, "Item length must be 21"); require(!isList(item), "Item must not be a list"); (uint256 itemLen, uint256 offset) = decodeLengthAndOffset(item.memPtr); require(itemLen == 21, "Decoded item length must be 21"); uint256 dataMemPtr = item.memPtr + offset; uint256 result; // solhint-disable-next-line no-inline-assembly assembly { result := mload(dataMemPtr) // right shift by 12 to make bytes20 result := div(result, exp(256, 12)) } return address(result); } /** * @notice Create a uint256 from a RLPItem. Leading zeros are invalid. * @dev Function is not a standard RLP decoding function and it used to decode to the Solidity native uint256 type * @param item RLPItem */ function toUint(RLPItem memory item) internal pure returns (uint256) { require(item.len > 0 && item.len <= 33, "Item length must be between 1 and 33 bytes"); require(!isList(item), "Item must not be a list"); (uint256 itemLen, uint256 offset) = decodeLengthAndOffset(item.memPtr); require(itemLen == item.len, "Decoded item length must be equal to the input data length"); uint256 dataLen = itemLen - offset; uint result; uint dataByte0; uint dataMemPtr = item.memPtr + offset; // solhint-disable-next-line no-inline-assembly assembly { result := mload(dataMemPtr) dataByte0 := byte(0, result) // shift to the correct location if necessary if lt(dataLen, WORD_SIZE) { result := div(result, exp(256, sub(WORD_SIZE, dataLen))) } } // Special case: scalar 0 should be encoded as 0x80 and _not_ as 0x00 require(!(dataByte0 == 0 && offset == 0), "Scalar 0 should be encoded as 0x80"); // Disallow leading zeros require(!(dataByte0 == 0 && dataLen > 1), "Leading zeros are invalid"); return result; } /** * @notice Create a bytes32 from a RLPItem * @dev Function is not a standard RLP decoding function and it used to decode to the Solidity native bytes32 type * @param item RLPItem */ function toBytes32(RLPItem memory item) internal pure returns (bytes32) { // 1 byte for the length prefix require(item.len == 33, "Item length must be 33"); require(!isList(item), "Item must not be a list"); (uint256 itemLen, uint256 offset) = decodeLengthAndOffset(item.memPtr); require(itemLen == 33, "Decoded item length must be 33"); uint256 dataMemPtr = item.memPtr + offset; bytes32 result; // solhint-disable-next-line no-inline-assembly assembly { result := mload(dataMemPtr) } return result; } /** * @notice Counts the number of payload items inside an RLP encoded list * @param item RLPItem * @return The number of items in a inside an RLP encoded list */ function countEncodedItems(RLPItem memory item) private pure returns (uint256) { uint256 count = 0; (, uint256 offset) = decodeLengthAndOffset(item.memPtr); uint256 currPtr = item.memPtr + offset; uint256 endPtr = item.memPtr + item.len; while (currPtr < endPtr) { (uint256 currLen, ) = decodeLengthAndOffset(currPtr); currPtr = currPtr + currLen; require(currPtr <= endPtr, "Invalid decoded length of RLP item found during counting items in a list"); count++; } return count; } /** * @notice Decodes the RLPItem's length and offset. * @dev This function is dangerous. Ensure that the returned length is within bounds that memPtr points to. * @param memPtr Pointer to the dynamic bytes array in memory * @return The length of the RLPItem (including the length field) and the offset of the payload */ function decodeLengthAndOffset(uint256 memPtr) internal pure returns (uint256, uint256) { uint256 decodedLength; uint256 offset; uint256 byte0; // solhint-disable-next-line no-inline-assembly assembly { byte0 := byte(0, mload(memPtr)) } if (byte0 < STRING_SHORT_START) { // Item is a single byte decodedLength = 1; offset = 0; } else if (STRING_SHORT_START <= byte0 && byte0 < STRING_LONG_START) { // The range of the first byte is between 0x80 and 0xb7 therefore it is a short string // decodedLength is between 1 and 56 bytes decodedLength = (byte0 - STRING_SHORT_START) + 1; if (decodedLength == 2){ uint256 byte1; // solhint-disable-next-line no-inline-assembly assembly { byte1 := byte(0, mload(add(memPtr, 1))) } // A single byte below 0x80 must be encoded as itself. require(byte1 >= STRING_SHORT_START, "Invalid short string encoding"); } offset = 1; } else if (STRING_LONG_START <= byte0 && byte0 < LIST_SHORT_START) { // The range of the first byte is between 0xb8 and 0xbf therefore it is a long string // lengthLen is between 1 and 8 bytes // dataLen is greater than 55 bytes uint256 dataLen; uint256 byte1; uint256 lengthLen; // solhint-disable-next-line no-inline-assembly assembly { lengthLen := sub(byte0, 0xb7) // The length of the length of the payload is encoded in the first byte. memPtr := add(memPtr, 1) // skip over the first byte // right shift to the correct position dataLen := div(mload(memPtr), exp(256, sub(WORD_SIZE, lengthLen))) decodedLength := add(dataLen, add(lengthLen, 1)) byte1 := byte(0, mload(memPtr)) } // Check that the length has no leading zeros require(byte1 != 0, "Invalid leading zeros in length of the length for a long string"); // Check that the value of length > MAX_SHORT_LEN require(dataLen > MAX_SHORT_LEN, "Invalid length for a long string"); // Calculate the offset offset = lengthLen + 1; } else if (LIST_SHORT_START <= byte0 && byte0 < LIST_LONG_START) { // The range of the first byte is between 0xc0 and 0xf7 therefore it is a short list // decodedLength is between 1 and 56 bytes decodedLength = (byte0 - LIST_SHORT_START) + 1; offset = 1; } else { // The range of the first byte is between 0xf8 and 0xff therefore it is a long list // lengthLen is between 1 and 8 bytes // dataLen is greater than 55 bytes uint256 dataLen; uint256 byte1; uint256 lengthLen; // solhint-disable-next-line no-inline-assembly assembly { lengthLen := sub(byte0, 0xf7) // The length of the length of the payload is encoded in the first byte. memPtr := add(memPtr, 1) // skip over the first byte // right shift to the correct position dataLen := div(mload(memPtr), exp(256, sub(WORD_SIZE, lengthLen))) decodedLength := add(dataLen, add(lengthLen, 1)) byte1 := byte(0, mload(memPtr)) } // Check that the length has no leading zeros require(byte1 != 0, "Invalid leading zeros in length of the length for a long list"); // Check that the value of length > MAX_SHORT_LEN require(dataLen > MAX_SHORT_LEN, "Invalid length for a long list"); // Calculate the offset offset = lengthLen + 1; } return (decodedLength, offset); } }
pragma solidity 0.5.11;
/**
* @notice Utility library to safely transfer ETH
* @dev transfer is no longer the recommended way to do ETH transfer.
* see issue: https://github.com/omisego/plasma-contracts/issues/312
*
* This library limits the amount of gas used for external calls with value to protect against potential DOS/griefing attacks that try to use up all the gas.
* see issue: https://github.com/omisego/plasma-contracts/issues/385
*/
library SafeEthTransfer {
/**
* @notice Try to transfer eth without using more gas than `gasStipend`.
* Reverts if it fails to transfer the ETH.
* @param receiver the address to receive ETH
* @param amount the amount of ETH (in wei) to transfer
* @param gasStipend the maximum amount of gas to be used for the call
*/
function transferRevertOnError(address payable receiver, uint256 amount, uint256 gasStipend)
internal
{
bool success = transferReturnResult(receiver, amount, gasStipend);
require(success, "SafeEthTransfer: failed to transfer ETH");
}
/**
* @notice Transfer ETH without using more gas than the `gasStipend`.
* Returns whether the transfer call is successful or not.
* @dev EVM will revert with "out of gas" error if there is not enough gas left for the call
* @param receiver the address to receive ETH
* @param amount the amount of ETH (in wei) to transfer
* @param gasStipend the maximum amount of gas to be used during the transfer call
* @return a flag showing the call is successful or not
*/
function transferReturnResult(address payable receiver, uint256 amount, uint256 gasStipend)
internal
returns (bool)
{
(bool success, ) = receiver.call.gas(gasStipend).value(amount)("");
return success;
}
}pragma solidity 0.5.11;
import "./Vault.sol";
import "./verifiers/IErc20DepositVerifier.sol";
import "../framework/PlasmaFramework.sol";
import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
import "openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol";
contract Erc20Vault is Vault {
using SafeERC20 for IERC20;
event Erc20Withdrawn(
address indexed receiver,
address indexed token,
uint256 amount
);
event DepositCreated(
address indexed depositor,
uint256 indexed blknum,
address indexed token,
uint256 amount
);
constructor(PlasmaFramework _framework) public Vault(_framework) {}
/**
* @notice Deposits approved amount of ERC20 token(s) into the contract
* Once the deposit is recognized, the owner (depositor) can transact on the OmiseGO Network
* The approve function of the ERC20 token contract must be called before calling this function
* for at least the amount that is deposited into the contract
* @param depositTx RLP-encoded transaction to act as the deposit
*/
function deposit(bytes calldata depositTx) external {
address depositVerifier = super.getEffectiveDepositVerifier();
require(depositVerifier != address(0), "Deposit verifier has not been set");
(address depositor, address token, uint256 amount) = IErc20DepositVerifier(depositVerifier)
.verify(depositTx, msg.sender, address(this));
IERC20(token).safeTransferFrom(depositor, address(this), amount);
uint256 blknum = super.submitDepositBlock(depositTx);
emit DepositCreated(msg.sender, blknum, token, amount);
}
/**
* @notice Withdraw ERC20 tokens that have successfully exited from the OmiseGO Network
* @param receiver Address of the recipient
* @param token Address of ERC20 token contract
* @param amount Amount to transfer
*/
function withdraw(address payable receiver, address token, uint256 amount) external onlyFromNonQuarantinedExitGame {
IERC20(token).safeTransfer(receiver, amount);
emit Erc20Withdrawn(receiver, token, amount);
}
}pragma solidity 0.5.11;
import "./Vault.sol";
import "./verifiers/IEthDepositVerifier.sol";
import "../framework/PlasmaFramework.sol";
import "../utils/SafeEthTransfer.sol";
contract EthVault is Vault {
event EthWithdrawn(
address indexed receiver,
uint256 amount
);
event WithdrawFailed(
address indexed receiver,
uint256 amount
);
event DepositCreated(
address indexed depositor,
uint256 indexed blknum,
address indexed token,
uint256 amount
);
uint256 public safeGasStipend;
constructor(PlasmaFramework _framework, uint256 _safeGasStipend) public Vault(_framework) {
safeGasStipend = _safeGasStipend;
}
/**
* @notice Allows a user to deposit ETH into the contract
* Once the deposit is recognized, the owner may transact on the OmiseGO Network
* @param _depositTx RLP-encoded transaction to act as the deposit
*/
function deposit(bytes calldata _depositTx) external payable {
address depositVerifier = super.getEffectiveDepositVerifier();
require(depositVerifier != address(0), "Deposit verifier has not been set");
IEthDepositVerifier(depositVerifier).verify(_depositTx, msg.value, msg.sender);
uint256 blknum = super.submitDepositBlock(_depositTx);
emit DepositCreated(msg.sender, blknum, address(0), msg.value);
}
/**
* @notice Withdraw ETH that has successfully exited from the OmiseGO Network
* @dev We do not want to block exit queue if a transfer is unsuccessful, so we don't revert on transfer error.
* However, if there is not enough gas left for the safeGasStipend, then the EVM _will_ revert with an 'out of gas' error.
* If this happens, the user should retry with higher gas.
* @param receiver Address of the recipient
* @param amount The amount of ETH to transfer
*/
function withdraw(address payable receiver, uint256 amount) external onlyFromNonQuarantinedExitGame {
bool success = SafeEthTransfer.transferReturnResult(receiver, amount, safeGasStipend);
if (success) {
emit EthWithdrawn(receiver, amount);
} else {
emit WithdrawFailed(receiver, amount);
}
}
}pragma solidity 0.5.11;
import "../framework/PlasmaFramework.sol";
import "../utils/OnlyFromAddress.sol";
/**
* @notice Base contract for vault implementation
* @dev This is the functionality to swap "deposit verifier"
* Setting a new deposit verifier allows an upgrade to a new deposit tx type without upgrading the vault
*/
contract Vault is OnlyFromAddress {
byte private constant LEAF_SALT = 0x00;
byte private constant NODE_SALT = 0x01;
event SetDepositVerifierCalled(address nextDepositVerifier);
PlasmaFramework internal framework;
bytes32[16] internal zeroHashes; // Pre-computes zero hashes to be used for building merkle tree for deposit block
/**
* @notice Stores deposit verifier contract addresses; first contract address is effective until the
* `newDepositVerifierMaturityTimestamp`; second contract address becomes effective after that timestamp
*/
address[2] public depositVerifiers;
uint256 public newDepositVerifierMaturityTimestamp = 2 ** 255; // point far in the future
constructor(PlasmaFramework _framework) public {
framework = _framework;
zeroHashes = getZeroHashes();
}
/**
* @dev Pre-computes zero hashes to be used for building Merkle tree for deposit block
*/
function getZeroHashes() private pure returns (bytes32[16] memory) {
bytes32[16] memory hashes;
bytes32 zeroHash = keccak256(abi.encodePacked(LEAF_SALT, uint256(0)));
for (uint i = 0; i < 16; i++) {
hashes[i] = zeroHash;
zeroHash = keccak256(abi.encodePacked(NODE_SALT, zeroHash, zeroHash));
}
return hashes;
}
/**
* @notice Checks whether the call originates from a non-quarantined exit game contract
*/
modifier onlyFromNonQuarantinedExitGame() {
require(
ExitGameRegistry(framework).isExitGameSafeToUse(msg.sender),
"Called from a non-registered or quarantined exit game contract"
);
_;
}
/**
* @notice Sets the deposit verifier contract, which may be called only by the operator
* @dev emit SetDepositVerifierCalled
* @dev When one contract is already set, the next one is effective after 2 * MIN_EXIT_PERIOD.
* This is to protect deposit transactions already in mempool,
* and also make sure user only needs to SE within first week when invalid vault is registered.
*
* see: https://github.com/omisego/plasma-contracts/issues/412
* https://github.com/omisego/plasma-contracts/issues/173
*
* @param _verifier Address of the verifier contract
*/
function setDepositVerifier(address _verifier) public onlyFrom(framework.getMaintainer()) {
require(_verifier != address(0), "Cannot set an empty address as deposit verifier");
if (depositVerifiers[0] != address(0)) {
depositVerifiers[0] = getEffectiveDepositVerifier();
depositVerifiers[1] = _verifier;
newDepositVerifierMaturityTimestamp = now + 2 * framework.minExitPeriod();
} else {
depositVerifiers[0] = _verifier;
}
emit SetDepositVerifierCalled(_verifier);
}
/**
* @notice Retrieves the currently effective deposit verifier contract address
* @return Contract address of the deposit verifier
*/
function getEffectiveDepositVerifier() public view returns (address) {
if (now < newDepositVerifierMaturityTimestamp) {
return depositVerifiers[0];
} else {
return depositVerifiers[1];
}
}
/**
* @notice Generate and submit a deposit block root to the PlasmaFramework
* @dev Designed to be called by the contract that inherits Vault
*/
function submitDepositBlock(bytes memory depositTx) internal returns (uint256) {
bytes32 root = getDepositBlockRoot(depositTx);
uint256 depositBlkNum = framework.submitDepositBlock(root);
return depositBlkNum;
}
function getDepositBlockRoot(bytes memory depositTx) private view returns (bytes32) {
bytes32 root = keccak256(abi.encodePacked(LEAF_SALT, depositTx));
for (uint i = 0; i < 16; i++) {
root = keccak256(abi.encodePacked(NODE_SALT, root, zeroHashes[i]));
}
return root;
}
}pragma solidity 0.5.11;
import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
import "./IErc20DepositVerifier.sol";
import {PaymentTransactionModel as DepositTx} from "../../transactions/PaymentTransactionModel.sol";
/**
* @notice Implementation of Erc20 deposit verifier using payment transaction as the deposit tx
*/
contract Erc20DepositVerifier is IErc20DepositVerifier {
uint256 public depositTxType;
uint256 public supportedOutputType;
constructor(uint256 txType, uint256 outputType) public {
depositTxType = txType;
supportedOutputType = outputType;
}
/**
* @notice Overrides the function of IErc20DepositVerifier and implements the verification logic
* for payment transaction
* @dev Vault address must be approved to transfer from the sender address before doing the deposit
* @return Verified (owner, token, amount) of the deposit ERC20 token data
*/
function verify(bytes calldata depositTx, address sender, address vault)
external
view
returns (
address owner,
address token,
uint256 amount
)
{
DepositTx.Transaction memory decodedTx = DepositTx.decode(depositTx);
require(decodedTx.txType == depositTxType, "Invalid transaction type");
require(decodedTx.inputs.length == 0, "Deposit must have no inputs");
require(decodedTx.outputs.length == 1, "Deposit must have exactly one output");
require(decodedTx.outputs[0].token != address(0), "Invalid output currency (ETH)");
require(decodedTx.outputs[0].outputType == supportedOutputType, "Invalid output type");
address depositorsAddress = DepositTx.getOutputOwner(decodedTx.outputs[0]);
require(depositorsAddress == sender, "Depositor's address must match sender's address");
IERC20 erc20 = IERC20(decodedTx.outputs[0].token);
require(erc20.allowance(depositorsAddress, vault) >= decodedTx.outputs[0].amount, "Tokens have not been approved");
return (depositorsAddress, decodedTx.outputs[0].token, decodedTx.outputs[0].amount);
}
}pragma solidity 0.5.11;
import "./IEthDepositVerifier.sol";
import {PaymentTransactionModel as DepositTx} from "../../transactions/PaymentTransactionModel.sol";
/**
* @notice Implementation of ETH deposit verifier using payment transaction as the deposit transaction
*/
contract EthDepositVerifier is IEthDepositVerifier {
uint256 public depositTxType;
uint256 public supportedOutputType;
constructor(uint256 txType, uint256 outputType) public {
depositTxType = txType;
supportedOutputType = outputType;
}
/**
* @notice Overrides the function of IEthDepositVerifier and implements the verification logic
* for payment transaction
*/
function verify(bytes calldata depositTx, uint256 amount, address sender) external view {
DepositTx.Transaction memory decodedTx = DepositTx.decode(depositTx);
require(decodedTx.txType == depositTxType, "Invalid transaction type");
require(decodedTx.inputs.length == 0, "Deposit must have no inputs");
require(decodedTx.outputs.length == 1, "Deposit must have exactly one output");
require(decodedTx.outputs[0].amount == amount, "Deposited value must match sent amount");
require(decodedTx.outputs[0].token == address(0), "Output requires correct currency (ETH)");
require(decodedTx.outputs[0].outputType == supportedOutputType, "Invalid output type");
address depositorsAddress = DepositTx.getOutputOwner(decodedTx.outputs[0]);
require(depositorsAddress == sender, "Depositor's address must match sender's address");
}
}pragma solidity 0.5.11;
interface IErc20DepositVerifier {
/**
* @notice Verifies a deposit transaction
* @param depositTx The deposit transaction
* @param sender The owner of the deposit transaction
* @param vault The address of the Erc20Vault contract
* @return Verified (owner, token, amount) of the deposit ERC20 token data
*/
function verify(bytes calldata depositTx, address sender, address vault)
external
view
returns (address owner, address token, uint256 amount);
}pragma solidity 0.5.11;
interface IEthDepositVerifier {
/**
* @notice Verifies a deposit transaction
* @param depositTx The deposit transaction
* @param amount The amount deposited
* @param sender The owner of the deposit transaction
*/
function verify(bytes calldata depositTx, uint256 amount, address sender) external view;
}pragma solidity ^0.5.0;
import "./ERC20.sol";
import "../../access/roles/MinterRole.sol";
/**
* @dev Extension of `ERC20` that adds a set of accounts with the `MinterRole`,
* which have permission to mint (create) new tokens as they see fit.
*
* At construction, the deployer of the contract is the only minter.
*/
contract ERC20Mintable is ERC20, MinterRole {
/**
* @dev See `ERC20._mint`.
*
* Requirements:
*
* - the caller must have the `MinterRole`.
*/
function mint(address account, uint256 amount) public onlyMinter returns (bool) {
_mint(account, amount);
return true;
}
}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) {
require(b <= a, "SafeMath: subtraction overflow");
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-solidity/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) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, "SafeMath: division by zero");
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) {
require(b != 0, "SafeMath: modulo by zero");
return a % b;
}
}pragma solidity ^0.5.0;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* (.note) This call _does not revert_ if the signature is invalid, or
* if the signer is otherwise unable to be retrieved. In those scenarios,
* the zero address is returned.
*
* (.warning) `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise)
* be too long), and then calling `toEthSignedMessageHash` on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
// Check the signature length
if (signature.length != 65) {
return (address(0));
}
// Divide the signature in r, s and v variables
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return address(0);
}
if (v != 27 && v != 28) {
return address(0);
}
// If the signature is valid (and not malleable), return the signer address
return ecrecover(hash, v, r, s);
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* replicates the behavior of the
* [`eth_sign`](https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign)
* JSON-RPC method.
*
* See `recover`.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
}
}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 aplied to your functions to restrict their use to
* the owner.
*/
contract Ownable {
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 = msg.sender;
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 msg.sender == _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;
}
}pragma solidity ^0.5.0;
/**
* @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.
*
* > 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.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
}pragma solidity ^0.5.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow, so we distribute
return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
}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.
*
* > 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);
}pragma solidity ^0.5.0;
import "./IERC20.sol";
import "../../math/SafeMath.sol";
import "../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value);
callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves.
// A Solidity high level call has three parts:
// 1. The target address is checked to verify it contains contract code
// 2. The call itself is made, and success asserted
// 3. The return value is decoded, which in turn checks the size of the returned data.
// solhint-disable-next-line max-line-length
require(address(token).isContract(), "SafeERC20: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = address(token).call(data);
require(success, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}pragma solidity ^0.5.0;
import "./IERC20.sol";
import "../../math/SafeMath.sol";
/**
* @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`.
*
* *For a detailed writeup see our guide [How to implement supply
* mechanisms](https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226).*
*
* 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 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(msg.sender, 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 value) public returns (bool) {
_approve(msg.sender, spender, value);
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 `value`.
* - 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, msg.sender, _allowances[sender][msg.sender].sub(amount));
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(msg.sender, spender, _allowances[msg.sender][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(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue));
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);
_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 Destoys `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 value) internal {
require(account != address(0), "ERC20: burn from the zero address");
_totalSupply = _totalSupply.sub(value);
_balances[account] = _balances[account].sub(value);
emit Transfer(account, address(0), value);
}
/**
* @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 value) internal {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = value;
emit Approval(owner, spender, value);
}
/**
* @dev Destoys `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, msg.sender, _allowances[account][msg.sender].sub(amount));
}
}pragma solidity ^0.5.0;
import "../Roles.sol";
contract MinterRole {
using Roles for Roles.Role;
event MinterAdded(address indexed account);
event MinterRemoved(address indexed account);
Roles.Role private _minters;
constructor () internal {
_addMinter(msg.sender);
}
modifier onlyMinter() {
require(isMinter(msg.sender), "MinterRole: caller does not have the Minter role");
_;
}
function isMinter(address account) public view returns (bool) {
return _minters.has(account);
}
function addMinter(address account) public onlyMinter {
_addMinter(account);
}
function renounceMinter() public {
_removeMinter(msg.sender);
}
function _addMinter(address account) internal {
_minters.add(account);
emit MinterAdded(account);
}
function _removeMinter(address account) internal {
_minters.remove(account);
emit MinterRemoved(account);
}
}pragma solidity ^0.5.0;
/**
* @title Roles
* @dev Library for managing addresses assigned to a Role.
*/
library Roles {
struct Role {
mapping (address => bool) bearer;
}
/**
* @dev Give an account access to this role.
*/
function add(Role storage role, address account) internal {
require(!has(role, account), "Roles: account already has role");
role.bearer[account] = true;
}
/**
* @dev Remove an account's access to this role.
*/
function remove(Role storage role, address account) internal {
require(has(role, account), "Roles: account does not have role");
role.bearer[account] = false;
}
/**
* @dev Check if an account has this role.
* @return bool
*/
function has(Role storage role, address account) internal view returns (bool) {
require(account != address(0), "Roles: account is the zero address");
return role.bearer[account];
}
}{
"libraries": {
"": {
"PaymentStartStandardExit": "0xf38380bbf08961123960fAd630a0609906849751",
"PaymentChallengeStandardExit": "0x24c0a84090135AD04F257C85E10db6B4A74E0738",
"PaymentProcessStandardExit": "0x5E84DF30ce17A9AC34E5474fc37a9c2267454518",
"PaymentStartInFlightExit": "0x081d7B167a94E7421Ea5D0016C3D01E0cFf6B557",
"PaymentPiggybackInFlightExit": "0x5d3EE50E31293B45A7eb8B864c0B015E169AC7D8",
"PaymentChallengeIFENotCanonical": "0x9e3108FB11cAEDA64e264B2953a0eDa81cf1a650",
"PaymentChallengeIFEInputSpent": "0xEf6133d149460C9B79e132517934f77F1E62b23e",
"PaymentChallengeIFEOutputSpent": "0x7A8C0D7F1e6dBe36AB3A9adE0C44a0b868bB703F",
"PaymentDeleteInFlightExit": "0x4f05855B6dF026726037500D10D91652b5C9e784",
"PaymentProcessInFlightExit": "0x67C3E5524dBE05B54fE08c3D4af65c273B87E630"
}
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [],
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"constant":true,"inputs":[{"internalType":"uint160[]","name":"exitIds","type":"uint160[]"}],"name":"standardExits","outputs":[{"components":[{"internalType":"bool","name":"exitable","type":"bool"},{"internalType":"uint256","name":"utxoPos","type":"uint256"},{"internalType":"bytes32","name":"outputId","type":"bytes32"},{"internalType":"address payable","name":"exitTarget","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"bondSize","type":"uint256"}],"internalType":"struct PaymentExitDataModel.StandardExit[]","name":"","type":"tuple[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"uint128","name":"newBondSize","type":"uint128"}],"name":"updateStartStandardExitBondSize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"bytes","name":"inFlightTx","type":"bytes"},{"internalType":"bytes","name":"inFlightTxInclusionProof","type":"bytes"},{"internalType":"uint256","name":"outputUtxoPos","type":"uint256"},{"internalType":"bytes","name":"challengingTx","type":"bytes"},{"internalType":"uint16","name":"challengingTxInputIndex","type":"uint16"},{"internalType":"bytes","name":"challengingTxWitness","type":"bytes"},{"internalType":"bytes32","name":"senderData","type":"bytes32"}],"internalType":"struct PaymentInFlightExitRouterArgs.ChallengeOutputSpent","name":"args","type":"tuple"}],"name":"challengeInFlightExitOutputSpent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint128","name":"newBondSize","type":"uint128"}],"name":"updatePiggybackBondSize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"bytes","name":"inFlightTx","type":"bytes"},{"internalType":"uint256","name":"inFlightTxPos","type":"uint256"},{"internalType":"bytes","name":"inFlightTxInclusionProof","type":"bytes"}],"name":"respondToNonCanonicalChallenge","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"piggybackBondSize","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"bytes","name":"inFlightTx","type":"bytes"},{"internalType":"bytes[]","name":"inputTxs","type":"bytes[]"},{"internalType":"uint256[]","name":"inputUtxosPos","type":"uint256[]"},{"internalType":"bytes[]","name":"inputTxsInclusionProofs","type":"bytes[]"},{"internalType":"bytes[]","name":"inFlightTxWitnesses","type":"bytes[]"}],"internalType":"struct PaymentInFlightExitRouterArgs.StartExitArgs","name":"args","type":"tuple"}],"name":"startInFlightExit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint160","name":"exitId","type":"uint160"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"token","type":"address"}],"name":"processExit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"uint256","name":"utxoPos","type":"uint256"},{"internalType":"bytes","name":"rlpOutputTx","type":"bytes"},{"internalType":"bytes","name":"outputTxInclusionProof","type":"bytes"}],"internalType":"struct PaymentStandardExitRouterArgs.StartStandardExitArgs","name":"args","type":"tuple"}],"name":"startStandardExit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"startIFEBondSize","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"INITIAL_PB_BOND_SIZE","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"bytes","name":"_txBytes","type":"bytes"}],"name":"getInFlightExitId","outputs":[{"internalType":"uint160","name":"","type":"uint160"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":true,"inputs":[],"name":"BOND_LOWER_BOUND_DIVISOR","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"BOND_UPPER_BOUND_MULTIPLIER","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"bytes","name":"inFlightTx","type":"bytes"},{"internalType":"uint16","name":"outputIndex","type":"uint16"}],"internalType":"struct PaymentInFlightExitRouterArgs.PiggybackInFlightExitOnOutputArgs","name":"args","type":"tuple"}],"name":"piggybackInFlightExitOnOutput","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"internalType":"uint128","name":"newBondSize","type":"uint128"}],"name":"updateStartIFEBondSize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"bool","name":"_isDeposit","type":"bool"},{"internalType":"bytes","name":"_txBytes","type":"bytes"},{"internalType":"uint256","name":"_utxoPos","type":"uint256"}],"name":"getStandardExitId","outputs":[{"internalType":"uint160","name":"","type":"uint160"}],"payable":false,"stateMutability":"pure","type":"function"},{"constant":false,"inputs":[{"internalType":"uint160","name":"exitId","type":"uint160"}],"name":"deleteNonPiggybackedInFlightExit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"bytes","name":"inFlightTx","type":"bytes"},{"internalType":"uint16","name":"inFlightTxInputIndex","type":"uint16"},{"internalType":"bytes","name":"challengingTx","type":"bytes"},{"internalType":"uint16","name":"challengingTxInputIndex","type":"uint16"},{"internalType":"bytes","name":"challengingTxWitness","type":"bytes"},{"internalType":"bytes","name":"inputTx","type":"bytes"},{"internalType":"uint256","name":"inputUtxoPos","type":"uint256"},{"internalType":"bytes32","name":"senderData","type":"bytes32"}],"internalType":"struct PaymentInFlightExitRouterArgs.ChallengeInputSpentArgs","name":"args","type":"tuple"}],"name":"challengeInFlightExitInputSpent","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"INITIAL_BOND_SIZE","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"INITIAL_IFE_BOND_SIZE","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"internalType":"uint160[]","name":"exitIds","type":"uint160[]"}],"name":"inFlightExits","outputs":[{"components":[{"internalType":"bool","name":"isCanonical","type":"bool"},{"internalType":"uint64","name":"exitStartTimestamp","type":"uint64"},{"internalType":"uint256","name":"exitMap","type":"uint256"},{"internalType":"uint256","name":"position","type":"uint256"},{"components":[{"internalType":"bytes32","name":"outputId","type":"bytes32"},{"internalType":"address payable","name":"exitTarget","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"piggybackBondSize","type":"uint256"}],"internalType":"struct PaymentExitDataModel.WithdrawData[4]","name":"inputs","type":"tuple[4]"},{"components":[{"internalType":"bytes32","name":"outputId","type":"bytes32"},{"internalType":"address payable","name":"exitTarget","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"piggybackBondSize","type":"uint256"}],"internalType":"struct PaymentExitDataModel.WithdrawData[4]","name":"outputs","type":"tuple[4]"},{"internalType":"address payable","name":"bondOwner","type":"address"},{"internalType":"uint256","name":"bondSize","type":"uint256"},{"internalType":"uint256","name":"oldestCompetitorPosition","type":"uint256"}],"internalType":"struct PaymentExitDataModel.InFlightExit[]","name":"","type":"tuple[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"uint160","name":"exitId","type":"uint160"},{"internalType":"bytes","name":"exitingTx","type":"bytes"},{"internalType":"bytes","name":"challengeTx","type":"bytes"},{"internalType":"uint16","name":"inputIndex","type":"uint16"},{"internalType":"bytes","name":"witness","type":"bytes"},{"internalType":"bytes32","name":"senderData","type":"bytes32"}],"internalType":"struct PaymentStandardExitRouterArgs.ChallengeStandardExitArgs","name":"args","type":"tuple"}],"name":"challengeStandardExit","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"bytes","name":"inFlightTx","type":"bytes"},{"internalType":"uint16","name":"inputIndex","type":"uint16"}],"internalType":"struct PaymentInFlightExitRouterArgs.PiggybackInFlightExitOnInputArgs","name":"args","type":"tuple"}],"name":"piggybackInFlightExitOnInput","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"components":[{"internalType":"bytes","name":"inputTx","type":"bytes"},{"internalType":"uint256","name":"inputUtxoPos","type":"uint256"},{"internalType":"bytes","name":"inFlightTx","type":"bytes"},{"internalType":"uint16","name":"inFlightTxInputIndex","type":"uint16"},{"internalType":"bytes","name":"competingTx","type":"bytes"},{"internalType":"uint16","name":"competingTxInputIndex","type":"uint16"},{"internalType":"uint256","name":"competingTxPos","type":"uint256"},{"internalType":"bytes","name":"competingTxInclusionProof","type":"bytes"},{"internalType":"bytes","name":"competingTxWitness","type":"bytes"}],"internalType":"struct PaymentInFlightExitRouterArgs.ChallengeCanonicityArgs","name":"args","type":"tuple"}],"name":"challengeInFlightExitNotCanonical","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"startStandardExitBondSize","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"contract PlasmaFramework","name":"framework","type":"address"},{"internalType":"uint256","name":"ethVaultId","type":"uint256"},{"internalType":"uint256","name":"erc20VaultId","type":"uint256"},{"internalType":"contract SpendingConditionRegistry","name":"spendingConditionRegistry","type":"address"},{"internalType":"contract IStateTransitionVerifier","name":"stateTransitionVerifier","type":"address"},{"internalType":"uint256","name":"supportTxType","type":"uint256"},{"internalType":"uint256","name":"safeGasStipend","type":"uint256"}],"internalType":"struct PaymentExitGameArgs.Args","name":"args","type":"tuple"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"bondSize","type":"uint128"}],"name":"IFEBondUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"bondSize","type":"uint128"}],"name":"PiggybackBondUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"initiator","type":"address"},{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"}],"name":"InFlightExitStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"exitTarget","type":"address"},{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":false,"internalType":"uint16","name":"inputIndex","type":"uint16"}],"name":"InFlightExitInputPiggybacked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint160","name":"exitId","type":"uint160"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"InFlightExitOmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InFlightBondReturnFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint160","name":"exitId","type":"uint160"},{"indexed":false,"internalType":"uint16","name":"outputIndex","type":"uint16"}],"name":"InFlightExitOutputWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint160","name":"exitId","type":"uint160"},{"indexed":false,"internalType":"uint16","name":"inputIndex","type":"uint16"}],"name":"InFlightExitInputWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"exitTarget","type":"address"},{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":false,"internalType":"uint16","name":"outputIndex","type":"uint16"}],"name":"InFlightExitOutputPiggybacked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"challenger","type":"address"},{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"challengeTxPosition","type":"uint256"}],"name":"InFlightExitChallenged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"challenger","type":"address"},{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"challengeTxPosition","type":"uint256"}],"name":"InFlightExitChallengeResponded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"challenger","type":"address"},{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":false,"internalType":"uint16","name":"inputIndex","type":"uint16"}],"name":"InFlightExitInputBlocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"challenger","type":"address"},{"indexed":true,"internalType":"bytes32","name":"txHash","type":"bytes32"},{"indexed":false,"internalType":"uint16","name":"outputIndex","type":"uint16"}],"name":"InFlightExitOutputBlocked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint160","name":"exitId","type":"uint160"}],"name":"InFlightExitDeleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"bondSize","type":"uint128"}],"name":"StandardExitBondUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint160","name":"exitId","type":"uint160"}],"name":"ExitStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"utxoPos","type":"uint256"}],"name":"ExitChallenged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint160","name":"exitId","type":"uint160"}],"name":"ExitOmitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint160","name":"exitId","type":"uint160"}],"name":"ExitFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BondReturnFailed","type":"event"}]Contract Creation Code
60806040523480156200001157600080fd5b5060405162004f3238038062004f328339810160408190526200003491620012a2565b8051601080546001600160a01b0319166001600160a01b03909216918217905560208201516040517f8c64ea4a00000000000000000000000000000000000000000000000000000000815283928392600092638c64ea4a916200009a9160040162001589565b60206040518083038186803b158015620000b357600080fd5b505afa158015620000c8573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620000ee919081019062001279565b90506001600160a01b0381166200013c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001339062001553565b60405180910390fd5b815160408084015190517f8c64ea4a0000000000000000000000000000000000000000000000000000000081526000926001600160a01b031691638c64ea4a916200018b919060040162001589565b60206040518083038186803b158015620001a457600080fd5b505afa158015620001b9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620001df919081019062001279565b90506001600160a01b03811662000224576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001339062001565565b8251602084015160408086015160a087015191517f07a97aa900000000000000000000000000000000000000000000000000000000815273f38380bbf08961123960fad630a0609906849751946307a97aa9946200028c94309492939192916004016200145b565b60c06040518083038186803b158015620002a557600080fd5b505af4158015620002ba573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620002e0919081019062001326565b8051600180546001600160a01b039283166001600160a01b031991821617909155602083015160028054919093169116179055604080820151516003556060808301516004908155608084015160055560a09093015160065585519086015160c087015192517f159cde2d0000000000000000000000000000000000000000000000000000000081527324c0a84090135ad04f257c85e10db6b4a74e07389463159cde2d946200039594939290910162001525565b60606040518083038186803b158015620003ae57600080fd5b505af4158015620003c3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620003e99190810190620012c3565b8051600b80546001600160a01b03199081166001600160a01b0393841617909155602080840151600c80548416918516919091179055604093840151600d5583516080810185528751841680825287851682840181905294871695820186905260c089015160609092018290526007805485169091179055600880548416909417909355600980549092169093179055600a556200049e906631bced02db000090600290819062000eed811b6200387417901c565b8051600e80546020808501516001600160801b03199283166001600160801b03958616178516700100000000000000000000000000000000918616820217909355604080860151600f805460608901516080909901519516919096161761ffff60801b191661ffff9687169094029390931761ffff60901b191672010000000000000000000000000000000000009590921694909402179091558551603080546001600160a01b0319166001600160a01b0390921691821790559186015190517f8c64ea4a00000000000000000000000000000000000000000000000000000000815260009550919350638c64ea4a92506200059d9160040162001589565b60206040518083038186803b158015620005b657600080fd5b505afa158015620005cb573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620005f1919081019062001279565b90506001600160a01b03811662000636576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001339062001553565b815160408084015190517f8c64ea4a0000000000000000000000000000000000000000000000000000000081526000926001600160a01b031691638c64ea4a9162000685919060040162001589565b60206040518083038186803b1580156200069e57600080fd5b505afa158015620006b3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620006d9919081019062001279565b90506001600160a01b0381166200071e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001339062001565565b82516060840151608085015160a08601516040517f91ac978000000000000000000000000000000000000000000000000000000000815273081d7b167a94e7421ea5d0016c3d01e0cff6b557946391ac978094620007839491939092600401620014f7565b60a06040518083038186803b1580156200079c57600080fd5b505af4158015620007b1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620007d7919081019062001305565b8051601280546001600160a01b03199081166001600160a01b0393841617909155602080840151516013556040808501516014805485169186169190911790556060850151601580549094169416939093179091556080909201516016558451918501518582015191517f32e6d869000000000000000000000000000000000000000000000000000000008152735d3ee50e31293b45a7eb8b864c0b015e169ac7d8936332e6d869936200089493919230929190600401620014b1565b60c06040518083038186803b158015620008ad57600080fd5b505af4158015620008c2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620008e89190810190620012e4565b8051601780546001600160a01b03199081166001600160a01b0393841617909155602083015151601855604080840151601980549093169316929092179055606080830151601a556080830151601b5560a092830151601c558551908601519286015191517f159cde2d000000000000000000000000000000000000000000000000000000008152739e3108fb11caeda64e264b2953a0eda81cf1a6509363159cde2d936200099b939260040162001525565b60606040518083038186803b158015620009b457600080fd5b505af4158015620009c9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250620009ef9190810190620012c3565b8051601d80546001600160a01b039283166001600160a01b0319918216179091556020830151601e8054919093169116179055604090810151601f558351606085015160c086015192517f159cde2d00000000000000000000000000000000000000000000000000000000815273ef6133d149460c9b79e132517934f77f1e62b23e9363159cde2d9362000a8b93909290919060040162001525565b60606040518083038186803b15801562000aa457600080fd5b505af415801562000ab9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525062000adf9190810190620012c3565b8051602080546001600160a01b03199081166001600160a01b03938416178255818401516021805483169185169190911790556040938401516022558351606080820186528851851680835290890151851682850181905260c08a01519287018390526023805485169092179091556024805490931617909155602555825180840180855287517fd4a2b4ef00000000000000000000000000000000000000000000000000000000909152935190938493169163d4a2b4ef916044808601929190818703018186803b15801562000bb557600080fd5b505afa15801562000bca573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525062000bf0919081019062001347565b815260c0850180516020928301528251602655918101516027556040805160808101825286516001600160a01b0390811680835287821683860181905291871693830184905294516060909201829052602880546001600160a01b031990811690961790556029805486169091179055602a8054909416909117909255602b9190915562000c95906683734dd0b080009060029081906200387462000eed821b17901c565b8051602c80546020808501516001600160801b03199283166001600160801b039586161785167001000000000000000000000000000000009186168202179093556040850151602d805460608801516080909801519416919095161761ffff60801b191661ffff9586169093029290921761ffff60901b19167201000000000000000000000000000000000000949091169390930292909217905562000d5290666379da05b6000090600290819062000eed811b6200387417901c565b8051602e80546020808501516001600160801b03199283166001600160801b03958616178516700100000000000000000000000000000000918616820217909355604080860151602f80546060808a01516080909a015191909616929097169190911761ffff60801b191661ffff9788169095029490941761ffff60901b191672010000000000000000000000000000000000009690951695909502939093179091558651603180546001600160a01b0319166001600160a01b039283161790559087015183517f8da5cb5b00000000000000000000000000000000000000000000000000000000815293516000975091169450638da5cb5b9350600480840193829003018186803b15801562000e6857600080fd5b505afa15801562000e7d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525062000ea3919081019062001279565b6001600160a01b03161462000ee6576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620001339062001577565b5062001622565b62000ef762000f38565b506040805160a0810182526001600160801b03949094168452600060208501526780000000000000009084015261ffff918216606084015216608082015290565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b805162000f7381620015f2565b92915050565b805162000f73816200160c565b600060e0828403121562000f9957600080fd5b62000fa560e062001599565b9050600062000fb5848462000f79565b825250602062000fc8848483016200126c565b602083015250604062000fde848285016200126c565b604083015250606062000ff48482850162000f79565b60608301525060806200100a8482850162000f79565b60808301525060a062001020848285016200126c565b60a08301525060c062001036848285016200126c565b60c08301525092915050565b6000602082840312156200105557600080fd5b62001061602062001599565b905060006200107184846200126c565b82525092915050565b6000606082840312156200108d57600080fd5b62001099606062001599565b90506000620010a9848462000f79565b8252506020620010bc8484830162000f79565b6020830152506040620010d2848285016200126c565b60408301525092915050565b600060c08284031215620010f157600080fd5b620010fd60c062001599565b905060006200110d848462000f79565b8252506020620011208484830162001042565b6020830152506040620011368482850162000f79565b60408301525060606200114c848285016200126c565b606083015250608062001162848285016200126c565b60808301525060a062001178848285016200126c565b60a08301525092915050565b600060a082840312156200119757600080fd5b620011a360a062001599565b90506000620011b3848462000f79565b8252506020620011c68484830162001042565b6020830152506040620011dc8482850162000f79565b6040830152506060620011f28482850162000f79565b606083015250608062001208848285016200126c565b60808301525092915050565b600060c082840312156200122757600080fd5b6200123360c062001599565b9050600062001243848462000f79565b8252506020620012568484830162000f79565b6020830152506040620011368482850162001042565b805162000f738162001617565b6000602082840312156200128c57600080fd5b60006200129a848462000f66565b949350505050565b600060e08284031215620012b557600080fd5b60006200129a848462000f86565b600060608284031215620012d657600080fd5b60006200129a84846200107a565b600060c08284031215620012f757600080fd5b60006200129a8484620010de565b600060a082840312156200131857600080fd5b60006200129a848462001184565b600060c082840312156200133957600080fd5b60006200129a848462001214565b6000602082840312156200135a57600080fd5b60006200129a84846200126c565b6200137381620015d6565b82525050565b600062001388601183620015c0565b7f496e76616c696420455448207661756c74000000000000000000000000000000815260200192915050565b6000620013c3601383620015c0565b7f496e76616c6964204552433230207661756c7400000000000000000000000000815260200192915050565b6000620013fe603b83620015c0565b7f5370656e64696e6720636f6e646974696f6e207265676973747279206f776e6581527f7273686970206e6565647320746f2062652072656e6f756e6365640000000000602082015260400192915050565b6200137381620015ef565b60a081016200146b828862001368565b6200147a602083018762001368565b62001489604083018662001450565b62001498606083018562001450565b620014a7608083018462001450565b9695505050505050565b60808101620014c1828762001368565b620014d0602083018662001368565b620014df604083018562001450565b620014ee606083018462001450565b95945050505050565b6080810162001507828762001368565b62001516602083018662001368565b620014df604083018562001368565b6060810162001535828662001368565b62001544602083018562001368565b6200129a604083018462001450565b6020808252810162000f738162001379565b6020808252810162000f7381620013b4565b6020808252810162000f7381620013ef565b6020810162000f73828462001450565b6040518181016001600160401b0381118282101715620015b857600080fd5b604052919050565b90815260200190565b600062000f7382620015e3565b600062000f7382620015c9565b6001600160a01b031690565b90565b620015fd81620015c9565b81146200160957600080fd5b50565b620015fd81620015d6565b620015fd81620015ef565b61390080620016326000396000f3fe6080604052600436106101815760003560e01c8063a0e403b1116100d1578063c170ecf51161008a578063df2933e211610064578063df2933e214610409578063e5bc60c714610429578063e83622981461043c578063fe32a1241461045c57610181565b8063c170ecf5146103b2578063c96e7c05146103c7578063cec9e1a7146103dc57610181565b8063a0e403b1146102fd578063a4a254411461031f578063b177ba2314610332578063b22d3e1414610352578063b3c2ec7614610372578063b9a3a28e1461039257610181565b80635a5285141161013e5780637702fa17116101185780637702fa17146102a65780637e2b2efe146102bb578063839c78f9146102d0578063905d6a99146102fd57610181565b80635a52851414610260578063684963511461027357806370e014621461029357610181565b80630ca5b67614610186578063163d7bc7146101bc5780632c3bad95146101de578063315fb7da146101fe57806336660de41461021e5780633f1214cf1461023e575b600080fd5b34801561019257600080fd5b506101a66101a136600461253c565b610471565b6040516101b39190613451565b60405180910390f35b3480156101c857600080fd5b506101dc6101d73660046127f1565b610576565b005b3480156101ea57600080fd5b506101dc6101f93660046126ed565b610679565b34801561020a57600080fd5b506101dc6102193660046127f1565b6107cd565b34801561022a57600080fd5b506101dc610239366004612615565b6108bb565b34801561024a57600080fd5b50610253610a15565b6040516101b391906136f5565b6101dc61026e366004612789565b610a7a565b34801561027f57600080fd5b506101dc61028e36600461282d565b610bfe565b6101dc6102a13660046127bd565b610c56565b3480156102b257600080fd5b50610253610d78565b3480156102c757600080fd5b50610253610dd8565b3480156102dc57600080fd5b506102f06102eb3660046125e1565b610de3565b6040516101b39190613703565b34801561030957600080fd5b50610312610df6565b6040516101b39190613711565b6101dc61032d366004612755565b610dfb565b34801561033e57600080fd5b506101dc61034d3660046127f1565b610f19565b34801561035e57600080fd5b506102f061036d36600461257d565b611007565b34801561037e57600080fd5b506101dc61038d36600461280f565b611032565b34801561039e57600080fd5b506101dc6103ad3660046126b9565b6110eb565b3480156103be57600080fd5b506102536111b8565b3480156103d357600080fd5b506102536111c3565b3480156103e857600080fd5b506103fc6103f736600461253c565b6111ce565b6040516101b39190613440565b34801561041557600080fd5b506101dc610424366004612721565b6113f7565b6101dc610437366004612755565b6114c4565b34801561044857600080fd5b506101dc610457366004612685565b6115e2565b34801561046857600080fd5b506102536116af565b606080838390506040519080825280602002602001820160405280156104b157816020015b61049e611c20565b8152602001906001900390816104965790505b50905060005b8381101561056c5760008585838181106104cd57fe5b90506020020160206104e2919081019061280f565b6001600160a01b0380821660009081526020818152604091829020825160c081018452815460ff161515815260018201549281019290925260028101549282019290925260038201549092166060830152600481015460808301526005015460a082015284519192509084908490811061055857fe5b6020908102919091010152506001016104b7565b5090505b92915050565b601060009054906101000a90046001600160a01b03166001600160a01b0316634b0a72bc6040518163ffffffff1660e01b815260040160206040518083038186803b1580156105c457600080fd5b505afa1580156105d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506105fc919081019061251e565b336001600160a01b0382161461062d5760405162461bcd60e51b8152600401610624906134d2565b60405180910390fd5b61063e600e8363ffffffff61170f16565b7fd21ccbbae3d29826d53310efdc993c64e27496dd31694b292f1a120344ec37ab8260405161066d91906136f5565b60405180910390a15050565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156106ca57600080fd5b505af11580156106de573d6000803e3d6000fd5b5050604080516060810182526023546001600160a01b0390811682526024541660208201526025548183015290516359cff02160e01b8152737a8c0d7f1e6dbe36ab3a9ade0c44a0b868bb703f93506359cff0219250610746919060119087906004016135b5565b60006040518083038186803b15801561075e57600080fd5b505af4158015610772573d6000803e3d6000fd5b50505050806001600160a01b0316634e0f74366040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156107b157600080fd5b505af11580156107c5573d6000803e3d6000fd5b505050505050565b603060009054906101000a90046001600160a01b03166001600160a01b0316634b0a72bc6040518163ffffffff1660e01b815260040160206040518083038186803b15801561081b57600080fd5b505afa15801561082f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610853919081019061251e565b336001600160a01b0382161461087b5760405162461bcd60e51b8152600401610624906134d2565b61088c602e8363ffffffff61170f16565b7f30ce291291dca42b8168ab211fafc36d3d4868292ed3259874ed00864432e20e8260405161066d91906136f5565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561090c57600080fd5b505af1158015610920573d6000803e3d6000fd5b505060408051606081018252601d546001600160a01b039081168252601e54166020820152601f54818301529051630c27711f60e11b8152739e3108fb11caeda64e264b2953a0eda81cf1a650935063184ee23e925061098c919060119089908990899060040161352f565b60006040518083038186803b1580156109a457600080fd5b505af41580156109b8573d6000803e3d6000fd5b50505050806001600160a01b0316634e0f74366040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156109f757600080fd5b505af1158015610a0b573d6000803e3d6000fd5b5050505050505050565b6040805160a081018252602e546001600160801b038082168352600160801b9182900481166020840152602f549081169383019390935261ffff90830481166060830152600160901b9092049091166080820152600090610a75906117fd565b905090565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610acb57600080fd5b505af1158015610adf573d6000803e3d6000fd5b50505050610aeb610d78565b6001600160801b0316803414610b135760405162461bcd60e51b815260040161062490613482565b6040805160a0810182526012546001600160a01b039081168252825160208181018552601354825283015260145481168284015260155416606082015260165460808201529051631ff4513b60e01b815273081d7b167a94e7421ea5d0016c3d01e0cff6b55791631ff4513b91610b929190601190889060040161369a565b60006040518083038186803b158015610baa57600080fd5b505af4158015610bbe573d6000803e3d6000fd5b5050505050806001600160a01b0316634e0f74366040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156107b157600080fd5b6031546001600160a01b0316338114610c295760405162461bcd60e51b8152600401610624906134d2565b610c3284611827565b15610c4657610c41848361184d565b610c50565b610c5084836118e9565b50505050565b601060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610ca757600080fd5b505af1158015610cbb573d6000803e3d6000fd5b50505050610cc76116af565b6001600160801b0316803414610cef5760405162461bcd60e51b815260040161062490613482565b6040805160c0810182526001546001600160a01b0390811682526002541660208083019190915282519081018352600354815281830152600480546060830152600554608083015260065460a083015291516332c812a760e11b815273f38380bbf08961123960fad630a060990684975192636590254e92610b929290916000918991016136c7565b6040805160a081018252602c546001600160801b038082168352600160801b9182900481166020840152602d549081169383019390935261ffff90830481166060830152600160901b9092049091166080820152600090610a75906117fd565b666379da05b6000081565b6000610dee82611959565b90505b919050565b600281565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e4c57600080fd5b505af1158015610e60573d6000803e3d6000fd5b50505050610e6c610a15565b6001600160801b0316803414610e945760405162461bcd60e51b815260040161062490613482565b6040805160c0810182526017546001600160a01b03908116825282516020818101855260185482528301526019541681830152601a546060820152601b546080820152601c5460a0820152905163082153e960e21b8152735d3ee50e31293b45a7eb8b864c0b015e169ac7d8916320854fa491610b9291906011908890600401613637565b603060009054906101000a90046001600160a01b03166001600160a01b0316634b0a72bc6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f6757600080fd5b505afa158015610f7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f9f919081019061251e565b336001600160a01b03821614610fc75760405162461bcd60e51b8152600401610624906134d2565b610fd8602c8363ffffffff61170f16565b7f63ccac61f03626a0e717e96da239ff8996745b6c4ddcaffdedc88d7ef938f5ec8260405161066d91906136f5565b6000611011611c55565b61101a83611977565b9050611027858583611a0a565b9150505b9392505050565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561108357600080fd5b505af1158015611097573d6000803e3d6000fd5b505060408051808201825260265481526027546020820152905163b1cf0fed60e01b8152734f05855b6df026726037500d10d91652b5c9e784935063b1cf0fed92506107469190601190879060040161360f565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561113c57600080fd5b505af1158015611150573d6000803e3d6000fd5b505060408051606081018252602080546001600160a01b0390811683526021541690820152602254818301529051634bb4a4df60e11b815273ef6133d149460c9b79e132517934f77f1e62b23e935063976949be925061074691906011908790600401613502565b6631bced02db000081565b6683734dd0b0800081565b6060808383905060405190808252806020026020018201604052801561120e57816020015b6111fb611c75565b8152602001906001900390816111f35790505b50905060005b8381101561056c57600085858381811061122a57fe5b905060200201602061123f919081019061280f565b6001600160a01b0381166000908152601160209081526040808320815161012081018352815460ff81161515825261010090046001600160401b03169381019390935260018101548383015260028101546060840152815160808082019093529495509193919290840191906003840190600490835b8282101561131c576040805160a081018252600584028601805482526001808201546001600160a01b0390811660208086019190915260028401549091169484019490945260038201546060840152600490910154608083015290835290920191016112b5565b5050509082525060408051608081019091526020909101906017830160046000835b828210156113a5576040805160a081018252600584028601805482526001808201546001600160a01b03908116602080860191909152600284015490911694840194909452600382015460608401526004909101546080830152908352909201910161133e565b50505090825250602b8201546001600160a01b03166020820152602c8201546040820152602d9091015460609091015283518490849081106113e357fe5b602090810291909101015250600101611214565b601060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561144857600080fd5b505af115801561145c573d6000803e3d6000fd5b505060408051606081018252600b546001600160a01b039081168252600c54166020820152600d5481830152905163edc91d4b60e01b81527324c0a84090135ad04f257c85e10db6b4a74e0738935063edc91d4b9250610746919060009087906004016135e2565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561151557600080fd5b505af1158015611529573d6000803e3d6000fd5b50505050611535610a15565b6001600160801b031680341461155d5760405162461bcd60e51b815260040161062490613482565b6040805160c0810182526017546001600160a01b03908116825282516020818101855260185482528301526019541681830152601a546060820152601b546080820152601c5460a08201529051631845801f60e11b8152735d3ee50e31293b45a7eb8b864c0b015e169ac7d89163308b003e91610b9291906011908890600401613637565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561163357600080fd5b505af1158015611647573d6000803e3d6000fd5b505060408051606081018252601d546001600160a01b039081168252601e54166020820152601f54818301529051631287d7e160e31b8152739e3108fb11caeda64e264b2953a0eda81cf1a650935063943ebf08925061074691906011908790600401613588565b6040805160a081018252600e546001600160801b038082168352600160801b9182900481166020840152600f549081169383019390935261ffff90830481166060830152600160901b9092049091166080820152600090610a75906117fd565b6040805160a08101825283546001600160801b038082168352600160801b918290048116602084015260018601549081169383019390935261ffff90830481166060830152600160901b909204909116608082015261176e9082611a7a565b8154600160801b90046001600160801b03161580159061179b575060018201546001600160801b03164210155b156117c2578154600160801b81046001600160801b03166001600160801b03199091161782555b81546001600160801b03918216600160801b02911617815560010180546001600160801b0319166001600160401b03426202a3000116179055565b600081604001516001600160801b031642101561181c57508051610df1565b506020810151610df1565b60006118436001600160a01b038316609763ffffffff611b3d16565b60ff161592915050565b604080516080810182526007546001600160a01b039081168252600854811660208301526009541681830152600a54606082015290516373c97b6d60e11b8152735e84df30ce17a9ac34e5474fc37a9c22674545189163e792f6da916118bd919060009087908790600401613665565b60006040518083038186803b1580156118d557600080fd5b505af41580156107c5573d6000803e3d6000fd5b604080516080810182526028546001600160a01b03908116825260295481166020830152602a541681830152602b5460608201529051632309cb3d60e01b81527367c3e5524dbe05b54fe08c3d4af65c273b87e63091632309cb3d916118bd919060119087908790600401613665565b80516020820120600090610dee9060691c609763ffffffff611b4716565b61197f611c55565b633b9aca00808304906127109084068190049084066429f16b11c68311156119b95760405162461bcd60e51b815260040161062490613462565b61ffff8211156119db5760405162461bcd60e51b815260040161062490613472565b604080516060810182526001600160401b03909416845261ffff92831660208501529116908201529050919050565b60008315611a5d57600083611a1e84611b54565b604051602001611a2f92919061341e565b604051602081830303815290604052805190602001209050611a55818460400151611be3565b91505061102b565b611a7283805190602001208360400151611be3565b949350505050565b6000611a85836117fd565b90506000826001600160801b031611611ab05760405162461bcd60e51b8152600401610624906134b2565b826060015161ffff16816001600160801b031681611aca57fe5b046001600160801b0316826001600160801b03161015611afc5760405162461bcd60e51b8152600401610624906134c2565b826080015161ffff16816001600160801b031602826001600160801b03161115611b385760405162461bcd60e51b815260040161062490613492565b505050565b60ff161c60011690565b600160ff919091161b1790565b6000600161271003826040015161ffff161115611b835760405162461bcd60e51b8152600401610624906134a2565b81516429f16b11c66001600160401b039091161115611bb45760405162461bcd60e51b8152600401610624906134e2565b506040810151602082015182516001600160401b0316633b9aca000261ffff9182166127100201911601919050565b6000606983901c61ffff60981b609884901b1617806001600160a01b0381168114611a725760405162461bcd60e51b8152600401610624906134f2565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b604080516060810182526000808252602082018190529181019190915290565b60405180610120016040528060001515815260200160006001600160401b031681526020016000815260200160008152602001611cb0611ce1565b8152602001611cbd611ce1565b815260200160006001600160a01b0316815260200160008152602001600081525090565b60405180608001604052806004905b611cf8611d0e565b815260200190600190039081611cf05790505090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b803561057081613830565b805161057081613830565b600082601f830112611d6357600080fd5b8135611d76611d7182613745565b61371f565b81815260209384019390925082018360005b83811015611db45781358601611d9e8882611e8c565b8452506020928301929190910190600101611d88565b5050505092915050565b60008083601f840112611dd057600080fd5b5081356001600160401b03811115611de757600080fd5b602083019150836020820283011115611dff57600080fd5b9250929050565b600082601f830112611e1757600080fd5b8135611e25611d7182613745565b91508181835260208401935060208101905083856020840282011115611e4a57600080fd5b60005b83811015611db45781611e608882611e81565b8452506020928301929190910190600101611e4d565b803561057081613847565b803561057081613850565b600082601f830112611e9d57600080fd5b8135611eab611d7182613765565b91508082526020830160208301858383011115611ec757600080fd5b611ed28382846137ee565b50505092915050565b60006101208284031215611eee57600080fd5b611ef961012061371f565b905081356001600160401b03811115611f1157600080fd5b611f1d84828501611e8c565b8252506020611f2e84848301611e81565b60208301525060408201356001600160401b03811115611f4d57600080fd5b611f5984828501611e8c565b6040830152506060611f6d84828501612508565b60608301525060808201356001600160401b03811115611f8c57600080fd5b611f9884828501611e8c565b60808301525060a0611fac84828501612508565b60a08301525060c0611fc084828501611e81565b60c08301525060e08201356001600160401b03811115611fdf57600080fd5b611feb84828501611e8c565b60e0830152506101008201356001600160401b0381111561200b57600080fd5b61201784828501611e8c565b6101008301525092915050565b6000610100828403121561203757600080fd5b61204261010061371f565b905081356001600160401b0381111561205a57600080fd5b61206684828501611e8c565b825250602061207784848301612508565b60208301525060408201356001600160401b0381111561209657600080fd5b6120a284828501611e8c565b60408301525060606120b684828501612508565b60608301525060808201356001600160401b038111156120d557600080fd5b6120e184828501611e8c565b60808301525060a08201356001600160401b0381111561210057600080fd5b61210c84828501611e8c565b60a08301525060c061212084828501611e81565b60c08301525060e061213484828501611e81565b60e08301525092915050565b600060e0828403121561215257600080fd5b61215c60e061371f565b905081356001600160401b0381111561217457600080fd5b61218084828501611e8c565b82525060208201356001600160401b0381111561219c57600080fd5b6121a884828501611e8c565b60208301525060406121bc84828501611e81565b60408301525060608201356001600160401b038111156121db57600080fd5b6121e784828501611e8c565b60608301525060806121fb84828501612508565b60808301525060a08201356001600160401b0381111561221a57600080fd5b61222684828501611e8c565b60a08301525060c061223a84828501611e81565b60c08301525092915050565b600060c0828403121561225857600080fd5b61226260c061371f565b905060006122708484612513565b82525060208201356001600160401b0381111561228c57600080fd5b61229884828501611e8c565b60208301525060408201356001600160401b038111156122b757600080fd5b6122c384828501611e8c565b60408301525060606122d784828501612508565b60608301525060808201356001600160401b038111156122f657600080fd5b61230284828501611e8c565b60808301525060a061231684828501611e81565b60a08301525092915050565b60006040828403121561233457600080fd5b61233e604061371f565b905081356001600160401b0381111561235657600080fd5b61236284828501611e8c565b825250602061237384848301612508565b60208301525092915050565b600060a0828403121561239157600080fd5b61239b60a061371f565b905081356001600160401b038111156123b357600080fd5b6123bf84828501611e8c565b82525060208201356001600160401b038111156123db57600080fd5b6123e784828501611d52565b60208301525060408201356001600160401b0381111561240657600080fd5b61241284828501611e06565b60408301525060608201356001600160401b0381111561243157600080fd5b61243d84828501611d52565b60608301525060808201356001600160401b0381111561245c57600080fd5b61246884828501611d52565b60808301525092915050565b60006060828403121561248657600080fd5b612490606061371f565b9050600061249e8484611e81565b82525060208201356001600160401b038111156124ba57600080fd5b6124c684828501611e8c565b60208301525060408201356001600160401b038111156124e557600080fd5b6124f184828501611e8c565b60408301525092915050565b803561057081613859565b803561057081613862565b80356105708161386b565b60006020828403121561253057600080fd5b6000611a728484611d47565b6000806020838503121561254f57600080fd5b82356001600160401b0381111561256557600080fd5b61257185828601611dbe565b92509250509250929050565b60008060006060848603121561259257600080fd5b600061259e8686611e76565b93505060208401356001600160401b038111156125ba57600080fd5b6125c686828701611e8c565b92505060406125d786828701611e81565b9150509250925092565b6000602082840312156125f357600080fd5b81356001600160401b0381111561260957600080fd5b611a7284828501611e8c565b60008060006060848603121561262a57600080fd5b83356001600160401b0381111561264057600080fd5b61264c86828701611e8c565b935050602061265d86828701611e81565b92505060408401356001600160401b0381111561267957600080fd5b6125d786828701611e8c565b60006020828403121561269757600080fd5b81356001600160401b038111156126ad57600080fd5b611a7284828501611edb565b6000602082840312156126cb57600080fd5b81356001600160401b038111156126e157600080fd5b611a7284828501612024565b6000602082840312156126ff57600080fd5b81356001600160401b0381111561271557600080fd5b611a7284828501612140565b60006020828403121561273357600080fd5b81356001600160401b0381111561274957600080fd5b611a7284828501612246565b60006020828403121561276757600080fd5b81356001600160401b0381111561277d57600080fd5b611a7284828501612322565b60006020828403121561279b57600080fd5b81356001600160401b038111156127b157600080fd5b611a728482850161237f565b6000602082840312156127cf57600080fd5b81356001600160401b038111156127e557600080fd5b611a7284828501612474565b60006020828403121561280357600080fd5b6000611a7284846124fd565b60006020828403121561282157600080fd5b6000611a728484612513565b60008060006060848603121561284257600080fd5b600061284e8686612513565b935050602061285f86828701611e81565b92505060406125d786828701611d3c565b600061102b8383612a9d565b600061288883836131b3565b50506105e00190565b600061289d838361328f565b505060c00190565b60006128b1838361339f565b505060a00190565b60006128c58383612a94565b505060200190565b6128d6816137a8565b82525050565b60006128e782613795565b6128f1818561379f565b9350836020820285016129038561378c565b8060005b8581101561293d57848403895281516129208582612870565b945061292b8361378c565b60209a909a0199925050600101612907565b5091979650505050505050565b600061295582613795565b61295f818561379f565b935061296a8361378c565b8060005b83811015612998578151612982888261287c565b975061298d8361378c565b92505060010161296e565b509495945050505050565b60006129ae82613795565b6129b8818561379f565b93506129c38361378c565b8060005b838110156129985781516129db8882612891565b97506129e68361378c565b9250506001016129c7565b6129fa81613799565b612a048184610df1565b9250612a0f82613792565b8060005b838110156107c5578151612a2787826128a5565b9650612a328361378c565b925050600101612a13565b6000612a4882613795565b612a52818561379f565b9350612a5d8361378c565b8060005b83811015612998578151612a7588826128b9565b9750612a808361378c565b925050600101612a61565b6128d6816137b3565b6128d681613792565b6000612aa882613795565b612ab2818561379f565b9350612ac28185602086016137fa565b612acb81613826565b9093019392505050565b6000612ae082613795565b612aea8185610df1565b9350612afa8185602086016137fa565b9290920192915050565b6128d6816137e3565b6000612b1a60348361379f565b7f626c6f636b4e756d2065786365656473206d61782073697a6520616c6c6f77658152736420696e20506c61736d614672616d65776f726b60601b602082015260400192915050565b6000612b7060228361379f565b7f7478496e6465782065786365656473207468652073697a65206f662075696e74815261189b60f11b602082015260400192915050565b6000612bb460208361379f565b7f496e7075742076616c7565206d757374206d61746368206d73672e76616c7565815260200192915050565b6000612bed60158361379f565b74084dedcc840e6d2f4ca40d2e640e8dede40d0d2ced605b1b815260200192915050565b6000612c1e60148361379f565b73092dcecc2d8d2c840deeae8e0eae840d2dcc8caf60631b815260200192915050565b6000612c4e60188361379f565b7f426f6e642073697a652063616e6e6f74206265207a65726f0000000000000000815260200192915050565b6000612c8760148361379f565b73426f6e642073697a6520697320746f6f206c6f7760601b815260200192915050565b6000612cb7601e8361379f565b7f43616c6c6572206164647265737320697320756e617574686f72697a65640000815260200192915050565b6000612cf060148361379f565b7324b73b30b634b210313637b1b590373ab6b132b960611b815260200192915050565b6000612d2060108361379f565b6f457869744964206f766572666c6f777360801b815260200192915050565b80516020830190610c508482612a94565b805161012080845260009190840190612d698282612a9d565b9150506020830151612d7e6020860182612a94565b5060408301518482036040860152612d968282612a9d565b9150506060830151612dab60608601826133fb565b5060808301518482036080860152612dc38282612a9d565b91505060a0830151612dd860a08601826133fb565b5060c0830151612deb60c0860182612a94565b5060e083015184820360e0860152612e038282612a9d565b915050610100830151848203610100860152612e1f8282612a9d565b95945050505050565b805161010080845260009190840190612e418282612a9d565b9150506020830151612e5660208601826133fb565b5060408301518482036040860152612e6e8282612a9d565b9150506060830151612e8360608601826133fb565b5060808301518482036080860152612e9b8282612a9d565b91505060a083015184820360a0860152612eb58282612a9d565b91505060c0830151612eca60c0860182612a94565b5060e083015161056c60e0860182612a94565b805160e080845260009190840190612ef58282612a9d565b91505060208301518482036020860152612f0f8282612a9d565b9150506040830151612f246040860182612a94565b5060608301518482036060860152612f3c8282612a9d565b9150506080830151612f5160808601826133fb565b5060a083015184820360a0860152612f698282612a9d565b91505060c083015161056c60c0860182612a94565b805160009060c0840190612f9285826133f2565b5060208301518482036020860152612faa8282612a9d565b91505060408301518482036040860152612fc48282612a9d565b9150506060830151612fd960608601826133fb565b5060808301518482036080860152612ff18282612a9d565b91505060a083015161056c60a0860182612a94565b805160608301906130178482612b04565b50602082015161302a6020850182612b04565b506040820151610c506040850182612a94565b8051604083019061304e8482612a94565b506020820151610c506020850182612a94565b805160c08301906130728482612b04565b5060208201516130856020850182612d3f565b5060408201516130986040850182612b04565b5060608201516130ab6060850182612a94565b5060808201516130be6080850182612a94565b5060a0820151610c5060a0850182612a94565b805160808301906130e28482612b04565b5060208201516130f56020850182612b04565b5060408201516131086040850182612b04565b506060820151610c506060850182612a94565b805160a083019061312c8482612b04565b50602082015161313f6020850182612d3f565b5060408201516131526040850182612b04565b5060608201516131656060850182612b04565b506080820151610c506080850182612a94565b805160c08301906131898482612b04565b50602082015161319c6020850182612b04565b5060408201516130986040850182612d3f565b9052565b80516105e08301906131c58482612a8b565b5060208201516131d86020850182613415565b5060408201516131eb6040850182612a94565b5060608201516131fe6060850182612a94565b50608082015161321160808501826129f1565b5060a08201516132256103008501826129f1565b5060c08201516132396105808501826128cd565b5060e082015161324d6105a0850182612a94565b50610100820151610c506105c0850182612a94565b805160408084526000919084019061327a8282612a9d565b915050602083015161056c60208601826133fb565b805160c08301906132a08482612a8b565b5060208201516132b36020850182612a94565b5060408201516132c66040850182612a94565b5060608201516130ab60608501826128cd565b805160a0808452600091908401906132f18282612a9d565b9150506020830151848203602086015261330b82826128dc565b915050604083015184820360408601526133258282612a3d565b9150506060830151848203606086015261333f82826128dc565b91505060808301518482036080860152612e1f82826128dc565b8051600090606084019061336d8582612a94565b50602083015184820360208601526133858282612a9d565b91505060408301518482036040860152612e1f8282612a9d565b805160a08301906133b08482612a94565b5060208201516133c360208501826128cd565b5060408201516133d660408501826128cd565b5060608201516131656060850182612a94565b6128d6816137b8565b6128d6816137cb565b6128d6816137c4565b6128d661341082613792565b613792565b6128d6816137d7565b600061342a8285612ad5565b91506134368284613404565b5060200192915050565b6020808252810161102b818461294a565b6020808252810161102b81846129a3565b60208082528101610dee81612b0d565b60208082528101610dee81612b63565b60208082528101610dee81612ba7565b60208082528101610dee81612be0565b60208082528101610dee81612c11565b60208082528101610dee81612c41565b60208082528101610dee81612c7a565b60208082528101610dee81612caa565b60208082528101610dee81612ce3565b60208082528101610dee81612d13565b60a081016135108286613006565b61351d60608301856131af565b8181036080830152612e1f8184612e28565b60e0810161353d8288613006565b61354a60608301876131af565b818103608083015261355c8186612a9d565b905061356b60a0830185612a94565b81810360c083015261357d8184612a9d565b979650505050505050565b60a081016135968286613006565b6135a360608301856131af565b8181036080830152612e1f8184612d50565b60a081016135c38286613006565b6135d060608301856131af565b8181036080830152612e1f8184612edd565b60a081016135f08286613006565b6135fd60608301856131af565b8181036080830152612e1f8184612f7e565b6080810161361d828661303d565b61362a60408301856131af565b611a7260608301846133f2565b61010081016136468286613061565b61365360c08301856131af565b81810360e0830152612e1f8184613262565b60e0810161367382876130d1565b61368060808301866131af565b61368d60a08301856133f2565b612e1f60c08301846128cd565b60e081016136a8828661311b565b6136b560a08301856131af565b81810360c0830152612e1f81846132d9565b61010081016136d68286613178565b6136e360c08301856131af565b81810360e0830152612e1f8184613359565b6020810161057082846133e9565b6020810161057082846133f2565b6020810161057082846133fb565b6040518181016001600160401b038111828210171561373d57600080fd5b604052919050565b60006001600160401b0382111561375b57600080fd5b5060209081020190565b60006001600160401b0382111561377b57600080fd5b506020601f91909101601f19160190565b60200190565b90565b5190565b50600490565b90815260200190565b6000610dee826137cb565b151590565b6001600160801b031690565b61ffff1690565b6001600160a01b031690565b6001600160401b031690565b6000610dee826137a8565b82818337506000910152565b60005b838110156138155781810151838201526020016137fd565b83811115610c505750506000910152565b601f01601f191690565b613839816137a8565b811461384457600080fd5b50565b613839816137b3565b61383981613792565b613839816137b8565b613839816137c4565b613839816137cb565b61387c611d0e565b506040805160a0810182526001600160801b03949094168452600060208501526780000000000000009084015261ffff91821660608401521660808201529056fea365627a7a723158209c00be5c05ba01aa62f0d0328e226c7eee7b07c7d18bf1f24a73b36e65a35a496c6578706572696d656e74616cf564736f6c634300050b00400000000000000000000000000d4c1222f5e839a911e2053860e45f18921d72ac00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a5230d65bfab06d0520bc8ec18474b999e5413af000000000000000000000000890d4b9d3e20c9390845e2bf2ccdd77a5822ac23000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000008fc
Deployed Bytecode
0x6080604052600436106101815760003560e01c8063a0e403b1116100d1578063c170ecf51161008a578063df2933e211610064578063df2933e214610409578063e5bc60c714610429578063e83622981461043c578063fe32a1241461045c57610181565b8063c170ecf5146103b2578063c96e7c05146103c7578063cec9e1a7146103dc57610181565b8063a0e403b1146102fd578063a4a254411461031f578063b177ba2314610332578063b22d3e1414610352578063b3c2ec7614610372578063b9a3a28e1461039257610181565b80635a5285141161013e5780637702fa17116101185780637702fa17146102a65780637e2b2efe146102bb578063839c78f9146102d0578063905d6a99146102fd57610181565b80635a52851414610260578063684963511461027357806370e014621461029357610181565b80630ca5b67614610186578063163d7bc7146101bc5780632c3bad95146101de578063315fb7da146101fe57806336660de41461021e5780633f1214cf1461023e575b600080fd5b34801561019257600080fd5b506101a66101a136600461253c565b610471565b6040516101b39190613451565b60405180910390f35b3480156101c857600080fd5b506101dc6101d73660046127f1565b610576565b005b3480156101ea57600080fd5b506101dc6101f93660046126ed565b610679565b34801561020a57600080fd5b506101dc6102193660046127f1565b6107cd565b34801561022a57600080fd5b506101dc610239366004612615565b6108bb565b34801561024a57600080fd5b50610253610a15565b6040516101b391906136f5565b6101dc61026e366004612789565b610a7a565b34801561027f57600080fd5b506101dc61028e36600461282d565b610bfe565b6101dc6102a13660046127bd565b610c56565b3480156102b257600080fd5b50610253610d78565b3480156102c757600080fd5b50610253610dd8565b3480156102dc57600080fd5b506102f06102eb3660046125e1565b610de3565b6040516101b39190613703565b34801561030957600080fd5b50610312610df6565b6040516101b39190613711565b6101dc61032d366004612755565b610dfb565b34801561033e57600080fd5b506101dc61034d3660046127f1565b610f19565b34801561035e57600080fd5b506102f061036d36600461257d565b611007565b34801561037e57600080fd5b506101dc61038d36600461280f565b611032565b34801561039e57600080fd5b506101dc6103ad3660046126b9565b6110eb565b3480156103be57600080fd5b506102536111b8565b3480156103d357600080fd5b506102536111c3565b3480156103e857600080fd5b506103fc6103f736600461253c565b6111ce565b6040516101b39190613440565b34801561041557600080fd5b506101dc610424366004612721565b6113f7565b6101dc610437366004612755565b6114c4565b34801561044857600080fd5b506101dc610457366004612685565b6115e2565b34801561046857600080fd5b506102536116af565b606080838390506040519080825280602002602001820160405280156104b157816020015b61049e611c20565b8152602001906001900390816104965790505b50905060005b8381101561056c5760008585838181106104cd57fe5b90506020020160206104e2919081019061280f565b6001600160a01b0380821660009081526020818152604091829020825160c081018452815460ff161515815260018201549281019290925260028101549282019290925260038201549092166060830152600481015460808301526005015460a082015284519192509084908490811061055857fe5b6020908102919091010152506001016104b7565b5090505b92915050565b601060009054906101000a90046001600160a01b03166001600160a01b0316634b0a72bc6040518163ffffffff1660e01b815260040160206040518083038186803b1580156105c457600080fd5b505afa1580156105d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506105fc919081019061251e565b336001600160a01b0382161461062d5760405162461bcd60e51b8152600401610624906134d2565b60405180910390fd5b61063e600e8363ffffffff61170f16565b7fd21ccbbae3d29826d53310efdc993c64e27496dd31694b292f1a120344ec37ab8260405161066d91906136f5565b60405180910390a15050565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156106ca57600080fd5b505af11580156106de573d6000803e3d6000fd5b5050604080516060810182526023546001600160a01b0390811682526024541660208201526025548183015290516359cff02160e01b8152737a8c0d7f1e6dbe36ab3a9ade0c44a0b868bb703f93506359cff0219250610746919060119087906004016135b5565b60006040518083038186803b15801561075e57600080fd5b505af4158015610772573d6000803e3d6000fd5b50505050806001600160a01b0316634e0f74366040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156107b157600080fd5b505af11580156107c5573d6000803e3d6000fd5b505050505050565b603060009054906101000a90046001600160a01b03166001600160a01b0316634b0a72bc6040518163ffffffff1660e01b815260040160206040518083038186803b15801561081b57600080fd5b505afa15801561082f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610853919081019061251e565b336001600160a01b0382161461087b5760405162461bcd60e51b8152600401610624906134d2565b61088c602e8363ffffffff61170f16565b7f30ce291291dca42b8168ab211fafc36d3d4868292ed3259874ed00864432e20e8260405161066d91906136f5565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561090c57600080fd5b505af1158015610920573d6000803e3d6000fd5b505060408051606081018252601d546001600160a01b039081168252601e54166020820152601f54818301529051630c27711f60e11b8152739e3108fb11caeda64e264b2953a0eda81cf1a650935063184ee23e925061098c919060119089908990899060040161352f565b60006040518083038186803b1580156109a457600080fd5b505af41580156109b8573d6000803e3d6000fd5b50505050806001600160a01b0316634e0f74366040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156109f757600080fd5b505af1158015610a0b573d6000803e3d6000fd5b5050505050505050565b6040805160a081018252602e546001600160801b038082168352600160801b9182900481166020840152602f549081169383019390935261ffff90830481166060830152600160901b9092049091166080820152600090610a75906117fd565b905090565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610acb57600080fd5b505af1158015610adf573d6000803e3d6000fd5b50505050610aeb610d78565b6001600160801b0316803414610b135760405162461bcd60e51b815260040161062490613482565b6040805160a0810182526012546001600160a01b039081168252825160208181018552601354825283015260145481168284015260155416606082015260165460808201529051631ff4513b60e01b815273081d7b167a94e7421ea5d0016c3d01e0cff6b55791631ff4513b91610b929190601190889060040161369a565b60006040518083038186803b158015610baa57600080fd5b505af4158015610bbe573d6000803e3d6000fd5b5050505050806001600160a01b0316634e0f74366040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156107b157600080fd5b6031546001600160a01b0316338114610c295760405162461bcd60e51b8152600401610624906134d2565b610c3284611827565b15610c4657610c41848361184d565b610c50565b610c5084836118e9565b50505050565b601060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610ca757600080fd5b505af1158015610cbb573d6000803e3d6000fd5b50505050610cc76116af565b6001600160801b0316803414610cef5760405162461bcd60e51b815260040161062490613482565b6040805160c0810182526001546001600160a01b0390811682526002541660208083019190915282519081018352600354815281830152600480546060830152600554608083015260065460a083015291516332c812a760e11b815273f38380bbf08961123960fad630a060990684975192636590254e92610b929290916000918991016136c7565b6040805160a081018252602c546001600160801b038082168352600160801b9182900481166020840152602d549081169383019390935261ffff90830481166060830152600160901b9092049091166080820152600090610a75906117fd565b666379da05b6000081565b6000610dee82611959565b90505b919050565b600281565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b158015610e4c57600080fd5b505af1158015610e60573d6000803e3d6000fd5b50505050610e6c610a15565b6001600160801b0316803414610e945760405162461bcd60e51b815260040161062490613482565b6040805160c0810182526017546001600160a01b03908116825282516020818101855260185482528301526019541681830152601a546060820152601b546080820152601c5460a0820152905163082153e960e21b8152735d3ee50e31293b45a7eb8b864c0b015e169ac7d8916320854fa491610b9291906011908890600401613637565b603060009054906101000a90046001600160a01b03166001600160a01b0316634b0a72bc6040518163ffffffff1660e01b815260040160206040518083038186803b158015610f6757600080fd5b505afa158015610f7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f9f919081019061251e565b336001600160a01b03821614610fc75760405162461bcd60e51b8152600401610624906134d2565b610fd8602c8363ffffffff61170f16565b7f63ccac61f03626a0e717e96da239ff8996745b6c4ddcaffdedc88d7ef938f5ec8260405161066d91906136f5565b6000611011611c55565b61101a83611977565b9050611027858583611a0a565b9150505b9392505050565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561108357600080fd5b505af1158015611097573d6000803e3d6000fd5b505060408051808201825260265481526027546020820152905163b1cf0fed60e01b8152734f05855b6df026726037500d10d91652b5c9e784935063b1cf0fed92506107469190601190879060040161360f565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561113c57600080fd5b505af1158015611150573d6000803e3d6000fd5b505060408051606081018252602080546001600160a01b0390811683526021541690820152602254818301529051634bb4a4df60e11b815273ef6133d149460c9b79e132517934f77f1e62b23e935063976949be925061074691906011908790600401613502565b6631bced02db000081565b6683734dd0b0800081565b6060808383905060405190808252806020026020018201604052801561120e57816020015b6111fb611c75565b8152602001906001900390816111f35790505b50905060005b8381101561056c57600085858381811061122a57fe5b905060200201602061123f919081019061280f565b6001600160a01b0381166000908152601160209081526040808320815161012081018352815460ff81161515825261010090046001600160401b03169381019390935260018101548383015260028101546060840152815160808082019093529495509193919290840191906003840190600490835b8282101561131c576040805160a081018252600584028601805482526001808201546001600160a01b0390811660208086019190915260028401549091169484019490945260038201546060840152600490910154608083015290835290920191016112b5565b5050509082525060408051608081019091526020909101906017830160046000835b828210156113a5576040805160a081018252600584028601805482526001808201546001600160a01b03908116602080860191909152600284015490911694840194909452600382015460608401526004909101546080830152908352909201910161133e565b50505090825250602b8201546001600160a01b03166020820152602c8201546040820152602d9091015460609091015283518490849081106113e357fe5b602090810291909101015250600101611214565b601060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561144857600080fd5b505af115801561145c573d6000803e3d6000fd5b505060408051606081018252600b546001600160a01b039081168252600c54166020820152600d5481830152905163edc91d4b60e01b81527324c0a84090135ad04f257c85e10db6b4a74e0738935063edc91d4b9250610746919060009087906004016135e2565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561151557600080fd5b505af1158015611529573d6000803e3d6000fd5b50505050611535610a15565b6001600160801b031680341461155d5760405162461bcd60e51b815260040161062490613482565b6040805160c0810182526017546001600160a01b03908116825282516020818101855260185482528301526019541681830152601a546060820152601b546080820152601c5460a08201529051631845801f60e11b8152735d3ee50e31293b45a7eb8b864c0b015e169ac7d89163308b003e91610b9291906011908890600401613637565b603060009054906101000a90046001600160a01b0316806001600160a01b031663ff27b1896040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561163357600080fd5b505af1158015611647573d6000803e3d6000fd5b505060408051606081018252601d546001600160a01b039081168252601e54166020820152601f54818301529051631287d7e160e31b8152739e3108fb11caeda64e264b2953a0eda81cf1a650935063943ebf08925061074691906011908790600401613588565b6040805160a081018252600e546001600160801b038082168352600160801b9182900481166020840152600f549081169383019390935261ffff90830481166060830152600160901b9092049091166080820152600090610a75906117fd565b6040805160a08101825283546001600160801b038082168352600160801b918290048116602084015260018601549081169383019390935261ffff90830481166060830152600160901b909204909116608082015261176e9082611a7a565b8154600160801b90046001600160801b03161580159061179b575060018201546001600160801b03164210155b156117c2578154600160801b81046001600160801b03166001600160801b03199091161782555b81546001600160801b03918216600160801b02911617815560010180546001600160801b0319166001600160401b03426202a3000116179055565b600081604001516001600160801b031642101561181c57508051610df1565b506020810151610df1565b60006118436001600160a01b038316609763ffffffff611b3d16565b60ff161592915050565b604080516080810182526007546001600160a01b039081168252600854811660208301526009541681830152600a54606082015290516373c97b6d60e11b8152735e84df30ce17a9ac34e5474fc37a9c22674545189163e792f6da916118bd919060009087908790600401613665565b60006040518083038186803b1580156118d557600080fd5b505af41580156107c5573d6000803e3d6000fd5b604080516080810182526028546001600160a01b03908116825260295481166020830152602a541681830152602b5460608201529051632309cb3d60e01b81527367c3e5524dbe05b54fe08c3d4af65c273b87e63091632309cb3d916118bd919060119087908790600401613665565b80516020820120600090610dee9060691c609763ffffffff611b4716565b61197f611c55565b633b9aca00808304906127109084068190049084066429f16b11c68311156119b95760405162461bcd60e51b815260040161062490613462565b61ffff8211156119db5760405162461bcd60e51b815260040161062490613472565b604080516060810182526001600160401b03909416845261ffff92831660208501529116908201529050919050565b60008315611a5d57600083611a1e84611b54565b604051602001611a2f92919061341e565b604051602081830303815290604052805190602001209050611a55818460400151611be3565b91505061102b565b611a7283805190602001208360400151611be3565b949350505050565b6000611a85836117fd565b90506000826001600160801b031611611ab05760405162461bcd60e51b8152600401610624906134b2565b826060015161ffff16816001600160801b031681611aca57fe5b046001600160801b0316826001600160801b03161015611afc5760405162461bcd60e51b8152600401610624906134c2565b826080015161ffff16816001600160801b031602826001600160801b03161115611b385760405162461bcd60e51b815260040161062490613492565b505050565b60ff161c60011690565b600160ff919091161b1790565b6000600161271003826040015161ffff161115611b835760405162461bcd60e51b8152600401610624906134a2565b81516429f16b11c66001600160401b039091161115611bb45760405162461bcd60e51b8152600401610624906134e2565b506040810151602082015182516001600160401b0316633b9aca000261ffff9182166127100201911601919050565b6000606983901c61ffff60981b609884901b1617806001600160a01b0381168114611a725760405162461bcd60e51b8152600401610624906134f2565b6040805160c081018252600080825260208201819052918101829052606081018290526080810182905260a081019190915290565b604080516060810182526000808252602082018190529181019190915290565b60405180610120016040528060001515815260200160006001600160401b031681526020016000815260200160008152602001611cb0611ce1565b8152602001611cbd611ce1565b815260200160006001600160a01b0316815260200160008152602001600081525090565b60405180608001604052806004905b611cf8611d0e565b815260200190600190039081611cf05790505090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915290565b803561057081613830565b805161057081613830565b600082601f830112611d6357600080fd5b8135611d76611d7182613745565b61371f565b81815260209384019390925082018360005b83811015611db45781358601611d9e8882611e8c565b8452506020928301929190910190600101611d88565b5050505092915050565b60008083601f840112611dd057600080fd5b5081356001600160401b03811115611de757600080fd5b602083019150836020820283011115611dff57600080fd5b9250929050565b600082601f830112611e1757600080fd5b8135611e25611d7182613745565b91508181835260208401935060208101905083856020840282011115611e4a57600080fd5b60005b83811015611db45781611e608882611e81565b8452506020928301929190910190600101611e4d565b803561057081613847565b803561057081613850565b600082601f830112611e9d57600080fd5b8135611eab611d7182613765565b91508082526020830160208301858383011115611ec757600080fd5b611ed28382846137ee565b50505092915050565b60006101208284031215611eee57600080fd5b611ef961012061371f565b905081356001600160401b03811115611f1157600080fd5b611f1d84828501611e8c565b8252506020611f2e84848301611e81565b60208301525060408201356001600160401b03811115611f4d57600080fd5b611f5984828501611e8c565b6040830152506060611f6d84828501612508565b60608301525060808201356001600160401b03811115611f8c57600080fd5b611f9884828501611e8c565b60808301525060a0611fac84828501612508565b60a08301525060c0611fc084828501611e81565b60c08301525060e08201356001600160401b03811115611fdf57600080fd5b611feb84828501611e8c565b60e0830152506101008201356001600160401b0381111561200b57600080fd5b61201784828501611e8c565b6101008301525092915050565b6000610100828403121561203757600080fd5b61204261010061371f565b905081356001600160401b0381111561205a57600080fd5b61206684828501611e8c565b825250602061207784848301612508565b60208301525060408201356001600160401b0381111561209657600080fd5b6120a284828501611e8c565b60408301525060606120b684828501612508565b60608301525060808201356001600160401b038111156120d557600080fd5b6120e184828501611e8c565b60808301525060a08201356001600160401b0381111561210057600080fd5b61210c84828501611e8c565b60a08301525060c061212084828501611e81565b60c08301525060e061213484828501611e81565b60e08301525092915050565b600060e0828403121561215257600080fd5b61215c60e061371f565b905081356001600160401b0381111561217457600080fd5b61218084828501611e8c565b82525060208201356001600160401b0381111561219c57600080fd5b6121a884828501611e8c565b60208301525060406121bc84828501611e81565b60408301525060608201356001600160401b038111156121db57600080fd5b6121e784828501611e8c565b60608301525060806121fb84828501612508565b60808301525060a08201356001600160401b0381111561221a57600080fd5b61222684828501611e8c565b60a08301525060c061223a84828501611e81565b60c08301525092915050565b600060c0828403121561225857600080fd5b61226260c061371f565b905060006122708484612513565b82525060208201356001600160401b0381111561228c57600080fd5b61229884828501611e8c565b60208301525060408201356001600160401b038111156122b757600080fd5b6122c384828501611e8c565b60408301525060606122d784828501612508565b60608301525060808201356001600160401b038111156122f657600080fd5b61230284828501611e8c565b60808301525060a061231684828501611e81565b60a08301525092915050565b60006040828403121561233457600080fd5b61233e604061371f565b905081356001600160401b0381111561235657600080fd5b61236284828501611e8c565b825250602061237384848301612508565b60208301525092915050565b600060a0828403121561239157600080fd5b61239b60a061371f565b905081356001600160401b038111156123b357600080fd5b6123bf84828501611e8c565b82525060208201356001600160401b038111156123db57600080fd5b6123e784828501611d52565b60208301525060408201356001600160401b0381111561240657600080fd5b61241284828501611e06565b60408301525060608201356001600160401b0381111561243157600080fd5b61243d84828501611d52565b60608301525060808201356001600160401b0381111561245c57600080fd5b61246884828501611d52565b60808301525092915050565b60006060828403121561248657600080fd5b612490606061371f565b9050600061249e8484611e81565b82525060208201356001600160401b038111156124ba57600080fd5b6124c684828501611e8c565b60208301525060408201356001600160401b038111156124e557600080fd5b6124f184828501611e8c565b60408301525092915050565b803561057081613859565b803561057081613862565b80356105708161386b565b60006020828403121561253057600080fd5b6000611a728484611d47565b6000806020838503121561254f57600080fd5b82356001600160401b0381111561256557600080fd5b61257185828601611dbe565b92509250509250929050565b60008060006060848603121561259257600080fd5b600061259e8686611e76565b93505060208401356001600160401b038111156125ba57600080fd5b6125c686828701611e8c565b92505060406125d786828701611e81565b9150509250925092565b6000602082840312156125f357600080fd5b81356001600160401b0381111561260957600080fd5b611a7284828501611e8c565b60008060006060848603121561262a57600080fd5b83356001600160401b0381111561264057600080fd5b61264c86828701611e8c565b935050602061265d86828701611e81565b92505060408401356001600160401b0381111561267957600080fd5b6125d786828701611e8c565b60006020828403121561269757600080fd5b81356001600160401b038111156126ad57600080fd5b611a7284828501611edb565b6000602082840312156126cb57600080fd5b81356001600160401b038111156126e157600080fd5b611a7284828501612024565b6000602082840312156126ff57600080fd5b81356001600160401b0381111561271557600080fd5b611a7284828501612140565b60006020828403121561273357600080fd5b81356001600160401b0381111561274957600080fd5b611a7284828501612246565b60006020828403121561276757600080fd5b81356001600160401b0381111561277d57600080fd5b611a7284828501612322565b60006020828403121561279b57600080fd5b81356001600160401b038111156127b157600080fd5b611a728482850161237f565b6000602082840312156127cf57600080fd5b81356001600160401b038111156127e557600080fd5b611a7284828501612474565b60006020828403121561280357600080fd5b6000611a7284846124fd565b60006020828403121561282157600080fd5b6000611a728484612513565b60008060006060848603121561284257600080fd5b600061284e8686612513565b935050602061285f86828701611e81565b92505060406125d786828701611d3c565b600061102b8383612a9d565b600061288883836131b3565b50506105e00190565b600061289d838361328f565b505060c00190565b60006128b1838361339f565b505060a00190565b60006128c58383612a94565b505060200190565b6128d6816137a8565b82525050565b60006128e782613795565b6128f1818561379f565b9350836020820285016129038561378c565b8060005b8581101561293d57848403895281516129208582612870565b945061292b8361378c565b60209a909a0199925050600101612907565b5091979650505050505050565b600061295582613795565b61295f818561379f565b935061296a8361378c565b8060005b83811015612998578151612982888261287c565b975061298d8361378c565b92505060010161296e565b509495945050505050565b60006129ae82613795565b6129b8818561379f565b93506129c38361378c565b8060005b838110156129985781516129db8882612891565b97506129e68361378c565b9250506001016129c7565b6129fa81613799565b612a048184610df1565b9250612a0f82613792565b8060005b838110156107c5578151612a2787826128a5565b9650612a328361378c565b925050600101612a13565b6000612a4882613795565b612a52818561379f565b9350612a5d8361378c565b8060005b83811015612998578151612a7588826128b9565b9750612a808361378c565b925050600101612a61565b6128d6816137b3565b6128d681613792565b6000612aa882613795565b612ab2818561379f565b9350612ac28185602086016137fa565b612acb81613826565b9093019392505050565b6000612ae082613795565b612aea8185610df1565b9350612afa8185602086016137fa565b9290920192915050565b6128d6816137e3565b6000612b1a60348361379f565b7f626c6f636b4e756d2065786365656473206d61782073697a6520616c6c6f77658152736420696e20506c61736d614672616d65776f726b60601b602082015260400192915050565b6000612b7060228361379f565b7f7478496e6465782065786365656473207468652073697a65206f662075696e74815261189b60f11b602082015260400192915050565b6000612bb460208361379f565b7f496e7075742076616c7565206d757374206d61746368206d73672e76616c7565815260200192915050565b6000612bed60158361379f565b74084dedcc840e6d2f4ca40d2e640e8dede40d0d2ced605b1b815260200192915050565b6000612c1e60148361379f565b73092dcecc2d8d2c840deeae8e0eae840d2dcc8caf60631b815260200192915050565b6000612c4e60188361379f565b7f426f6e642073697a652063616e6e6f74206265207a65726f0000000000000000815260200192915050565b6000612c8760148361379f565b73426f6e642073697a6520697320746f6f206c6f7760601b815260200192915050565b6000612cb7601e8361379f565b7f43616c6c6572206164647265737320697320756e617574686f72697a65640000815260200192915050565b6000612cf060148361379f565b7324b73b30b634b210313637b1b590373ab6b132b960611b815260200192915050565b6000612d2060108361379f565b6f457869744964206f766572666c6f777360801b815260200192915050565b80516020830190610c508482612a94565b805161012080845260009190840190612d698282612a9d565b9150506020830151612d7e6020860182612a94565b5060408301518482036040860152612d968282612a9d565b9150506060830151612dab60608601826133fb565b5060808301518482036080860152612dc38282612a9d565b91505060a0830151612dd860a08601826133fb565b5060c0830151612deb60c0860182612a94565b5060e083015184820360e0860152612e038282612a9d565b915050610100830151848203610100860152612e1f8282612a9d565b95945050505050565b805161010080845260009190840190612e418282612a9d565b9150506020830151612e5660208601826133fb565b5060408301518482036040860152612e6e8282612a9d565b9150506060830151612e8360608601826133fb565b5060808301518482036080860152612e9b8282612a9d565b91505060a083015184820360a0860152612eb58282612a9d565b91505060c0830151612eca60c0860182612a94565b5060e083015161056c60e0860182612a94565b805160e080845260009190840190612ef58282612a9d565b91505060208301518482036020860152612f0f8282612a9d565b9150506040830151612f246040860182612a94565b5060608301518482036060860152612f3c8282612a9d565b9150506080830151612f5160808601826133fb565b5060a083015184820360a0860152612f698282612a9d565b91505060c083015161056c60c0860182612a94565b805160009060c0840190612f9285826133f2565b5060208301518482036020860152612faa8282612a9d565b91505060408301518482036040860152612fc48282612a9d565b9150506060830151612fd960608601826133fb565b5060808301518482036080860152612ff18282612a9d565b91505060a083015161056c60a0860182612a94565b805160608301906130178482612b04565b50602082015161302a6020850182612b04565b506040820151610c506040850182612a94565b8051604083019061304e8482612a94565b506020820151610c506020850182612a94565b805160c08301906130728482612b04565b5060208201516130856020850182612d3f565b5060408201516130986040850182612b04565b5060608201516130ab6060850182612a94565b5060808201516130be6080850182612a94565b5060a0820151610c5060a0850182612a94565b805160808301906130e28482612b04565b5060208201516130f56020850182612b04565b5060408201516131086040850182612b04565b506060820151610c506060850182612a94565b805160a083019061312c8482612b04565b50602082015161313f6020850182612d3f565b5060408201516131526040850182612b04565b5060608201516131656060850182612b04565b506080820151610c506080850182612a94565b805160c08301906131898482612b04565b50602082015161319c6020850182612b04565b5060408201516130986040850182612d3f565b9052565b80516105e08301906131c58482612a8b565b5060208201516131d86020850182613415565b5060408201516131eb6040850182612a94565b5060608201516131fe6060850182612a94565b50608082015161321160808501826129f1565b5060a08201516132256103008501826129f1565b5060c08201516132396105808501826128cd565b5060e082015161324d6105a0850182612a94565b50610100820151610c506105c0850182612a94565b805160408084526000919084019061327a8282612a9d565b915050602083015161056c60208601826133fb565b805160c08301906132a08482612a8b565b5060208201516132b36020850182612a94565b5060408201516132c66040850182612a94565b5060608201516130ab60608501826128cd565b805160a0808452600091908401906132f18282612a9d565b9150506020830151848203602086015261330b82826128dc565b915050604083015184820360408601526133258282612a3d565b9150506060830151848203606086015261333f82826128dc565b91505060808301518482036080860152612e1f82826128dc565b8051600090606084019061336d8582612a94565b50602083015184820360208601526133858282612a9d565b91505060408301518482036040860152612e1f8282612a9d565b805160a08301906133b08482612a94565b5060208201516133c360208501826128cd565b5060408201516133d660408501826128cd565b5060608201516131656060850182612a94565b6128d6816137b8565b6128d6816137cb565b6128d6816137c4565b6128d661341082613792565b613792565b6128d6816137d7565b600061342a8285612ad5565b91506134368284613404565b5060200192915050565b6020808252810161102b818461294a565b6020808252810161102b81846129a3565b60208082528101610dee81612b0d565b60208082528101610dee81612b63565b60208082528101610dee81612ba7565b60208082528101610dee81612be0565b60208082528101610dee81612c11565b60208082528101610dee81612c41565b60208082528101610dee81612c7a565b60208082528101610dee81612caa565b60208082528101610dee81612ce3565b60208082528101610dee81612d13565b60a081016135108286613006565b61351d60608301856131af565b8181036080830152612e1f8184612e28565b60e0810161353d8288613006565b61354a60608301876131af565b818103608083015261355c8186612a9d565b905061356b60a0830185612a94565b81810360c083015261357d8184612a9d565b979650505050505050565b60a081016135968286613006565b6135a360608301856131af565b8181036080830152612e1f8184612d50565b60a081016135c38286613006565b6135d060608301856131af565b8181036080830152612e1f8184612edd565b60a081016135f08286613006565b6135fd60608301856131af565b8181036080830152612e1f8184612f7e565b6080810161361d828661303d565b61362a60408301856131af565b611a7260608301846133f2565b61010081016136468286613061565b61365360c08301856131af565b81810360e0830152612e1f8184613262565b60e0810161367382876130d1565b61368060808301866131af565b61368d60a08301856133f2565b612e1f60c08301846128cd565b60e081016136a8828661311b565b6136b560a08301856131af565b81810360c0830152612e1f81846132d9565b61010081016136d68286613178565b6136e360c08301856131af565b81810360e0830152612e1f8184613359565b6020810161057082846133e9565b6020810161057082846133f2565b6020810161057082846133fb565b6040518181016001600160401b038111828210171561373d57600080fd5b604052919050565b60006001600160401b0382111561375b57600080fd5b5060209081020190565b60006001600160401b0382111561377b57600080fd5b506020601f91909101601f19160190565b60200190565b90565b5190565b50600490565b90815260200190565b6000610dee826137cb565b151590565b6001600160801b031690565b61ffff1690565b6001600160a01b031690565b6001600160401b031690565b6000610dee826137a8565b82818337506000910152565b60005b838110156138155781810151838201526020016137fd565b83811115610c505750506000910152565b601f01601f191690565b613839816137a8565b811461384457600080fd5b50565b613839816137b3565b61383981613792565b613839816137b8565b613839816137c4565b613839816137cb565b61387c611d0e565b506040805160a0810182526001600160801b03949094168452600060208501526780000000000000009084015261ffff91821660608401521660808201529056fea365627a7a723158209c00be5c05ba01aa62f0d0328e226c7eee7b07c7d18bf1f24a73b36e65a35a496c6578706572696d656e74616cf564736f6c634300050b0040
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000000d4c1222f5e839a911e2053860e45f18921d72ac00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000a5230d65bfab06d0520bc8ec18474b999e5413af000000000000000000000000890d4b9d3e20c9390845e2bf2ccdd77a5822ac23000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000008fc
-----Decoded View---------------
Arg [0] : args (tuple):
Arg [1] : framework (address): 0x0D4C1222f5e839a911e2053860e45F18921D72ac
Arg [2] : ethVaultId (uint256): 1
Arg [3] : erc20VaultId (uint256): 2
Arg [4] : spendingConditionRegistry (address): 0xA5230d65bfAB06d0520BC8eC18474B999e5413aF
Arg [5] : stateTransitionVerifier (address): 0x890D4b9D3E20C9390845e2bF2CCDd77a5822Ac23
Arg [6] : supportTxType (uint256): 1
Arg [7] : safeGasStipend (uint256): 2300
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000000d4c1222f5e839a911e2053860e45f18921d72ac
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [3] : 000000000000000000000000a5230d65bfab06d0520bc8ec18474b999e5413af
Arg [4] : 000000000000000000000000890d4b9d3e20c9390845e2bf2ccdd77a5822ac23
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [6] : 00000000000000000000000000000000000000000000000000000000000008fc
Deployed Bytecode Sourcemap
505:1863:48:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3713:419:64;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;3713:419:64;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;4484:222;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;4484:222:64;;;;;;;;:::i;:::-;;10816:231:62;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;10816:231:62;;;;;;;;:::i;12946:203::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;12946:203:62;;;;;;;;:::i;9895:332::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;9895:332:62;;;;;;;;:::i;12689:107::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;12689:107:62;;;:::i;:::-;;;;;;;;7609:264;;;;;;;;;:::i;1409:332:48:-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1409:332:48;;;;;;;;:::i;4811:295:64:-;;;;;;;;;:::i;12162:105:62:-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;12162:105:62;;;:::i;2055:68::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2055:68:62;;;:::i;2200:166:48:-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;2200:166:48;;;;;;;;:::i;:::-;;;;;;;;2233:51:62;;8:9:-1;5:2;;;30:1;27;20:12;5:2;2233:51:62;;;:::i;:::-;;;;;;;;8689:327;;;;;;;;;:::i;12423:195::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;12423:195:62;;;;;;;;:::i;1826:288:48:-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;1826:288:48;;;;;;;;:::i;11467:166:62:-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;11467:166:62;;;;;;;;:::i;10406:232::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;10406:232:62;;;;;;;;:::i;1296:65:64:-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1296:65:64;;;:::i;1855:69:62:-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;1855:69:62;;;:::i;7007:420::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;7007:420:62;;;;;;;;:::i;:::-;;;;;;;;5209:226:64;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;5209:226:64;;;;;;;;:::i;8120:324:62:-;;;;;;;;;:::i;9198:240::-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;9198:240:62;;;;;;;;:::i;4207:123:64:-;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4207:123:64;;;:::i;3713:419::-;3787:42;3841:48;3932:7;;:14;;3892:55;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;3841:106:64;-1:-1:-1;3962:6:64;3957:147;3974:18;;;3957:147;;;4012:14;4029:7;;4037:1;4029:10;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;4064:29:64;;;:15;:29;;;;;;;;;;;;4053:40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:8;;4012:27;;-1:-1:-1;4053:40:64;:5;;4059:1;;4053:8;;;;;;;;;;;;;;;:40;-1:-1:-1;3994:3:64;;3957:147;;;-1:-1:-1;4120:5:64;-1:-1:-1;3713:419:64;;;;;:::o;4484:222::-;4562:9;;;;;;;;;-1:-1:-1;;;;;4562:9:64;-1:-1:-1;;;;;4562:23:64;;:25;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;4562:25:64;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;4562:25:64;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;4562:25:64;;;;;;;;;109:10:91;-1:-1:-1;;;;;109:20:91;;;101:63;;;;-1:-1:-1;;;101:63:91;;;;;;;;;;;;;;;;;4599:49:64;:21;4636:11;4599:49;:36;:49;:::i;:::-;4663:36;4687:11;4663:36;;;;;;;;;;;;;;;4484:222;;:::o;10816:231:62:-;10958:9;;;;;;;;;-1:-1:-1;;;;;10958:9:62;462:18:89;-1:-1:-1;;;;;462:39:89;;:41;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;462:41:89;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;10983:34:62;;;;;;;;:30;:34;-1:-1:-1;;;;;10983:34:62;;;;;;;;;;;;;;;;;;:57;;-1:-1:-1;;;10983:57:62;;:34;;-1:-1:-1;10983:34:62;;-1:-1:-1;10983:57:62;;:34;11018:15;;11035:4;;10983:57;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;10983:57:62;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;10983:57:62;;;;524:18:89;-1:-1:-1;;;;;524:41:89;;:43;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;524:43:89;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;524:43:89;;;;10816:231:62;;:::o;12946:203::-;13016:9;;;;;;;;;-1:-1:-1;;;;;13016:9:62;-1:-1:-1;;;;;13016:23:62;;:25;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;13016:25:62;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;13016:25:62;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;13016:25:62;;;;;;;;;109:10:91;-1:-1:-1;;;;;109:20:91;;;101:63;;;;-1:-1:-1;;;101:63:91;;;;;;;;;13053:41:62;:13;13082:11;13053:41;:28;:41;:::i;:::-;13109:33;13130:11;13109:33;;;;;;;9895:332;10088:9;;;;;;;;;-1:-1:-1;;;;;10088:9:62;462:18:89;-1:-1:-1;;;;;462:39:89;;:41;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;462:41:89;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;10113:37:62;;;;;;;;:29;:37;-1:-1:-1;;;;;10113:37:62;;;;;;;;;;;;;;;;;;:107;;-1:-1:-1;;;10113:107:62;;:37;;-1:-1:-1;10113:37:62;;-1:-1:-1;10113:107:62;;:37;10151:15;;10168:10;;10180:13;;10195:24;;10113:107;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;10113:107:62;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;10113:107:62;;;;524:18:89;-1:-1:-1;;;;;524:41:89;;:43;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;524:43:89;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;524:43:89;;;;9895:332:62;;;;:::o;12689:107::-;12765:22;;;;;;;;:13;:22;-1:-1:-1;;;;;12765:22:62;;;;;-1:-1:-1;;;12765:22:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;12765:22:62;;;;;;;;;;12739:7;;12765:24;;:22;:24::i;:::-;12758:31;;12689:107;:::o;7609:264::-;7745:9;;;;;;;;;-1:-1:-1;;;;;7745:9:62;462:18:89;-1:-1:-1;;;;;462:39:89;;:41;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;462:41:89;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;462:41:89;;;;7778:18:62;:16;:18::i;:::-;-1:-1:-1;;;;;54:131:92;124:6;111:9;:19;103:64;;;;-1:-1:-1;;;103:64:92;;;;;;;;;7812:31:62;;;;;;;;:27;:31;-1:-1:-1;;;;;7812:31:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:54;;-1:-1:-1;;;7812:54:62;;:31;;;;:54;;:31;7844:15;;7861:4;;7812:31;:54;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;7812:54:62;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;7812:54:62;;;;513:1:89;524:18;-1:-1:-1;;;;;524:41:89;;:43;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;1409:332:48;1496:15;;-1:-1:-1;;;;;1496:15:48;109:10:91;:20;;101:63;;;;-1:-1:-1;;;101:63:91;;;;;;;;;1528:29:48;1550:6;1528:21;:29::i;:::-;1524:211;;;1573:60;1619:6;1627:5;1573:45;:60::i;:::-;1524:211;;;1664:60;1710:6;1718:5;1664:45;:60::i;:::-;1409:332;;;;:::o;4811:295:64:-;4969:9;;;;;;;;;-1:-1:-1;;;;;4969:9:64;462:18:89;-1:-1:-1;;;;;462:39:89;;:41;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;462:41:89;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;462:41:89;;;;5002:27:64;:25;:27::i;:::-;-1:-1:-1;;;;;54:131:92;124:6;111:9;:19;103:64;;;;-1:-1:-1;;;103:64:92;;;;;;;;;5045:31:64;;;;;;;;:27;:31;-1:-1:-1;;;;;5045:31:64;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:54;;-1:-1:-1;;;5045:54:64;;:31;;;;:54;;:31;;-1:-1:-1;;5094:4:64;;5045:54;;;12162:105:62;12237:21;;;;;;;;:12;:21;-1:-1:-1;;;;;12237:21:62;;;;;-1:-1:-1;;;12237:21:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;12237:21:62;;;;;;;;;;12211:7;;12237:23;;:21;:23::i;2055:68::-;2102:21;2055:68;:::o;2200:166:48:-;2295:7;2325:34;2350:8;2325:24;:34::i;:::-;2318:41;;2200:166;;;;:::o;2233:51:62:-;2283:1;2233:51;:::o;8689:327::-;8871:9;;;;;;;;;-1:-1:-1;;;;;8871:9:62;462:18:89;-1:-1:-1;;;;;462:39:89;;:41;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;462:41:89;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;462:41:89;;;;8904:19:62;:17;:19::i;:::-;-1:-1:-1;;;;;54:131:92;124:6;111:9;:19;103:64;;;;-1:-1:-1;;;103:64:92;;;;;;;;;8939:47:62;;;;;;;;:31;:47;-1:-1:-1;;;;;8939:47:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:70;;-1:-1:-1;;;8939:70:62;;:47;;;;:70;;:47;8987:15;;9004:4;;8939:47;:70;;;12423:195;12492:9;;;;;;;;;-1:-1:-1;;;;;12492:9:62;-1:-1:-1;;;;;12492:23:62;;:25;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;12492:25:62;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;12492:25:62;;;;;;;101:4:-1;97:9;90:4;84;80:15;76:31;69:5;65:43;126:6;120:4;113:20;0:138;12492:25:62;;;;;;;;;109:10:91;-1:-1:-1;;;;;109:20:91;;;101:63;;;;-1:-1:-1;;;101:63:91;;;;;;;;;12529:40:62;:12;12557:11;12529:40;:27;:40;:::i;:::-;12584:27;12599:11;12584:27;;;;;;;1826:288:48;1956:7;1979:30;;:::i;:::-;2012:23;2026:8;2012:13;:23::i;:::-;1979:56;;2052:55;2077:10;2089:8;2099:7;2052:24;:55::i;:::-;2045:62;;;1826:288;;;;;;:::o;11467:166:62:-;11545:9;;;;;;;;;-1:-1:-1;;;;;11545:9:62;462:18:89;-1:-1:-1;;;;;462:39:89;;:41;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;462:41:89;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;11566:35:62;;;;;;;;:31;:35;;;;;;;;;:60;;-1:-1:-1;;;11566:60:62;;:35;;-1:-1:-1;11566:35:62;;-1:-1:-1;11566:60:62;;:35;11602:15;;11619:6;;11566:60;;;;10406:232;10550:9;;;;;;;;;-1:-1:-1;;;;;10550:9:62;462:18:89;-1:-1:-1;;;;;462:39:89;;:41;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;462:41:89;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;10575:33:62;;;;;;;;:29;:33;;-1:-1:-1;;;;;10575:33:62;;;;;;;;;;;;;;;;;;:56;;-1:-1:-1;;;10575:56:62;;:33;;-1:-1:-1;10575:33:62;;-1:-1:-1;10575:56:62;;:33;10609:15;;10626:4;;10575:56;;;;1296:65:64;1340:21;1296:65;:::o;1855:69:62:-;1903:21;1855:69;:::o;7007:420::-;7081:42;7135:48;7226:7;;:14;;7186:55;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;-1:-1:-1;7135:106:62;-1:-1:-1;7256:6:62;7251:148;7268:18;;;7251:148;;;7307:14;7324:7;;7332:1;7324:10;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;7359:29:62;;:21;:29;;;:15;:29;;;;;;;;7348:40;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;7348:40:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7359:29;;-1:-1:-1;7348:40:62;;7359:29;;7348:40;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;7348:40:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;7348:40:62;;;-1:-1:-1;7348:40:62;;;;;;;;;;;;;;;;;;-1:-1:-1;7348:40:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;7348:40:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;7348:40:62;;;-1:-1:-1;7348:40:62;;;;-1:-1:-1;;;;;7348:40:62;;;;;;;;;;;;;;;;;;;;;;;:8;;:5;;7354:1;;7348:8;;;;;;;;;;;;;;;:40;-1:-1:-1;7288:3:62;;7251:148;;5209:226:64;5345:9;;;;;;;;;-1:-1:-1;;;;;5345:9:64;462:18:89;-1:-1:-1;;;;;462:39:89;;:41;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;462:41:89;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;5370:35:64;;;;;;;;:31;:35;-1:-1:-1;;;;;5370:35:64;;;;;;;;;;;;;;;;;;:58;;-1:-1:-1;;;5370:58:64;;:35;;-1:-1:-1;5370:35:64;;-1:-1:-1;5370:58:64;;:35;-1:-1:-1;;5423:4:64;;5370:58;;;;8120:324:62;8300:9;;;;;;;;;-1:-1:-1;;;;;8300:9:62;462:18:89;-1:-1:-1;;;;;462:39:89;;:41;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;462:41:89;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;462:41:89;;;;8333:19:62;:17;:19::i;:::-;-1:-1:-1;;;;;54:131:92;124:6;111:9;:19;103:64;;;;-1:-1:-1;;;103:64:92;;;;;;;;;8368:46:62;;;;;;;;:31;:46;-1:-1:-1;;;;;8368:46:62;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:69;;-1:-1:-1;;;8368:69:62;;:46;;;;:69;;:46;8415:15;;8432:4;;8368:46;:69;;;9198:240;9344:9;;;;;;;;;-1:-1:-1;;;;;9344:9:62;462:18:89;-1:-1:-1;;;;;462:39:89;;:41;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;462:41:89;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;-1:-1;;9369:39:62;;;;;;;;:29;:39;-1:-1:-1;;;;;9369:39:62;;;;;;;;;;;;;;;;;;:62;;-1:-1:-1;;;9369:62:62;;:39;;-1:-1:-1;9369:39:62;;-1:-1:-1;9369:62:62;;:39;9409:15;;9426:4;;9369:62;;;;4207:123:64;4291:30;;;;;;;;:21;:30;-1:-1:-1;;;;;4291:30:64;;;;;-1:-1:-1;;;4291:30:64;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;4291:30:64;;;;;;;;;;4265:7;;4291:32;;:30;:32::i;2307:381:68:-;2392:35;;;;;;;;;;-1:-1:-1;;;;;2392:35:68;;;;;-1:-1:-1;;;2392:35:68;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;2392:35:68;;;;;;;;;;;;2415:11;2392:16;:35::i;:::-;2442:20;;-1:-1:-1;;;2442:20:68;;-1:-1:-1;;;;;2442:20:68;:25;;;;:60;;-1:-1:-1;2478:24:68;;;;-1:-1:-1;;;;;2478:24:68;2471:3;:31;;2442:60;2438:135;;;2542:20;;-1:-1:-1;;;2542:20:68;;-1:-1:-1;;;;;2542:20:68;-1:-1:-1;;;;;;2518:44:68;;;;;;2438:135;2582:34;;-1:-1:-1;;;;;2582:34:68;;;-1:-1:-1;;;2582:34:68;;;;;;-1:-1:-1;2626:24:68;:55;;-1:-1:-1;;;;;;2626:55:68;-1:-1:-1;;;;;2660:3:68;598:6;2653:28;2626:55;;;;2307:381::o;2753:233::-;2814:7;2843:4;:24;;;-1:-1:-1;;;;;2837:30:68;:3;:30;2833:147;;;-1:-1:-1;2890:21:68;;2883:28;;2833:147;-1:-1:-1;2949:20:68;;;;2942:27;;281:118:69;345:4;368:19;-1:-1:-1;;;;;368:14:69;;383:3;368:19;:14;:19;:::i;:::-;:24;;;;281:118;-1:-1:-1;;281:118:69:o;5729:151:64:-;5808:33;;;;;;;;:29;:33;-1:-1:-1;;;;;5808:33:64;;;;;;;;;;;;;;;;;;;;;;;;;;:65;;-1:-1:-1;;;5808:65:64;;:33;;;;:65;;:33;-1:-1:-1;;5859:6:64;;5867:5;;5808:65;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;5808:65:64;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;11935:151:62;12014:33;;;;;;;;:29;:33;-1:-1:-1;;;;;12014:33:62;;;;;;;;;;;;;;;;;;;;;;;;;;:65;;-1:-1:-1;;;12014:65:62;;:33;;;;:65;;:33;12048:15;;12065:6;;12073:5;;12014:65;;;;1785:164:69;1901:19;;;;;;1858:7;;1892:49;;1925:3;1893:35;1937:3;1892:49;:44;:49;:::i;2150:491:93:-;2202:15;;:::i;:::-;410:10;2248:18;;;;464:5;2295:18;;;2294:32;;;;2364:15;;805:59;2399:25;;;2391:90;;;;-1:-1:-1;;;2391:90:93;;;;;;;;;666:11;2499:7;:23;;2491:70;;;;-1:-1:-1;;;2491:70:93;;;;;;;;;2578:56;;;;;;;;-1:-1:-1;;;;;2578:56:93;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2150:491:93;;;:::o;1140:480:69:-;1317:7;1344:10;1340:191;;;1370:16;1416:8;1426:17;:8;:15;:17::i;:::-;1399:45;;;;;;;;;;;;;49:4:-1;39:7;30;26:21;22:32;13:7;6:49;1399:45:69;;;1389:56;;;;;;1370:75;;1466:54;1489:8;1499;:20;;;1466:22;:54::i;:::-;1459:61;;;;;1340:191;1548:65;1581:8;1571:19;;;;;;1592:8;:20;;;1548:22;:65::i;:::-;1541:72;1140:480;-1:-1:-1;;;;1140:480:69:o;2992:417:68:-;3082:23;3108:14;3117:4;3108:8;:14::i;:::-;3082:40;;3154:1;3140:11;-1:-1:-1;;;;;3140:15:68;;3132:52;;;;-1:-1:-1;;;3132:52:68;;;;;;;;;3235:4;:22;;;3217:40;;:15;-1:-1:-1;;;;;3217:40:68;;;;;;;-1:-1:-1;;;;;3202:55:68;:11;-1:-1:-1;;;;;3202:55:68;;;3194:88;;;;-1:-1:-1;;;3194:88:68;;;;;;;;;3351:4;:25;;;3324:52;;3332:15;-1:-1:-1;;;;;3324:24:68;:52;3308:11;-1:-1:-1;;;;;3300:20:68;:76;;3292:110;;;;-1:-1:-1;;;3292:110:68;;;;;;;;;2992:417;;;:::o;1134:150:88:-;1257:15;;;1275:1;1257:19;;1134:150::o;425:144::-;180:1;549:13;;;;;;541:21;;425:144::o;1698:315:93:-;1758:7;537:1;464:5;525:13;1785:3;:15;;;:35;;;;1777:68;;;;-1:-1:-1;;;1777:68:93;;;;;;;;;1863:12;;805:59;-1:-1:-1;;;;;1863:29:93;;;;;1855:62;;;;-1:-1:-1;;;1855:62:93;;;;;;;;;-1:-1:-1;1991:15:93;;;;1965:11;;;;1935:12;;-1:-1:-1;;;;;1935:27:93;410:10;1935:27;:71;1965:23;;;464:5;1965:23;1935:53;:71;;;1698:315;;;:::o;1955:373:69:-;2071:7;2132:3;2112:23;;;-1:-1:-1;;;2165:3:69;2140:28;;;;2111:58;;-1:-1:-1;;;;;2237:22:69;;:32;;2229:61;;;;-1:-1:-1;;;2229:61:69;;;;;;;;505:1863:48;;;;;;;;;-1:-1:-1;505:1863:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;-1:-1:-1;505:1863:48;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;505:1863:48;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;505:1863:48;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;-1:-1:-1;505:1863:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;5:130:-1:-;72:20;;97:33;72:20;97:33;;142:134;220:13;;238:33;220:13;238:33;;299:685;;417:3;410:4;402:6;398:17;394:27;384:2;;435:1;432;425:12;384:2;472:6;459:20;494:81;509:65;567:6;509:65;;;494:81;;;603:21;;;647:4;635:17;;;;485:90;;-1:-1;660:14;;635:17;755:1;740:238;765:6;762:1;759:13;740:238;;;848:3;835:17;827:6;823:30;872:42;910:3;898:10;872:42;;;860:55;;-1:-1;938:4;929:14;;;;957;;;;;787:1;780:9;740:238;;;744:14;377:607;;;;;;;;1010:352;;;1140:3;1133:4;1125:6;1121:17;1117:27;1107:2;;1158:1;1155;1148:12;1107:2;-1:-1;1178:20;;-1:-1;;;;;1207:30;;1204:2;;;1250:1;1247;1240:12;1204:2;1284:4;1276:6;1272:17;1260:29;;1335:3;1327:4;1319:6;1315:17;1305:8;1301:32;1298:41;1295:2;;;1352:1;1349;1342:12;1295:2;1100:262;;;;;;1388:699;;1501:3;1494:4;1486:6;1482:17;1478:27;1468:2;;1519:1;1516;1509:12;1468:2;1556:6;1543:20;1578:76;1593:60;1646:6;1593:60;;1578:76;1569:85;;1671:5;1696:6;1689:5;1682:21;1726:4;1718:6;1714:17;1704:27;;1748:4;1743:3;1739:14;1732:21;;1801:6;1848:3;1840:4;1832:6;1828:17;1823:3;1819:27;1816:36;1813:2;;;1865:1;1862;1855:12;1813:2;1890:1;1875:206;1900:6;1897:1;1894:13;1875:206;;;1958:3;1980:37;2013:3;2001:10;1980:37;;;1968:50;;-1:-1;2041:4;2032:14;;;;2060;;;;;1922:1;1915:9;1875:206;;2095:124;2159:20;;2184:30;2159:20;2184:30;;2226:130;2293:20;;2318:33;2293:20;2318:33;;2364:432;;2461:3;2454:4;2446:6;2442:17;2438:27;2428:2;;2479:1;2476;2469:12;2428:2;2516:6;2503:20;2538:60;2553:44;2590:6;2553:44;;2538:60;2529:69;;2618:6;2611:5;2604:21;2654:4;2646:6;2642:17;2687:4;2680:5;2676:16;2722:3;2713:6;2708:3;2704:16;2701:25;2698:2;;;2739:1;2736;2729:12;2698:2;2749:41;2783:6;2778:3;2773;2749:41;;;2421:375;;;;;;;;3320:2054;;3451:5;3439:9;3434:3;3430:19;3426:31;3423:2;;;3470:1;3467;3460:12;3423:2;3488:21;3503:5;3488:21;;;3479:30;-1:-1;3562:31;;-1:-1;;;;;3602:30;;3599:2;;;3645:1;3642;3635:12;3599:2;3679:54;3729:3;3720:6;3709:9;3705:22;3679:54;;;3655:79;;-1:-1;3803:2;3836:49;3881:3;3857:22;;;3836:49;;;3829:4;3822:5;3818:16;3811:75;3755:142;3981:2;3970:9;3966:18;3953:32;-1:-1;;;;;3997:6;3994:30;3991:2;;;4037:1;4034;4027:12;3991:2;4072:54;4122:3;4113:6;4102:9;4098:22;4072:54;;;4065:4;4058:5;4054:16;4047:80;3907:231;4204:2;4237:48;4281:3;4272:6;4261:9;4257:22;4237:48;;;4230:4;4223:5;4219:16;4212:74;4148:149;4382:3;4371:9;4367:19;4354:33;-1:-1;;;;;4399:6;4396:30;4393:2;;;4439:1;4436;4429:12;4393:2;4474:54;4524:3;4515:6;4504:9;4500:22;4474:54;;;4467:4;4460:5;4456:16;4449:80;4307:233;4607:3;4641:48;4685:3;4676:6;4665:9;4661:22;4641:48;;;4634:4;4627:5;4623:16;4616:74;4550:151;4761:3;4795:49;4840:3;4831:6;4820:9;4816:22;4795:49;;;4788:4;4781:5;4777:16;4770:75;4711:145;4955:3;4944:9;4940:19;4927:33;-1:-1;;;;;4972:6;4969:30;4966:2;;;5012:1;5009;5002:12;4966:2;5047:54;5097:3;5088:6;5077:9;5073:22;5047:54;;;5040:4;5033:5;5029:16;5022:80;4866:247;5205:3;5194:9;5190:19;5177:33;-1:-1;;;;;5222:6;5219:30;5216:2;;;5262:1;5259;5252:12;5216:2;5298:54;5348:3;5339:6;5328:9;5324:22;5298:54;;;5290:5;5283;5279:17;5272:81;5123:241;3417:1957;;;;;5448:1798;;5579:5;5567:9;5562:3;5558:19;5554:31;5551:2;;;5598:1;5595;5588:12;5551:2;5616:21;5631:5;5616:21;;;5607:30;-1:-1;5693:31;;-1:-1;;;;;5733:30;;5730:2;;;5776:1;5773;5766:12;5730:2;5810:54;5860:3;5851:6;5840:9;5836:22;5810:54;;;5786:79;;-1:-1;5942:2;5975:48;6019:3;5995:22;;;5975:48;;;5968:4;5961:5;5957:16;5950:74;5886:149;6122:2;6111:9;6107:18;6094:32;-1:-1;;;;;6138:6;6135:30;6132:2;;;6178:1;6175;6168:12;6132:2;6213:54;6263:3;6254:6;6243:9;6239:22;6213:54;;;6206:4;6199:5;6195:16;6188:80;6045:234;6348:2;6381:48;6425:3;6416:6;6405:9;6401:22;6381:48;;;6374:4;6367:5;6363:16;6356:74;6289:152;6535:3;6524:9;6520:19;6507:33;-1:-1;;;;;6552:6;6549:30;6546:2;;;6592:1;6589;6582:12;6546:2;6627:54;6677:3;6668:6;6657:9;6653:22;6627:54;;;6620:4;6613:5;6609:16;6602:80;6451:242;6774:3;6763:9;6759:19;6746:33;-1:-1;;;;;6791:6;6788:30;6785:2;;;6831:1;6828;6821:12;6785:2;6866:54;6916:3;6907:6;6896:9;6892:22;6866:54;;;6859:4;6852:5;6848:16;6841:80;6703:229;6990:3;7024:49;7069:3;7060:6;7049:9;7045:22;7024:49;;;7017:4;7010:5;7006:16;6999:75;6942:143;7141:3;7175:49;7220:3;7211:6;7200:9;7196:22;7175:49;;;7168:4;7161:5;7157:16;7150:75;7095:141;5545:1701;;;;;7317:1651;;7445:4;7433:9;7428:3;7424:19;7420:30;7417:2;;;7463:1;7460;7453:12;7417:2;7481:20;7496:4;7481:20;;;7472:29;-1:-1;7557:31;;-1:-1;;;;;7597:30;;7594:2;;;7640:1;7637;7630:12;7594:2;7674:54;7724:3;7715:6;7704:9;7700:22;7674:54;;;7650:79;;-1:-1;7838:2;7823:18;;7810:32;-1:-1;;;;;7851:30;;7848:2;;;7894:1;7891;7884:12;7848:2;7929:54;7979:3;7970:6;7959:9;7955:22;7929:54;;;7922:4;7915:5;7911:16;7904:80;7750:245;8054:2;8087:49;8132:3;8123:6;8112:9;8108:22;8087:49;;;8080:4;8073:5;8069:16;8062:75;8005:143;8235:2;8224:9;8220:18;8207:32;-1:-1;;;;;8251:6;8248:30;8245:2;;;8291:1;8288;8281:12;8245:2;8326:54;8376:3;8367:6;8356:9;8352:22;8326:54;;;8319:4;8312:5;8308:16;8301:80;8158:234;8461:3;8495:48;8539:3;8530:6;8519:9;8515:22;8495:48;;;8488:4;8481:5;8477:16;8470:74;8402:153;8649:3;8638:9;8634:19;8621:33;-1:-1;;;;;8666:6;8663:30;8660:2;;;8706:1;8703;8696:12;8660:2;8741:54;8791:3;8782:6;8771:9;8767:22;8741:54;;;8734:4;8727:5;8723:16;8716:80;8565:242;8863:3;8897:49;8942:3;8933:6;8922:9;8918:22;8897:49;;;8890:4;8883:5;8879:16;8872:75;8817:141;7411:1557;;;;;9044:1364;;9177:4;9165:9;9160:3;9156:19;9152:30;9149:2;;;9195:1;9192;9185:12;9149:2;9213:20;9228:4;9213:20;;;9204:29;-1:-1;9285:1;9316:49;9361:3;9341:9;9316:49;;;9292:74;;-1:-1;9460:2;9445:18;;9432:32;-1:-1;;;;;9473:30;;9470:2;;;9516:1;9513;9506:12;9470:2;9551:54;9601:3;9592:6;9581:9;9577:22;9551:54;;;9544:4;9537:5;9533:16;9526:80;9387:230;9702:2;9691:9;9687:18;9674:32;-1:-1;;;;;9718:6;9715:30;9712:2;;;9758:1;9755;9748:12;9712:2;9793:54;9843:3;9834:6;9823:9;9819:22;9793:54;;;9786:4;9779:5;9775:16;9768:80;9627:232;9915:2;9948:48;9992:3;9983:6;9972:9;9968:22;9948:48;;;9941:4;9934:5;9930:16;9923:74;9869:139;10089:3;10078:9;10074:19;10061:33;-1:-1;;;;;10106:6;10103:30;10100:2;;;10146:1;10143;10136:12;10100:2;10181:54;10231:3;10222:6;10211:9;10207:22;10181:54;;;10174:4;10167:5;10163:16;10156:80;10018:229;10303:3;10337:49;10382:3;10373:6;10362:9;10358:22;10337:49;;;10330:4;10323:5;10319:16;10312:75;10257:141;9143:1265;;;;;10491:594;;10631:4;10619:9;10614:3;10610:19;10606:30;10603:2;;;10649:1;10646;10639:12;10603:2;10667:20;10682:4;10667:20;;;10658:29;-1:-1;10743:31;;-1:-1;;;;;10783:30;;10780:2;;;10826:1;10823;10816:12;10780:2;10860:54;10910:3;10901:6;10890:9;10886:22;10860:54;;;10836:79;;-1:-1;10982:2;11015:48;11059:3;11035:22;;;11015:48;;;11008:4;11001:5;10997:16;10990:74;10936:139;10597:488;;;;;11829:1493;;11950:4;11938:9;11933:3;11929:19;11925:30;11922:2;;;11968:1;11965;11958:12;11922:2;11986:20;12001:4;11986:20;;;11977:29;-1:-1;12062:31;;-1:-1;;;;;12102:30;;12099:2;;;12145:1;12142;12135:12;12099:2;12179:54;12229:3;12220:6;12209:9;12205:22;12179:54;;;12155:79;;-1:-1;12327:2;12312:18;;12299:32;-1:-1;;;;;12340:30;;12337:2;;;12383:1;12380;12373:12;12337:2;12418:75;12489:3;12480:6;12469:9;12465:22;12418:75;;;12411:4;12404:5;12400:16;12393:101;12255:250;12592:2;12581:9;12577:18;12564:32;-1:-1;;;;;12608:6;12605:30;12602:2;;;12648:1;12645;12638:12;12602:2;12683:70;12749:3;12740:6;12729:9;12725:22;12683:70;;;12676:4;12669:5;12665:16;12658:96;12515:250;12862:2;12851:9;12847:18;12834:32;-1:-1;;;;;12878:6;12875:30;12872:2;;;12918:1;12915;12908:12;12872:2;12953:75;13024:3;13015:6;13004:9;13000:22;12953:75;;;12946:4;12939:5;12935:16;12928:101;12775:265;13133:3;13122:9;13118:19;13105:33;-1:-1;;;;;13150:6;13147:30;13144:2;;;13190:1;13187;13180:12;13144:2;13225:75;13296:3;13287:6;13276:9;13272:22;13225:75;;;13218:4;13211:5;13207:16;13200:101;13050:262;11916:1406;;;;;13394:835;;13523:4;13511:9;13506:3;13502:19;13498:30;13495:2;;;13541:1;13538;13531:12;13495:2;13559:20;13574:4;13559:20;;;13550:29;-1:-1;13632:1;13663:49;13708:3;13688:9;13663:49;;;13639:74;;-1:-1;13809:2;13794:18;;13781:32;-1:-1;;;;;13822:30;;13819:2;;;13865:1;13862;13855:12;13819:2;13900:54;13950:3;13941:6;13930:9;13926:22;13900:54;;;13893:4;13886:5;13882:16;13875:80;13734:232;14062:2;14051:9;14047:18;14034:32;-1:-1;;;;;14078:6;14075:30;14072:2;;;14118:1;14115;14108:12;14072:2;14153:54;14203:3;14194:6;14183:9;14179:22;14153:54;;;14146:4;14139:5;14135:16;14128:80;13976:243;13489:740;;;;;14236:130;14303:20;;14328:33;14303:20;14328:33;;14373:128;14439:20;;14464:32;14439:20;14464:32;;14508:130;14575:20;;14600:33;14575:20;14600:33;;14782:263;;14897:2;14885:9;14876:7;14872:23;14868:32;14865:2;;;14913:1;14910;14903:12;14865:2;14948:1;14965:64;15021:7;15001:9;14965:64;;15052:397;;;15191:2;15179:9;15170:7;15166:23;15162:32;15159:2;;;15207:1;15204;15197:12;15159:2;15242:31;;-1:-1;;;;;15282:30;;15279:2;;;15325:1;15322;15315:12;15279:2;15353:80;15425:7;15416:6;15405:9;15401:22;15353:80;;;15343:90;;;;15221:218;15153:296;;;;;;15456:589;;;;15600:2;15588:9;15579:7;15575:23;15571:32;15568:2;;;15616:1;15613;15606:12;15568:2;15651:1;15668:50;15710:7;15690:9;15668:50;;;15658:60;;15630:94;15783:2;15772:9;15768:18;15755:32;-1:-1;;;;;15799:6;15796:30;15793:2;;;15839:1;15836;15829:12;15793:2;15859:62;15913:7;15904:6;15893:9;15889:22;15859:62;;;15849:72;;15734:193;15958:2;15976:53;16021:7;16012:6;16001:9;15997:22;15976:53;;;15966:63;;15937:98;15562:483;;;;;;16052:345;;16165:2;16153:9;16144:7;16140:23;16136:32;16133:2;;;16181:1;16178;16171:12;16133:2;16216:31;;-1:-1;;;;;16256:30;;16253:2;;;16299:1;16296;16289:12;16253:2;16319:62;16373:7;16364:6;16353:9;16349:22;16319:62;;16404:699;;;;16560:2;16548:9;16539:7;16535:23;16531:32;16528:2;;;16576:1;16573;16566:12;16528:2;16611:31;;-1:-1;;;;;16651:30;;16648:2;;;16694:1;16691;16684:12;16648:2;16714:62;16768:7;16759:6;16748:9;16744:22;16714:62;;;16704:72;;16590:192;16813:2;16831:53;16876:7;16867:6;16856:9;16852:22;16831:53;;;16821:63;;16792:98;16949:2;16938:9;16934:18;16921:32;-1:-1;;;;;16965:6;16962:30;16959:2;;;17005:1;17002;16995:12;16959:2;17025:62;17079:7;17070:6;17059:9;17055:22;17025:62;;17110:411;;17256:2;17244:9;17235:7;17231:23;17227:32;17224:2;;;17272:1;17269;17262:12;17224:2;17307:31;;-1:-1;;;;;17347:30;;17344:2;;;17390:1;17387;17380:12;17344:2;17410:95;17497:7;17488:6;17477:9;17473:22;17410:95;;17528:411;;17674:2;17662:9;17653:7;17649:23;17645:32;17642:2;;;17690:1;17687;17680:12;17642:2;17725:31;;-1:-1;;;;;17765:30;;17762:2;;;17808:1;17805;17798:12;17762:2;17828:95;17915:7;17906:6;17895:9;17891:22;17828:95;;17946:405;;18089:2;18077:9;18068:7;18064:23;18060:32;18057:2;;;18105:1;18102;18095:12;18057:2;18140:31;;-1:-1;;;;;18180:30;;18177:2;;;18223:1;18220;18213:12;18177:2;18243:92;18327:7;18318:6;18307:9;18303:22;18243:92;;18358:415;;18506:2;18494:9;18485:7;18481:23;18477:32;18474:2;;;18522:1;18519;18512:12;18474:2;18557:31;;-1:-1;;;;;18597:30;;18594:2;;;18640:1;18637;18630:12;18594:2;18660:97;18749:7;18740:6;18729:9;18725:22;18660:97;;18780:429;;18935:2;18923:9;18914:7;18910:23;18906:32;18903:2;;;18951:1;18948;18941:12;18903:2;18986:31;;-1:-1;;;;;19026:30;;19023:2;;;19069:1;19066;19059:12;19023:2;19089:104;19185:7;19176:6;19165:9;19161:22;19089:104;;19654:391;;19790:2;19778:9;19769:7;19765:23;19761:32;19758:2;;;19806:1;19803;19796:12;19758:2;19841:31;;-1:-1;;;;;19881:30;;19878:2;;;19924:1;19921;19914:12;19878:2;19944:85;20021:7;20012:6;20001:9;19997:22;19944:85;;20052:407;;20196:2;20184:9;20175:7;20171:23;20167:32;20164:2;;;20212:1;20209;20202:12;20164:2;20247:31;;-1:-1;;;;;20287:30;;20284:2;;;20330:1;20327;20320:12;20284:2;20350:93;20435:7;20426:6;20415:9;20411:22;20350:93;;20466:241;;20570:2;20558:9;20549:7;20545:23;20541:32;20538:2;;;20586:1;20583;20576:12;20538:2;20621:1;20638:53;20683:7;20663:9;20638:53;;20714:241;;20818:2;20806:9;20797:7;20793:23;20789:32;20786:2;;;20834:1;20831;20824:12;20786:2;20869:1;20886:53;20931:7;20911:9;20886:53;;20962:491;;;;21100:2;21088:9;21079:7;21075:23;21071:32;21068:2;;;21116:1;21113;21106:12;21068:2;21151:1;21168:53;21213:7;21193:9;21168:53;;;21158:63;;21130:97;21258:2;21276:53;21321:7;21312:6;21301:9;21297:22;21276:53;;;21266:63;;21237:98;21366:2;21384:53;21429:7;21420:6;21409:9;21405:22;21384:53;;21461:193;;21580:68;21644:3;21636:6;21580:68;;21663:282;;21802:102;21900:3;21892:6;21802:102;;;-1:-1;;21933:5;21924:15;;21795:150;21954:281;;22093:102;22191:3;22183:6;22093:102;;;-1:-1;;22224:4;22215:14;;22086:149;22244:281;;22383:102;22481:3;22473:6;22383:102;;;-1:-1;;22514:4;22505:14;;22376:149;22534:189;;22629:54;22679:3;22671:6;22629:54;;;-1:-1;;22712:4;22703:14;;22622:101;22731:127;22820:32;22846:5;22820:32;;;22815:3;22808:45;22802:56;;;23130:876;;23279:55;23328:5;23279:55;;;23347:89;23429:6;23424:3;23347:89;;;23340:96;;23459:3;23501:4;23493:6;23489:17;23484:3;23480:27;23528:57;23579:5;23528:57;;;23605:7;23633:1;23618:349;23643:6;23640:1;23637:13;23618:349;;;23705:9;23699:4;23695:20;23690:3;23683:33;23750:6;23744:13;23772:82;23849:4;23834:13;23772:82;;;23764:90;;23871:61;23925:6;23871:61;;;23955:4;23946:14;;;;;23861:71;-1:-1;;23665:1;23658:9;23618:349;;;-1:-1;23980:4;;23258:748;-1:-1;;;;;;;23258:748;24111:898;;24308:80;24382:5;24308:80;;;24401:112;24506:6;24501:3;24401:112;;;24394:119;;24534:82;24610:5;24534:82;;;24636:7;24664:1;24649:338;24674:6;24671:1;24668:13;24649:338;;;24741:6;24735:13;24762:115;24873:3;24858:13;24762:115;;;24755:122;;24894:86;24973:6;24894:86;;;24884:96;-1:-1;;24696:1;24689:9;24649:338;;;-1:-1;25000:3;;24287:722;-1:-1;;;;;24287:722;25114:898;;25311:80;25385:5;25311:80;;;25404:112;25509:6;25504:3;25404:112;;;25397:119;;25537:82;25613:5;25537:82;;;25639:7;25667:1;25652:338;25677:6;25674:1;25671:13;25652:338;;;25744:6;25738:13;25765:115;25876:3;25861:13;25765:115;;;25758:122;;25897:86;25976:6;25897:86;;;25887:96;-1:-1;;25699:1;25692:9;25652:338;;26119:832;26290:74;26358:5;26290:74;;;26377:100;26470:6;26465:3;26377:100;;;26370:107;;26498:76;26568:5;26498:76;;;26594:7;26622:1;26607:332;26632:6;26629:1;26626:13;26607:332;;;26699:6;26693:13;26720:115;26831:3;26816:13;26720:115;;;26713:122;;26852:80;26925:6;26852:80;;;26842:90;-1:-1;;26654:1;26647:9;26607:332;;26990:678;;27129:50;27173:5;27129:50;;;27192:84;27269:6;27264:3;27192:84;;;27185:91;;27297:52;27343:5;27297:52;;;27369:7;27397:1;27382:264;27407:6;27404:1;27401:13;27382:264;;;27474:6;27468:13;27495:71;27562:3;27547:13;27495:71;;;27488:78;;27583:56;27632:6;27583:56;;;27573:66;-1:-1;;27429:1;27422:9;27382:264;;27676:94;27743:21;27758:5;27743:21;;27777:103;27850:24;27868:5;27850:24;;28005:359;;28123:38;28155:5;28123:38;;;28173:78;28244:6;28239:3;28173:78;;;28166:85;;28256:52;28301:6;28296:3;28289:4;28282:5;28278:16;28256:52;;;28329:29;28351:6;28329:29;;;28320:39;;;;28103:261;-1:-1;;;28103:261;28371:356;;28499:38;28531:5;28499:38;;;28549:88;28630:6;28625:3;28549:88;;;28542:95;;28642:52;28687:6;28682:3;28675:4;28668:5;28664:16;28642:52;;;28706:16;;;;;28479:248;-1:-1;;28479:248;29072:164;29173:57;29224:5;29173:57;;30169:465;;30329:67;30393:2;30388:3;30329:67;;;30429:66;30409:87;;-1:-1;;;30525:2;30516:12;;30509:88;30625:2;30616:12;;30315:319;-1:-1;;30315:319;30643:465;;30803:67;30867:2;30862:3;30803:67;;;30903:66;30883:87;;-1:-1;;;30999:2;30990:12;;30983:88;31099:2;31090:12;;30789:319;-1:-1;;30789:319;31117:364;;31277:67;31341:2;31336:3;31277:67;;;31377:66;31357:87;;31472:2;31463:12;;31263:218;-1:-1;;31263:218;31490:364;;31650:67;31714:2;31709:3;31650:67;;;-1:-1;;;31730:87;;31845:2;31836:12;;31636:218;-1:-1;;31636:218;31863:364;;32023:67;32087:2;32082:3;32023:67;;;-1:-1;;;32103:87;;32218:2;32209:12;;32009:218;-1:-1;;32009:218;32236:364;;32396:67;32460:2;32455:3;32396:67;;;32496:66;32476:87;;32591:2;32582:12;;32382:218;-1:-1;;32382:218;32609:364;;32769:67;32833:2;32828:3;32769:67;;;-1:-1;;;32849:87;;32964:2;32955:12;;32755:218;-1:-1;;32755:218;32982:364;;33142:67;33206:2;33201:3;33142:67;;;33242:66;33222:87;;33337:2;33328:12;;33128:218;-1:-1;;33128:218;33355:364;;33515:67;33579:2;33574:3;33515:67;;;-1:-1;;;33595:87;;33710:2;33701:12;;33501:218;-1:-1;;33501:218;33728:364;;33888:67;33952:2;33947:3;33888:67;;;-1:-1;;;33968:87;;34083:2;34074:12;;33874:218;-1:-1;;33874:218;34181:339;34401:22;;34326:4;34317:14;;;34429:70;34321:3;34401:22;34429:70;;34658:2136;34921:22;;34851:5;34956:37;;;34658:2136;;34842:15;;;;35008:75;34842:15;34921:22;35008:75;;;35000:83;;34872:223;35176:4;35169:5;35165:16;35159:23;35188:71;35253:4;35248:3;35244:14;35230:12;35188:71;;;35105:160;35344:4;35337:5;35333:16;35327:23;35396:3;35390:4;35386:14;35379:4;35374:3;35370:14;35363:38;35416:75;35486:4;35472:12;35416:75;;;35408:83;;35275:228;35592:4;35585:5;35581:16;35575:23;35604:69;35667:4;35662:3;35658:14;35644:12;35604:69;;;35513:166;35759:4;35752:5;35748:16;35742:23;35811:3;35805:4;35801:14;35794:4;35789:3;35785:14;35778:38;35831:75;35901:4;35887:12;35831:75;;;35823:83;;35689:229;36008:4;36001:5;35997:16;35991:23;36020:69;36083:4;36078:3;36074:14;36060:12;36020:69;;;35928:167;36178:4;36171:5;36167:16;36161:23;36190:71;36255:4;36250:3;36246:14;36232:12;36190:71;;;36105:162;36361:4;36354:5;36350:16;36344:23;36413:3;36407:4;36403:14;36396:4;36391:3;36387:14;36380:38;36433:75;36503:4;36489:12;36433:75;;;36425:83;;36277:243;36607:5;36600;36596:17;36590:24;36661:3;36655:4;36651:14;36643:5;36638:3;36634:15;36627:39;36681:75;36751:4;36737:12;36681:75;;;36673:83;34824:1970;-1:-1;;;;;34824:1970;36932:1883;37198:22;;37125:5;37233:37;;;36932:1883;;37116:15;;;;37285:75;37116:15;37198:22;37285:75;;;37277:83;;37146:226;37461:4;37454:5;37450:16;37444:23;37473:69;37536:4;37531:3;37527:14;37513:12;37473:69;;;37382:166;37630:4;37623:5;37619:16;37613:23;37682:3;37676:4;37672:14;37665:4;37660:3;37656:14;37649:38;37702:75;37772:4;37758:12;37702:75;;;37694:83;;37558:231;37881:4;37874:5;37870:16;37864:23;37893:69;37956:4;37951:3;37947:14;37933:12;37893:69;;;37799:169;38057:4;38050:5;38046:16;38040:23;38109:3;38103:4;38099:14;38092:4;38087:3;38083:14;38076:38;38129:75;38199:4;38185:12;38129:75;;;38121:83;;37978:238;38292:4;38285:5;38281:16;38275:23;38344:3;38338:4;38334:14;38327:4;38322:3;38318:14;38311:38;38364:75;38434:4;38420:12;38364:75;;;38356:83;;38226:225;38532:4;38525:5;38521:16;38515:23;38544:71;38609:4;38604:3;38600:14;38586:12;38544:71;;;38461:160;38700:4;38693:5;38689:16;38683:23;38712:71;38777:4;38772:3;38768:14;38754:12;38712:71;;38947:1718;39206:22;;39134:4;39241:37;;;38947:1718;;39125:14;;;;39293:75;39125:14;39206:22;39293:75;;;39285:83;;39154:226;39473:4;39466:5;39462:16;39456:23;39525:3;39519:4;39515:14;39508:4;39503:3;39499:14;39492:38;39545:75;39615:4;39601:12;39545:75;;;39537:83;;39390:242;39714:4;39707:5;39703:16;39697:23;39726:71;39791:4;39786:3;39782:14;39768:12;39726:71;;;39642:161;39885:4;39878:5;39874:16;39868:23;39937:3;39931:4;39927:14;39920:4;39915:3;39911:14;39904:38;39957:75;40027:4;40013:12;39957:75;;;39949:83;;39813:231;40136:4;40129:5;40125:16;40119:23;40148:69;40211:4;40206:3;40202:14;40188:12;40148:69;;;40054:169;40312:4;40305:5;40301:16;40295:23;40364:3;40358:4;40354:14;40347:4;40342:3;40338:14;40331:38;40384:75;40454:4;40440:12;40384:75;;;40376:83;;40233:238;40550:4;40543:5;40539:16;40533:23;40562:71;40627:4;40622:3;40618:14;40604:12;40562:71;;40807:1440;41072:22;;40807:1440;;41004:4;40995:14;;;41100:70;40999:3;41072:22;41100:70;;;41024:152;41254:4;41247:5;41243:16;41237:23;41306:3;41300:4;41296:14;41289:4;41284:3;41280:14;41273:38;41326:75;41396:4;41382:12;41326:75;;;41318:83;;41186:227;41493:4;41486:5;41482:16;41476:23;41545:3;41539:4;41535:14;41528:4;41523:3;41519:14;41512:38;41565:75;41635:4;41621:12;41565:75;;;41557:83;;41423:229;41731:4;41724:5;41720:16;41714:23;41743:69;41806:4;41801:3;41797:14;41783:12;41743:69;;;41662:156;41894:4;41887:5;41883:16;41877:23;41946:3;41940:4;41936:14;41929:4;41924:3;41920:14;41913:38;41966:75;42036:4;42022:12;41966:75;;;41958:83;;41828:225;42132:4;42125:5;42121:16;42115:23;42144:71;42209:4;42204:3;42200:14;42186:12;42144:71;;42359:762;42587:22;;42516:4;42507:14;;;42615:95;42511:3;42587:22;42615:95;;;42536:180;42810:4;42803:5;42799:16;42793:23;42822:106;42922:4;42917:3;42913:14;42899:12;42822:106;;;42726:208;43017:4;43010:5;43006:16;43000:23;43029:71;43094:4;43089:3;43085:14;43071:12;43029:71;;45852:523;46084:22;;46009:4;46000:14;;;46112:70;46004:3;46084:22;46112:70;;;46029:159;46271:4;46264:5;46260:16;46254:23;46283:71;46348:4;46343:3;46339:14;46325:12;46283:71;;46485:1315;46713:22;;46642:4;46633:14;;;46741:95;46637:3;46713:22;46741:95;;;46662:180;46938:4;46931:5;46927:16;46921:23;46950:125;47069:4;47064:3;47060:14;47046:12;46950:125;;;46852:229;47163:4;47156:5;47152:16;47146:23;47175:95;47264:4;47259:3;47255:14;47241:12;47175:95;;;47091:185;47358:4;47351:5;47347:16;47341:23;47370:71;47435:4;47430:3;47426:14;47412:12;47370:71;;;47286:161;47526:4;47519:5;47515:16;47509:23;47538:71;47603:4;47598:3;47594:14;47580:12;47538:71;;;47457:158;47696:4;47689:5;47685:16;47679:23;47708:71;47773:4;47768:3;47764:14;47750:12;47708:71;;47906:916;48134:22;;48063:4;48054:14;;;48162:95;48058:3;48134:22;48162:95;;;48083:180;48340:4;48333:5;48329:16;48323:23;48352:89;48435:4;48430:3;48426:14;48412:12;48352:89;;;48273:174;48526:4;48519:5;48515:16;48509:23;48538:91;48623:4;48618:3;48614:14;48600:12;48538:91;;;48457:178;48718:4;48711:5;48707:16;48701:23;48730:71;48795:4;48790:3;48786:14;48772:12;48730:71;;49946:1207;50174:22;;50103:4;50094:14;;;50202:95;50098:3;50174:22;50202:95;;;50123:180;50395:4;50388:5;50384:16;50378:23;50407:125;50526:4;50521:3;50517:14;50503:12;50407:125;;;50313:225;50632:4;50625:5;50621:16;50615:23;50644:106;50744:4;50739:3;50735:14;50721:12;50644:106;;;50548:208;50843:4;50836:5;50832:16;50826:23;50855:104;50953:4;50948:3;50944:14;50930:12;50855:104;;;50766:199;51049:4;51042:5;51038:16;51032:23;51061:71;51126:4;51121:3;51117:14;51103:12;51061:71;;51255:1317;51487:22;;51412:4;51403:14;;;51515:94;51407:3;51487:22;51515:94;;;51432:183;51693:4;51686:5;51682:16;51676:23;51705:96;51795:4;51790:3;51786:14;51772:12;51705:96;;;51625:182;51903:4;51896:5;51892:16;51886:23;51915:125;52034:4;52029:3;52025:14;52011:12;51915:125;;52579:132;52688:18;;52682:29;52809:1812;53022:22;;52948:5;52939:15;;;53050:56;52943:3;53022:22;53050:56;;;52969:143;53199:4;53192:5;53188:16;53182:23;53211:61;53266:4;53261:3;53257:14;53243:12;53211:61;;;53122:156;53354:4;53347:5;53343:16;53337:23;53366:63;53423:4;53418:3;53414:14;53400:12;53366:63;;;53288:147;53512:4;53505:5;53501:16;53495:23;53524:63;53581:4;53576:3;53572:14;53558:12;53524:63;;;53445:148;53668:4;53661:5;53657:16;53651:23;53680:157;53831:4;53826:3;53822:14;53808:12;53680:157;;;53603:240;53919:4;53912:5;53908:16;53902:23;53931:158;54082:5;54077:3;54073:15;54059:12;53931:158;;;53853:242;54173:4;54166:5;54162:16;54156:23;54185:80;54258:5;54253:3;54249:15;54235:12;54185:80;;;54105:166;54348:4;54341:5;54337:16;54331:23;54360:64;54417:5;54412:3;54408:15;54394:12;54360:64;;;54281:149;54523:5;54516;54512:17;54506:24;54536:64;54593:5;54588:3;54584:15;54570:12;54536:64;;54777:649;55060:22;;54988:4;55095:37;;;54777:649;;54979:14;;;;55147:75;54979:14;55060:22;55147:75;;;55139:83;;55008:226;55313:4;55306:5;55302:16;55296:23;55325:69;55388:4;55383:3;55379:14;55365:12;55325:69;;56473:1119;56682:22;;56612:4;56603:14;;;56710:56;56607:3;56682:22;56710:56;;;56632:140;56848:4;56841:5;56837:16;56831:23;56860:63;56917:4;56912:3;56908:14;56894:12;56860:63;;;56782:147;57006:4;56999:5;56995:16;56989:23;57018:63;57075:4;57070:3;57066:14;57052:12;57018:63;;;56939:148;57166:4;57159:5;57155:16;57149:23;57178:79;57251:4;57246:3;57242:14;57228:12;57178:79;;57710:1578;57955:22;;57883:4;57990:37;;;57710:1578;;57874:14;;;;58042:75;57874:14;57955:22;58042:75;;;58034:83;;57903:226;58206:4;58199:5;58195:16;58189:23;58258:3;58252:4;58248:14;58241:4;58236:3;58232:14;58225:38;58278:117;58390:4;58376:12;58278:117;;;58270:125;;58139:268;58489:4;58482:5;58478:16;58472:23;58541:3;58535:4;58531:14;58524:4;58519:3;58515:14;58508:38;58561:107;58663:4;58649:12;58561:107;;;58553:115;;58417:263;58772:4;58765:5;58761:16;58755:23;58824:3;58818:4;58814:14;58807:4;58802:3;58798:14;58791:38;58844:117;58956:4;58942:12;58844:117;;;58836:125;;58690:283;59061:4;59054:5;59050:16;59044:23;59113:3;59107:4;59103:14;59096:4;59091:3;59087:14;59080:38;59133:117;59245:4;59231:12;59133:117;;59422:877;59680:22;;59422:877;;59611:4;59602:14;;;59708:70;59606:3;59680:22;59708:70;;;59631:153;59864:4;59857:5;59853:16;59847:23;59916:3;59910:4;59906:14;59899:4;59894:3;59890:14;59883:38;59936:75;60006:4;59992:12;59936:75;;;59928:83;;59794:229;60114:4;60107:5;60103:16;60097:23;60166:3;60160:4;60156:14;60149:4;60144:3;60140:14;60133:38;60186:75;60256:4;60242:12;60186:75;;60397:974;60606:22;;60536:4;60527:14;;;60634:62;60531:3;60606:22;60634:62;;;60556:146;60781:4;60774:5;60770:16;60764:23;60793:79;60866:4;60861:3;60857:14;60843:12;60793:79;;;60712:166;60952:4;60945:5;60941:16;60935:23;60964:63;61021:4;61016:3;61012:14;60998:12;60964:63;;;60888:145;61108:4;61101:5;61097:16;61091:23;61120:63;61177:4;61172:3;61168:14;61154:12;61120:63;;61378:113;61461:24;61479:5;61461:24;;61498:113;61581:24;61599:5;61581:24;;61864:110;61945:23;61962:5;61945:23;;62452:152;62553:45;62573:24;62591:5;62573:24;;;62553:45;;62611:100;62682:23;62699:5;62682:23;;62718:401;;62890:93;62979:3;62970:6;62890:93;;;62883:100;;62994:75;63065:3;63056:6;62994:75;;;-1:-1;63091:2;63082:12;;62871:248;-1:-1;;62871:248;63126:465;63346:2;63360:47;;;63331:18;;63421:160;63331:18;63567:6;63421:160;;63598:465;63818:2;63832:47;;;63803:18;;63893:160;63803:18;64039:6;63893:160;;64070:407;64261:2;64275:47;;;64246:18;;64336:131;64246:18;64336:131;;64484:407;64675:2;64689:47;;;64660:18;;64750:131;64660:18;64750:131;;64898:407;65089:2;65103:47;;;65074:18;;65164:131;65074:18;65164:131;;65312:407;65503:2;65517:47;;;65488:18;;65578:131;65488:18;65578:131;;65726:407;65917:2;65931:47;;;65902:18;;65992:131;65902:18;65992:131;;66140:407;66331:2;66345:47;;;66316:18;;66406:131;66316:18;66406:131;;66554:407;66745:2;66759:47;;;66730:18;;66820:131;66730:18;66820:131;;66968:407;67159:2;67173:47;;;67144:18;;67234:131;67144:18;67234:131;;67382:407;67573:2;67587:47;;;67558:18;;67648:131;67558:18;67648:131;;67796:407;67987:2;68001:47;;;67972:18;;68062:131;67972:18;68062:131;;68210:857;68562:3;68547:19;;68577:135;68551:9;68685:6;68577:135;;;68723:110;68829:2;68818:9;68814:18;68805:6;68723:110;;;68882:9;68876:4;68872:20;68866:3;68855:9;68851:19;68844:49;68907:150;69052:4;69043:6;68907:150;;69074:1049;69434:3;69419:19;;69449:135;69423:9;69557:6;69449:135;;;69595:110;69701:2;69690:9;69686:18;69677:6;69595:110;;;69754:9;69748:4;69744:20;69738:3;69727:9;69723:19;69716:49;69779:84;69858:4;69849:6;69779:84;;;69771:92;;69874:81;69950:3;69939:9;69935:19;69926:6;69874:81;;;70004:9;69998:4;69994:20;69988:3;69977:9;69973:19;69966:49;70029:84;70108:4;70099:6;70029:84;;;70021:92;69405:718;-1:-1;;;;;;;69405:718;70130:857;70482:3;70467:19;;70497:135;70471:9;70605:6;70497:135;;;70643:110;70749:2;70738:9;70734:18;70725:6;70643:110;;;70802:9;70796:4;70792:20;70786:3;70775:9;70771:19;70764:49;70827:150;70972:4;70963:6;70827:150;;70994:845;71340:3;71325:19;;71355:135;71329:9;71463:6;71355:135;;;71501:110;71607:2;71596:9;71592:18;71583:6;71501:110;;;71660:9;71654:4;71650:20;71644:3;71633:9;71629:19;71622:49;71685:144;71824:4;71815:6;71685:144;;71846:865;72202:3;72187:19;;72217:135;72191:9;72325:6;72217:135;;;72363:110;72469:2;72458:9;72454:18;72445:6;72363:110;;;72522:9;72516:4;72512:20;72506:3;72495:9;72491:19;72484:49;72547:154;72696:4;72687:6;72547:154;;72718:640;72986:3;72971:19;;73001:135;72975:9;73109:6;73001:135;;;73147:110;73253:2;73242:9;73238:18;73229:6;73147:110;;;73268:80;73344:2;73333:9;73329:18;73320:6;73268:80;;73365:894;73735:3;73720:19;;73750:135;73724:9;73858:6;73750:135;;;73896:111;74002:3;73991:9;73987:19;73978:6;73896:111;;;74056:9;74050:4;74046:20;74040:3;74029:9;74025:19;74018:49;74081:168;74244:4;74235:6;74081:168;;75171:762;75467:3;75452:19;;75482:135;75456:9;75590:6;75482:135;;;75628:111;75734:3;75723:9;75719:19;75710:6;75628:111;;;75750:81;75826:3;75815:9;75811:19;75802:6;75750:81;;;75842;75918:3;75907:9;75903:19;75894:6;75842:81;;76709:818;77041:3;77026:19;;77056:135;77030:9;77164:6;77056:135;;;77202:111;77308:3;77297:9;77293:19;77284:6;77202:111;;;77362:9;77356:4;77352:20;77346:3;77335:9;77331:19;77324:49;77387:130;77512:4;77503:6;77387:130;;77534:850;77882:3;77867:19;;77897:135;77871:9;78005:6;77897:135;;;78043:111;78149:3;78138:9;78134:19;78125:6;78043:111;;;78203:9;78197:4;78193:20;78187:3;78176:9;78172:19;78165:49;78228:146;78369:4;78360:6;78228:146;;78391:213;78509:2;78494:18;;78523:71;78498:9;78567:6;78523:71;;78611:213;78729:2;78714:18;;78743:71;78718:9;78787:6;78743:71;;78831:209;78947:2;78932:18;;78961:69;78936:9;79003:6;78961:69;;79047:256;79109:2;79103:9;79135:17;;;-1:-1;;;;;79195:34;;79231:22;;;79192:62;79189:2;;;79267:1;79264;79257:12;79189:2;79283;79276:22;79087:216;;-1:-1;79087:216;79310:305;;-1:-1;;;;;79462:6;79459:30;79456:2;;;79502:1;79499;79492:12;79456:2;-1:-1;79537:4;79525:17;;;79590:15;;79393:222;79929:317;;-1:-1;;;;;80060:6;80057:30;80054:2;;;80100:1;80097;80090:12;80054:2;-1:-1;80231:4;80167;80144:17;;;;-1:-1;;80140:33;80221:15;;79991:255;80581:152;80706:4;80697:14;;80654:79;81108:119;81214:3;81200:27;81388:138;81492:12;;81463:63;81873:129;-1:-1;81989:3;;81967:35;83046:181;83167:19;;;83216:4;83207:14;;83160:67;84684:91;;84746:24;84764:5;84746:24;;84888:85;84954:13;84947:21;;84930:43;85059:113;-1:-1;;;;;85121:46;;85104:68;85179:84;85251:6;85240:18;;85223:40;85270:121;-1:-1;;;;;85332:54;;85315:76;85477:96;-1:-1;;;;;85538:30;;85521:52;85580:161;;85679:57;85730:5;85679:57;;87504:145;87585:6;87580:3;87575;87562:30;-1:-1;87641:1;87623:16;;87616:27;87555:94;87658:268;87723:1;87730:101;87744:6;87741:1;87738:13;87730:101;;;87811:11;;;87805:18;87792:11;;;87785:39;87766:2;87759:10;87730:101;;;87846:6;87843:1;87840:13;87837:2;;;-1:-1;;87911:1;87893:16;;87886:27;87707:219;88015:97;88103:2;88083:14;-1:-1;;88079:28;;88063:49;88120:117;88189:24;88207:5;88189:24;;;88182:5;88179:35;88169:2;;88228:1;88225;88218:12;88169:2;88163:74;;88244:111;88310:21;88325:5;88310:21;;88362:117;88431:24;88449:5;88431:24;;88486:117;88555:24;88573:5;88555:24;;88610:115;88678:23;88695:5;88678:23;;88732:117;88801:24;88819:5;88801:24;;88899:74;1708:13:68;;:::i;:::-;-1:-1:-1;1851:264:68;;;;;;;;-1:-1:-1;;;;;1851:264:68;;;;;;-1:-1:-1;1851:264:68;;;;1827:7;1851:264;;;;;;;;;;;;;;;;;;1560:562::o
Swarm Source
bzzr://9c00be5c05ba01aa62f0d0328e226c7eee7b07c7d18bf1f24a73b36e65a35a49
Loading...
Loading
Loading...
Loading
OVERVIEW
The OMG Network's exit game contract.Net Worth in USD
$487.58
Net Worth in ETH
0.237908
Token Allocations
ETH
100.00%
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 100.00% | $2,048.66 | 0.238 | $487.58 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.