From 7008e882c0508939da117cd90fe06e7006b85300 Mon Sep 17 00:00:00 2001 From: Amir Bandeali Date: Mon, 4 Dec 2017 13:25:01 -0800 Subject: Add version to new EtherToken, fix typos --- packages/contracts/contracts/base/ERC20Token.sol | 3 +- .../contracts/contracts/deprecated/EtherToken.sol | 2 +- packages/contracts/contracts/tokens/EtherToken.sol | 58 ---------- .../contracts/contracts/tokens/EtherToken_v2.sol | 58 ++++++++++ packages/contracts/test/ts/ether_token_v2.ts | 118 +++++++++++++++++++++ packages/contracts/util/artifacts.ts | 2 + 6 files changed, 181 insertions(+), 60 deletions(-) delete mode 100644 packages/contracts/contracts/tokens/EtherToken.sol create mode 100644 packages/contracts/contracts/tokens/EtherToken_v2.sol create mode 100644 packages/contracts/test/ts/ether_token_v2.ts (limited to 'packages/contracts') diff --git a/packages/contracts/contracts/base/ERC20Token.sol b/packages/contracts/contracts/base/ERC20Token.sol index 765cd7274..f0117894d 100644 --- a/packages/contracts/contracts/base/ERC20Token.sol +++ b/packages/contracts/contracts/base/ERC20Token.sol @@ -20,7 +20,8 @@ contract ERC20Token is Token { /// @param _value Amount to transfer. /// @return Success of transfer. function transferFrom(address _from, address _to, uint _value) returns (bool) { - require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value >= balances[_to]); + 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) { diff --git a/packages/contracts/contracts/deprecated/EtherToken.sol b/packages/contracts/contracts/deprecated/EtherToken.sol index 68148e095..566782e35 100644 --- a/packages/contracts/contracts/deprecated/EtherToken.sol +++ b/packages/contracts/contracts/deprecated/EtherToken.sol @@ -18,7 +18,7 @@ pragma solidity 0.4.11; -import "./UnlimitedAllowanceToken.sol"; +import "./../tokens/UnlimitedAllowanceToken.sol"; import "./../base/SafeMath.sol"; contract EtherToken is UnlimitedAllowanceToken, SafeMath { diff --git a/packages/contracts/contracts/tokens/EtherToken.sol b/packages/contracts/contracts/tokens/EtherToken.sol deleted file mode 100644 index 5c0f0e536..000000000 --- a/packages/contracts/contracts/tokens/EtherToken.sol +++ /dev/null @@ -1,58 +0,0 @@ -/* - - 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.11; - -import "./ERC20Token.sol"; -import "./../base/SafeMath.sol"; - -contract EtherToken is ERC20Token, SafeMath { - - string constant public name = "Ether Token"; - string constant public symbol = "WETH"; - uint8 constant public decimals = 18; - - /// @dev Fallback to calling deposit when ether is sent directly to contract. - function() - public - payable - { - deposit(); - } - - /// @dev Buys tokens with Ether, exchanging them 1:1. - function deposit() - public - payable - { - balances[msg.sender] = safeAdd(balances[msg.sender], msg.value); - totalSupply = safeAdd(totalSupply, msg.value); - Transfer(address(0), msg.sender, msg.value); - } - - /// @dev Sells tokens in exchange for Ether, exchanging them 1:1. - /// @param amount Number of tokens to sell. - function withdraw(uint amount) - public - { - balances[msg.sender] = safeSub(balances[msg.sender], amount); - totalSupply = safeSub(totalSupply, amount); - require(msg.sender.send(amount)); - Tranfer(msg.sender, address(0), msg.value); - } -} diff --git a/packages/contracts/contracts/tokens/EtherToken_v2.sol b/packages/contracts/contracts/tokens/EtherToken_v2.sol new file mode 100644 index 000000000..a75a2aa7c --- /dev/null +++ b/packages/contracts/contracts/tokens/EtherToken_v2.sol @@ -0,0 +1,58 @@ +/* + + 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.11; + +import "./../base/ERC20Token.sol"; +import "./../base/SafeMath.sol"; + +contract EtherToken_v2 is ERC20Token, SafeMath { + + string constant public name = "Ether Token"; + string constant public symbol = "WETH"; + uint8 constant public decimals = 18; + + /// @dev Fallback to calling deposit when ether is sent directly to contract. + function() + public + payable + { + deposit(); + } + + /// @dev Buys tokens with Ether, exchanging them 1:1. + function deposit() + public + payable + { + balances[msg.sender] = safeAdd(balances[msg.sender], msg.value); + totalSupply = safeAdd(totalSupply, msg.value); + Transfer(address(0), msg.sender, msg.value); + } + + /// @dev Sells tokens in exchange for Ether, exchanging them 1:1. + /// @param amount Number of tokens to sell. + function withdraw(uint amount) + public + { + balances[msg.sender] = safeSub(balances[msg.sender], amount); + totalSupply = safeSub(totalSupply, amount); + require(msg.sender.send(amount)); + Transfer(msg.sender, address(0), amount); + } +} diff --git a/packages/contracts/test/ts/ether_token_v2.ts b/packages/contracts/test/ts/ether_token_v2.ts new file mode 100644 index 000000000..857371578 --- /dev/null +++ b/packages/contracts/test/ts/ether_token_v2.ts @@ -0,0 +1,118 @@ +import {ZeroEx, ZeroExError} from '0x.js'; +import {BigNumber} from 'bignumber.js'; +import * as chai from 'chai'; +import promisify = require('es6-promisify'); +import Web3 = require('web3'); + +import {Artifacts} from '../../util/artifacts'; + +import {chaiSetup} from './utils/chai_setup'; + +const {EtherToken} = new Artifacts(artifacts); + +chaiSetup.configure(); +const expect = chai.expect; + +// In order to benefit from type-safety, we re-assign the global web3 instance injected by Truffle +// with type `any` to a variable of type `Web3`. +const web3: Web3 = (global as any).web3; + +contract('EtherToken', (accounts: string[]) => { + const account = accounts[0]; + const gasPrice = ZeroEx.toBaseUnitAmount(new BigNumber(20), 9); + let zeroEx: ZeroEx; + let etherTokenAddress: string; + before(async () => { + etherTokenAddress = EtherToken.address; + zeroEx = new ZeroEx(web3.currentProvider, { + gasPrice, + etherTokenContractAddress: etherTokenAddress, + }); + }); + + const sendTransactionAsync = promisify(web3.eth.sendTransaction); + const getEthBalanceAsync = async (owner: string) => { + const balanceStr = await promisify(web3.eth.getBalance)(owner); + const balance = new BigNumber(balanceStr); + return balance; + }; + + describe('deposit', () => { + it('should throw if caller attempts to deposit more Ether than caller balance', async () => { + const initEthBalance = await getEthBalanceAsync(account); + const ethToDeposit = initEthBalance.plus(1); + + return expect(zeroEx.etherToken.depositAsync(ethToDeposit, account)) + .to.be.rejectedWith(ZeroExError.InsufficientEthBalanceForDeposit); + }); + + it('should convert deposited Ether to wrapped Ether tokens', async () => { + const initEthBalance = await getEthBalanceAsync(account); + const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account); + + const ethToDeposit = new BigNumber(web3.toWei(1, 'ether')); + + const txHash = await zeroEx.etherToken.depositAsync(ethToDeposit, account); + const receipt = await zeroEx.awaitTransactionMinedAsync(txHash); + + const ethSpentOnGas = gasPrice.times(receipt.gasUsed); + const finalEthBalance = await getEthBalanceAsync(account); + const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account); + + expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas))); + expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit)); + }); + }); + + describe('withdraw', () => { + it('should throw if caller attempts to withdraw greater than caller balance', async () => { + const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account); + const ethTokensToWithdraw = initEthTokenBalance.plus(1); + + return expect(zeroEx.etherToken.withdrawAsync(ethTokensToWithdraw, account)) + .to.be.rejectedWith(ZeroExError.InsufficientWEthBalanceForWithdrawal); + }); + + it('should convert ether tokens to ether with sufficient balance', async () => { + const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account); + const initEthBalance = await getEthBalanceAsync(account); + const ethTokensToWithdraw = initEthTokenBalance; + expect(ethTokensToWithdraw).to.not.be.bignumber.equal(0); + const txHash = await zeroEx.etherToken.withdrawAsync(ethTokensToWithdraw, account); + const receipt = await zeroEx.awaitTransactionMinedAsync(txHash); + + const ethSpentOnGas = gasPrice.times(receipt.gasUsed); + const finalEthBalance = await getEthBalanceAsync(account); + const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account); + + expect(finalEthBalance).to.be.bignumber + .equal(initEthBalance.plus(ethTokensToWithdraw.minus(ethSpentOnGas))); + expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.minus(ethTokensToWithdraw)); + }); + }); + + describe('fallback', () => { + it('should convert sent ether to ether tokens', async () => { + const initEthBalance = await getEthBalanceAsync(account); + const initEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account); + + const ethToDeposit = ZeroEx.toBaseUnitAmount(new BigNumber(1), 18); + + const txHash = await sendTransactionAsync({ + from: account, + to: etherTokenAddress, + value: ethToDeposit, + gasPrice, + }); + + const receipt = await zeroEx.awaitTransactionMinedAsync(txHash); + + const ethSpentOnGas = gasPrice.times(receipt.gasUsed); + const finalEthBalance = await getEthBalanceAsync(account); + const finalEthTokenBalance = await zeroEx.token.getBalanceAsync(etherTokenAddress, account); + + expect(finalEthBalance).to.be.bignumber.equal(initEthBalance.minus(ethToDeposit.plus(ethSpentOnGas))); + expect(finalEthTokenBalance).to.be.bignumber.equal(initEthTokenBalance.plus(ethToDeposit)); + }); + }); +}); diff --git a/packages/contracts/util/artifacts.ts b/packages/contracts/util/artifacts.ts index b15c9216f..12321ac66 100644 --- a/packages/contracts/util/artifacts.ts +++ b/packages/contracts/util/artifacts.ts @@ -7,6 +7,7 @@ export class Artifacts { public ZRXToken: any; public DummyToken: any; public EtherToken: any; + public EtherToken_v2: any; public MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress: any; public MaliciousToken: any; constructor(artifacts: any) { @@ -18,6 +19,7 @@ export class Artifacts { this.ZRXToken = artifacts.require('ZRXToken'); this.DummyToken = artifacts.require('DummyToken'); this.EtherToken = artifacts.require('EtherToken'); + this.EtherToken_v2 = artifacts.require('EtherToken_v2'); this.MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress = artifacts.require( 'MultiSigWalletWithTimeLockExceptRemoveAuthorizedAddress'); this.MaliciousToken = artifacts.require('MaliciousToken'); -- cgit v1.2.3