Source Code
Latest 25 from a total of 26 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Schedule | 12964672 | 1697 days ago | IN | 0.05088494 ETH | 0.01977951 | ||||
| Schedule | 11107271 | 1984 days ago | IN | 0.3046 ETH | 0.00606521 | ||||
| Schedule | 11087849 | 1987 days ago | IN | 0.2448 ETH | 0.00178012 | ||||
| Schedule | 11087781 | 1987 days ago | IN | 0.22926024 ETH | 0.00177984 | ||||
| Schedule | 11087247 | 1987 days ago | IN | 0.1121004 ETH | 0.00445032 | ||||
| Schedule | 11086543 | 1987 days ago | IN | 0.959 ETH | 0.0260985 | ||||
| Schedule | 11063535 | 1990 days ago | IN | 0.12421573 ETH | 0.01413838 | ||||
| Schedule | 10603761 | 2061 days ago | IN | 0.0156 ETH | 0.00505838 | ||||
| Schedule | 10603707 | 2061 days ago | IN | 0.01627263 ETH | 0.00991531 | ||||
| Schedule | 10603638 | 2061 days ago | IN | 0.02428 ETH | 0.00505838 | ||||
| Schedule | 10603593 | 2061 days ago | IN | 0.02173 ETH | 0.00254816 | ||||
| Schedule | 10603499 | 2061 days ago | IN | 0.02173 ETH | 0.00254816 | ||||
| Schedule | 10603297 | 2061 days ago | IN | 0.02804 ETH | 0.01786071 | ||||
| Schedule | 10603207 | 2061 days ago | IN | 0.0631032 ETH | 0.03126031 | ||||
| Schedule | 10602818 | 2061 days ago | IN | 0.01804128 ETH | 0.01754823 | ||||
| Schedule | 10602683 | 2061 days ago | IN | 0.02 ETH | 0.00962359 | ||||
| Schedule | 10602660 | 2061 days ago | IN | 0.0120516 ETH | 0.00490955 | ||||
| Schedule | 10602642 | 2061 days ago | IN | 0.0141 ETH | 0.00490955 | ||||
| Schedule | 10601934 | 2061 days ago | IN | 0.09809 ETH | 0.04021776 | ||||
| Schedule | 10601830 | 2062 days ago | IN | 0.1031506 ETH | 0.02061061 | ||||
| Schedule | 10440401 | 2086 days ago | IN | 0.00711 ETH | 0.0090361 | ||||
| Schedule | 8858100 | 2339 days ago | IN | 0.00380002 ETH | 0.00569294 | ||||
| Schedule | 8393869 | 2412 days ago | IN | 0.10523422 ETH | 0.00333361 | ||||
| Schedule | 6630238 | 2704 days ago | IN | 300.04804 ETH | 0.01076852 | ||||
| Schedule | 6629832 | 2704 days ago | IN | 280.02402 ETH | 0.01076724 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| - | 12964672 | 1697 days ago | 0.05088494 ETH | ||||
| - | 11107271 | 1984 days ago | 0.3046 ETH | ||||
| - | 11107271 | 1984 days ago | 0.3046 ETH | ||||
| - | 11087849 | 1987 days ago | 0.2448 ETH | ||||
| - | 11087849 | 1987 days ago | 0.2448 ETH | ||||
| - | 11087781 | 1987 days ago | 0.22926024 ETH | ||||
| - | 11087781 | 1987 days ago | 0.22926024 ETH | ||||
| - | 11087247 | 1987 days ago | 0.1121004 ETH | ||||
| - | 11087247 | 1987 days ago | 0.1121004 ETH | ||||
| - | 11086543 | 1987 days ago | 0.959 ETH | ||||
| - | 11063535 | 1990 days ago | 0.12421573 ETH | ||||
| - | 10603761 | 2061 days ago | 0.0156 ETH | ||||
| - | 10603707 | 2061 days ago | 0.01627263 ETH | ||||
| - | 10603638 | 2061 days ago | 0.02428 ETH | ||||
| - | 10603593 | 2061 days ago | 0.02173 ETH | ||||
| - | 10603499 | 2061 days ago | 0.02173 ETH | ||||
| - | 10603297 | 2061 days ago | 0.02804 ETH | ||||
| - | 10603207 | 2061 days ago | 0.0631032 ETH | ||||
| - | 10602818 | 2061 days ago | 0.01804128 ETH | ||||
| - | 10602683 | 2061 days ago | 0.02 ETH | ||||
| - | 10602660 | 2061 days ago | 0.0120516 ETH | ||||
| - | 10602642 | 2061 days ago | 0.0141 ETH | ||||
| - | 10601934 | 2061 days ago | 0.09809 ETH | ||||
| - | 10601830 | 2062 days ago | 0.1031506 ETH | ||||
| - | 10440401 | 2086 days ago | 0.00711 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract contains unverified libraries: PaymentLib, RequestLib
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:
BlockScheduler
Compiler Version
v0.4.24+commit.e67f0147
Optimization Enabled:
No with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity)
/**
*Submitted for verification at Etherscan.io on 2018-08-29
*/
pragma solidity 0.4.24;
contract Ownable {
address public owner;
event OwnershipRenounced(address indexed previousOwner);
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev The Ownable constructor sets the original `owner` of the contract to the sender
* account.
*/
constructor() public {
owner = msg.sender;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
/**
* @dev Allows the current owner to relinquish control of the contract.
* @notice Renouncing to ownership will leave the contract without an owner.
* It will not be possible to call the functions with the `onlyOwner`
* modifier anymore.
*/
function renounceOwnership() public onlyOwner {
emit OwnershipRenounced(owner);
owner = address(0);
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function transferOwnership(address _newOwner) public onlyOwner {
_transferOwnership(_newOwner);
}
/**
* @dev Transfers control of the contract to a newOwner.
* @param _newOwner The address to transfer ownership to.
*/
function _transferOwnership(address _newOwner) internal {
require(_newOwner != address(0));
emit OwnershipTransferred(owner, _newOwner);
owner = _newOwner;
}
}
contract Pausable is Ownable {
event Pause();
event Unpause();
bool public paused = false;
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!paused);
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(paused);
_;
}
/**
* @dev called by the owner to pause, triggers stopped state
*/
function pause() public onlyOwner whenNotPaused {
paused = true;
emit Pause();
}
/**
* @dev called by the owner to unpause, returns to normal state
*/
function unpause() public onlyOwner whenPaused {
paused = false;
emit Unpause();
}
}
/**
* @title SchedulerInterface
* @dev The base contract that the higher contracts: BaseScheduler, BlockScheduler and TimestampScheduler all inherit from.
*/
contract SchedulerInterface {
function schedule(address _toAddress, bytes _callData, uint[8] _uintArgs)
public payable returns (address);
function computeEndowment(uint _bounty, uint _fee, uint _callGas, uint _callValue, uint _gasPrice)
public view returns (uint);
}
contract TransactionRequestInterface {
// Primary actions
function execute() public returns (bool);
function cancel() public returns (bool);
function claim() public payable returns (bool);
// Proxy function
function proxy(address recipient, bytes callData) public payable returns (bool);
// Data accessors
function requestData() public view returns (address[6], bool[3], uint[15], uint8[1]);
function callData() public view returns (bytes);
// Pull mechanisms for payments.
function refundClaimDeposit() public returns (bool);
function sendFee() public returns (bool);
function sendBounty() public returns (bool);
function sendOwnerEther() public returns (bool);
function sendOwnerEther(address recipient) public returns (bool);
}
contract TransactionRequestCore is TransactionRequestInterface {
using RequestLib for RequestLib.Request;
using RequestScheduleLib for RequestScheduleLib.ExecutionWindow;
RequestLib.Request txnRequest;
bool private initialized = false;
/*
* addressArgs[0] - meta.createdBy
* addressArgs[1] - meta.owner
* addressArgs[2] - paymentData.feeRecipient
* addressArgs[3] - txnData.toAddress
*
* uintArgs[0] - paymentData.fee
* uintArgs[1] - paymentData.bounty
* uintArgs[2] - schedule.claimWindowSize
* uintArgs[3] - schedule.freezePeriod
* uintArgs[4] - schedule.reservedWindowSize
* uintArgs[5] - schedule.temporalUnit
* uintArgs[6] - schedule.windowSize
* uintArgs[7] - schedule.windowStart
* uintArgs[8] - txnData.callGas
* uintArgs[9] - txnData.callValue
* uintArgs[10] - txnData.gasPrice
* uintArgs[11] - claimData.requiredDeposit
*/
function initialize(
address[4] addressArgs,
uint[12] uintArgs,
bytes callData
)
public payable
{
require(!initialized);
txnRequest.initialize(addressArgs, uintArgs, callData);
initialized = true;
}
/*
* Allow receiving ether. This is needed if there is a large increase in
* network gas prices.
*/
function() public payable {}
/*
* Actions
*/
function execute() public returns (bool) {
return txnRequest.execute();
}
function cancel() public returns (bool) {
return txnRequest.cancel();
}
function claim() public payable returns (bool) {
return txnRequest.claim();
}
/*
* Data accessor functions.
*/
// Declaring this function `view`, although it creates a compiler warning, is
// necessary to return values from it.
function requestData()
public view returns (address[6], bool[3], uint[15], uint8[1])
{
return txnRequest.serialize();
}
function callData()
public view returns (bytes data)
{
data = txnRequest.txnData.callData;
}
/**
* @dev Proxy a call from this contract to another contract.
* This function is only callable by the scheduler and can only
* be called after the execution window ends. One purpose is to
* provide a way to transfer assets held by this contract somewhere else.
* For example, if this request was used to buy tokens during an ICO,
* it would become the owner of the tokens and this function would need
* to be called with the encoded data to the token contract to transfer
* the assets somewhere else. */
function proxy(address _to, bytes _data)
public payable returns (bool success)
{
require(txnRequest.meta.owner == msg.sender && txnRequest.schedule.isAfterWindow());
/* solium-disable-next-line */
return _to.call.value(msg.value)(_data);
}
/*
* Pull based payment functions.
*/
function refundClaimDeposit() public returns (bool) {
txnRequest.refundClaimDeposit();
}
function sendFee() public returns (bool) {
return txnRequest.sendFee();
}
function sendBounty() public returns (bool) {
return txnRequest.sendBounty();
}
function sendOwnerEther() public returns (bool) {
return txnRequest.sendOwnerEther();
}
function sendOwnerEther(address recipient) public returns (bool) {
return txnRequest.sendOwnerEther(recipient);
}
/** Event duplication from RequestLib.sol. This is so
* that these events are available on the contracts ABI.*/
event Aborted(uint8 reason);
event Cancelled(uint rewardPayment, uint measuredGasConsumption);
event Claimed();
event Executed(uint bounty, uint fee, uint measuredGasConsumption);
}
contract RequestFactoryInterface {
event RequestCreated(address request, address indexed owner, int indexed bucket, uint[12] params);
function createRequest(address[3] addressArgs, uint[12] uintArgs, bytes callData) public payable returns (address);
function createValidatedRequest(address[3] addressArgs, uint[12] uintArgs, bytes callData) public payable returns (address);
function validateRequestParams(address[3] addressArgs, uint[12] uintArgs, uint endowment) public view returns (bool[6]);
function isKnownRequest(address _address) public view returns (bool);
}
contract TransactionRecorder {
address owner;
bool public wasCalled;
uint public lastCallValue;
address public lastCaller;
bytes public lastCallData = "";
uint public lastCallGas;
function TransactionRecorder() public {
owner = msg.sender;
}
function() payable public {
lastCallGas = gasleft();
lastCallData = msg.data;
lastCaller = msg.sender;
lastCallValue = msg.value;
wasCalled = true;
}
function __reset__() public {
lastCallGas = 0;
lastCallData = "";
lastCaller = 0x0;
lastCallValue = 0;
wasCalled = false;
}
function kill() public {
require(msg.sender == owner);
selfdestruct(owner);
}
}
contract Proxy {
SchedulerInterface public scheduler;
address public receipient;
address public scheduledTransaction;
address public owner;
function Proxy(address _scheduler, address _receipient, uint _payout, uint _gasPrice, uint _delay) public payable {
scheduler = SchedulerInterface(_scheduler);
receipient = _receipient;
owner = msg.sender;
scheduledTransaction = scheduler.schedule.value(msg.value)(
this, // toAddress
"", // callData
[
2000000, // The amount of gas to be sent with the transaction.
_payout, // The amount of wei to be sent.
255, // The size of the execution window.
block.number + _delay, // The start of the execution window.
_gasPrice, // The gasprice for the transaction
12345 wei, // The fee included in the transaction.
224455 wei, // The bounty that awards the executor of the transaction.
20000 wei // The required amount of wei the claimer must send as deposit.
]
);
}
function () public payable {
if (msg.value > 0) {
receipient.transfer(msg.value);
}
}
function sendOwnerEther(address _receipient) public {
if (msg.sender == owner && _receipient != 0x0) {
TransactionRequestInterface(scheduledTransaction).sendOwnerEther(_receipient);
}
}
}
/// Super simple token contract that moves funds into the owner account on creation and
/// only exposes an API to be used for `test/proxy.js`
contract SimpleToken {
address public owner;
mapping(address => uint) balances;
function SimpleToken (uint _initialSupply) public {
owner = msg.sender;
balances[owner] = _initialSupply;
}
function transfer (address _to, uint _amount)
public returns (bool success)
{
require(balances[msg.sender] > _amount);
balances[msg.sender] -= _amount;
balances[_to] += _amount;
success = true;
}
uint public constant rate = 30;
function buyTokens()
public payable returns (bool success)
{
require(msg.value > 0);
balances[msg.sender] += msg.value * rate;
success = true;
}
function balanceOf (address _who)
public view returns (uint balance)
{
balance = balances[_who];
}
}
/**
* @title SafeMath
* @dev Math operations with safety checks that throw on error
*/
library SafeMath {
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a * b;
require(a == 0 || c / a == b);
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
// require(b > 0); // Solidity automatically throws when dividing by 0
uint256 c = a / b;
// require(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
}
/**
* @title BaseScheduler
* @dev The foundational contract which provides the API for scheduling future transactions on the Alarm Client.
*/
contract BaseScheduler is SchedulerInterface {
// The RequestFactory which produces requests for this scheduler.
address public factoryAddress;
// The TemporalUnit (Block or Timestamp) for this scheduler.
RequestScheduleLib.TemporalUnit public temporalUnit;
// The address which will be sent the fee payments.
address public feeRecipient;
/*
* @dev Fallback function to be able to receive ether. This can occur
* legitimately when scheduling fails due to a validation error.
*/
function() public payable {}
/// Event that bubbles up the address of new requests made with this scheduler.
event NewRequest(address request);
/**
* @dev Schedules a new TransactionRequest using the 'full' parameters.
* @param _toAddress The address destination of the transaction.
* @param _callData The bytecode that will be included with the transaction.
* @param _uintArgs [0] The callGas of the transaction.
* @param _uintArgs [1] The value of ether to be sent with the transaction.
* @param _uintArgs [2] The size of the execution window of the transaction.
* @param _uintArgs [3] The (block or timestamp) of when the execution window starts.
* @param _uintArgs [4] The gasPrice which will be used to execute this transaction.
* @param _uintArgs [5] The fee attached to this transaction.
* @param _uintArgs [6] The bounty attached to this transaction.
* @param _uintArgs [7] The deposit required to claim this transaction.
* @return The address of the new TransactionRequest.
*/
function schedule (
address _toAddress,
bytes _callData,
uint[8] _uintArgs
)
public payable returns (address newRequest)
{
RequestFactoryInterface factory = RequestFactoryInterface(factoryAddress);
uint endowment = computeEndowment(
_uintArgs[6], //bounty
_uintArgs[5], //fee
_uintArgs[0], //callGas
_uintArgs[1], //callValue
_uintArgs[4] //gasPrice
);
require(msg.value >= endowment);
if (temporalUnit == RequestScheduleLib.TemporalUnit.Blocks) {
newRequest = factory.createValidatedRequest.value(msg.value)(
[
msg.sender, // meta.owner
feeRecipient, // paymentData.feeRecipient
_toAddress // txnData.toAddress
],
[
_uintArgs[5], // paymentData.fee
_uintArgs[6], // paymentData.bounty
255, // scheduler.claimWindowSize
10, // scheduler.freezePeriod
16, // scheduler.reservedWindowSize
uint(temporalUnit), // scheduler.temporalUnit (1: block, 2: timestamp)
_uintArgs[2], // scheduler.windowSize
_uintArgs[3], // scheduler.windowStart
_uintArgs[0], // txnData.callGas
_uintArgs[1], // txnData.callValue
_uintArgs[4], // txnData.gasPrice
_uintArgs[7] // claimData.requiredDeposit
],
_callData
);
} else if (temporalUnit == RequestScheduleLib.TemporalUnit.Timestamp) {
newRequest = factory.createValidatedRequest.value(msg.value)(
[
msg.sender, // meta.owner
feeRecipient, // paymentData.feeRecipient
_toAddress // txnData.toAddress
],
[
_uintArgs[5], // paymentData.fee
_uintArgs[6], // paymentData.bounty
60 minutes, // scheduler.claimWindowSize
3 minutes, // scheduler.freezePeriod
5 minutes, // scheduler.reservedWindowSize
uint(temporalUnit), // scheduler.temporalUnit (1: block, 2: timestamp)
_uintArgs[2], // scheduler.windowSize
_uintArgs[3], // scheduler.windowStart
_uintArgs[0], // txnData.callGas
_uintArgs[1], // txnData.callValue
_uintArgs[4], // txnData.gasPrice
_uintArgs[7] // claimData.requiredDeposit
],
_callData
);
} else {
// unsupported temporal unit
revert();
}
require(newRequest != 0x0);
emit NewRequest(newRequest);
return newRequest;
}
function computeEndowment(
uint _bounty,
uint _fee,
uint _callGas,
uint _callValue,
uint _gasPrice
)
public view returns (uint)
{
return PaymentLib.computeEndowment(
_bounty,
_fee,
_callGas,
_callValue,
_gasPrice,
RequestLib.getEXECUTION_GAS_OVERHEAD()
);
}
}
/**
* @title BlockScheduler
* @dev Top-level contract that exposes the API to the Ethereum Alarm Clock service and passes in blocks as temporal unit.
*/
contract BlockScheduler is BaseScheduler {
/**
* @dev Constructor
* @param _factoryAddress Address of the RequestFactory which creates requests for this scheduler.
*/
constructor(address _factoryAddress, address _feeRecipient) public {
require(_factoryAddress != 0x0);
// Default temporal unit is block number.
temporalUnit = RequestScheduleLib.TemporalUnit.Blocks;
// Sets the factoryAddress variable found in BaseScheduler contract.
factoryAddress = _factoryAddress;
// Sets the fee recipient for these schedulers.
feeRecipient = _feeRecipient;
}
}
/**
* @title TimestampScheduler
* @dev Top-level contract that exposes the API to the Ethereum Alarm Clock service and passes in timestamp as temporal unit.
*/
contract TimestampScheduler is BaseScheduler {
/**
* @dev Constructor
* @param _factoryAddress Address of the RequestFactory which creates requests for this scheduler.
*/
constructor(address _factoryAddress, address _feeRecipient) public {
require(_factoryAddress != 0x0);
// Default temporal unit is timestamp.
temporalUnit = RequestScheduleLib.TemporalUnit.Timestamp;
// Sets the factoryAddress variable found in BaseScheduler contract.
factoryAddress = _factoryAddress;
// Sets the fee recipient for these schedulers.
feeRecipient = _feeRecipient;
}
}
/// Truffle-specific contract (Not a part of the EAC)
contract Migrations {
address public owner;
uint public last_completed_migration;
modifier restricted() {
if (msg.sender == owner) {
_;
}
}
function Migrations() public {
owner = msg.sender;
}
function setCompleted(uint completed) restricted public {
last_completed_migration = completed;
}
function upgrade(address new_address) restricted public {
Migrations upgraded = Migrations(new_address);
upgraded.setCompleted(last_completed_migration);
}
}
/**
* @title ExecutionLib
* @dev Contains the logic for executing a scheduled transaction.
*/
library ExecutionLib {
struct ExecutionData {
address toAddress; /// The destination of the transaction.
bytes callData; /// The bytecode that will be sent with the transaction.
uint callValue; /// The wei value that will be sent with the transaction.
uint callGas; /// The amount of gas to be sent with the transaction.
uint gasPrice; /// The gasPrice that should be set for the transaction.
}
/**
* @dev Send the transaction according to the parameters outlined in ExecutionData.
* @param self The ExecutionData object.
*/
function sendTransaction(ExecutionData storage self)
internal returns (bool)
{
/// Should never actually reach this require check, but here in case.
require(self.gasPrice <= tx.gasprice);
/* solium-disable security/no-call-value */
return self.toAddress.call.value(self.callValue).gas(self.callGas)(self.callData);
}
/**
* Returns the maximum possible gas consumption that a transaction request
* may consume. The EXTRA_GAS value represents the overhead involved in
* request execution.
*/
function CALL_GAS_CEILING(uint EXTRA_GAS)
internal view returns (uint)
{
return block.gaslimit - EXTRA_GAS;
}
/*
* @dev Validation: ensure that the callGas is not above the total possible gas
* for a call.
*/
function validateCallGas(uint callGas, uint EXTRA_GAS)
internal view returns (bool)
{
return callGas < CALL_GAS_CEILING(EXTRA_GAS);
}
/*
* @dev Validation: ensure that the toAddress is not set to the empty address.
*/
function validateToAddress(address toAddress)
internal pure returns (bool)
{
return toAddress != 0x0;
}
}
library MathLib {
uint constant INT_MAX = 57896044618658097711785492504343953926634992332820282019728792003956564819967; // 2**255 - 1
/*
* Subtracts b from a in a manner such that zero is returned when an
* underflow condition is met.
*/
// function flooredSub(uint a, uint b) returns (uint) {
// if (b >= a) {
// return 0;
// } else {
// return a - b;
// }
// }
// /*
// * Adds b to a in a manner that throws an exception when overflow
// * conditions are met.
// */
// function safeAdd(uint a, uint b) returns (uint) {
// if (a + b >= a) {
// return a + b;
// } else {
// throw;
// }
// }
// /*
// * Multiplies a by b in a manner that throws an exception when overflow
// * conditions are met.
// */
// function safeMultiply(uint a, uint b) returns (uint) {
// var result = a * b;
// if (b == 0 || result / b == a) {
// return a * b;
// } else {
// throw;
// }
// }
/*
* Return the larger of a or b. Returns a if a == b.
*/
function max(uint a, uint b)
public pure returns (uint)
{
if (a >= b) {
return a;
} else {
return b;
}
}
/*
* Return the larger of a or b. Returns a if a == b.
*/
function min(uint a, uint b)
public pure returns (uint)
{
if (a <= b) {
return a;
} else {
return b;
}
}
/*
* Returns a represented as a signed integer in a manner that throw an
* exception if casting to signed integer would result in a negative
* number.
*/
function safeCastSigned(uint a)
public pure returns (int)
{
assert(a <= INT_MAX);
return int(a);
}
}
/**
* @title RequestMetaLib
* @dev Small library holding all the metadata about a TransactionRequest.
*/
library RequestMetaLib {
struct RequestMeta {
address owner; /// The address that created this request.
address createdBy; /// The address of the RequestFactory which created this request.
bool isCancelled; /// Was the TransactionRequest cancelled?
bool wasCalled; /// Was the TransactionRequest called?
bool wasSuccessful; /// Was the return value from the TransactionRequest execution successful?
}
}
library RequestLib {
using ClaimLib for ClaimLib.ClaimData;
using ExecutionLib for ExecutionLib.ExecutionData;
using PaymentLib for PaymentLib.PaymentData;
using RequestMetaLib for RequestMetaLib.RequestMeta;
using RequestScheduleLib for RequestScheduleLib.ExecutionWindow;
using SafeMath for uint;
struct Request {
ExecutionLib.ExecutionData txnData;
RequestMetaLib.RequestMeta meta;
PaymentLib.PaymentData paymentData;
ClaimLib.ClaimData claimData;
RequestScheduleLib.ExecutionWindow schedule;
}
enum AbortReason {
WasCancelled, //0
AlreadyCalled, //1
BeforeCallWindow, //2
AfterCallWindow, //3
ReservedForClaimer, //4
InsufficientGas, //5
TooLowGasPrice //6
}
event Aborted(uint8 reason);
event Cancelled(uint rewardPayment, uint measuredGasConsumption);
event Claimed();
event Executed(uint bounty, uint fee, uint measuredGasConsumption);
/**
* @dev Validate the initialization parameters of a transaction request.
*/
function validate(
address[4] _addressArgs,
uint[12] _uintArgs,
uint _endowment
)
public view returns (bool[6] isValid)
{
// The order of these errors matters as it determines which
// ValidationError event codes are logged when validation fails.
isValid[0] = PaymentLib.validateEndowment(
_endowment,
_uintArgs[1], //bounty
_uintArgs[0], //fee
_uintArgs[8], //callGas
_uintArgs[9], //callValue
_uintArgs[10], //gasPrice
EXECUTION_GAS_OVERHEAD
);
isValid[1] = RequestScheduleLib.validateReservedWindowSize(
_uintArgs[4], //reservedWindowSize
_uintArgs[6] //windowSize
);
isValid[2] = RequestScheduleLib.validateTemporalUnit(_uintArgs[5]);
isValid[3] = RequestScheduleLib.validateWindowStart(
RequestScheduleLib.TemporalUnit(MathLib.min(_uintArgs[5], 2)),
_uintArgs[3], //freezePeriod
_uintArgs[7] //windowStart
);
isValid[4] = ExecutionLib.validateCallGas(
_uintArgs[8], //callGas
EXECUTION_GAS_OVERHEAD
);
isValid[5] = ExecutionLib.validateToAddress(_addressArgs[3]);
return isValid;
}
/**
* @dev Initialize a new Request.
*/
function initialize(
Request storage self,
address[4] _addressArgs,
uint[12] _uintArgs,
bytes _callData
)
public returns (bool)
{
address[6] memory addressValues = [
0x0, // self.claimData.claimedBy
_addressArgs[0], // self.meta.createdBy
_addressArgs[1], // self.meta.owner
_addressArgs[2], // self.paymentData.feeRecipient
0x0, // self.paymentData.bountyBenefactor
_addressArgs[3] // self.txnData.toAddress
];
bool[3] memory boolValues = [false, false, false];
uint[15] memory uintValues = [
0, // self.claimData.claimDeposit
_uintArgs[0], // self.paymentData.fee
0, // self.paymentData.feeOwed
_uintArgs[1], // self.paymentData.bounty
0, // self.paymentData.bountyOwed
_uintArgs[2], // self.schedule.claimWindowSize
_uintArgs[3], // self.schedule.freezePeriod
_uintArgs[4], // self.schedule.reservedWindowSize
_uintArgs[5], // self.schedule.temporalUnit
_uintArgs[6], // self.schedule.windowSize
_uintArgs[7], // self.schedule.windowStart
_uintArgs[8], // self.txnData.callGas
_uintArgs[9], // self.txnData.callValue
_uintArgs[10], // self.txnData.gasPrice
_uintArgs[11] // self.claimData.requiredDeposit
];
uint8[1] memory uint8Values = [
0
];
require(deserialize(self, addressValues, boolValues, uintValues, uint8Values, _callData));
return true;
}
function serialize(Request storage self)
internal view returns(address[6], bool[3], uint[15], uint8[1])
{
address[6] memory addressValues = [
self.claimData.claimedBy,
self.meta.createdBy,
self.meta.owner,
self.paymentData.feeRecipient,
self.paymentData.bountyBenefactor,
self.txnData.toAddress
];
bool[3] memory boolValues = [
self.meta.isCancelled,
self.meta.wasCalled,
self.meta.wasSuccessful
];
uint[15] memory uintValues = [
self.claimData.claimDeposit,
self.paymentData.fee,
self.paymentData.feeOwed,
self.paymentData.bounty,
self.paymentData.bountyOwed,
self.schedule.claimWindowSize,
self.schedule.freezePeriod,
self.schedule.reservedWindowSize,
uint(self.schedule.temporalUnit),
self.schedule.windowSize,
self.schedule.windowStart,
self.txnData.callGas,
self.txnData.callValue,
self.txnData.gasPrice,
self.claimData.requiredDeposit
];
uint8[1] memory uint8Values = [
self.claimData.paymentModifier
];
return (addressValues, boolValues, uintValues, uint8Values);
}
/**
* @dev Populates a Request object from the full output of `serialize`.
*
* Parameter order is alphabetical by type, then namespace, then name.
*/
function deserialize(
Request storage self,
address[6] _addressValues,
bool[3] _boolValues,
uint[15] _uintValues,
uint8[1] _uint8Values,
bytes _callData
)
internal returns (bool)
{
// callData is special.
self.txnData.callData = _callData;
// Address values
self.claimData.claimedBy = _addressValues[0];
self.meta.createdBy = _addressValues[1];
self.meta.owner = _addressValues[2];
self.paymentData.feeRecipient = _addressValues[3];
self.paymentData.bountyBenefactor = _addressValues[4];
self.txnData.toAddress = _addressValues[5];
// Boolean values
self.meta.isCancelled = _boolValues[0];
self.meta.wasCalled = _boolValues[1];
self.meta.wasSuccessful = _boolValues[2];
// UInt values
self.claimData.claimDeposit = _uintValues[0];
self.paymentData.fee = _uintValues[1];
self.paymentData.feeOwed = _uintValues[2];
self.paymentData.bounty = _uintValues[3];
self.paymentData.bountyOwed = _uintValues[4];
self.schedule.claimWindowSize = _uintValues[5];
self.schedule.freezePeriod = _uintValues[6];
self.schedule.reservedWindowSize = _uintValues[7];
self.schedule.temporalUnit = RequestScheduleLib.TemporalUnit(_uintValues[8]);
self.schedule.windowSize = _uintValues[9];
self.schedule.windowStart = _uintValues[10];
self.txnData.callGas = _uintValues[11];
self.txnData.callValue = _uintValues[12];
self.txnData.gasPrice = _uintValues[13];
self.claimData.requiredDeposit = _uintValues[14];
// Uint8 values
self.claimData.paymentModifier = _uint8Values[0];
return true;
}
function execute(Request storage self)
internal returns (bool)
{
/*
* Execute the TransactionRequest
*
* +---------------------+
* | Phase 1: Validation |
* +---------------------+
*
* Must pass all of the following checks:
*
* 1. Not already called.
* 2. Not cancelled.
* 3. Not before the execution window.
* 4. Not after the execution window.
* 5. if (claimedBy == 0x0 or msg.sender == claimedBy):
* - windowStart <= block.number
* - block.number <= windowStart + windowSize
* else if (msg.sender != claimedBy):
* - windowStart + reservedWindowSize <= block.number
* - block.number <= windowStart + windowSize
* else:
* - throw (should be impossible)
*
* 6. gasleft() == callGas
* 7. tx.gasprice >= txnData.gasPrice
*
* +--------------------+
* | Phase 2: Execution |
* +--------------------+
*
* 1. Mark as called (must be before actual execution to prevent
* re-entrance)
* 2. Send Transaction and record success or failure.
*
* +---------------------+
* | Phase 3: Accounting |
* +---------------------+
*
* 1. Calculate and send fee amount.
* 2. Calculate and send bounty amount.
* 3. Send remaining ether back to owner.
*
*/
// Record the gas at the beginning of the transaction so we can
// calculate how much has been used later.
uint startGas = gasleft();
// +----------------------+
// | Begin: Authorization |
// +----------------------+
if (gasleft() < requiredExecutionGas(self).sub(PRE_EXECUTION_GAS)) {
emit Aborted(uint8(AbortReason.InsufficientGas));
return false;
} else if (self.meta.wasCalled) {
emit Aborted(uint8(AbortReason.AlreadyCalled));
return false;
} else if (self.meta.isCancelled) {
emit Aborted(uint8(AbortReason.WasCancelled));
return false;
} else if (self.schedule.isBeforeWindow()) {
emit Aborted(uint8(AbortReason.BeforeCallWindow));
return false;
} else if (self.schedule.isAfterWindow()) {
emit Aborted(uint8(AbortReason.AfterCallWindow));
return false;
} else if (self.claimData.isClaimed() && msg.sender != self.claimData.claimedBy && self.schedule.inReservedWindow()) {
emit Aborted(uint8(AbortReason.ReservedForClaimer));
return false;
} else if (self.txnData.gasPrice > tx.gasprice) {
emit Aborted(uint8(AbortReason.TooLowGasPrice));
return false;
}
// +--------------------+
// | End: Authorization |
// +--------------------+
// +------------------+
// | Begin: Execution |
// +------------------+
// Mark as being called before sending transaction to prevent re-entrance.
self.meta.wasCalled = true;
// Send the transaction...
// The transaction is allowed to fail and the executing agent will still get the bounty.
// `.sendTransaction()` will return false on a failed exeuction.
self.meta.wasSuccessful = self.txnData.sendTransaction();
// +----------------+
// | End: Execution |
// +----------------+
// +-------------------+
// | Begin: Accounting |
// +-------------------+
// Compute the fee amount
if (self.paymentData.hasFeeRecipient()) {
self.paymentData.feeOwed = self.paymentData.getFee()
.add(self.paymentData.feeOwed);
}
// Record this locally so that we can log it later.
// `.sendFee()` below will change `self.paymentData.feeOwed` to 0 to prevent re-entrance.
uint totalFeePayment = self.paymentData.feeOwed;
// Send the fee. This transaction may also fail but can be called again after
// execution.
self.paymentData.sendFee();
// Compute the bounty amount.
self.paymentData.bountyBenefactor = msg.sender;
if (self.claimData.isClaimed()) {
// If the transaction request was claimed, we add the deposit to the bounty whether
// or not the same agent who claimed is executing.
self.paymentData.bountyOwed = self.claimData.claimDeposit
.add(self.paymentData.bountyOwed);
// To prevent re-entrance we zero out the claim deposit since it is now accounted for
// in the bounty value.
self.claimData.claimDeposit = 0;
// Depending on when the transaction request was claimed, we apply the modifier to the
// bounty payment and add it to the bounty already owed.
self.paymentData.bountyOwed = self.paymentData.getBountyWithModifier(self.claimData.paymentModifier)
.add(self.paymentData.bountyOwed);
} else {
// Not claimed. Just add the full bounty.
self.paymentData.bountyOwed = self.paymentData.getBounty().add(self.paymentData.bountyOwed);
}
// Take down the amount of gas used so far in execution to compensate the executing agent.
uint measuredGasConsumption = startGas.sub(gasleft()).add(EXECUTE_EXTRA_GAS);
// // +----------------------------------------------------------------------+
// // | NOTE: All code after this must be accounted for by EXECUTE_EXTRA_GAS |
// // +----------------------------------------------------------------------+
// Add the gas reimbursment amount to the bounty.
self.paymentData.bountyOwed = measuredGasConsumption
.mul(self.txnData.gasPrice)
.add(self.paymentData.bountyOwed);
// Log the bounty and fee. Otherwise it is non-trivial to figure
// out how much was payed.
emit Executed(self.paymentData.bountyOwed, totalFeePayment, measuredGasConsumption);
// Attempt to send the bounty. as with `.sendFee()` it may fail and need to be caled after execution.
self.paymentData.sendBounty();
// If any ether is left, send it back to the owner of the transaction request.
_sendOwnerEther(self, self.meta.owner);
// +-----------------+
// | End: Accounting |
// +-----------------+
// Successful
return true;
}
// This is the amount of gas that it takes to enter from the
// `TransactionRequest.execute()` contract into the `RequestLib.execute()`
// method at the point where the gas check happens.
uint public constant PRE_EXECUTION_GAS = 25000; // TODO is this number still accurate?
/*
* The amount of gas needed to complete the execute method after
* the transaction has been sent.
*/
uint public constant EXECUTION_GAS_OVERHEAD = 180000; // TODO check accuracy of this number
/*
* The amount of gas used by the portion of the `execute` function
* that cannot be accounted for via gas tracking.
*/
uint public constant EXECUTE_EXTRA_GAS = 90000; // again, check for accuracy... Doubled this from Piper's original - Logan
/*
* Constant value to account for the gas usage that cannot be accounted
* for using gas-tracking within the `cancel` function.
*/
uint public constant CANCEL_EXTRA_GAS = 85000; // Check accuracy
function getEXECUTION_GAS_OVERHEAD()
public pure returns (uint)
{
return EXECUTION_GAS_OVERHEAD;
}
function requiredExecutionGas(Request storage self)
public view returns (uint requiredGas)
{
requiredGas = self.txnData.callGas.add(EXECUTION_GAS_OVERHEAD);
}
/*
* @dev Performs the checks to see if a request can be cancelled.
* Must satisfy the following conditions.
*
* 1. Not Cancelled
* 2. either:
* * not wasCalled && afterExecutionWindow
* * not claimed && beforeFreezeWindow && msg.sender == owner
*/
function isCancellable(Request storage self)
public view returns (bool)
{
if (self.meta.isCancelled) {
// already cancelled!
return false;
} else if (!self.meta.wasCalled && self.schedule.isAfterWindow()) {
// not called but after the window
return true;
} else if (!self.claimData.isClaimed() && self.schedule.isBeforeFreeze() && msg.sender == self.meta.owner) {
// not claimed and before freezePeriod and owner is cancelling
return true;
} else {
// otherwise cannot cancel
return false;
}
}
/*
* Cancel the transaction request, attempting to send all appropriate
* refunds. To incentivise cancellation by other parties, a small reward
* payment is issued to the party that cancels the request if they are not
* the owner.
*/
function cancel(Request storage self)
public returns (bool)
{
uint startGas = gasleft();
uint rewardPayment;
uint measuredGasConsumption;
// Checks if this transactionRequest can be cancelled.
require(isCancellable(self));
// Set here to prevent re-entrance attacks.
self.meta.isCancelled = true;
// Refund the claim deposit (if there is one)
require(self.claimData.refundDeposit());
// Send a reward to the cancelling agent if they are not the owner.
// This is to incentivize the cancelling of expired transaction requests.
// This also guarantees that it is being cancelled after the call window
// since the `isCancellable()` function checks this.
if (msg.sender != self.meta.owner) {
// Create the rewardBenefactor
address rewardBenefactor = msg.sender;
// Create the rewardOwed variable, it is one-hundredth
// of the bounty.
uint rewardOwed = self.paymentData.bountyOwed
.add(self.paymentData.bounty.div(100));
// Calculate the amount of gas cancelling agent used in this transaction.
measuredGasConsumption = startGas
.sub(gasleft())
.add(CANCEL_EXTRA_GAS);
// Add their gas fees to the reward.W
rewardOwed = measuredGasConsumption
.mul(tx.gasprice)
.add(rewardOwed);
// Take note of the rewardPayment to log it.
rewardPayment = rewardOwed;
// Transfers the rewardPayment.
if (rewardOwed > 0) {
self.paymentData.bountyOwed = 0;
rewardBenefactor.transfer(rewardOwed);
}
}
// Log it!
emit Cancelled(rewardPayment, measuredGasConsumption);
// Send the remaining ether to the owner.
return sendOwnerEther(self);
}
/*
* @dev Performs some checks to verify that a transaction request is claimable.
* @param self The Request object.
*/
function isClaimable(Request storage self)
internal view returns (bool)
{
// Require not claimed and not cancelled.
require(!self.claimData.isClaimed());
require(!self.meta.isCancelled);
// Require that it's in the claim window and the value sent is over the required deposit.
require(self.schedule.inClaimWindow());
require(msg.value >= self.claimData.requiredDeposit);
return true;
}
/*
* @dev Claims the request.
* @param self The Request object.
* Payable because it requires the sender to send enough ether to cover the claimDeposit.
*/
function claim(Request storage self)
internal returns (bool claimed)
{
require(isClaimable(self));
emit Claimed();
return self.claimData.claim(self.schedule.computePaymentModifier());
}
/*
* @dev Refund claimer deposit.
*/
function refundClaimDeposit(Request storage self)
public returns (bool)
{
require(self.meta.isCancelled || self.schedule.isAfterWindow());
return self.claimData.refundDeposit();
}
/*
* Send fee. Wrapper over the real function that perform an extra
* check to see if it's after the execution window (and thus the first transaction failed)
*/
function sendFee(Request storage self)
public returns (bool)
{
if (self.schedule.isAfterWindow()) {
return self.paymentData.sendFee();
}
return false;
}
/*
* Send bounty. Wrapper over the real function that performs an extra
* check to see if it's after execution window (and thus the first transaction failed)
*/
function sendBounty(Request storage self)
public returns (bool)
{
/// check wasCalled
if (self.schedule.isAfterWindow()) {
return self.paymentData.sendBounty();
}
return false;
}
function canSendOwnerEther(Request storage self)
public view returns(bool)
{
return self.meta.isCancelled || self.schedule.isAfterWindow() || self.meta.wasCalled;
}
/**
* Send owner ether. Wrapper over the real function that performs an extra
* check to see if it's after execution window (and thus the first transaction failed)
*/
function sendOwnerEther(Request storage self, address recipient)
public returns (bool)
{
require(recipient != 0x0);
if(canSendOwnerEther(self) && msg.sender == self.meta.owner) {
return _sendOwnerEther(self, recipient);
}
return false;
}
/**
* Send owner ether. Wrapper over the real function that performs an extra
* check to see if it's after execution window (and thus the first transaction failed)
*/
function sendOwnerEther(Request storage self)
public returns (bool)
{
if(canSendOwnerEther(self)) {
return _sendOwnerEther(self, self.meta.owner);
}
return false;
}
function _sendOwnerEther(Request storage self, address recipient)
private returns (bool)
{
// Note! This does not do any checks since it is used in the execute function.
// The public version of the function should be used for checks and in the cancel function.
uint ownerRefund = address(this).balance
.sub(self.claimData.claimDeposit)
.sub(self.paymentData.bountyOwed)
.sub(self.paymentData.feeOwed);
/* solium-disable security/no-send */
return recipient.send(ownerRefund);
}
}
/**
* @title RequestScheduleLib
* @dev Library containing the logic for request scheduling.
*/
library RequestScheduleLib {
using SafeMath for uint;
/**
* The manner in which this schedule specifies time.
*
* Null: present to require this value be explicitely specified
* Blocks: execution schedule determined by block.number
* Timestamp: execution schedule determined by block.timestamp
*/
enum TemporalUnit {
Null, // 0
Blocks, // 1
Timestamp // 2
}
struct ExecutionWindow {
TemporalUnit temporalUnit; /// The type of unit used to measure time.
uint windowStart; /// The starting point in temporal units from which the transaction can be executed.
uint windowSize; /// The length in temporal units of the execution time period.
uint freezePeriod; /// The length in temporal units before the windowStart where no activity is allowed.
uint reservedWindowSize; /// The length in temporal units at the beginning of the executionWindow in which only the claim address can execute.
uint claimWindowSize; /// The length in temporal units before the freezeperiod in which an address can claim the execution.
}
/**
* @dev Get the `now` represented in the temporal units assigned to this request.
* @param self The ExecutionWindow object.
* @return The unsigned integer representation of `now` in appropiate temporal units.
*/
function getNow(ExecutionWindow storage self)
public view returns (uint)
{
return _getNow(self.temporalUnit);
}
/**
* @dev Internal function to return the `now` based on the appropiate temporal units.
* @param _temporalUnit The assigned TemporalUnit to this transaction.
*/
function _getNow(TemporalUnit _temporalUnit)
internal view returns (uint)
{
if (_temporalUnit == TemporalUnit.Timestamp) {
return block.timestamp;
}
if (_temporalUnit == TemporalUnit.Blocks) {
return block.number;
}
/// Only reaches here if the unit is unset, unspecified or unsupported.
revert();
}
/**
* @dev The modifier that will be applied to the bounty value depending
* on when a call was claimed.
*/
function computePaymentModifier(ExecutionWindow storage self)
internal view returns (uint8)
{
uint paymentModifier = (getNow(self).sub(firstClaimBlock(self)))
.mul(100)
.div(self.claimWindowSize);
assert(paymentModifier <= 100);
return uint8(paymentModifier);
}
/*
* Helper: computes the end of the execution window.
*/
function windowEnd(ExecutionWindow storage self)
internal view returns (uint)
{
return self.windowStart.add(self.windowSize);
}
/*
* Helper: computes the end of the reserved portion of the execution
* window.
*/
function reservedWindowEnd(ExecutionWindow storage self)
internal view returns (uint)
{
return self.windowStart.add(self.reservedWindowSize);
}
/*
* Helper: computes the time when the request will be frozen until execution.
*/
function freezeStart(ExecutionWindow storage self)
internal view returns (uint)
{
return self.windowStart.sub(self.freezePeriod);
}
/*
* Helper: computes the time when the request will be frozen until execution.
*/
function firstClaimBlock(ExecutionWindow storage self)
internal view returns (uint)
{
return freezeStart(self).sub(self.claimWindowSize);
}
/*
* Helper: Returns boolean if we are before the execution window.
*/
function isBeforeWindow(ExecutionWindow storage self)
internal view returns (bool)
{
return getNow(self) < self.windowStart;
}
/*
* Helper: Returns boolean if we are after the execution window.
*/
function isAfterWindow(ExecutionWindow storage self)
internal view returns (bool)
{
return getNow(self) > windowEnd(self);
}
/*
* Helper: Returns boolean if we are inside the execution window.
*/
function inWindow(ExecutionWindow storage self)
internal view returns (bool)
{
return self.windowStart <= getNow(self) && getNow(self) < windowEnd(self);
}
/*
* Helper: Returns boolean if we are inside the reserved portion of the
* execution window.
*/
function inReservedWindow(ExecutionWindow storage self)
internal view returns (bool)
{
return self.windowStart <= getNow(self) && getNow(self) < reservedWindowEnd(self);
}
/*
* @dev Helper: Returns boolean if we are inside the claim window.
*/
function inClaimWindow(ExecutionWindow storage self)
internal view returns (bool)
{
/// Checks that the firstClaimBlock is in the past or now.
/// Checks that now is before the start of the freezePeriod.
return firstClaimBlock(self) <= getNow(self) && getNow(self) < freezeStart(self);
}
/*
* Helper: Returns boolean if we are before the freeze period.
*/
function isBeforeFreeze(ExecutionWindow storage self)
internal view returns (bool)
{
return getNow(self) < freezeStart(self);
}
/*
* Helper: Returns boolean if we are before the claim window.
*/
function isBeforeClaimWindow(ExecutionWindow storage self)
internal view returns (bool)
{
return getNow(self) < firstClaimBlock(self);
}
///---------------
/// VALIDATION
///---------------
/**
* @dev Validation: Ensure that the reservedWindowSize is less than or equal to the windowSize.
* @param _reservedWindowSize The size of the reserved window.
* @param _windowSize The size of the execution window.
* @return True if the reservedWindowSize is within the windowSize.
*/
function validateReservedWindowSize(uint _reservedWindowSize, uint _windowSize)
public pure returns (bool)
{
return _reservedWindowSize <= _windowSize;
}
/**
* @dev Validation: Ensure that the startWindow is at least freezePeriod amount of time in the future.
* @param _temporalUnit The temporalUnit of this request.
* @param _freezePeriod The freezePeriod in temporal units.
* @param _windowStart The time in the future which represents the start of the execution window.
* @return True if the windowStart is at least freezePeriod amount of time in the future.
*/
function validateWindowStart(TemporalUnit _temporalUnit, uint _freezePeriod, uint _windowStart)
public view returns (bool)
{
return _getNow(_temporalUnit).add(_freezePeriod) <= _windowStart;
}
/*
* Validation: ensure that the temporal unit passed in is constrained to 0 or 1
*/
function validateTemporalUnit(uint _temporalUnitAsUInt)
public pure returns (bool)
{
return (_temporalUnitAsUInt != uint(TemporalUnit.Null) &&
(_temporalUnitAsUInt == uint(TemporalUnit.Blocks) ||
_temporalUnitAsUInt == uint(TemporalUnit.Timestamp))
);
}
}
library ClaimLib {
struct ClaimData {
address claimedBy; // The address that has claimed the txRequest.
uint claimDeposit; // The deposit amount that was put down by the claimer.
uint requiredDeposit; // The required deposit to claim the txRequest.
uint8 paymentModifier; // An integer constrained between 0-100 that will be applied to the
// request payment as a percentage.
}
/*
* @dev Mark the request as being claimed.
* @param self The ClaimData that is being accessed.
* @param paymentModifier The payment modifier.
*/
function claim(
ClaimData storage self,
uint8 _paymentModifier
)
internal returns (bool)
{
self.claimedBy = msg.sender;
self.claimDeposit = msg.value;
self.paymentModifier = _paymentModifier;
return true;
}
/*
* Helper: returns whether this request is claimed.
*/
function isClaimed(ClaimData storage self)
internal view returns (bool)
{
return self.claimedBy != 0x0;
}
/*
* @dev Refund the claim deposit to claimer.
* @param self The Request.ClaimData
* Called in RequestLib's `cancel()` and `refundClaimDeposit()`
*/
function refundDeposit(ClaimData storage self)
internal returns (bool)
{
// Check that the claim deposit is non-zero.
if (self.claimDeposit > 0) {
uint depositAmount;
depositAmount = self.claimDeposit;
self.claimDeposit = 0;
/* solium-disable security/no-send */
return self.claimedBy.send(depositAmount);
}
return true;
}
}
/**
* Library containing the functionality for the bounty and fee payments.
* - Bounty payments are the reward paid to the executing agent of transaction
* requests.
* - Fee payments are the cost of using a Scheduler to make transactions. It is
* a way for developers to monetize their work on the EAC.
*/
library PaymentLib {
using SafeMath for uint;
struct PaymentData {
uint bounty; /// The amount in wei to be paid to the executing agent of the TransactionRequest.
address bountyBenefactor; /// The address that the bounty will be sent to.
uint bountyOwed; /// The amount that is owed to the bountyBenefactor.
uint fee; /// The amount in wei that will be paid to the FEE_RECIPIENT address.
address feeRecipient; /// The address that the fee will be sent to.
uint feeOwed; /// The amount that is owed to the feeRecipient.
}
///---------------
/// GETTERS
///---------------
/**
* @dev Getter function that returns true if a request has a benefactor.
*/
function hasFeeRecipient(PaymentData storage self)
internal view returns (bool)
{
return self.feeRecipient != 0x0;
}
/**
* @dev Computes the amount to send to the feeRecipient.
*/
function getFee(PaymentData storage self)
internal view returns (uint)
{
return self.fee;
}
/**
* @dev Computes the amount to send to the agent that executed the request.
*/
function getBounty(PaymentData storage self)
internal view returns (uint)
{
return self.bounty;
}
/**
* @dev Computes the amount to send to the address that fulfilled the request
* with an additional modifier. This is used when the call was claimed.
*/
function getBountyWithModifier(PaymentData storage self, uint8 _paymentModifier)
internal view returns (uint)
{
return getBounty(self).mul(_paymentModifier).div(100);
}
///---------------
/// SENDERS
///---------------
/**
* @dev Send the feeOwed amount to the feeRecipient.
* Note: The send is allowed to fail.
*/
function sendFee(PaymentData storage self)
internal returns (bool)
{
uint feeAmount = self.feeOwed;
if (feeAmount > 0) {
// re-entrance protection.
self.feeOwed = 0;
/* solium-disable security/no-send */
return self.feeRecipient.send(feeAmount);
}
return true;
}
/**
* @dev Send the bountyOwed amount to the bountyBenefactor.
* Note: The send is allowed to fail.
*/
function sendBounty(PaymentData storage self)
internal returns (bool)
{
uint bountyAmount = self.bountyOwed;
if (bountyAmount > 0) {
// re-entrance protection.
self.bountyOwed = 0;
return self.bountyBenefactor.send(bountyAmount);
}
return true;
}
///---------------
/// Endowment
///---------------
/**
* @dev Compute the endowment value for the given TransactionRequest parameters.
* See request_factory.rst in docs folder under Check #1 for more information about
* this calculation.
*/
function computeEndowment(
uint _bounty,
uint _fee,
uint _callGas,
uint _callValue,
uint _gasPrice,
uint _gasOverhead
)
public pure returns (uint)
{
return _bounty
.add(_fee)
.add(_callGas.mul(_gasPrice))
.add(_gasOverhead.mul(_gasPrice))
.add(_callValue);
}
/*
* Validation: ensure that the request endowment is sufficient to cover.
* - bounty
* - fee
* - gasReimbursment
* - callValue
*/
function validateEndowment(uint _endowment, uint _bounty, uint _fee, uint _callGas, uint _callValue, uint _gasPrice, uint _gasOverhead)
public pure returns (bool)
{
return _endowment >= computeEndowment(
_bounty,
_fee,
_callGas,
_callValue,
_gasPrice,
_gasOverhead
);
}
}
/**
* @title IterTools
* @dev Utility library that iterates through a boolean array of length 6.
*/
library IterTools {
/*
* @dev Return true if all of the values in the boolean array are true.
* @param _values A boolean array of length 6.
* @return True if all values are true, False if _any_ are false.
*/
function all(bool[6] _values)
public pure returns (bool)
{
for (uint i = 0; i < _values.length; i++) {
if (!_values[i]) {
return false;
}
}
return true;
}
}
/*
The MIT License (MIT)
Copyright (c) 2018 Murray Software, LLC.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//solhint-disable max-line-length
//solhint-disable no-inline-assembly
contract CloneFactory {
event CloneCreated(address indexed target, address clone);
function createClone(address target) internal returns (address result) {
bytes memory clone = hex"600034603b57603080600f833981f36000368180378080368173bebebebebebebebebebebebebebebebebebebebe5af43d82803e15602c573d90f35b3d90fd";
bytes20 targetBytes = bytes20(target);
for (uint i = 0; i < 20; i++) {
clone[26 + i] = targetBytes[i];
}
assembly {
let len := mload(clone)
let data := add(clone, 0x20)
result := create(0, data, len)
}
}
}
/// Example of using the Scheduler from a smart contract to delay a payment.
contract DelayedPayment {
SchedulerInterface public scheduler;
address recipient;
address owner;
address public payment;
uint lockedUntil;
uint value;
uint twentyGwei = 20000000000 wei;
constructor(
address _scheduler,
uint _numBlocks,
address _recipient,
uint _value
) public payable {
scheduler = SchedulerInterface(_scheduler);
lockedUntil = block.number + _numBlocks;
recipient = _recipient;
owner = msg.sender;
value = _value;
uint endowment = scheduler.computeEndowment(
twentyGwei,
twentyGwei,
200000,
0,
twentyGwei
);
payment = scheduler.schedule.value(endowment)( // 0.1 ether is to pay for gas, bounty and fee
this, // send to self
"", // and trigger fallback function
[
200000, // The amount of gas to be sent with the transaction.
0, // The amount of wei to be sent.
255, // The size of the execution window.
lockedUntil, // The start of the execution window.
twentyGwei, // The gasprice for the transaction (aka 20 gwei)
twentyGwei, // The fee included in the transaction.
twentyGwei, // The bounty that awards the executor of the transaction.
twentyGwei * 2 // The required amount of wei the claimer must send as deposit.
]
);
assert(address(this).balance >= value);
}
function () public payable {
if (msg.value > 0) { //this handles recieving remaining funds sent while scheduling (0.1 ether)
return;
} else if (address(this).balance > 0) {
payout();
} else {
revert();
}
}
function payout()
public returns (bool)
{
require(block.number >= lockedUntil);
recipient.transfer(value);
return true;
}
function collectRemaining()
public returns (bool)
{
owner.transfer(address(this).balance);
}
}
/// Example of using the Scheduler from a smart contract to delay a payment.
contract RecurringPayment {
SchedulerInterface public scheduler;
uint paymentInterval;
uint paymentValue;
uint lockedUntil;
address recipient;
address public currentScheduledTransaction;
event PaymentScheduled(address indexed scheduledTransaction, address recipient, uint value);
event PaymentExecuted(address indexed scheduledTransaction, address recipient, uint value);
function RecurringPayment(
address _scheduler,
uint _paymentInterval,
uint _paymentValue,
address _recipient
) public payable {
scheduler = SchedulerInterface(_scheduler);
paymentInterval = _paymentInterval;
recipient = _recipient;
paymentValue = _paymentValue;
schedule();
}
function ()
public payable
{
if (msg.value > 0) { //this handles recieving remaining funds sent while scheduling (0.1 ether)
return;
}
process();
}
function process() public returns (bool) {
payout();
schedule();
}
function payout()
private returns (bool)
{
require(block.number >= lockedUntil);
require(address(this).balance >= paymentValue);
recipient.transfer(paymentValue);
emit PaymentExecuted(currentScheduledTransaction, recipient, paymentValue);
return true;
}
function schedule()
private returns (bool)
{
lockedUntil = block.number + paymentInterval;
currentScheduledTransaction = scheduler.schedule.value(0.1 ether)( // 0.1 ether is to pay for gas, bounty and fee
this, // send to self
"", // and trigger fallback function
[
1000000, // The amount of gas to be sent with the transaction. Accounts for payout + new contract deployment
0, // The amount of wei to be sent.
255, // The size of the execution window.
lockedUntil, // The start of the execution window.
20000000000 wei, // The gasprice for the transaction (aka 20 gwei)
20000000000 wei, // The fee included in the transaction.
20000000000 wei, // The bounty that awards the executor of the transaction.
30000000000 wei // The required amount of wei the claimer must send as deposit.
]
);
emit PaymentScheduled(currentScheduledTransaction, recipient, paymentValue);
}
}
/**
* @title RequestFactory
* @dev Contract which will produce new TransactionRequests.
*/
contract RequestFactory is RequestFactoryInterface, CloneFactory, Pausable {
using IterTools for bool[6];
TransactionRequestCore public transactionRequestCore;
uint constant public BLOCKS_BUCKET_SIZE = 240; //~1h
uint constant public TIMESTAMP_BUCKET_SIZE = 3600; //1h
constructor(
address _transactionRequestCore
)
public
{
require(_transactionRequestCore != 0x0);
transactionRequestCore = TransactionRequestCore(_transactionRequestCore);
}
/**
* @dev The lowest level interface for creating a transaction request.
*
* @param _addressArgs [0] - meta.owner
* @param _addressArgs [1] - paymentData.feeRecipient
* @param _addressArgs [2] - txnData.toAddress
* @param _uintArgs [0] - paymentData.fee
* @param _uintArgs [1] - paymentData.bounty
* @param _uintArgs [2] - schedule.claimWindowSize
* @param _uintArgs [3] - schedule.freezePeriod
* @param _uintArgs [4] - schedule.reservedWindowSize
* @param _uintArgs [5] - schedule.temporalUnit
* @param _uintArgs [6] - schedule.windowSize
* @param _uintArgs [7] - schedule.windowStart
* @param _uintArgs [8] - txnData.callGas
* @param _uintArgs [9] - txnData.callValue
* @param _uintArgs [10] - txnData.gasPrice
* @param _uintArgs [11] - claimData.requiredDeposit
* @param _callData - The call data
*/
function createRequest(
address[3] _addressArgs,
uint[12] _uintArgs,
bytes _callData
)
whenNotPaused
public payable returns (address)
{
// Create a new transaction request clone from transactionRequestCore.
address transactionRequest = createClone(transactionRequestCore);
// Call initialize on the transaction request clone.
TransactionRequestCore(transactionRequest).initialize.value(msg.value)(
[
msg.sender, // Created by
_addressArgs[0], // meta.owner
_addressArgs[1], // paymentData.feeRecipient
_addressArgs[2] // txnData.toAddress
],
_uintArgs, //uint[12]
_callData
);
// Track the address locally
requests[transactionRequest] = true;
// Log the creation.
emit RequestCreated(
transactionRequest,
_addressArgs[0],
getBucket(_uintArgs[7], RequestScheduleLib.TemporalUnit(_uintArgs[5])),
_uintArgs
);
return transactionRequest;
}
/**
* The same as createRequest except that it requires validation prior to
* creation.
*
* Parameters are the same as `createRequest`
*/
function createValidatedRequest(
address[3] _addressArgs,
uint[12] _uintArgs,
bytes _callData
)
public payable returns (address)
{
bool[6] memory isValid = validateRequestParams(
_addressArgs,
_uintArgs,
msg.value
);
if (!isValid.all()) {
if (!isValid[0]) {
emit ValidationError(uint8(Errors.InsufficientEndowment));
}
if (!isValid[1]) {
emit ValidationError(uint8(Errors.ReservedWindowBiggerThanExecutionWindow));
}
if (!isValid[2]) {
emit ValidationError(uint8(Errors.InvalidTemporalUnit));
}
if (!isValid[3]) {
emit ValidationError(uint8(Errors.ExecutionWindowTooSoon));
}
if (!isValid[4]) {
emit ValidationError(uint8(Errors.CallGasTooHigh));
}
if (!isValid[5]) {
emit ValidationError(uint8(Errors.EmptyToAddress));
}
// Try to return the ether sent with the message
msg.sender.transfer(msg.value);
return 0x0;
}
return createRequest(_addressArgs, _uintArgs, _callData);
}
/// ----------------------------
/// Internal
/// ----------------------------
/*
* @dev The enum for launching `ValidationError` events and mapping them to an error.
*/
enum Errors {
InsufficientEndowment,
ReservedWindowBiggerThanExecutionWindow,
InvalidTemporalUnit,
ExecutionWindowTooSoon,
CallGasTooHigh,
EmptyToAddress
}
event ValidationError(uint8 error);
/*
* @dev Validate the constructor arguments for either `createRequest` or `createValidatedRequest`.
*/
function validateRequestParams(
address[3] _addressArgs,
uint[12] _uintArgs,
uint _endowment
)
public view returns (bool[6])
{
return RequestLib.validate(
[
msg.sender, // meta.createdBy
_addressArgs[0], // meta.owner
_addressArgs[1], // paymentData.feeRecipient
_addressArgs[2] // txnData.toAddress
],
_uintArgs,
_endowment
);
}
/// Mapping to hold known requests.
mapping (address => bool) requests;
function isKnownRequest(address _address)
public view returns (bool isKnown)
{
return requests[_address];
}
function getBucket(uint windowStart, RequestScheduleLib.TemporalUnit unit)
public pure returns(int)
{
uint bucketSize;
/* since we want to handle both blocks and timestamps
and do not want to get into case where buckets overlaps
block buckets are going to be negative ints
timestamp buckets are going to be positive ints
we'll overflow after 2**255-1 blocks instead of 2**256-1 since we encoding this on int256
*/
int sign;
if (unit == RequestScheduleLib.TemporalUnit.Blocks) {
bucketSize = BLOCKS_BUCKET_SIZE;
sign = -1;
} else if (unit == RequestScheduleLib.TemporalUnit.Timestamp) {
bucketSize = TIMESTAMP_BUCKET_SIZE;
sign = 1;
} else {
revert();
}
return sign * int(windowStart - (windowStart % bucketSize));
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"constant":false,"inputs":[{"name":"_toAddress","type":"address"},{"name":"_callData","type":"bytes"},{"name":"_uintArgs","type":"uint256[8]"}],"name":"schedule","outputs":[{"name":"newRequest","type":"address"}],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"temporalUnit","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"feeRecipient","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"_bounty","type":"uint256"},{"name":"_fee","type":"uint256"},{"name":"_callGas","type":"uint256"},{"name":"_callValue","type":"uint256"},{"name":"_gasPrice","type":"uint256"}],"name":"computeEndowment","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"factoryAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_factoryAddress","type":"address"},{"name":"_feeRecipient","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"request","type":"address"}],"name":"NewRequest","type":"event"}]Contract Creation Code
608060405234801561001057600080fd5b50604051604080610ddf833981018060405281019080805190602001909291908051906020019092919050505060008273ffffffffffffffffffffffffffffffffffffffff161415151561006357600080fd5b6001600060146101000a81548160ff0219169083600281111561008257fe5b0217905550816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050610cc6806101196000396000f30060806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630801b6541461006f5780633c8a072e1461015b578063469048401461019457806363b6240d146101eb578063966dae0e14610254575b005b610119600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192908061010001906008806020026040519081016040528092919082600860200280828437820191505050505091929192905050506102ab565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561016757600080fd5b50610170610ac7565b6040518082600281111561018057fe5b60ff16815260200191505060405180910390f35b3480156101a057600080fd5b506101a9610ada565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f757600080fd5b5061023e6004803603810190808035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050610b00565b6040518082815260200191505060405180910390f35b34801561026057600080fd5b50610269610c75565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691506103408460066008811015156102e657fe5b60200201518560056008811015156102fa57fe5b602002015186600060088110151561030e57fe5b602002015187600160088110151561032257fe5b602002015188600460088110151561033657fe5b6020020151610b00565b905080341015151561035157600080fd5b6001600281111561035e57fe5b600060149054906101000a900460ff16600281111561037957fe5b14156106be578173ffffffffffffffffffffffffffffffffffffffff1663d7ceab4c346060604051908101604052803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152506101806040519081016040528089600560088110151561047a57fe5b6020020151815260200189600660088110151561049357fe5b6020020151815260200160ff8152602001600a815260200160108152602001600060149054906101000a900460ff1660028111156104cd57fe5b81526020018960026008811015156104e157fe5b602002015181526020018960036008811015156104fa57fe5b6020020151815260200189600060088110151561051357fe5b6020020151815260200189600160088110151561052c57fe5b6020020151815260200189600460088110151561054557fe5b6020020151815260200189600760088110151561055e57fe5b6020020151815250896040518563ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018084600360200280838360005b838110156105bd5780820151818401526020810190506105a2565b5050505090500183600c60200280838360005b838110156105eb5780820151818401526020810190506105d0565b5050505090500180602001828103825283818151815260200191508051906020019080838360005b8381101561062e578082015181840152602081019050610613565b50505050905090810190601f16801561065b5780820380516001836020036101000a031916815260200191505b509450505050506020604051808303818588803b15801561067b57600080fd5b505af115801561068f573d6000803e3d6000fd5b50505050506040513d60208110156106a657600080fd5b81019080805190602001909291905050509250610a32565b6002808111156106ca57fe5b600060149054906101000a900460ff1660028111156106e557fe5b1415610a2c578173ffffffffffffffffffffffffffffffffffffffff1663d7ceab4c346060604051908101604052803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815250610180604051908101604052808960056008811015156107e657fe5b602002015181526020018960066008811015156107ff57fe5b60200201518152602001610e10815260200160b4815260200161012c8152602001600060149054906101000a900460ff16600281111561083b57fe5b815260200189600260088110151561084f57fe5b6020020151815260200189600360088110151561086857fe5b6020020151815260200189600060088110151561088157fe5b6020020151815260200189600160088110151561089a57fe5b602002015181526020018960046008811015156108b357fe5b602002015181526020018960076008811015156108cc57fe5b6020020151815250896040518563ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018084600360200280838360005b8381101561092b578082015181840152602081019050610910565b5050505090500183600c60200280838360005b8381101561095957808201518184015260208101905061093e565b5050505090500180602001828103825283818151815260200191508051906020019080838360005b8381101561099c578082015181840152602081019050610981565b50505050905090810190601f1680156109c95780820380516001836020036101000a031916815260200191505b509450505050506020604051808303818588803b1580156109e957600080fd5b505af11580156109fd573d6000803e3d6000fd5b50505050506040513d6020811015610a1457600080fd5b81019080805190602001909291905050509250610a31565b600080fd5b5b60008373ffffffffffffffffffffffffffffffffffffffff1614151515610a5857600080fd5b7f2749295aa7ffdbd4d16719dc03d592cd081eebd9bb790ceedce201a40675fc0383604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a182925050509392505050565b600060149054906101000a900460ff1681565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600073b5090c9c05cea1899d1b86c30d99f4d0d3440b05635ee345e4878787878773e6d49e39bf23ddd992c9182a0ef69ef624a44b6363708f89406040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b158015610b8157600080fd5b505af4158015610b95573d6000803e3d6000fd5b505050506040513d6020811015610bab57600080fd5b81019080805190602001909291905050506040518763ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180878152602001868152602001858152602001848152602001838152602001828152602001965050505050505060206040518083038186803b158015610c2f57600080fd5b505af4158015610c43573d6000803e3d6000fd5b505050506040513d6020811015610c5957600080fd5b8101908080519060200190929190505050905095945050505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a723058202d7db794eb99eeced52798e43517c1d60517c60fdebbb23c783649d41185c27b0029000000000000000000000000ff5c4b7ec93dd70b862af027bb7f3d9900002c4d00000000000000000000000047863b9e8c590323768e4352a78ca759bbd37e8b
Deployed Bytecode
0x60806040526004361061006d576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630801b6541461006f5780633c8a072e1461015b578063469048401461019457806363b6240d146101eb578063966dae0e14610254575b005b610119600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192908061010001906008806020026040519081016040528092919082600860200280828437820191505050505091929192905050506102ab565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561016757600080fd5b50610170610ac7565b6040518082600281111561018057fe5b60ff16815260200191505060405180910390f35b3480156101a057600080fd5b506101a9610ada565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3480156101f757600080fd5b5061023e6004803603810190808035906020019092919080359060200190929190803590602001909291908035906020019092919080359060200190929190505050610b00565b6040518082815260200191505060405180910390f35b34801561026057600080fd5b50610269610c75565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b60008060008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1691506103408460066008811015156102e657fe5b60200201518560056008811015156102fa57fe5b602002015186600060088110151561030e57fe5b602002015187600160088110151561032257fe5b602002015188600460088110151561033657fe5b6020020151610b00565b905080341015151561035157600080fd5b6001600281111561035e57fe5b600060149054906101000a900460ff16600281111561037957fe5b14156106be578173ffffffffffffffffffffffffffffffffffffffff1663d7ceab4c346060604051908101604052803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152506101806040519081016040528089600560088110151561047a57fe5b6020020151815260200189600660088110151561049357fe5b6020020151815260200160ff8152602001600a815260200160108152602001600060149054906101000a900460ff1660028111156104cd57fe5b81526020018960026008811015156104e157fe5b602002015181526020018960036008811015156104fa57fe5b6020020151815260200189600060088110151561051357fe5b6020020151815260200189600160088110151561052c57fe5b6020020151815260200189600460088110151561054557fe5b6020020151815260200189600760088110151561055e57fe5b6020020151815250896040518563ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018084600360200280838360005b838110156105bd5780820151818401526020810190506105a2565b5050505090500183600c60200280838360005b838110156105eb5780820151818401526020810190506105d0565b5050505090500180602001828103825283818151815260200191508051906020019080838360005b8381101561062e578082015181840152602081019050610613565b50505050905090810190601f16801561065b5780820380516001836020036101000a031916815260200191505b509450505050506020604051808303818588803b15801561067b57600080fd5b505af115801561068f573d6000803e3d6000fd5b50505050506040513d60208110156106a657600080fd5b81019080805190602001909291905050509250610a32565b6002808111156106ca57fe5b600060149054906101000a900460ff1660028111156106e557fe5b1415610a2c578173ffffffffffffffffffffffffffffffffffffffff1663d7ceab4c346060604051908101604052803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018a73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815250610180604051908101604052808960056008811015156107e657fe5b602002015181526020018960066008811015156107ff57fe5b60200201518152602001610e10815260200160b4815260200161012c8152602001600060149054906101000a900460ff16600281111561083b57fe5b815260200189600260088110151561084f57fe5b6020020151815260200189600360088110151561086857fe5b6020020151815260200189600060088110151561088157fe5b6020020151815260200189600160088110151561089a57fe5b602002015181526020018960046008811015156108b357fe5b602002015181526020018960076008811015156108cc57fe5b6020020151815250896040518563ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018084600360200280838360005b8381101561092b578082015181840152602081019050610910565b5050505090500183600c60200280838360005b8381101561095957808201518184015260208101905061093e565b5050505090500180602001828103825283818151815260200191508051906020019080838360005b8381101561099c578082015181840152602081019050610981565b50505050905090810190601f1680156109c95780820380516001836020036101000a031916815260200191505b509450505050506020604051808303818588803b1580156109e957600080fd5b505af11580156109fd573d6000803e3d6000fd5b50505050506040513d6020811015610a1457600080fd5b81019080805190602001909291905050509250610a31565b600080fd5b5b60008373ffffffffffffffffffffffffffffffffffffffff1614151515610a5857600080fd5b7f2749295aa7ffdbd4d16719dc03d592cd081eebd9bb790ceedce201a40675fc0383604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a182925050509392505050565b600060149054906101000a900460ff1681565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600073b5090c9c05cea1899d1b86c30d99f4d0d3440b05635ee345e4878787878773e6d49e39bf23ddd992c9182a0ef69ef624a44b6363708f89406040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b158015610b8157600080fd5b505af4158015610b95573d6000803e3d6000fd5b505050506040513d6020811015610bab57600080fd5b81019080805190602001909291905050506040518763ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180878152602001868152602001858152602001848152602001838152602001828152602001965050505050505060206040518083038186803b158015610c2f57600080fd5b505af4158015610c43573d6000803e3d6000fd5b505050506040513d6020811015610c5957600080fd5b8101908080519060200190929190505050905095945050505050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff16815600a165627a7a723058202d7db794eb99eeced52798e43517c1d60517c60fdebbb23c783649d41185c27b0029
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000ff5c4b7ec93dd70b862af027bb7f3d9900002c4d00000000000000000000000047863b9e8c590323768e4352a78ca759bbd37e8b
-----Decoded View---------------
Arg [0] : _factoryAddress (address): 0xFf5c4B7EC93dd70b862aF027Bb7f3D9900002c4D
Arg [1] : _feeRecipient (address): 0x47863b9E8C590323768E4352A78Ca759BBd37E8B
-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 000000000000000000000000ff5c4b7ec93dd70b862af027bb7f3d9900002c4d
Arg [1] : 00000000000000000000000047863b9e8c590323768e4352a78ca759bbd37e8b
Libraries Used
PaymentLib : 0xb5090c9c05cea1899d1b86c30d99f4d0d3440b05UnverifiedRequestLib : 0xe6d49e39bf23ddd992c9182a0ef69ef624a44b63Unverified
Swarm Source
bzzr://2d7db794eb99eeced52798e43517c1d60517c60fdebbb23c783649d41185c27b
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
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.