-
Notifications
You must be signed in to change notification settings - Fork 11.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Escrows #1014
Escrows #1014
Changes from 1 commit
2d2075a
b879f4c
48b64ce
3600240
fe63f2e
3a33ffb
d389fd3
e01edb3
5dfb9ce
6166248
cd84c37
dabb3d7
4422858
be8d65f
597aba7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,22 +6,23 @@ import "../ownership/Ownable.sol"; | |
|
||
/** | ||
* @title RefundEscrow | ||
* @dev Escrow that holds investor funds for a unique benefitiary, and allows for | ||
* either withdrawal by the benefiatiary, or refunds to the investors. | ||
* @dev Escrow that holds funds for a beneficiary, deposited from multiple parties. | ||
* The contract owner may close the deposit period, and allow for either withdrawal | ||
* by the beneficiary, or refunds to the depositors. | ||
*/ | ||
contract RefundEscrow is ConditionalEscrow, Ownable { | ||
enum State { Active, Refunding, Closed } | ||
|
||
event Closed(); | ||
event RefundsEnabled(); | ||
event Refunded(address indexed investor, uint256 weiAmount); | ||
event Refunded(address indexed refundee, uint256 weiAmount); | ||
|
||
State public state; | ||
address public beneficiary; | ||
|
||
/** | ||
* @dev Constructor. | ||
* @param _beneficiary The beneficiary of the investments. | ||
* @param _beneficiary The beneficiary of the deposits. | ||
*/ | ||
constructor(address _beneficiary) public { | ||
require(_beneficiary != address(0)); | ||
|
@@ -31,23 +32,16 @@ contract RefundEscrow is ConditionalEscrow, Ownable { | |
|
||
/** | ||
* @dev Stores funds that may later be refunded. | ||
* @param _investor The address funds will be sent to if a refund occurs. | ||
* @param _refundee The address funds will be sent to if a refund occurs. | ||
*/ | ||
function invest(address _investor) payable public { | ||
function deposit(address _refundee) payable public { | ||
require(state == State.Active); | ||
super.deposit(_investor); | ||
} | ||
|
||
/** | ||
* @dev Disable the base deposit function, use invest instead. | ||
*/ | ||
function deposit(address _payee) payable public { | ||
revert(); | ||
super.deposit(_refundee); | ||
} | ||
|
||
/** | ||
* @dev Allows for the beneficiary to withdraw their funds, rejecting | ||
* further investments. | ||
* further deposits. | ||
*/ | ||
function close() onlyOwner public { | ||
require(state == State.Active); | ||
|
@@ -56,7 +50,7 @@ contract RefundEscrow is ConditionalEscrow, Ownable { | |
} | ||
|
||
/** | ||
* @dev Allows for refunds to take place, rejecting further investments. | ||
* @dev Allows for refunds to take place, rejecting further deposits. | ||
*/ | ||
function enableRefunds() onlyOwner public { | ||
require(state == State.Active); | ||
|
@@ -67,23 +61,23 @@ contract RefundEscrow is ConditionalEscrow, Ownable { | |
/** | ||
* @dev Withdraws the beneficiary's funds. | ||
*/ | ||
function withdraw() public { | ||
function beneficiaryWithdraw() public { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like to get a second/third opinion on this name. Alternative: |
||
require(state == State.Closed); | ||
beneficiary.transfer(address(this).balance); | ||
} | ||
|
||
/** | ||
* @dev Refunds an investor. | ||
* @param _investor The address to refund. | ||
* @dev Refunds a refundee. | ||
* @param _refundee The address to refund. | ||
*/ | ||
function refund(address _investor) public { | ||
uint256 amount = depositsOf(_investor); | ||
super.withdraw(_investor); | ||
emit Refunded(_investor, amount); | ||
function withdraw(address _refundee) public { | ||
uint256 amount = depositsOf(_refundee); | ||
super.withdraw(_refundee); | ||
emit Refunded(_refundee, amount); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove when we add events to |
||
} | ||
|
||
/** | ||
* @dev Returns whether investors can withdraw their investments (be refunded). | ||
* @dev Returns whether refundees can withdraw their deposits (be refunded). | ||
*/ | ||
function withdrawalAllowed(address _payee) public view returns (bool) { | ||
return state == State.Refunding; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,42 +6,42 @@ require('chai') | |
.use(require('chai-bignumber')(BigNumber)) | ||
.should(); | ||
|
||
export default function ([payer1, payer2, payee1, payee2]) { | ||
export function shouldBehaveLikeEscrow([payer1, payer2, payee1, payee2]) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It can be the default export and have a name at the same time. We need a library-wide convention for this. Currently all of the behavio(u)rs use a default export so we should stick to that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. None of the other behaviors give a name to this function though. I'd leave it anonymous and default, and implement the library-wide change in a different PR. Opened #1027 |
||
const amount = web3.toWei(42.0, 'ether'); | ||
|
||
describe('as an escrow', function () { | ||
it('can accept a single deposit', async function () { | ||
await this.contract.deposit(payee1, { from: payer1, value: amount }); | ||
await this.escrow.deposit(payee1, { from: payer1, value: amount }); | ||
|
||
const balance = await web3.eth.getBalance(this.contract.address); | ||
const deposit = await this.contract.depositsOf(payee1); | ||
const balance = await web3.eth.getBalance(this.escrow.address); | ||
const deposit = await this.escrow.depositsOf(payee1); | ||
|
||
balance.should.be.bignumber.equal(amount); | ||
deposit.should.be.bignumber.equal(amount); | ||
}); | ||
|
||
it('reverts on empty deposits', async function () { | ||
await this.contract.deposit(payee1, { from: payer1, value: 0 }).should.be.rejectedWith(EVMRevert); | ||
await this.escrow.deposit(payee1, { from: payer1, value: 0 }).should.be.rejectedWith(EVMRevert); | ||
}); | ||
|
||
it('can add multiple deposits on a single account', async function () { | ||
await this.contract.deposit(payee1, { from: payer1, value: amount }); | ||
await this.contract.deposit(payee1, { from: payer2, value: amount * 2 }); | ||
await this.escrow.deposit(payee1, { from: payer1, value: amount }); | ||
await this.escrow.deposit(payee1, { from: payer2, value: amount * 2 }); | ||
|
||
const balance = await web3.eth.getBalance(this.contract.address); | ||
const deposit = await this.contract.depositsOf(payee1); | ||
const balance = await web3.eth.getBalance(this.escrow.address); | ||
const deposit = await this.escrow.depositsOf(payee1); | ||
|
||
balance.should.be.bignumber.equal(amount * 3); | ||
deposit.should.be.bignumber.equal(amount * 3); | ||
}); | ||
|
||
it('can track deposits to multiple accounts', async function () { | ||
await this.contract.deposit(payee1, { from: payer1, value: amount }); | ||
await this.contract.deposit(payee2, { from: payer1, value: amount * 2 }); | ||
await this.escrow.deposit(payee1, { from: payer1, value: amount }); | ||
await this.escrow.deposit(payee2, { from: payer1, value: amount * 2 }); | ||
|
||
const balance = await web3.eth.getBalance(this.contract.address); | ||
const depositPayee1 = await this.contract.depositsOf(payee1); | ||
const depositPayee2 = await this.contract.depositsOf(payee2); | ||
const balance = await web3.eth.getBalance(this.escrow.address); | ||
const depositPayee1 = await this.escrow.depositsOf(payee1); | ||
const depositPayee2 = await this.escrow.depositsOf(payee2); | ||
|
||
balance.should.be.bignumber.equal(amount * 3); | ||
depositPayee1.should.be.bignumber.equal(amount); | ||
|
@@ -51,11 +51,11 @@ export default function ([payer1, payer2, payee1, payee2]) { | |
it('can withdraw payments from any account', async function () { | ||
const payeeInitialBalance = await web3.eth.getBalance(payee1); | ||
|
||
await this.contract.deposit(payee1, { from: payer1, value: amount }); | ||
await this.contract.withdraw(payee1, { from: payer2 }); | ||
await this.escrow.deposit(payee1, { from: payer1, value: amount }); | ||
await this.escrow.withdraw(payee1, { from: payer2 }); | ||
|
||
const escrowBalance = await web3.eth.getBalance(this.contract.address); | ||
const finalDeposit = await this.contract.depositsOf(payee1); | ||
const escrowBalance = await web3.eth.getBalance(this.escrow.address); | ||
const finalDeposit = await this.escrow.depositsOf(payee1); | ||
const payeeFinalBalance = await web3.eth.getBalance(payee1); | ||
|
||
escrowBalance.should.be.bignumber.equal(0); | ||
|
@@ -64,7 +64,7 @@ export default function ([payer1, payer2, payee1, payee2]) { | |
}); | ||
|
||
it('reverts on empty withdrawals', async function () { | ||
await this.contract.withdraw(payee1, { from: payer1 }).should.be.rejectedWith(EVMRevert); | ||
await this.escrow.withdraw(payee1, { from: payer1 }).should.be.rejectedWith(EVMRevert); | ||
}); | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels unnecessary as well. I would remove it.