parent
9c5975a706
commit
d1af3ef1b3
@ -0,0 +1,42 @@ |
|||||||
|
pragma solidity ^0.4.8; |
||||||
|
|
||||||
|
import "./Ownable.sol"; |
||||||
|
|
||||||
|
/// @title Contracts that should not own Ether |
||||||
|
/// @author Remco Bloemen <remco@2π.com> |
||||||
|
/// |
||||||
|
/// This tries to block incoming ether to prevent accidental |
||||||
|
/// loss of Ether. Should Ether end up in the contrat, it will |
||||||
|
/// allow the owner to reclaim this ether. |
||||||
|
/// |
||||||
|
/// @notice Ether can still be send to this contract by: |
||||||
|
/// * calling functions labeled `payable` |
||||||
|
/// * `selfdestruct(contract_address)` |
||||||
|
/// * mining directly to the contract address |
||||||
|
contract HasNoEther is Ownable { |
||||||
|
|
||||||
|
/// Constructor that rejects incoming Ether |
||||||
|
/// @dev The flag `payabe` is added so we can access `msg.value` |
||||||
|
/// without compiler warning. If we leave out payable, then |
||||||
|
/// Solidity will allow inheriting contracts to implement a |
||||||
|
/// payable constructor. By doing it this way we prevent a |
||||||
|
/// payable constructor from working. |
||||||
|
/// Alternatively we could use assembly to access msg.value. |
||||||
|
function HasNoEther() payable { |
||||||
|
if(msg.value > 0) { |
||||||
|
throw; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// Disallow direct send by settings a default function without `payable` |
||||||
|
function() external { |
||||||
|
} |
||||||
|
|
||||||
|
/// Transfer all Ether owned by the contract to the owner |
||||||
|
/// @dev What if owner is itself a contract marked HasNoEther? |
||||||
|
function reclaimEther() external onlyOwner { |
||||||
|
if(!owner.send(this.balance)) { |
||||||
|
throw; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
'use strict'; |
||||||
|
import expectThrow from './helpers/expectThrow'; |
||||||
|
import toPromise from './helpers/toPromise'; |
||||||
|
const HasNoEther = artifacts.require('../contracts/lifecycle/HasNoEther.sol'); |
||||||
|
const HasNoEtherTest = artifacts.require('../helpers/HasNoEtherTest.sol'); |
||||||
|
const ForceEther = artifacts.require('../helpers/ForceEther.sol'); |
||||||
|
|
||||||
|
contract('HasNoEther', function(accounts) { |
||||||
|
const amount = web3.toWei('1', 'ether'); |
||||||
|
|
||||||
|
it('should be constructorable', async function() { |
||||||
|
let hasNoEther = await HasNoEtherTest.new(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should not accept ether in constructor', async function() { |
||||||
|
await expectThrow(HasNoEtherTest.new({value: amount})); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should not accept ether', async function() { |
||||||
|
let hasNoEther = await HasNoEtherTest.new(); |
||||||
|
|
||||||
|
await expectThrow( |
||||||
|
toPromise(web3.eth.sendTransaction)({ |
||||||
|
from: accounts[1], |
||||||
|
to: hasNoEther.address, |
||||||
|
value: amount, |
||||||
|
}), |
||||||
|
); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should allow owner to reclaim ether', async function() { |
||||||
|
// Create contract
|
||||||
|
let hasNoEther = await HasNoEtherTest.new(); |
||||||
|
const startBalance = await web3.eth.getBalance(hasNoEther.address); |
||||||
|
assert.equal(startBalance, 0); |
||||||
|
|
||||||
|
// Force ether into it
|
||||||
|
await ForceEther.new(hasNoEther.address, {value: amount}); |
||||||
|
const forcedBalance = await web3.eth.getBalance(hasNoEther.address); |
||||||
|
assert.equal(forcedBalance, amount); |
||||||
|
|
||||||
|
// Reclaim
|
||||||
|
const ownerStartBalance = await web3.eth.getBalance(accounts[0]); |
||||||
|
await hasNoEther.reclaimEther(); |
||||||
|
const ownerFinalBalance = await web3.eth.getBalance(accounts[0]); |
||||||
|
const finalBalance = await web3.eth.getBalance(hasNoEther.address); |
||||||
|
assert.equal(finalBalance, 0); |
||||||
|
assert.isAbove(ownerFinalBalance, ownerStartBalance); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should allow only owner to reclaim ether', async function() { |
||||||
|
// Create contract
|
||||||
|
let hasNoEther = await HasNoEtherTest.new({from: accounts[0]}); |
||||||
|
|
||||||
|
// Force ether into it
|
||||||
|
await ForceEther.new(hasNoEther.address, {value: amount}); |
||||||
|
const forcedBalance = await web3.eth.getBalance(hasNoEther.address); |
||||||
|
assert.equal(forcedBalance, amount); |
||||||
|
|
||||||
|
// Reclaim
|
||||||
|
await expectThrow(hasNoEther.reclaimEther({from: accounts[1]})); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,13 @@ |
|||||||
|
pragma solidity ^0.4.8; |
||||||
|
|
||||||
|
// @title Force Ether into a contract. |
||||||
|
// @notice even |
||||||
|
// if the contract is not payable. |
||||||
|
// @notice To use, construct the contract with the target as argument. |
||||||
|
// @author Remco Bloemen <remco@neufund.org> |
||||||
|
contract ForceEther { |
||||||
|
function ForceEther(address target) payable { |
||||||
|
// Selfdestruct transfers all Ether to the arget address |
||||||
|
selfdestruct(target); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
pragma solidity ^0.4.8; |
||||||
|
|
||||||
|
import "../../contracts/ownership/HasNoEther.sol"; |
||||||
|
|
||||||
|
contract HasNoEtherTest is HasNoEther { |
||||||
|
|
||||||
|
// Constructor with explicit payable — should still fail |
||||||
|
function HasNoEtherTest() payable { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// Default function with explicit payable — should still fail |
||||||
|
function() external payable { |
||||||
|
throw; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
export default async promise => { |
||||||
|
try { |
||||||
|
await promise; |
||||||
|
} catch (error) { |
||||||
|
// TODO: Check jump destination to destinguish between a throw
|
||||||
|
// and an actual invalid jump.
|
||||||
|
const invalidJump = error.message.search('invalid JUMP') >= 0; |
||||||
|
assert(invalidJump, "Expected throw, got '" + error + "' instead"); |
||||||
|
return; |
||||||
|
} |
||||||
|
assert.fail('Expected throw not received'); |
||||||
|
}; |
@ -0,0 +1,4 @@ |
|||||||
|
export default func => |
||||||
|
(...args) => |
||||||
|
new Promise((accept, reject) => |
||||||
|
func(...args, (error, data) => error ? reject(error) : accept(data))); |
Loading…
Reference in new issue