Skip to content
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

WIP: Fix transfer dividendability #218

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 51 additions & 2 deletions contracts/token/ERC20DividendableEth.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ contract ERC20DividendableEth is ERC20MintableDetailed {

uint256 public dividendsPerToken; // This is a decimal number
mapping(address => uint256) public lastDPT; // These are decimal numbers
mapping(address => uint256) public adjustmentDPT;

constructor(
string memory name,
Expand All @@ -31,6 +32,52 @@ contract ERC20DividendableEth is ERC20MintableDetailed {
releaseDividends(msg.value);
}

/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(
address recipient,
uint256 amount
) public returns (bool) {
uint256 weight = amount.divd(this.balanceOf(recipient));
uint256 differentialDPT = lastDPT[msg.sender].subd(lastDPT[recipient]);
uint256 weightedDifferential = differentialDPT.muld(weight);
adjustmentDPT[recipient] = adjustmentDPT[recipient]
.addd(weightedDifferential);
return super.transfer(recipient, amount);
}

/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for `sender`'s tokens of at least
* `amount`.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) public returns (bool) {
uint256 weight = amount.divd(this.balanceOf(recipient));
uint256 differentialDPT = lastDPT[sender]
.subd(lastDPT[recipient]);
uint256 weightedDifferential = differentialDPT.muld(weight);
adjustmentDPT[recipient] = adjustmentDPT[recipient]
.addd(weightedDifferential);
return super.transferFrom(sender, recipient, amount);
}

/**
* @dev Function to update the account of the sender
* @notice Will revert if account need not be updated
Expand Down Expand Up @@ -58,8 +105,9 @@ contract ERC20DividendableEth is ERC20MintableDetailed {
{
uint256 owing = dividendsOwing(account);
require(owing > 0, "Account need not be updated now.");
account.transfer(owing);
adjustmentDPT[account] = 0;
lastDPT[account] = dividendsPerToken;
account.transfer(owing);
return owing;
}

Expand All @@ -68,7 +116,8 @@ contract ERC20DividendableEth is ERC20MintableDetailed {
* @param account The account for which to compute the dividends
*/
function dividendsOwing(address account) internal view returns(uint256) {
uint256 owedDPT = dividendsPerToken.subd(lastDPT[account]);
uint256 owedDPT = dividendsPerToken
.subd(lastDPT[account]).addd(adjustmentDPT[account]);
return this.balanceOf(account).muld(owedDPT);
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"@hq20/fixidity": "^0.1.0-alpha.1"
},
"devDependencies": {
"@hq20/fixidity": "0.1.0-alpha.1",
"@hq20/fixidity": "0.1.0-alpha.2",
"@openzeppelin/contracts": "2.5.0",
"@openzeppelin/test-helpers": "0.5.4",
"@truffle/hdwallet-provider": "1.0.31",
Expand Down
25 changes: 24 additions & 1 deletion test/token/ERC20DividendableEth.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { should } from 'chai';

// tslint:disable-next-line:no-var-requires
const { balance, BN, constants, ether, expectEvent, expectRevert, send } = require('@openzeppelin/test-helpers');
const { BN, ether, expectRevert } = require('@openzeppelin/test-helpers');

import { TestERC20DividendableEthInstance } from '../../types/truffle-contracts';

Expand All @@ -22,6 +22,7 @@ contract('ERC20DividendableEth', (accounts) => {
const dividendsPerToken = ether('0.1');
const claimedDividends1 = ether('4');
const claimedDividends2 = ether('6');
const transferTokens = ether('6');

let erc20dividendableEth: TestERC20DividendableEthInstance;

Expand Down Expand Up @@ -91,4 +92,26 @@ contract('ERC20DividendableEth', (accounts) => {
BN(await erc20dividendableEth.claimDividends.call({ from: account1 }))
.should.be.bignumber.equal(claimedDividends2);
});

/**
* @test {ERC20DividendableEth#updateAccount} and {ERC20DividendableEth#claimDividends}
*/
it('dividends can be claimed after transfer of dividends-bearing tokens', async () => {
await erc20dividendableEth.releaseDividends({ from: user1, value: releasedDividends.toString()});
await erc20dividendableEth.claimDividends({ from: account2 });
await erc20dividendableEth.transfer(account2, transferTokens, { from: account1 });
BN(await erc20dividendableEth.claimDividends.call({ from: account2 }))
.should.be.bignumber.gt(ether('6.59')).and.lt(ether('6.61'));
});

/**
* @test {ERC20DividendableEth#updateAccount} and {ERC20DividendableEth#claimDividends}
*/
it('dividends can be claimed after transfer of non-dividends-bearing tokens', async () => {
await erc20dividendableEth.releaseDividends({ from: user1, value: releasedDividends.toString()});
await erc20dividendableEth.claimDividends({ from: account1 });
await erc20dividendableEth.transfer(account2, transferTokens, { from: account1 });
BN(await erc20dividendableEth.claimDividends.call({ from: account2 }))
.should.be.bignumber.equal(claimedDividends2);
});
});
53 changes: 6 additions & 47 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,12 @@
"@ethersproject/constants" ">=5.0.0-beta.128"
"@ethersproject/logger" ">=5.0.0-beta.129"

"@hq20/[email protected].1":
version "0.1.0-alpha.1"
resolved "https://registry.yarnpkg.com/@hq20/fixidity/-/fixidity-0.1.0-alpha.1.tgz#c33878c390815e48d80cc2bee6e9bd90943a912f"
integrity sha512-MPrDHLWny6qO5hbfjT0un7gNCt9HnJRnII4/NFKKIiFVaao2OImtfvLuJhtpv1vX7oDH73Z4eSXW43dmWuIXsQ==
"@hq20/[email protected].2":
version "0.1.0-alpha.2"
resolved "https://registry.yarnpkg.com/@hq20/fixidity/-/fixidity-0.1.0-alpha.2.tgz#a3e0ddcc71aac68578757d89a167470766b621e0"
integrity sha512-slSKTCgtymLY2mo7aB4P7qq65SR46lI4zcyeg30f2TLuz01oa4Aa+EX/PRewHV5ZFKnhDCnMfy0s6SeIgQMGbA==
dependencies:
"@truffle/hdwallet-provider" "1.0.29"
"@truffle/hdwallet-provider" "1.0.31"
bignumber.js "9.0.0"

"@hq20/[email protected]":
Expand Down Expand Up @@ -232,21 +232,6 @@
resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.8.tgz#dc94ca36393403449d4b7461bf9452c241e53ec1"
integrity sha512-x55rtRuNfRO1azmZ30iR0pf0OJ6flQqbax1hJz+Avk1K5fdmOv5cr22s9qFnwTWnS6Bw0jvJEoR0ITsM7cPKtQ==

"@truffle/[email protected]":
version "1.0.29"
resolved "https://registry.yarnpkg.com/@truffle/hdwallet-provider/-/hdwallet-provider-1.0.29.tgz#711bf29049750fe629edb8c304ee5c6b04919a92"
integrity sha512-GN/eUINLXHnsUPx3PJvGd6WKnAOXP0MYK/aVhlE7PekYapHjimg0EVo8HrnDSQw6LxYRFiXP3lhyEK6J9witbg==
dependencies:
any-promise "^1.3.0"
bindings "^1.5.0"
bip39 "^2.4.2"
ethereum-protocol "^1.0.1"
ethereumjs-tx "^1.0.0"
ethereumjs-util "^6.1.0"
ethereumjs-wallet "^0.6.3"
web3 "1.2.1"
web3-provider-engine "https://github.com/trufflesuite/provider-engine#web3-one"

"@truffle/[email protected]":
version "1.0.31"
resolved "https://registry.yarnpkg.com/@truffle/hdwallet-provider/-/hdwallet-provider-1.0.31.tgz#fafb4caa7a1bc234dd2af3543fa98eb491f257e1"
Expand All @@ -260,7 +245,7 @@
ethereumjs-util "^6.1.0"
ethereumjs-wallet "^0.6.3"
web3 "1.2.1"
web3-provider-engine "git+https://github.com/trufflesuite/provider-engine.git#web3-one"
web3-provider-engine "https://github.com/trufflesuite/provider-engine#web3-one"

"@truffle/interface-adapter@^0.3.0":
version "0.3.3"
Expand Down Expand Up @@ -7885,7 +7870,6 @@ [email protected]:

"web3-provider-engine@git+https://github.com/trufflesuite/provider-engine.git#web3-one":
version "14.0.6"
uid "3538c60bc4836b73ccae1ac3f64c8fed8ef19c1a"
resolved "git+https://github.com/trufflesuite/provider-engine.git#3538c60bc4836b73ccae1ac3f64c8fed8ef19c1a"
dependencies:
async "^2.5.0"
Expand All @@ -7909,31 +7893,6 @@ [email protected]:
xhr "^2.2.0"
xtend "^4.0.1"

"web3-provider-engine@https://github.com/trufflesuite/provider-engine#web3-one":
version "14.0.6"
resolved "https://github.com/trufflesuite/provider-engine#3538c60bc4836b73ccae1ac3f64c8fed8ef19c1a"
dependencies:
async "^2.5.0"
backoff "^2.5.0"
clone "^2.0.0"
cross-fetch "^2.1.0"
eth-block-tracker "^3.0.0"
eth-json-rpc-infura "^3.1.0"
eth-sig-util "^1.4.2"
ethereumjs-block "^1.2.2"
ethereumjs-tx "^1.2.0"
ethereumjs-util "^5.1.5"
ethereumjs-vm "^2.3.4"
json-rpc-error "^2.0.0"
json-stable-stringify "^1.0.1"
promise-to-callback "^1.0.0"
readable-stream "^2.2.9"
request "^2.85.0"
semaphore "^1.0.3"
ws "^5.1.1"
xhr "^2.2.0"
xtend "^4.0.1"

[email protected]:
version "1.2.1"
resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.2.1.tgz#c93ea003a42e7b894556f7e19dd3540f947f5013"
Expand Down