Browse Source

Merge pull request #413 from frangio/add-safe-erc20

Add SafeERC20 helpers
Francisco Giordano 8 years ago
parent
commit
dcdc453a55
3 changed files with 146 additions and 0 deletions
  1. 24 0
      contracts/token/SafeERC20.sol
  2. 38 0
      test/SafeERC20.js
  3. 84 0
      test/helpers/SafeERC20Helper.sol

+ 24 - 0
contracts/token/SafeERC20.sol

@@ -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 - 0
test/SafeERC20.js

@@ -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 - 0
test/helpers/SafeERC20Helper.sol

@@ -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);
+  }
+}