Skip to content

Commit

Permalink
adding ERC23 and approveWithCall() implementations - OpenZeppelin#346
Browse files Browse the repository at this point in the history
  • Loading branch information
Peter Murray committed Aug 3, 2017
1 parent 287b873 commit 7928c64
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 11 deletions.
5 changes: 1 addition & 4 deletions contracts/ownership/HasNoTokens.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,8 @@ contract HasNoTokens is Ownable {

/**
* @dev Reject all ERC23 compatible tokens
* @param from_ address The address that is transferring the tokens
* @param value_ uint256 the amount of the specified token
* @param data_ Bytes The data passed from the caller.
*/
function tokenFallback(address from_, uint256 value_, bytes data_) external {
function tokenFallback(address, uint256, bytes) external {
revert();
}

Expand Down
5 changes: 5 additions & 0 deletions contracts/token/ApprovalReceiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pragma solidity ^0.4.11;

contract ApprovalReceiver {
function receiveApproval(address from, uint value, address tokenContract, bytes extraData) returns (bool);
}
7 changes: 7 additions & 0 deletions contracts/token/ERC23.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pragma solidity ^0.4.11;

import "./ERC20Basic.sol";

contract ERC23 is ERC20Basic {
function transfer(address to, uint value, string fallback, bytes data) returns (bool ok);
}
5 changes: 5 additions & 0 deletions contracts/token/ERC23DefaultReceiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pragma solidity ^0.4.11;

contract ERC23DefaultReceiver {
function onTokenReceived(address sender, uint value, bytes data) returns (bool ok);
}
2 changes: 1 addition & 1 deletion contracts/token/LimitedTransferToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ contract LimitedTransferToken is ERC20 {
* @dev Overwriting transferableTokens(address holder, uint64 time) is the way to provide the
* specific logic for limiting token transferability for a holder over time.
*/
function transferableTokens(address holder, uint64 time) constant public returns (uint256) {
function transferableTokens(address holder, uint64) constant public returns (uint256) {
return balanceOf(holder);
}
}
38 changes: 38 additions & 0 deletions contracts/token/StandardERC23Token.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
pragma solidity ^0.4.11;

import './BasicToken.sol';
import './ERC23.sol';

/**
* @title Standard ERC23 token
*
* @dev Implementation of the ERC23 standard token.
* @dev https://github.com/ethereum/EIPs/issues/223
* @dev Based on code by Dexaran: https://github.com/Dexaran/ERC23-tokens/tree/Custom-Fallback/token/ERC223
*/
contract StandardERC23Token is ERC23, BasicToken {

//
// overridden to call transfer(to, value, fallback, data)
//
function transfer(address _to, uint256 _value) returns (bool) {
transfer(_to, _value, "onTokenReceived(address,uint256,bytes)", new bytes(0));
}

function transfer(address to, uint value, string fallback, bytes data) returns (bool ok) {
uint destinationCodeLength;

assembly {
destinationCodeLength:= extcodesize(to)
}

balances[msg.sender] = balances[msg.sender].sub(value);
balances[to] = balances[to].add(value);
if(destinationCodeLength > 0) {
assert(to.call.value(0)(bytes4(sha3(fallback)), msg.sender, value, data));
}
Transfer(msg.sender, to, value);
return true;
}

}
13 changes: 13 additions & 0 deletions contracts/token/StandardTokenWithCall.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pragma solidity ^0.4.11;

import './StandardToken.sol';
import './ApprovalReceiver.sol';

contract StandardTokenWithCall is StandardToken {

function approveAndCall(address _spender, uint256 _value, bytes _extraData) returns (bool success) {
assert(approve(_spender, _value));
return ApprovalReceiver(_spender).receiveApproval(msg.sender, _value, this, _extraData);
}

}
40 changes: 40 additions & 0 deletions test/StandardTokenWithCall.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'use strict';

const assertJump = require('./helpers/assertJump');
var StandardTokenWithCallMock = artifacts.require('./helpers/StandardTokenWithCallMock.sol');
var ApprovalReceiverMock = artifacts.require('./helpers/ApprovalReceiverMock.sol');

function awaitEvent(event, handler) {
return new Promise((resolve, reject) => {
function wrappedHandler(...args) {
Promise.resolve(handler(...args)).then(resolve).catch(reject);
}

event.watch(wrappedHandler);
});
}

contract('StandardTokenWithCall', function(accounts) {
let token;
let receiver;

beforeEach(async function() {
token = await StandardTokenWithCallMock.new(accounts[0], 100);
receiver = await ApprovalReceiverMock.new();
});

it('should approve and call', async function () {
var event = null;
var filter = receiver.ApprovalReceived({}, {});

await token.approveAndCall(receiver.address, 50, '0x123456');
await awaitEvent(filter, (err, e) => {
event = e;
})
assert.isOk(event);
assert.equal(event.args.from, accounts[0]);
assert.equal(event.args.value, 50);
assert.equal(event.args.tokenContract, token.address);
assert.equal(event.args.extra, '0x123456');
})
})
18 changes: 18 additions & 0 deletions test/helpers/ApprovalReceiverMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
pragma solidity ^0.4.11;


import '../../contracts/token/ApprovalReceiver.sol';
import '../../contracts/token/ERC20.sol';

// mock class using StandardTokenWithCall
contract ApprovalReceiverMock is ApprovalReceiver {

event ApprovalReceived(address from, uint value, address tokenContract, bytes extra);

function receiveApproval(address from, uint value, address tokenContract, bytes extraData) returns (bool) {
assert(ERC20(tokenContract).transferFrom(from, this, value));
ApprovalReceived(from, value, tokenContract, extraData);
return true;
}

}
4 changes: 1 addition & 3 deletions test/helpers/ReentrancyAttack.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ pragma solidity ^0.4.11;
contract ReentrancyAttack {

function callSender(bytes4 data) {
if(!msg.sender.call(data)) {
throw;
}
assert(msg.sender.call(data));
}

}
4 changes: 1 addition & 3 deletions test/helpers/ReentrancyMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ contract ReentrancyMock is ReentrancyGuard {
if(n > 0) {
count();
bool result = this.call(func, n - 1);
if(result != true) {
throw;
}
assert(result);
}
}

Expand Down
15 changes: 15 additions & 0 deletions test/helpers/StandardTokenWithCallMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pragma solidity ^0.4.11;


import '../../contracts/token/StandardTokenWithCall.sol';


// mock class using StandardTokenWithCall
contract StandardTokenWithCallMock is StandardTokenWithCall {

function StandardTokenWithCallMock(address initialAccount, uint256 initialBalance) {
balances[initialAccount] = initialBalance;
totalSupply = initialBalance;
}

}

0 comments on commit 7928c64

Please sign in to comment.