forked from OpenZeppelin/openzeppelin-contracts
-
Notifications
You must be signed in to change notification settings - Fork 2
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 OpenZeppelin#417 from abarmat/feature-splitpayment…
…-contract Feature SplitPayment contract
- Loading branch information
Showing
3 changed files
with
153 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,65 @@ | ||
pragma solidity ^0.4.11; | ||
|
||
import '../math/SafeMath.sol'; | ||
|
||
/** | ||
* @title SplitPayment | ||
* @dev Base contract that supports multiple payees claiming funds sent to this contract | ||
* according to the proportion they own. | ||
*/ | ||
contract SplitPayment { | ||
using SafeMath for uint256; | ||
|
||
uint256 public totalShares = 0; | ||
uint256 public totalReleased = 0; | ||
|
||
mapping(address => uint256) public shares; | ||
mapping(address => uint256) public released; | ||
address[] public payees; | ||
|
||
/** | ||
* @dev Constructor | ||
*/ | ||
function SplitPayment(address[] _payees, uint256[] _shares) { | ||
require(_payees.length == _shares.length); | ||
|
||
for (uint256 i = 0; i < _payees.length; i++) { | ||
addPayee(_payees[i], _shares[i]); | ||
} | ||
} | ||
|
||
/** | ||
* @dev Add a new payee to the contract. | ||
* @param _payee The address of the payee to add. | ||
* @param _shares The number of shares owned by the payee. | ||
*/ | ||
function addPayee(address _payee, uint256 _shares) internal { | ||
require(_payee != address(0)); | ||
require(_shares > 0); | ||
require(shares[_payee] == 0); | ||
|
||
payees.push(_payee); | ||
shares[_payee] = _shares; | ||
totalShares = totalShares.add(_shares); | ||
} | ||
|
||
/** | ||
* @dev Claim your share of the balance. | ||
*/ | ||
function claim() public { | ||
address payee = msg.sender; | ||
|
||
require(shares[payee] > 0); | ||
|
||
uint256 totalReceived = this.balance.add(totalReleased); | ||
uint256 payment = totalReceived.mul(shares[payee]).div(totalShares).sub(released[payee]); | ||
|
||
require(payment != 0); | ||
require(this.balance >= payment); | ||
|
||
released[payee] = released[payee].add(payment); | ||
totalReleased = totalReleased.add(payment); | ||
|
||
payee.transfer(payment); | ||
} | ||
} |
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,78 @@ | ||
const BigNumber = web3.BigNumber | ||
|
||
const should = require('chai') | ||
.use(require('chai-as-promised')) | ||
.use(require('chai-bignumber')(BigNumber)) | ||
.should() | ||
|
||
const EVMThrow = require('./helpers/EVMThrow.js') | ||
const SplitPaymentMock = artifacts.require('./helpers/SplitPaymentMock.sol') | ||
|
||
contract('SplitPayment', function ([owner, payee1, payee2, payee3, nonpayee1, payer1]) { | ||
const amount = web3.toWei(1.0, 'ether') | ||
|
||
beforeEach(async function () { | ||
this.payees = [payee1, payee2, payee3] | ||
this.shares = [20, 10, 70] | ||
|
||
this.contract = await SplitPaymentMock.new(this.payees, this.shares) | ||
}) | ||
|
||
it('should accept payments', async function () { | ||
await web3.eth.sendTransaction({ from: owner, to: this.contract.address, value: amount }) | ||
|
||
const balance = web3.eth.getBalance(this.contract.address) | ||
balance.should.be.bignumber.equal(amount) | ||
}) | ||
|
||
it('should store shares if address is payee', async function () { | ||
const shares = await this.contract.shares.call(payee1) | ||
shares.should.be.bignumber.not.equal(0) | ||
}) | ||
|
||
it('should not store shares if address is not payee', async function () { | ||
const shares = await this.contract.shares.call(nonpayee1) | ||
shares.should.be.bignumber.equal(0) | ||
}) | ||
|
||
it('should throw if no funds to claim', async function () { | ||
await this.contract.claim({from: payee1}).should.be.rejectedWith(EVMThrow) | ||
}) | ||
|
||
it('should throw if non-payee want to claim', async function () { | ||
await web3.eth.sendTransaction({from: payer1, to: this.contract.address, value: amount}) | ||
await this.contract.claim({from: nonpayee1}).should.be.rejectedWith(EVMThrow) | ||
}) | ||
|
||
it('should distribute funds to payees', async function () { | ||
await web3.eth.sendTransaction({from: payer1, to: this.contract.address, value: amount}) | ||
|
||
// receive funds | ||
const initBalance = web3.eth.getBalance(this.contract.address) | ||
initBalance.should.be.bignumber.equal(amount) | ||
|
||
// distribute to payees | ||
const initAmount1 = web3.eth.getBalance(payee1) | ||
await this.contract.claim({from: payee1}) | ||
const profit1 = web3.eth.getBalance(payee1) - initAmount1 | ||
assert(Math.abs(profit1 - web3.toWei(0.20, 'ether')) < 1e16) | ||
|
||
const initAmount2 = web3.eth.getBalance(payee2) | ||
await this.contract.claim({from: payee2}) | ||
const profit2 = web3.eth.getBalance(payee2) - initAmount2 | ||
assert(Math.abs(profit2 - web3.toWei(0.10, 'ether')) < 1e16) | ||
|
||
const initAmount3 = web3.eth.getBalance(payee3) | ||
await this.contract.claim({from: payee3}) | ||
const profit3 = web3.eth.getBalance(payee3) - initAmount3 | ||
assert(Math.abs(profit3 - web3.toWei(0.70, 'ether')) < 1e16) | ||
|
||
// end balance should be zero | ||
const endBalance = web3.eth.getBalance(this.contract.address) | ||
endBalance.should.be.bignumber.equal(0) | ||
|
||
// check correct funds released accounting | ||
const totalReleased = await this.contract.totalReleased.call() | ||
totalReleased.should.be.bignumber.equal(initBalance) | ||
}) | ||
}) |
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,10 @@ | ||
pragma solidity ^0.4.11; | ||
|
||
import '../../contracts/payment/SplitPayment.sol'; | ||
|
||
// mock class using SplitPayment | ||
contract SplitPaymentMock is SplitPayment { | ||
function SplitPaymentMock(address[] _payees, uint256[] _shares) | ||
SplitPayment(_payees, _shares) payable {} | ||
function () payable {} | ||
} |