-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #140 from ethereum-alarm-clock/feature/recurring-p…
…ayment-example Added RecurringPayment contract example
- Loading branch information
Showing
2 changed files
with
151 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
pragma solidity ^0.4.21; | ||
|
||
import "contracts/Interface/SchedulerInterface.sol"; | ||
|
||
/// 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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
require("chai") | ||
.use(require("chai-as-promised")) | ||
.should() | ||
|
||
const { expect } = require("chai") | ||
|
||
const config = require("../../config") | ||
const { waitUntilBlock } = require("@digix/tempo")(web3) | ||
|
||
const BlockScheduler = artifacts.require("./BlockScheduler.sol") | ||
const RequestFactory = artifacts.require("./RequestFactory.sol") | ||
const TransactionRequestCore = artifacts.require("./TransactionRequestCore.sol") | ||
const TransactionRequestInterface = artifacts.require("./TransactionRequestInterface") | ||
const RecurringPayment = artifacts.require("./RecurringPayment.sol") | ||
|
||
const getBalance = async address => parseInt(await config.web3.eth.getBalance(address), 10) | ||
const execute = async (recurringPayment, paymentInterval, miner) => { | ||
const scheduledTransactionAddress = await recurringPayment.currentScheduledTransaction() | ||
|
||
const currentBlock = await config.web3.eth.getBlockNumber() | ||
await waitUntilBlock(0, currentBlock + paymentInterval) | ||
|
||
const scheduledTransaction = TransactionRequestInterface.at(scheduledTransactionAddress) | ||
await scheduledTransaction.execute({ from: miner, gas: 3000000, gasPrice: 20000000000 }) | ||
|
||
const nextScheduledTransactionAddress = await recurringPayment.currentScheduledTransaction() | ||
|
||
expect(nextScheduledTransactionAddress).to.not.equals(scheduledTransactionAddress) | ||
} | ||
|
||
contract("Recurring payments", (accounts) => { | ||
it("should schedule and execute recurring payments transaction", async () => { | ||
const transactionRequestCore = await TransactionRequestCore.deployed() | ||
|
||
const requestFactory = await RequestFactory.new(transactionRequestCore.address) | ||
const blockScheduler = await BlockScheduler.new( | ||
requestFactory.address, | ||
"0xecc9c5fff8937578141592e7E62C2D2E364311b8" | ||
) | ||
|
||
const paymentInterval = 50 | ||
const paymentValue = 10 ** 17 // 0.1 ETH | ||
let numberOfIntervals = 3 | ||
const expectedPayout = numberOfIntervals * paymentValue | ||
const recipient = accounts[1] | ||
const miner = accounts[2] | ||
|
||
// * 2 to cover scheduling cost, fixed in smart contract to 0.1 ETH | ||
const totalPayment = numberOfIntervals * paymentValue * 2 | ||
|
||
const recurringPayment = await RecurringPayment.new( | ||
blockScheduler.address, | ||
paymentInterval, | ||
paymentValue, | ||
recipient, | ||
{ value: totalPayment } | ||
) | ||
|
||
const recipientBalance = await getBalance(recipient) | ||
/* eslint no-plusplus: "off" */ | ||
/* eslint no-await-in-loop: "off" */ | ||
while (numberOfIntervals--) { | ||
await execute(recurringPayment, paymentInterval, miner) | ||
} | ||
|
||
const recipientBalanceAfter = await getBalance(recipient) | ||
expect(recipientBalanceAfter).to.equals(recipientBalance + expectedPayout) | ||
}) | ||
}) |