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

Add SafeERC20 helpers #413

Merged
merged 3 commits into from
Aug 28, 2017
Merged
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
24 changes: 24 additions & 0 deletions contracts/token/SafeERC20.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
pragma solidity ^0.4.11;

import './ERC20Basic.sol';
import './ERC20.sol';

/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure.
* To use this library you can add a `using SafeERC20 for ERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
function safeTransfer(ERC20Basic token, address to, uint256 value) internal {
assert(token.transfer(to, value));
}

function safeTransferFrom(ERC20 token, address from, address to, uint256 value) internal {
assert(token.transferFrom(from, to, value));
}

function safeApprove(ERC20 token, address spender, uint256 value) internal {
assert(token.approve(spender, value));
}
}
38 changes: 38 additions & 0 deletions test/SafeERC20.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import EVMThrow from './helpers/EVMThrow';

require('chai')
.use(require('chai-as-promised'))
.should();

const SafeERC20Helper = artifacts.require('./helpers/SafeERC20Helper.sol');

contract('SafeERC20', function () {

beforeEach(async function () {
this.helper = await SafeERC20Helper.new();
});

it('should throw on failed transfer', async function () {
await this.helper.doFailingTransfer().should.be.rejectedWith(EVMThrow);
});

it('should throw on failed transferFrom', async function () {
await this.helper.doFailingTransferFrom().should.be.rejectedWith(EVMThrow);
});

it('should throw on failed approve', async function () {
await this.helper.doFailingApprove().should.be.rejectedWith(EVMThrow);
});

it('should not throw on succeeding transfer', async function () {
await this.helper.doSucceedingTransfer().should.be.fulfilled;
});

it('should not throw on succeeding transferFrom', async function () {
await this.helper.doSucceedingTransferFrom().should.be.fulfilled;
});

it('should not throw on succeeding approve', async function () {
await this.helper.doSucceedingApprove().should.be.fulfilled;
});
});
84 changes: 84 additions & 0 deletions test/helpers/SafeERC20Helper.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
pragma solidity ^0.4.11;

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

contract ERC20FailingMock is ERC20 {
function transfer(address, uint256) returns (bool) {
return false;
}

function transferFrom(address, address, uint256) returns (bool) {
return false;
}

function approve(address, uint256) returns (bool) {
return false;
}

function balanceOf(address) constant returns (uint256) {
return 0;
}

function allowance(address, address) constant returns (uint256) {
return 0;
}
}

contract ERC20SucceedingMock is ERC20 {
function transfer(address, uint256) returns (bool) {
return true;
}

function transferFrom(address, address, uint256) returns (bool) {
return true;
}

function approve(address, uint256) returns (bool) {
return true;
}

function balanceOf(address) constant returns (uint256) {
return 0;
}

function allowance(address, address) constant returns (uint256) {
return 0;
}
}

contract SafeERC20Helper {
using SafeERC20 for ERC20;

ERC20 failing;
ERC20 succeeding;

function SafeERC20Helper() {
failing = new ERC20FailingMock();
succeeding = new ERC20SucceedingMock();
}

function doFailingTransfer() {
failing.safeTransfer(0, 0);
}

function doFailingTransferFrom() {
failing.safeTransferFrom(0, 0, 0);
}

function doFailingApprove() {
failing.safeApprove(0, 0);
}

function doSucceedingTransfer() {
succeeding.safeTransfer(0, 0);
}

function doSucceedingTransferFrom() {
succeeding.safeTransferFrom(0, 0, 0);
}

function doSucceedingApprove() {
succeeding.safeApprove(0, 0);
}
}