Quickstart¶
Introduction¶
This guide is inteded for smart contract developers that may want to use the EAC services from within their own applications. Since all the functionality of the Alarm Clock is built into the Ethereum chain via smart contracts, it can be accessed from other contracts. This makes it useful as a foundational tool to design and implement more complex utilities that depend on future transactions. For this guide we will be using the Solidity language. If you are unfamiliar with Solidity we recommend you familiarize yourself with its documentation first.
Scheduling your first transaction¶
The first step is to establish how we will interact with the EAC service’s
Scheduler
contract. We can use the Scheduler Interface to accomplish this.
The Scheduler interface contract contains some logic that is shared between both the
Block Scheduler and the Timestamp Scheduler. The function that we are interested in is the
schedule() function. See the signature of this function below:
function schedule(address _toAddress,
bytes _callData,
uint[8] _uintArgs)
public payable returns (address);
SchedulerInterface.sol
is an abstract contract that exposes the API for the Schedulers
including the schedule()
function that we will use in the contract we write.
function schedule
which will
return the address of the newly created TransactionRequestInterface
contract.
Now lets write a simple contract that can use the scheduling service.
pragma solidity 0.4.24;
import "contracts/Interface/SchedulerInterface.sol";
/// 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);
}
}
The contract above is designed to lock away and then send to receiver
whatever ether it is given for
numBlocks
blocks. In its constructor, it makes a call to the
schedule
method on the scheduler
contract. We would pass in
the address of the scheduler we would want to interact with as the first
parameter of the constructor. For instance, if we wanted to use the Block
Scheduler that is deployed on the Kovan test net we would use address
0x1afc19a7e642761ba2b55d2a45b32c7ef08269d1
.
The schedule function takes 10 arguments, each of which we will go over in order.
address toAddress
: Theaddress
which the transaction will be sent to.bytes callData
: Thebytes
that will be used as the data for the transaction.uint callGas
: The amount of gas that will be sent with the transaction.uint callValue
: The amount of ether (in wei) that will be sent with the transaction.uint8 windowSize
: The number of blocks afterwindowSize
during which the transaction will still be executable.uint windowStart
: The first block number that the transaction will be executable.uint gasPrice
: The gas price (in wei) which must be sent by the executing party to execute the transaction.uint fee
: The fee amount (in wei) included in the transaction for protocol maintainers.uint bounty
: The payment (in wei)included in the transaction to incentivse the executing argumentsuint deposit
: (optional) Required amount of ether (in wei) to be staked by executing agents
The 0.1 ether
amount passed as value to schedule
method pays for gas, fee and bounty. The remaining amount of ether
will be returned automatically to the deployed DelayedPayment
.
Let’s look at the other function on this contract. For those unfamiliar with solidity,
the function without a name is known as the fallback function. The fallback
function is what triggers if no method is found on the contract that matches
the sent data, or if data is not included. Usually, sending a simple value transfer function
will trigger the fallback function. In this case, we explicitly pass an empty string
as the callData
variable so that the scheduled transaction will trigger this
function when it is executed.
In this example we are locking the sent ether in DelayedPayment
contract and using
scheduling to trigger the fallback function. When the fallback function is executed,
it will route the call into the payout()
function.
The payout()
function will check the current block number and check if it is not
below the lockedUntil
time or else it reverts the transaction. After it
checks that the current block number is greater than or equal to the lockedUntil
variable, the function will transfer the entrie balance of the contract to the
specified recipient.
As can be seen, this will make it so that a payment is scheduled for a future
date but won’t actually be sent until that date. This example uses a simple
payment, but the EAC will work with arbritrary logic. As the logic for a scheduled
transaction increases, be sure to increase the required callGas
in accordance.
Right now, the gas limit for a transaction is somewhere in the ballpark of 8,000,000
so there’s plenty of wiggle room for experimentation.