Skip to content

Commit

Permalink
Merge pull request #140 from ethereum-alarm-clock/feature/recurring-p…
Browse files Browse the repository at this point in the history
…ayment-example

Added RecurringPayment contract example
  • Loading branch information
kosecki123 committed Apr 23, 2018
2 parents efcaf5e + e4a8846 commit 2e0471e
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 0 deletions.
82 changes: 82 additions & 0 deletions contracts/_examples/RecurringPayment.sol
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);
}
}
69 changes: 69 additions & 0 deletions test/examples/RecurringPayment.js
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)
})
})

0 comments on commit 2e0471e

Please sign in to comment.