From b2f350dca6afeb26393388bcc0cdad2a36ed407c Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Wed, 5 Sep 2018 11:17:18 -0300 Subject: [PATCH] Remove Claimable, DelayedClaimable, Heritable (#1274) * remove Claimable, DelayedClaimable, Heritable * remove SimpleSavingsWallet example which used Heritable (cherry picked from commit 0dc711732a297e70af63f23a9b52e4b3712eac40) --- contracts/examples/SimpleSavingsWallet.sol | 40 ------- contracts/ownership/Claimable.sol | 39 ------- contracts/ownership/DelayedClaimable.sol | 40 ------- contracts/ownership/Heritable.sol | 119 -------------------- test/Heritable.test.js | 120 --------------------- test/SimpleSavingsWallet.test.js | 40 ------- test/ownership/Claimable.test.js | 46 -------- test/ownership/DelayedClaimable.test.js | 55 ---------- 8 files changed, 499 deletions(-) delete mode 100644 contracts/examples/SimpleSavingsWallet.sol delete mode 100644 contracts/ownership/Claimable.sol delete mode 100644 contracts/ownership/DelayedClaimable.sol delete mode 100644 contracts/ownership/Heritable.sol delete mode 100644 test/Heritable.test.js delete mode 100644 test/SimpleSavingsWallet.test.js delete mode 100644 test/ownership/Claimable.test.js delete mode 100644 test/ownership/DelayedClaimable.test.js diff --git a/contracts/examples/SimpleSavingsWallet.sol b/contracts/examples/SimpleSavingsWallet.sol deleted file mode 100644 index e6bfabba7e3..00000000000 --- a/contracts/examples/SimpleSavingsWallet.sol +++ /dev/null @@ -1,40 +0,0 @@ -pragma solidity ^0.4.24; - -import "../ownership/Heritable.sol"; - - -/** - * @title SimpleSavingsWallet - * @dev Simplest form of savings wallet whose ownership can be claimed by a heir - * if owner dies. - * In this example, we take a very simple savings wallet providing two operations - * (to send and receive funds) and extend its capabilities by making it Heritable. - * The account that creates the contract is set as owner, who has the authority to - * choose an heir account. Heir account can reclaim the contract ownership in the - * case that the owner dies. - */ -contract SimpleSavingsWallet is Heritable { - - event Sent(address indexed payee, uint256 amount, uint256 balance); - event Received(address indexed payer, uint256 amount, uint256 balance); - - - constructor(uint256 _heartbeatTimeout) Heritable(_heartbeatTimeout) public {} - - /** - * @dev wallet can receive funds. - */ - function () external payable { - emit Received(msg.sender, msg.value, address(this).balance); - } - - /** - * @dev wallet can send funds - */ - function sendTo(address _payee, uint256 _amount) public onlyOwner { - require(_payee != address(0) && _payee != address(this)); - require(_amount > 0); - _payee.transfer(_amount); - emit Sent(_payee, _amount, address(this).balance); - } -} diff --git a/contracts/ownership/Claimable.sol b/contracts/ownership/Claimable.sol deleted file mode 100644 index 9027c11f52d..00000000000 --- a/contracts/ownership/Claimable.sol +++ /dev/null @@ -1,39 +0,0 @@ -pragma solidity ^0.4.24; - - -import "./Ownable.sol"; - - -/** - * @title Claimable - * @dev Extension for the Ownable contract, where the ownership needs to be claimed. - * This allows the new owner to accept the transfer. - */ -contract Claimable is Ownable { - address public pendingOwner; - - /** - * @dev Modifier throws if called by any account other than the pendingOwner. - */ - modifier onlyPendingOwner() { - require(msg.sender == pendingOwner); - _; - } - - /** - * @dev Allows the current owner to set the pendingOwner address. - * @param newOwner The address to transfer ownership to. - */ - function transferOwnership(address newOwner) public onlyOwner { - pendingOwner = newOwner; - } - - /** - * @dev Allows the pendingOwner address to finalize the transfer. - */ - function claimOwnership() public onlyPendingOwner { - emit OwnershipTransferred(owner, pendingOwner); - owner = pendingOwner; - pendingOwner = address(0); - } -} diff --git a/contracts/ownership/DelayedClaimable.sol b/contracts/ownership/DelayedClaimable.sol deleted file mode 100644 index a9c0857f8e9..00000000000 --- a/contracts/ownership/DelayedClaimable.sol +++ /dev/null @@ -1,40 +0,0 @@ -pragma solidity ^0.4.24; - -import "./Claimable.sol"; - - -/** - * @title DelayedClaimable - * @dev Extension for the Claimable contract, where the ownership needs to be claimed before/after - * a certain block number. - */ -contract DelayedClaimable is Claimable { - - uint256 public end; - uint256 public start; - - /** - * @dev Used to specify the time period during which a pending - * owner can claim ownership. - * @param _start The earliest time ownership can be claimed. - * @param _end The latest time ownership can be claimed. - */ - function setLimits(uint256 _start, uint256 _end) public onlyOwner { - require(_start <= _end); - end = _end; - start = _start; - } - - /** - * @dev Allows the pendingOwner address to finalize the transfer, as long as it is called within - * the specified start and end time. - */ - function claimOwnership() public onlyPendingOwner { - require((block.number <= end) && (block.number >= start)); - emit OwnershipTransferred(owner, pendingOwner); - owner = pendingOwner; - pendingOwner = address(0); - end = 0; - } - -} diff --git a/contracts/ownership/Heritable.sol b/contracts/ownership/Heritable.sol deleted file mode 100644 index 88e6fee62f4..00000000000 --- a/contracts/ownership/Heritable.sol +++ /dev/null @@ -1,119 +0,0 @@ -pragma solidity ^0.4.24; - - -import "./Ownable.sol"; - - -/** - * @title Heritable - * @dev The Heritable contract provides ownership transfer capabilities, in the - * case that the current owner stops "heartbeating". Only the heir can pronounce the - * owner's death. - */ -contract Heritable is Ownable { - address private heir_; - - // Time window the owner has to notify they are alive. - uint256 private heartbeatTimeout_; - - // Timestamp of the owner's death, as pronounced by the heir. - uint256 private timeOfDeath_; - - event HeirChanged(address indexed owner, address indexed newHeir); - event OwnerHeartbeated(address indexed owner); - event OwnerProclaimedDead( - address indexed owner, - address indexed heir, - uint256 timeOfDeath - ); - event HeirOwnershipClaimed( - address indexed previousOwner, - address indexed newOwner - ); - - - /** - * @dev Throw an exception if called by any account other than the heir's. - */ - modifier onlyHeir() { - require(msg.sender == heir_); - _; - } - - - /** - * @notice Create a new Heritable Contract with heir address 0x0. - * @param _heartbeatTimeout time available for the owner to notify they are alive, - * before the heir can take ownership. - */ - constructor(uint256 _heartbeatTimeout) public { - heartbeatTimeout_ = _heartbeatTimeout; - } - - function setHeir(address _newHeir) public onlyOwner { - require(_newHeir != owner); - heartbeat(); - emit HeirChanged(owner, _newHeir); - heir_ = _newHeir; - } - - /** - * @dev Use these getter functions to access the internal variables in - * an inherited contract. - */ - function heir() public view returns(address) { - return heir_; - } - - function heartbeatTimeout() public view returns(uint256) { - return heartbeatTimeout_; - } - - function timeOfDeath() public view returns(uint256) { - return timeOfDeath_; - } - - /** - * @dev set heir = 0x0 - */ - function removeHeir() public onlyOwner { - heartbeat(); - heir_ = address(0); - } - - /** - * @dev Heir can pronounce the owners death. To claim the ownership, they will - * have to wait for `heartbeatTimeout` seconds. - */ - function proclaimDeath() public onlyHeir { - require(_ownerLives()); - emit OwnerProclaimedDead(owner, heir_, timeOfDeath_); - // solium-disable-next-line security/no-block-members - timeOfDeath_ = block.timestamp; - } - - /** - * @dev Owner can send a heartbeat if they were mistakenly pronounced dead. - */ - function heartbeat() public onlyOwner { - emit OwnerHeartbeated(owner); - timeOfDeath_ = 0; - } - - /** - * @dev Allows heir to transfer ownership only if heartbeat has timed out. - */ - function claimHeirOwnership() public onlyHeir { - require(!_ownerLives()); - // solium-disable-next-line security/no-block-members - require(block.timestamp >= timeOfDeath_ + heartbeatTimeout_); - emit OwnershipTransferred(owner, heir_); - emit HeirOwnershipClaimed(owner, heir_); - owner = heir_; - timeOfDeath_ = 0; - } - - function _ownerLives() internal view returns (bool) { - return timeOfDeath_ == 0; - } -} diff --git a/test/Heritable.test.js b/test/Heritable.test.js deleted file mode 100644 index 77036417905..00000000000 --- a/test/Heritable.test.js +++ /dev/null @@ -1,120 +0,0 @@ -const { increaseTime } = require('./helpers/increaseTime'); -const { expectThrow } = require('./helpers/expectThrow'); -const { assertRevert } = require('./helpers/assertRevert'); - -const NULL_ADDRESS = '0x0000000000000000000000000000000000000000'; - -const Heritable = artifacts.require('Heritable'); - -const BigNumber = web3.BigNumber; - -require('chai') - .use(require('chai-bignumber')(BigNumber)) - .should(); - -contract('Heritable', function ([_, owner, heir, anyone]) { - const heartbeatTimeout = 4141; - let heritable; - - beforeEach(async function () { - heritable = await Heritable.new(heartbeatTimeout, { from: owner }); - }); - - it('should start off with an owner, but without heir', async function () { - const heir = await heritable.heir(); - - owner.should.be.a('string').that.is.not.equal(NULL_ADDRESS); - heir.should.be.a('string').that.is.equal(NULL_ADDRESS); - }); - - it('only owner should set heir', async function () { - await heritable.setHeir(heir, { from: owner }); - await expectThrow(heritable.setHeir(heir, { from: anyone })); - }); - - it('owner can\'t be heir', async function () { - await assertRevert(heritable.setHeir(owner, { from: owner })); - }); - - it('owner can remove heir', async function () { - await heritable.setHeir(heir, { from: owner }); - (await heritable.heir()).should.equal(heir); - - await heritable.removeHeir({ from: owner }); - (await heritable.heir()).should.equal(NULL_ADDRESS); - }); - - it('heir can claim ownership only if owner is dead and timeout was reached', async function () { - await heritable.setHeir(heir, { from: owner }); - await expectThrow(heritable.claimHeirOwnership({ from: heir })); - - await heritable.proclaimDeath({ from: heir }); - await increaseTime(1); - await expectThrow(heritable.claimHeirOwnership({ from: heir })); - - await increaseTime(heartbeatTimeout); - await heritable.claimHeirOwnership({ from: heir }); - (await heritable.heir()).should.equal(heir); - }); - - it('only heir can proclaim death', async function () { - await assertRevert(heritable.proclaimDeath({ from: owner })); - await assertRevert(heritable.proclaimDeath({ from: anyone })); - }); - - it('heir can\'t proclaim death if owner is death', async function () { - await heritable.setHeir(heir, { from: owner }); - await heritable.proclaimDeath({ from: heir }); - await assertRevert(heritable.proclaimDeath({ from: heir })); - }); - - it('heir can\'t claim ownership if owner heartbeats', async function () { - await heritable.setHeir(heir, { from: owner }); - - await heritable.proclaimDeath({ from: heir }); - await heritable.heartbeat({ from: owner }); - await expectThrow(heritable.claimHeirOwnership({ from: heir })); - - await heritable.proclaimDeath({ from: heir }); - await increaseTime(heartbeatTimeout); - await heritable.heartbeat({ from: owner }); - await expectThrow(heritable.claimHeirOwnership({ from: heir })); - }); - - it('should log events appropriately', async function () { - const setHeirLogs = (await heritable.setHeir(heir, { from: owner })).logs; - const setHeirEvent = setHeirLogs.find(e => e.event === 'HeirChanged'); - - setHeirEvent.args.owner.should.equal(owner); - setHeirEvent.args.newHeir.should.equal(heir); - - const heartbeatLogs = (await heritable.heartbeat({ from: owner })).logs; - const heartbeatEvent = heartbeatLogs.find(e => e.event === 'OwnerHeartbeated'); - - heartbeatEvent.args.owner.should.equal(owner); - - const proclaimDeathLogs = (await heritable.proclaimDeath({ from: heir })).logs; - const ownerDeadEvent = proclaimDeathLogs.find(e => e.event === 'OwnerProclaimedDead'); - - ownerDeadEvent.args.owner.should.equal(owner); - ownerDeadEvent.args.heir.should.equal(heir); - - await increaseTime(heartbeatTimeout); - const claimHeirOwnershipLogs = (await heritable.claimHeirOwnership({ from: heir })).logs; - const ownershipTransferredEvent = claimHeirOwnershipLogs.find(e => e.event === 'OwnershipTransferred'); - const heirOwnershipClaimedEvent = claimHeirOwnershipLogs.find(e => e.event === 'HeirOwnershipClaimed'); - - ownershipTransferredEvent.args.previousOwner.should.equal(owner); - ownershipTransferredEvent.args.newOwner.should.equal(heir); - heirOwnershipClaimedEvent.args.previousOwner.should.equal(owner); - heirOwnershipClaimedEvent.args.newOwner.should.equal(heir); - }); - - it('timeOfDeath can be queried', async function () { - (await heritable.timeOfDeath()).should.be.bignumber.equal(0); - }); - - it('heartbeatTimeout can be queried', async function () { - (await heritable.heartbeatTimeout()).should.be.bignumber.equal(heartbeatTimeout); - }); -}); diff --git a/test/SimpleSavingsWallet.test.js b/test/SimpleSavingsWallet.test.js deleted file mode 100644 index 3582816451a..00000000000 --- a/test/SimpleSavingsWallet.test.js +++ /dev/null @@ -1,40 +0,0 @@ -const { expectThrow } = require('./helpers/expectThrow'); -const { ethGetBalance, ethSendTransaction } = require('./helpers/web3'); - -const SimpleSavingsWallet = artifacts.require('SimpleSavingsWallet'); - -const BigNumber = web3.BigNumber; - -require('chai') - .use(require('chai-bignumber')(BigNumber)) - .should(); - -contract('SimpleSavingsWallet', function ([_, owner, anyone]) { - let savingsWallet; - - const paymentAmount = 4242; - - beforeEach(async function () { - savingsWallet = await SimpleSavingsWallet.new(4141, { from: owner }); - }); - - it('should receive funds', async function () { - await ethSendTransaction({ from: owner, to: savingsWallet.address, value: paymentAmount }); - const balance = await ethGetBalance(savingsWallet.address); - balance.should.be.bignumber.equal(paymentAmount); - }); - - it('owner can send funds', async function () { - // Receive payment so we have some money to spend. - await ethSendTransaction({ from: anyone, to: savingsWallet.address, value: 1000000 }); - - await expectThrow(savingsWallet.sendTo(0, paymentAmount, { from: owner })); - await expectThrow(savingsWallet.sendTo(savingsWallet.address, paymentAmount, { from: owner })); - await expectThrow(savingsWallet.sendTo(anyone, 0, { from: owner })); - - const balance = await ethGetBalance(anyone); - await savingsWallet.sendTo(anyone, paymentAmount, { from: owner }); - const updatedBalance = await ethGetBalance(anyone); - balance.plus(paymentAmount).should.be.bignumber.equal(updatedBalance); - }); -}); diff --git a/test/ownership/Claimable.test.js b/test/ownership/Claimable.test.js deleted file mode 100644 index bfc1355d5f7..00000000000 --- a/test/ownership/Claimable.test.js +++ /dev/null @@ -1,46 +0,0 @@ -const { assertRevert } = require('../helpers/assertRevert'); - -const Claimable = artifacts.require('Claimable'); - -const BigNumber = web3.BigNumber; - -require('chai') - .use(require('chai-bignumber')(BigNumber)) - .should(); - -contract('Claimable', function ([_, owner, newOwner, anyone]) { - let claimable; - - beforeEach(async function () { - claimable = await Claimable.new({ from: owner }); - }); - - it('should have an owner', async function () { - (await claimable.owner()).should.not.equal(0); - }); - - it('changes pendingOwner after transfer', async function () { - await claimable.transferOwnership(newOwner, { from: owner }); - (await claimable.pendingOwner()).should.equal(newOwner); - }); - - it('should prevent to claimOwnership from anyone', async function () { - await assertRevert(claimable.claimOwnership({ from: anyone })); - }); - - it('should prevent non-owners from transfering', async function () { - await assertRevert(claimable.transferOwnership(anyone, { from: anyone })); - }); - - describe('after initiating a transfer', function () { - beforeEach(async function () { - await claimable.transferOwnership(newOwner, { from: owner }); - }); - - it('changes allow pending owner to claim ownership', async function () { - await claimable.claimOwnership({ from: newOwner }); - - (await claimable.owner()).should.equal(newOwner); - }); - }); -}); diff --git a/test/ownership/DelayedClaimable.test.js b/test/ownership/DelayedClaimable.test.js deleted file mode 100644 index e65ac86c41d..00000000000 --- a/test/ownership/DelayedClaimable.test.js +++ /dev/null @@ -1,55 +0,0 @@ -const { assertRevert } = require('../helpers/assertRevert'); - -const BigNumber = web3.BigNumber; - -require('chai') - .use(require('chai-bignumber')(BigNumber)) - .should(); - -const DelayedClaimable = artifacts.require('DelayedClaimable'); - -contract('DelayedClaimable', function ([_, owner, newOwner]) { - beforeEach(async function () { - this.delayedClaimable = await DelayedClaimable.new({ from: owner }); - }); - - it('can set claim blocks', async function () { - await this.delayedClaimable.transferOwnership(newOwner, { from: owner }); - await this.delayedClaimable.setLimits(0, 1000, { from: owner }); - - (await this.delayedClaimable.end()).should.be.bignumber.equal(1000); - - (await this.delayedClaimable.start()).should.be.bignumber.equal(0); - }); - - it('changes pendingOwner after transfer successful', async function () { - await this.delayedClaimable.transferOwnership(newOwner, { from: owner }); - await this.delayedClaimable.setLimits(0, 1000, { from: owner }); - - (await this.delayedClaimable.end()).should.be.bignumber.equal(1000); - - (await this.delayedClaimable.start()).should.be.bignumber.equal(0); - - (await this.delayedClaimable.pendingOwner()).should.equal(newOwner); - await this.delayedClaimable.claimOwnership({ from: newOwner }); - (await this.delayedClaimable.owner()).should.equal(newOwner); - }); - - it('changes pendingOwner after transfer fails', async function () { - await this.delayedClaimable.transferOwnership(newOwner, { from: owner }); - await this.delayedClaimable.setLimits(100, 110, { from: owner }); - - (await this.delayedClaimable.end()).should.be.bignumber.equal(110); - - (await this.delayedClaimable.start()).should.be.bignumber.equal(100); - - (await this.delayedClaimable.pendingOwner()).should.equal(newOwner); - await assertRevert(this.delayedClaimable.claimOwnership({ from: newOwner })); - (await this.delayedClaimable.owner()).should.not.equal(newOwner); - }); - - it('set end and start invalid values fail', async function () { - await this.delayedClaimable.transferOwnership(newOwner, { from: owner }); - await assertRevert(this.delayedClaimable.setLimits(1001, 1000, { from: owner })); - }); -});