From 6f2cb661638f86bae7a000f69cca1a6ba32a7087 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 11 Dec 2017 13:19:13 -0800 Subject: Refactor unlimited allowance logic out of ERC20Token --- .../contracts/contracts/test/DummyToken_v2.sol | 2 +- packages/contracts/contracts/test/Mintable_v2.sol | 4 +- packages/contracts/contracts/tokens/ERC20Token.sol | 16 +- .../contracts/contracts/tokens/EtherToken_v2.sol | 6 +- .../tokens/UnlimitedAllowanceToken_v2.sol | 46 ++++++ packages/contracts/test/ts/erc20Token.ts | 126 --------------- .../contracts/test/ts/unlimitedAllowanceToken.ts | 119 --------------- .../contracts/test/ts/unlimited_allowance_token.ts | 119 +++++++++++++++ .../test/ts/unlimited_allowance_token_v2.ts | 126 +++++++++++++++ packages/contracts/test/ts/zrxToken.ts | 170 --------------------- packages/contracts/test/ts/zrx_token.ts | 170 +++++++++++++++++++++ 11 files changed, 470 insertions(+), 434 deletions(-) create mode 100644 packages/contracts/contracts/tokens/UnlimitedAllowanceToken_v2.sol delete mode 100644 packages/contracts/test/ts/erc20Token.ts delete mode 100644 packages/contracts/test/ts/unlimitedAllowanceToken.ts create mode 100644 packages/contracts/test/ts/unlimited_allowance_token.ts create mode 100644 packages/contracts/test/ts/unlimited_allowance_token_v2.ts delete mode 100644 packages/contracts/test/ts/zrxToken.ts create mode 100644 packages/contracts/test/ts/zrx_token.ts (limited to 'packages') diff --git a/packages/contracts/contracts/test/DummyToken_v2.sol b/packages/contracts/contracts/test/DummyToken_v2.sol index 24c604369..e3392bf64 100644 --- a/packages/contracts/contracts/test/DummyToken_v2.sol +++ b/packages/contracts/contracts/test/DummyToken_v2.sol @@ -8,7 +8,7 @@ contract DummyToken_v2 is Mintable_v2, Ownable_v2 { string public symbol; uint public decimals; - function DummyToken( + function DummyToken_v2( string _name, string _symbol, uint _decimals, diff --git a/packages/contracts/contracts/test/Mintable_v2.sol b/packages/contracts/contracts/test/Mintable_v2.sol index 95e2fcfe3..a2605791f 100644 --- a/packages/contracts/contracts/test/Mintable_v2.sol +++ b/packages/contracts/contracts/test/Mintable_v2.sol @@ -1,13 +1,13 @@ pragma solidity 0.4.18; -import "./../tokens/ERC20Token.sol"; +import "./../tokens/UnlimitedAllowanceToken_v2.sol"; import "./../lib/SafeMath_v2.sol"; /* * Mintable * Base contract that creates a mintable UnlimitedAllowanceToken */ -contract Mintable_v2 is ERC20Token, SafeMath_v2 { +contract Mintable_v2 is UnlimitedAllowanceToken_v2, SafeMath_v2 { function mint(uint _value) public { diff --git a/packages/contracts/contracts/tokens/ERC20Token.sol b/packages/contracts/contracts/tokens/ERC20Token.sol index 4596331c1..5ec81032f 100644 --- a/packages/contracts/contracts/tokens/ERC20Token.sol +++ b/packages/contracts/contracts/tokens/ERC20Token.sol @@ -1,11 +1,9 @@ -pragma solidity ^0.4.18; +pragma solidity 0.4.18; import "./Token_v2.sol"; contract ERC20Token is Token_v2 { - uint constant MAX_UINT = 2**256 - 1; - function transfer(address _to, uint _value) public returns (bool) @@ -17,22 +15,14 @@ contract ERC20Token is Token_v2 { return true; } - /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance. See https://github.com/ethereum/EIPs/issues/717 - /// @param _from Address to transfer from. - /// @param _to Address to transfer to. - /// @param _value Amount to transfer. - /// @return Success of transfer. function transferFrom(address _from, address _to, uint _value) public returns (bool) { - uint allowance = allowed[_from][msg.sender]; - require(balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to]); + require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value >= balances[_to]); balances[_to] += _value; balances[_from] -= _value; - if (allowance < MAX_UINT) { - allowed[_from][msg.sender] -= _value; - } + allowed[_from][msg.sender] -= _value; Transfer(_from, _to, _value); return true; } diff --git a/packages/contracts/contracts/tokens/EtherToken_v2.sol b/packages/contracts/contracts/tokens/EtherToken_v2.sol index d56d54432..a501013e4 100644 --- a/packages/contracts/contracts/tokens/EtherToken_v2.sol +++ b/packages/contracts/contracts/tokens/EtherToken_v2.sol @@ -18,10 +18,10 @@ pragma solidity 0.4.18; -import "./ERC20Token.sol"; +import "./UnlimitedAllowanceToken_v2.sol"; import "./../lib/SafeMath_v2.sol"; -contract EtherToken_v2 is ERC20Token, SafeMath_v2 { +contract EtherToken_v2 is UnlimitedAllowanceToken_v2, SafeMath_v2 { string constant public name = "Ether Token"; string constant public symbol = "WETH"; @@ -46,7 +46,7 @@ contract EtherToken_v2 is ERC20Token, SafeMath_v2 { } /// @dev Sells tokens in exchange for Ether, exchanging them 1:1. - /// @param amount Number of tokens to sell. + /// @param _value Number of tokens to sell. function withdraw(uint _value) public { diff --git a/packages/contracts/contracts/tokens/UnlimitedAllowanceToken_v2.sol b/packages/contracts/contracts/tokens/UnlimitedAllowanceToken_v2.sol new file mode 100644 index 000000000..2da54fee2 --- /dev/null +++ b/packages/contracts/contracts/tokens/UnlimitedAllowanceToken_v2.sol @@ -0,0 +1,46 @@ +/* + + Copyright 2017 ZeroEx Intl. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +pragma solidity 0.4.18; + +import "./ERC20Token.sol"; + +contract UnlimitedAllowanceToken_v2 is ERC20Token { + + uint constant MAX_UINT = 2**256 - 1; + + /// @dev ERC20 transferFrom, modified such that an allowance of MAX_UINT represents an unlimited allowance. See https://github.com/ethereum/EIPs/issues/717 + /// @param _from Address to transfer from. + /// @param _to Address to transfer to. + /// @param _value Amount to transfer. + /// @return Success of transfer. + function transferFrom(address _from, address _to, uint _value) + public + returns (bool) + { + uint allowance = allowed[_from][msg.sender]; + require(balances[_from] >= _value && allowance >= _value && balances[_to] + _value >= balances[_to]); + balances[_to] += _value; + balances[_from] -= _value; + if (allowance < MAX_UINT) { + allowed[_from][msg.sender] -= _value; + } + Transfer(_from, _to, _value); + return true; + } +} diff --git a/packages/contracts/test/ts/erc20Token.ts b/packages/contracts/test/ts/erc20Token.ts deleted file mode 100644 index 1edcfcbbd..000000000 --- a/packages/contracts/test/ts/erc20Token.ts +++ /dev/null @@ -1,126 +0,0 @@ -import {ZeroEx} from '0x.js'; -import {BigNumber} from 'bignumber.js'; -import * as chai from 'chai'; -import * as Web3 from 'web3'; - -import {Artifacts} from '../../util/artifacts'; -import {constants} from '../../util/constants'; -import {ContractInstance} from '../../util/types'; - -import {chaiSetup} from './utils/chai_setup'; - -const {DummyTokenV2} = new Artifacts(artifacts); -const web3: Web3 = (global as any).web3; -chaiSetup.configure(); -const expect = chai.expect; - -contract('ERC20Token', (accounts: string[]) => { - const config = { - networkId: constants.TESTRPC_NETWORK_ID, - }; - const zeroEx = new ZeroEx(web3.currentProvider, config); - const owner = accounts[0]; - const spender = accounts[1]; - - const MAX_MINT_VALUE = new BigNumber(100000000000000000000); - let tokenAddress: string; - let token: ContractInstance; - - beforeEach(async () => { - token = await DummyTokenV2.new({from: owner}); - await token.mint(MAX_MINT_VALUE, {from: owner}); - tokenAddress = token.address; - }); - - describe('transfer', () => { - it('should throw if owner has insufficient balance', async () => { - const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const amountToTransfer = ownerBalance.plus(1); - return expect(token.transfer.call(spender, amountToTransfer, {from: owner})) - .to.be.rejectedWith(constants.REVERT); - }); - - it('should transfer balance from sender to receiver', async () => { - const receiver = spender; - const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const amountToTransfer = new BigNumber(1); - await zeroEx.token.transferAsync(tokenAddress, owner, receiver, amountToTransfer); - const finalOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const finalReceiverBalance = await zeroEx.token.getBalanceAsync(tokenAddress, receiver); - - const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer); - const expectedFinalReceiverBalance = amountToTransfer; - expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance); - expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance); - }); - - it('should return true on a 0 value transfer', async () => { - const didReturnTrue = await token.transfer.call(spender, 0, {from: owner}); - expect(didReturnTrue).to.be.true(); - }); - }); - - describe('transferFrom', () => { - it('should throw if owner has insufficient balance', async () => { - const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const amountToTransfer = ownerBalance.plus(1); - await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer); - return expect(token.transferFrom.call(owner, spender, amountToTransfer, {from: spender})) - .to.be.rejectedWith(constants.REVERT); - }); - - it('should throw if spender has insufficient allowance', async () => { - const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const amountToTransfer = ownerBalance; - - const spenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender); - const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; - expect(spenderAllowanceIsInsufficient).to.be.true(); - - return expect(token.transferFrom.call(owner, spender, amountToTransfer, {from: spender})) - .to.be.rejectedWith(constants.REVERT); - }); - - it('should return true on a 0 value transfer', async () => { - const amountToTransfer = 0; - const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); - expect(didReturnTrue).to.be.true(); - }); - - it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => { - const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; - await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance); - await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer); - - const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender); - expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance); - }); - - it('should transfer the correct balances if spender has sufficient allowance', async () => { - const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = initOwnerBalance; - await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance); - await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer); - - const newOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const newSpenderBalance = await zeroEx.token.getBalanceAsync(tokenAddress, spender); - - expect(newOwnerBalance).to.be.bignumber.equal(0); - expect(newSpenderBalance).to.be.bignumber.equal(initOwnerBalance); - }); - - it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => { - const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = initOwnerBalance; - await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance); - await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer); - - const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender); - expect(newSpenderAllowance).to.be.bignumber.equal(0); - }); - }); -}); diff --git a/packages/contracts/test/ts/unlimitedAllowanceToken.ts b/packages/contracts/test/ts/unlimitedAllowanceToken.ts deleted file mode 100644 index 3b4d2db77..000000000 --- a/packages/contracts/test/ts/unlimitedAllowanceToken.ts +++ /dev/null @@ -1,119 +0,0 @@ -import {ZeroEx} from '0x.js'; -import {BigNumber} from 'bignumber.js'; -import * as chai from 'chai'; -import * as Web3 from 'web3'; - -import {Artifacts} from '../../util/artifacts'; -import {constants} from '../../util/constants'; -import {ContractInstance} from '../../util/types'; - -import {chaiSetup} from './utils/chai_setup'; - -const {DummyToken} = new Artifacts(artifacts); -const web3: Web3 = (global as any).web3; -chaiSetup.configure(); -const expect = chai.expect; - -contract('UnlimitedAllowanceToken', (accounts: string[]) => { - const config = { - networkId: constants.TESTRPC_NETWORK_ID, - }; - const zeroEx = new ZeroEx(web3.currentProvider, config); - const owner = accounts[0]; - const spender = accounts[1]; - - const MAX_MINT_VALUE = new BigNumber(100000000000000000000); - let tokenAddress: string; - let token: ContractInstance; - - beforeEach(async () => { - token = await DummyToken.new({from: owner}); - await token.mint(MAX_MINT_VALUE, {from: owner}); - tokenAddress = token.address; - }); - - describe('transfer', () => { - it('should transfer balance from sender to receiver', async () => { - const receiver = spender; - const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const amountToTransfer = new BigNumber(1); - await zeroEx.token.transferAsync(tokenAddress, owner, receiver, amountToTransfer); - const finalOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const finalReceiverBalance = await zeroEx.token.getBalanceAsync(tokenAddress, receiver); - - const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer); - const expectedFinalReceiverBalance = amountToTransfer; - expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance); - expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance); - }); - - it('should return true on a 0 value transfer', async () => { - const didReturnTrue = await token.transfer.call(spender, 0, {from: owner}); - expect(didReturnTrue).to.be.true(); - }); - }); - - describe('transferFrom', () => { - it('should return false if owner has insufficient balance', async () => { - const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const amountToTransfer = ownerBalance.plus(1); - await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer); - const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); - expect(didReturnTrue).to.be.false(); - }); - - it('should return false if spender has insufficient allowance', async () => { - const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const amountToTransfer = ownerBalance; - - const spenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender); - const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; - expect(spenderAllowanceIsInsufficient).to.be.true(); - - const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); - expect(didReturnTrue).to.be.false(); - }); - - it('should return true on a 0 value transfer', async () => { - const amountToTransfer = 0; - const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); - expect(didReturnTrue).to.be.true(); - }); - - it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => { - const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; - await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance); - await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer); - - const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender); - expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance); - }); - - it('should transfer the correct balances if spender has sufficient allowance', async () => { - const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = initOwnerBalance; - await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance); - await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer); - - const newOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const newSpenderBalance = await zeroEx.token.getBalanceAsync(tokenAddress, spender); - - expect(newOwnerBalance).to.be.bignumber.equal(0); - expect(newSpenderBalance).to.be.bignumber.equal(initOwnerBalance); - }); - - it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => { - const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = initOwnerBalance; - await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance); - await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer); - - const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender); - expect(newSpenderAllowance).to.be.bignumber.equal(0); - }); - }); -}); diff --git a/packages/contracts/test/ts/unlimited_allowance_token.ts b/packages/contracts/test/ts/unlimited_allowance_token.ts new file mode 100644 index 000000000..3b4d2db77 --- /dev/null +++ b/packages/contracts/test/ts/unlimited_allowance_token.ts @@ -0,0 +1,119 @@ +import {ZeroEx} from '0x.js'; +import {BigNumber} from 'bignumber.js'; +import * as chai from 'chai'; +import * as Web3 from 'web3'; + +import {Artifacts} from '../../util/artifacts'; +import {constants} from '../../util/constants'; +import {ContractInstance} from '../../util/types'; + +import {chaiSetup} from './utils/chai_setup'; + +const {DummyToken} = new Artifacts(artifacts); +const web3: Web3 = (global as any).web3; +chaiSetup.configure(); +const expect = chai.expect; + +contract('UnlimitedAllowanceToken', (accounts: string[]) => { + const config = { + networkId: constants.TESTRPC_NETWORK_ID, + }; + const zeroEx = new ZeroEx(web3.currentProvider, config); + const owner = accounts[0]; + const spender = accounts[1]; + + const MAX_MINT_VALUE = new BigNumber(100000000000000000000); + let tokenAddress: string; + let token: ContractInstance; + + beforeEach(async () => { + token = await DummyToken.new({from: owner}); + await token.mint(MAX_MINT_VALUE, {from: owner}); + tokenAddress = token.address; + }); + + describe('transfer', () => { + it('should transfer balance from sender to receiver', async () => { + const receiver = spender; + const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const amountToTransfer = new BigNumber(1); + await zeroEx.token.transferAsync(tokenAddress, owner, receiver, amountToTransfer); + const finalOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const finalReceiverBalance = await zeroEx.token.getBalanceAsync(tokenAddress, receiver); + + const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer); + const expectedFinalReceiverBalance = amountToTransfer; + expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance); + expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance); + }); + + it('should return true on a 0 value transfer', async () => { + const didReturnTrue = await token.transfer.call(spender, 0, {from: owner}); + expect(didReturnTrue).to.be.true(); + }); + }); + + describe('transferFrom', () => { + it('should return false if owner has insufficient balance', async () => { + const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const amountToTransfer = ownerBalance.plus(1); + await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer); + const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); + expect(didReturnTrue).to.be.false(); + }); + + it('should return false if spender has insufficient allowance', async () => { + const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const amountToTransfer = ownerBalance; + + const spenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender); + const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; + expect(spenderAllowanceIsInsufficient).to.be.true(); + + const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); + expect(didReturnTrue).to.be.false(); + }); + + it('should return true on a 0 value transfer', async () => { + const amountToTransfer = 0; + const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); + expect(didReturnTrue).to.be.true(); + }); + + it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => { + const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; + await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance); + await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer); + + const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender); + expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance); + }); + + it('should transfer the correct balances if spender has sufficient allowance', async () => { + const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = initOwnerBalance; + await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance); + await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer); + + const newOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const newSpenderBalance = await zeroEx.token.getBalanceAsync(tokenAddress, spender); + + expect(newOwnerBalance).to.be.bignumber.equal(0); + expect(newSpenderBalance).to.be.bignumber.equal(initOwnerBalance); + }); + + it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => { + const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = initOwnerBalance; + await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance); + await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer); + + const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender); + expect(newSpenderAllowance).to.be.bignumber.equal(0); + }); + }); +}); diff --git a/packages/contracts/test/ts/unlimited_allowance_token_v2.ts b/packages/contracts/test/ts/unlimited_allowance_token_v2.ts new file mode 100644 index 000000000..73e12f00c --- /dev/null +++ b/packages/contracts/test/ts/unlimited_allowance_token_v2.ts @@ -0,0 +1,126 @@ +import {ZeroEx} from '0x.js'; +import {BigNumber} from 'bignumber.js'; +import * as chai from 'chai'; +import * as Web3 from 'web3'; + +import {Artifacts} from '../../util/artifacts'; +import {constants} from '../../util/constants'; +import {ContractInstance} from '../../util/types'; + +import {chaiSetup} from './utils/chai_setup'; + +const {DummyTokenV2} = new Artifacts(artifacts); +const web3: Web3 = (global as any).web3; +chaiSetup.configure(); +const expect = chai.expect; + +contract('UnlimitedAllowanceTokenV2', (accounts: string[]) => { + const config = { + networkId: constants.TESTRPC_NETWORK_ID, + }; + const zeroEx = new ZeroEx(web3.currentProvider, config); + const owner = accounts[0]; + const spender = accounts[1]; + + const MAX_MINT_VALUE = new BigNumber(100000000000000000000); + let tokenAddress: string; + let token: ContractInstance; + + beforeEach(async () => { + token = await DummyTokenV2.new({from: owner}); + await token.mint(MAX_MINT_VALUE, {from: owner}); + tokenAddress = token.address; + }); + + describe('transfer', () => { + it('should throw if owner has insufficient balance', async () => { + const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const amountToTransfer = ownerBalance.plus(1); + return expect(token.transfer.call(spender, amountToTransfer, {from: owner})) + .to.be.rejectedWith(constants.REVERT); + }); + + it('should transfer balance from sender to receiver', async () => { + const receiver = spender; + const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const amountToTransfer = new BigNumber(1); + await zeroEx.token.transferAsync(tokenAddress, owner, receiver, amountToTransfer); + const finalOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const finalReceiverBalance = await zeroEx.token.getBalanceAsync(tokenAddress, receiver); + + const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer); + const expectedFinalReceiverBalance = amountToTransfer; + expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance); + expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance); + }); + + it('should return true on a 0 value transfer', async () => { + const didReturnTrue = await token.transfer.call(spender, 0, {from: owner}); + expect(didReturnTrue).to.be.true(); + }); + }); + + describe('transferFrom', () => { + it('should throw if owner has insufficient balance', async () => { + const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const amountToTransfer = ownerBalance.plus(1); + await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, amountToTransfer); + return expect(token.transferFrom.call(owner, spender, amountToTransfer, {from: spender})) + .to.be.rejectedWith(constants.REVERT); + }); + + it('should throw if spender has insufficient allowance', async () => { + const ownerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const amountToTransfer = ownerBalance; + + const spenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender); + const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; + expect(spenderAllowanceIsInsufficient).to.be.true(); + + return expect(token.transferFrom.call(owner, spender, amountToTransfer, {from: spender})) + .to.be.rejectedWith(constants.REVERT); + }); + + it('should return true on a 0 value transfer', async () => { + const amountToTransfer = 0; + const didReturnTrue = await token.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); + expect(didReturnTrue).to.be.true(); + }); + + it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => { + const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; + await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance); + await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer); + + const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender); + expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance); + }); + + it('should transfer the correct balances if spender has sufficient allowance', async () => { + const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = initOwnerBalance; + await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance); + await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer); + + const newOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const newSpenderBalance = await zeroEx.token.getBalanceAsync(tokenAddress, spender); + + expect(newOwnerBalance).to.be.bignumber.equal(0); + expect(newSpenderBalance).to.be.bignumber.equal(initOwnerBalance); + }); + + it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => { + const initOwnerBalance = await zeroEx.token.getBalanceAsync(tokenAddress, owner); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = initOwnerBalance; + await zeroEx.token.setAllowanceAsync(tokenAddress, owner, spender, initSpenderAllowance); + await zeroEx.token.transferFromAsync(tokenAddress, owner, spender, spender, amountToTransfer); + + const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(tokenAddress, owner, spender); + expect(newSpenderAllowance).to.be.bignumber.equal(0); + }); + }); +}); diff --git a/packages/contracts/test/ts/zrxToken.ts b/packages/contracts/test/ts/zrxToken.ts deleted file mode 100644 index 1c9655402..000000000 --- a/packages/contracts/test/ts/zrxToken.ts +++ /dev/null @@ -1,170 +0,0 @@ -import {ZeroEx} from '0x.js'; -import {BigNumber} from 'bignumber.js'; -import * as chai from 'chai'; -import Web3 = require('web3'); - -import {Artifacts} from '../../util/artifacts'; -import {constants} from '../../util/constants'; -import {ContractInstance} from '../../util/types'; - -import {chaiSetup} from './utils/chai_setup'; - -chaiSetup.configure(); -const expect = chai.expect; -const {Exchange, ZRXToken} = new Artifacts(artifacts); -const web3: Web3 = (global as any).web3; - -contract('ZRXToken', (accounts: string[]) => { - const owner = accounts[0]; - const spender = accounts[1]; - let zeroEx: ZeroEx; - - let MAX_UINT: BigNumber; - - let zrx: ContractInstance; - let zrxAddress: string; - - beforeEach(async () => { - zeroEx = new ZeroEx(web3.currentProvider, { - exchangeContractAddress: Exchange.address, - networkId: constants.TESTRPC_NETWORK_ID, - }); - zrxAddress = zeroEx.exchange.getZRXTokenAddress(); - zrx = await ZRXToken.at(zrxAddress); - MAX_UINT = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; - }); - - describe('constants', () => { - it('should have 18 decimals', async () => { - const decimals = new BigNumber(await zrx.decimals.call()); - const expectedDecimals = 18; - expect(decimals).to.be.bignumber.equal(expectedDecimals); - }); - - it('should have a total supply of 1 billion tokens', async () => { - const totalSupply = new BigNumber(await zrx.totalSupply.call()); - const expectedTotalSupply = 1000000000; - expect(ZeroEx.toUnitAmount(totalSupply, 18)).to.be.bignumber.equal(expectedTotalSupply); - }); - - it('should be named 0x Protocol Token', async () => { - const name = await zrx.name.call(); - const expectedName = '0x Protocol Token'; - expect(name).to.be.equal(expectedName); - }); - - it('should have the symbol ZRX', async () => { - const symbol = await zrx.symbol.call(); - const expectedSymbol = 'ZRX'; - expect(symbol).to.be.equal(expectedSymbol); - }); - }); - - describe('constructor', () => { - it('should initialize owner balance to totalSupply', async () => { - const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); - const totalSupply = new BigNumber(await zrx.totalSupply.call()); - expect(totalSupply).to.be.bignumber.equal(ownerBalance); - }); - }); - - describe('transfer', () => { - it('should transfer balance from sender to receiver', async () => { - const receiver = spender; - const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); - const amountToTransfer = new BigNumber(1); - const txHash = await zeroEx.token.transferAsync(zrxAddress, owner, receiver, amountToTransfer); - await zeroEx.awaitTransactionMinedAsync(txHash); - const finalOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); - const finalReceiverBalance = await zeroEx.token.getBalanceAsync(zrxAddress, receiver); - - const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer); - const expectedFinalReceiverBalance = amountToTransfer; - expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance); - expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance); - }); - - it('should return true on a 0 value transfer', async () => { - const didReturnTrue = await zrx.transfer.call(spender, 0, {from: owner}); - expect(didReturnTrue).to.be.true(); - }); - }); - - describe('transferFrom', () => { - it('should return false if owner has insufficient balance', async () => { - const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); - const amountToTransfer = ownerBalance.plus(1); - let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, amountToTransfer); - await zeroEx.awaitTransactionMinedAsync(txHash); - const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); - expect(didReturnTrue).to.be.false(); - // Reset allowance - txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, new BigNumber(0)); - await zeroEx.awaitTransactionMinedAsync(txHash); - }); - - it('should return false if spender has insufficient allowance', async () => { - const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); - const amountToTransfer = ownerBalance; - - const spenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender); - const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; - expect(spenderAllowanceIsInsufficient).to.be.true(); - - const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); - expect(didReturnTrue).to.be.false(); - }); - - it('should return true on a 0 value transfer', async () => { - const amountToTransfer = 0; - const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); - expect(didReturnTrue).to.be.true(); - }); - - it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => { - const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = MAX_UINT; - let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance); - await zeroEx.awaitTransactionMinedAsync(txHash); - txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer); - await zeroEx.awaitTransactionMinedAsync(txHash); - - const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender); - expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance); - // Restore balance - txHash = await zeroEx.token.transferAsync(zrxAddress, spender, owner, amountToTransfer); - await zeroEx.awaitTransactionMinedAsync(txHash); - }); - - it('should transfer the correct balances if spender has sufficient allowance', async () => { - const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); - const initSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = initOwnerBalance; - let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance); - await zeroEx.awaitTransactionMinedAsync(txHash); - txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer); - await zeroEx.awaitTransactionMinedAsync(txHash); - - const newOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); - const newSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender); - - expect(newOwnerBalance).to.be.bignumber.equal(0); - expect(newSpenderBalance).to.be.bignumber.equal(initSpenderBalance.plus(initOwnerBalance)); - }); - - it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => { - const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); - const amountToTransfer = initOwnerBalance; - const initSpenderAllowance = initOwnerBalance; - let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance); - await zeroEx.awaitTransactionMinedAsync(txHash); - txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer); - await zeroEx.awaitTransactionMinedAsync(txHash); - - const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender); - expect(newSpenderAllowance).to.be.bignumber.equal(0); - }); - }); -}); diff --git a/packages/contracts/test/ts/zrx_token.ts b/packages/contracts/test/ts/zrx_token.ts new file mode 100644 index 000000000..1c9655402 --- /dev/null +++ b/packages/contracts/test/ts/zrx_token.ts @@ -0,0 +1,170 @@ +import {ZeroEx} from '0x.js'; +import {BigNumber} from 'bignumber.js'; +import * as chai from 'chai'; +import Web3 = require('web3'); + +import {Artifacts} from '../../util/artifacts'; +import {constants} from '../../util/constants'; +import {ContractInstance} from '../../util/types'; + +import {chaiSetup} from './utils/chai_setup'; + +chaiSetup.configure(); +const expect = chai.expect; +const {Exchange, ZRXToken} = new Artifacts(artifacts); +const web3: Web3 = (global as any).web3; + +contract('ZRXToken', (accounts: string[]) => { + const owner = accounts[0]; + const spender = accounts[1]; + let zeroEx: ZeroEx; + + let MAX_UINT: BigNumber; + + let zrx: ContractInstance; + let zrxAddress: string; + + beforeEach(async () => { + zeroEx = new ZeroEx(web3.currentProvider, { + exchangeContractAddress: Exchange.address, + networkId: constants.TESTRPC_NETWORK_ID, + }); + zrxAddress = zeroEx.exchange.getZRXTokenAddress(); + zrx = await ZRXToken.at(zrxAddress); + MAX_UINT = zeroEx.token.UNLIMITED_ALLOWANCE_IN_BASE_UNITS; + }); + + describe('constants', () => { + it('should have 18 decimals', async () => { + const decimals = new BigNumber(await zrx.decimals.call()); + const expectedDecimals = 18; + expect(decimals).to.be.bignumber.equal(expectedDecimals); + }); + + it('should have a total supply of 1 billion tokens', async () => { + const totalSupply = new BigNumber(await zrx.totalSupply.call()); + const expectedTotalSupply = 1000000000; + expect(ZeroEx.toUnitAmount(totalSupply, 18)).to.be.bignumber.equal(expectedTotalSupply); + }); + + it('should be named 0x Protocol Token', async () => { + const name = await zrx.name.call(); + const expectedName = '0x Protocol Token'; + expect(name).to.be.equal(expectedName); + }); + + it('should have the symbol ZRX', async () => { + const symbol = await zrx.symbol.call(); + const expectedSymbol = 'ZRX'; + expect(symbol).to.be.equal(expectedSymbol); + }); + }); + + describe('constructor', () => { + it('should initialize owner balance to totalSupply', async () => { + const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); + const totalSupply = new BigNumber(await zrx.totalSupply.call()); + expect(totalSupply).to.be.bignumber.equal(ownerBalance); + }); + }); + + describe('transfer', () => { + it('should transfer balance from sender to receiver', async () => { + const receiver = spender; + const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); + const amountToTransfer = new BigNumber(1); + const txHash = await zeroEx.token.transferAsync(zrxAddress, owner, receiver, amountToTransfer); + await zeroEx.awaitTransactionMinedAsync(txHash); + const finalOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); + const finalReceiverBalance = await zeroEx.token.getBalanceAsync(zrxAddress, receiver); + + const expectedFinalOwnerBalance = initOwnerBalance.minus(amountToTransfer); + const expectedFinalReceiverBalance = amountToTransfer; + expect(finalOwnerBalance).to.be.bignumber.equal(expectedFinalOwnerBalance); + expect(finalReceiverBalance).to.be.bignumber.equal(expectedFinalReceiverBalance); + }); + + it('should return true on a 0 value transfer', async () => { + const didReturnTrue = await zrx.transfer.call(spender, 0, {from: owner}); + expect(didReturnTrue).to.be.true(); + }); + }); + + describe('transferFrom', () => { + it('should return false if owner has insufficient balance', async () => { + const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); + const amountToTransfer = ownerBalance.plus(1); + let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, amountToTransfer); + await zeroEx.awaitTransactionMinedAsync(txHash); + const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); + expect(didReturnTrue).to.be.false(); + // Reset allowance + txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, new BigNumber(0)); + await zeroEx.awaitTransactionMinedAsync(txHash); + }); + + it('should return false if spender has insufficient allowance', async () => { + const ownerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); + const amountToTransfer = ownerBalance; + + const spenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender); + const spenderAllowanceIsInsufficient = spenderAllowance.cmp(amountToTransfer) < 0; + expect(spenderAllowanceIsInsufficient).to.be.true(); + + const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); + expect(didReturnTrue).to.be.false(); + }); + + it('should return true on a 0 value transfer', async () => { + const amountToTransfer = 0; + const didReturnTrue = await zrx.transferFrom.call(owner, spender, amountToTransfer, {from: spender}); + expect(didReturnTrue).to.be.true(); + }); + + it('should not modify spender allowance if spender allowance is 2^256 - 1', async () => { + const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = MAX_UINT; + let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance); + await zeroEx.awaitTransactionMinedAsync(txHash); + txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer); + await zeroEx.awaitTransactionMinedAsync(txHash); + + const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender); + expect(initSpenderAllowance).to.be.bignumber.equal(newSpenderAllowance); + // Restore balance + txHash = await zeroEx.token.transferAsync(zrxAddress, spender, owner, amountToTransfer); + await zeroEx.awaitTransactionMinedAsync(txHash); + }); + + it('should transfer the correct balances if spender has sufficient allowance', async () => { + const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); + const initSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = initOwnerBalance; + let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance); + await zeroEx.awaitTransactionMinedAsync(txHash); + txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer); + await zeroEx.awaitTransactionMinedAsync(txHash); + + const newOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); + const newSpenderBalance = await zeroEx.token.getBalanceAsync(zrxAddress, spender); + + expect(newOwnerBalance).to.be.bignumber.equal(0); + expect(newSpenderBalance).to.be.bignumber.equal(initSpenderBalance.plus(initOwnerBalance)); + }); + + it('should modify allowance if spender has sufficient allowance less than 2^256 - 1', async () => { + const initOwnerBalance = await zeroEx.token.getBalanceAsync(zrxAddress, owner); + const amountToTransfer = initOwnerBalance; + const initSpenderAllowance = initOwnerBalance; + let txHash = await zeroEx.token.setAllowanceAsync(zrxAddress, owner, spender, initSpenderAllowance); + await zeroEx.awaitTransactionMinedAsync(txHash); + txHash = await zeroEx.token.transferFromAsync(zrxAddress, owner, spender, spender, amountToTransfer); + await zeroEx.awaitTransactionMinedAsync(txHash); + + const newSpenderAllowance = await zeroEx.token.getAllowanceAsync(zrxAddress, owner, spender); + expect(newSpenderAllowance).to.be.bignumber.equal(0); + }); + }); +}); -- cgit v1.2.3