├── test ├── .keep └── tokenks │ └── Token.test.js ├── .npmignore ├── .gitignore ├── .travis.yml ├── assets └── cover.png ├── truffle.js ├── migrations └── 1_initial_migration.js ├── .editorconfig ├── contracts ├── mocks │ └── TokenMock.sol ├── Migrations.sol └── tokens │ ├── ERC20.sol │ └── Token.sol ├── .eslintrc.js ├── CONTRIBUTING.md ├── LICENSE ├── package.json └── README.md /test/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .node* 3 | node_modules 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .node* 3 | node_modules 4 | build 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10" 4 | - "9" 5 | -------------------------------------------------------------------------------- /assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Goodnoon/Erthereum-ERC20-/HEAD/assets/cover.png -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // See 3 | // to customize your Truffle configuration! 4 | }; 5 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require('./Migrations.sol'); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | max_line_length = 100 7 | insert_final_newline = false 8 | trim_trailing_whitespace = true 9 | end_of_line = lf 10 | charset = utf-8 11 | -------------------------------------------------------------------------------- /contracts/mocks/TokenMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "../../contracts/tokens/Token.sol"; 4 | 5 | /** 6 | * @dev This is an example contract implementation of Token. 7 | */ 8 | contract TokenMock is Token { 9 | 10 | constructor() 11 | public 12 | { 13 | tokenName = "Mock Token"; 14 | tokenSymbol = "MCK"; 15 | tokenDecimals = 18; 16 | tokenTotalSupply = 300000000000000000000000000; 17 | balances[msg.sender] = tokenTotalSupply; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'env': { 3 | 'es6': false, 4 | 'node': true, 5 | }, 6 | 'parserOptions': { 7 | 'sourceType': 'module', 8 | 'ecmaVersion': 8, 9 | }, 10 | 'rules': { 11 | 'indent': [ 12 | 'error', 13 | 2, 14 | ], 15 | 'linebreak-style': [ 16 | 'error', 17 | 'unix', 18 | ], 19 | 'quotes': [ 20 | 'error', 21 | 'single', 22 | ], 23 | 'semi': [ 24 | 'error', 25 | 'always', 26 | ], 27 | }, 28 | }; -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Issues 4 | 5 | We use GitHub issues to track public bugs. Please ensure your description is clear and has sufficient instructions to be able to reproduce the issue. 6 | 7 | ## Pull Requests 8 | 9 | * Fork the repo and create your branch from master. 10 | * If you've added code that should be tested, add tests. 11 | * Ensure the test suite passes. 12 | 13 | ## Coding Style 14 | 15 | * Avoid abbreviations. 16 | * Use PEP-8 style guidelines, if not otherwise outlined here. 17 | * Use 2-space indents (no tabs). 18 | * Maximum line length is 100 characters. 19 | * Trailing whitespaces should be trimmed in each line. 20 | * TODO comments should indicate the responsible person (e.g. `// TODO(john): comment goes here`) 21 | * For the sake of simplicity, we're using only TODO comments, no FIXMEs, etc. 22 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "@0xcert/ethereum-utils/contracts/ownership/Ownable.sol"; 4 | 5 | /** 6 | * @dev Truffle migrations manager. 7 | */ 8 | contract Migrations is Ownable { 9 | uint public lastCompletedMigration; 10 | 11 | /** 12 | * @dev Contract constructor. 13 | */ 14 | constructor() 15 | public 16 | { 17 | owner = msg.sender; 18 | } 19 | 20 | /** 21 | * @dev Sets migration state. 22 | * @param _completed Last completed migration number. 23 | */ 24 | function setCompleted( 25 | uint _completed 26 | ) 27 | public 28 | onlyOwner() 29 | { 30 | lastCompletedMigration = _completed; 31 | } 32 | 33 | /** 34 | * @dev Permorms migration. 35 | * @param _addr New migration address. 36 | */ 37 | function upgrade( 38 | address _addr 39 | ) 40 | public 41 | onlyOwner() 42 | { 43 | Migrations upgraded = Migrations(_addr); 44 | upgraded.setCompleted(lastCompletedMigration); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2017-2018 0xcert, d.o.o. https://0xcert.org 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@0xcert/ethereum-erc20", 3 | "version": "1.2.1", 4 | "description": "Fungible token standard implementation for the Ethereum blockchain.", 5 | "main": "truffle.js", 6 | "directories": { 7 | "contracts": "contracts", 8 | "migrations": "migrations", 9 | "test": "test" 10 | }, 11 | "scripts": { 12 | "clean": "rm -Rf ./build", 13 | "compile": "truffle compile", 14 | "console": "truffle console", 15 | "flatten": "mkdir -p build && truffle-flattener contracts/**/*.sol >> build/bundle.sol", 16 | "lint": "eslint './**/*.js?(x)'", 17 | "migrate": "truffle migrate", 18 | "networks": "truffle networks", 19 | "postpublish": "npm run clean", 20 | "prepublish": "npm run compile", 21 | "test": "npm run clean && truffle test" 22 | }, 23 | "author": "0xcert", 24 | "license": "MIT", 25 | "repository": { 26 | "type": "git", 27 | "url": "git+https://github.com/0xcert/ethereum-erc20.git" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/0xcert/etheethereum-erc20reum/issues" 31 | }, 32 | "homepage": "https://github.com/0xcert/ethereum-erc20#readme", 33 | "keywords": [ 34 | "ethereum", 35 | "blockchain", 36 | "eth", 37 | "contract", 38 | "contracts", 39 | "smart", 40 | "smart-contract", 41 | "smart-contracts", 42 | "token", 43 | "tokens", 44 | "fungible", 45 | "fungability", 46 | "eip", 47 | "erc", 48 | "erc20", 49 | "erc-20", 50 | "standard" 51 | ], 52 | "devDependencies": { 53 | "truffle": "^4.1.11", 54 | "truffle-flattener": "^1.2.5" 55 | }, 56 | "dependencies": { 57 | "@0xcert/ethereum-utils": "^1.1.0" 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /contracts/tokens/ERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | /** 4 | * @title A standard interface for tokens. 5 | */ 6 | interface ERC20 { 7 | 8 | /** 9 | * @dev Returns the name of the token. 10 | */ 11 | function name() 12 | external 13 | view 14 | returns (string _name); 15 | 16 | /** 17 | * @dev Returns the symbol of the token. 18 | */ 19 | function symbol() 20 | external 21 | view 22 | returns (string _symbol); 23 | 24 | /** 25 | * @dev Returns the number of decimals the token uses. 26 | */ 27 | function decimals() 28 | external 29 | view 30 | returns (uint8 _decimals); 31 | 32 | /** 33 | * @dev Returns the total token supply. 34 | */ 35 | function totalSupply() 36 | external 37 | view 38 | returns (uint256 _totalSupply); 39 | 40 | /** 41 | * @dev Returns the account balance of another account with address _owner. 42 | * @param _owner The address from which the balance will be retrieved. 43 | */ 44 | function balanceOf( 45 | address _owner 46 | ) 47 | external 48 | view 49 | returns (uint256 _balance); 50 | 51 | /** 52 | * @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The 53 | * function SHOULD throw if the _from account balance does not have enough tokens to spend. 54 | * @param _to The address of the recipient. 55 | * @param _value The amount of token to be transferred. 56 | */ 57 | function transfer( 58 | address _to, 59 | uint256 _value 60 | ) 61 | external 62 | returns (bool _success); 63 | 64 | /** 65 | * @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the 66 | * Transfer event. 67 | * @param _from The address of the sender. 68 | * @param _to The address of the recipient. 69 | * @param _value The amount of token to be transferred. 70 | */ 71 | function transferFrom( 72 | address _from, 73 | address _to, 74 | uint256 _value 75 | ) 76 | external 77 | returns (bool _success); 78 | 79 | /** 80 | * @dev Allows _spender to withdraw from your account multiple times, up to 81 | * the _value amount. If this function is called again it overwrites the current 82 | * allowance with _value. 83 | * @param _spender The address of the account able to transfer the tokens. 84 | * @param _value The amount of tokens to be approved for transfer. 85 | */ 86 | function approve( 87 | address _spender, 88 | uint256 _value 89 | ) 90 | external 91 | returns (bool _success); 92 | 93 | /** 94 | * @dev Returns the amount which _spender is still allowed to withdraw from _owner. 95 | * @param _owner The address of the account owning tokens. 96 | * @param _spender The address of the account able to transfer the tokens. 97 | */ 98 | function allowance( 99 | address _owner, 100 | address _spender 101 | ) 102 | external 103 | view 104 | returns (uint256 _remaining); 105 | 106 | /** 107 | * @dev Triggers when tokens are transferred, including zero value transfers. 108 | */ 109 | event Transfer( 110 | address indexed _from, 111 | address indexed _to, 112 | uint256 _value 113 | ); 114 | 115 | /** 116 | * @dev Triggers on any successful call to approve(address _spender, uint256 _value). 117 | */ 118 | event Approval( 119 | address indexed _owner, 120 | address indexed _spender, 121 | uint256 _value 122 | ); 123 | 124 | } 125 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ![Build Status](https://travis-ci.org/0xcert/ethereum-erc20.svg?branch=master) [![NPM Version](https://badge.fury.io/js/@0xcert%2Fethereum-erc20.svg)](https://badge.fury.io/js/0xcert%2Fethereum-erc20) [![Dependencies Status](https://david-dm.org/0xcert/ethereum-erc20.svg)](https://david-dm.org/0xcert/ethereum-erc20) [![Bug Bounty](https://img.shields.io/badge/bounty-pending-2930e8.svg)](https://github.com/0xcert/ethereum-erc20/issues) 4 | 5 | This is a complete implementation of the [ERC-20](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md) fungible token standard for the Ethereum blockchain. This is an open source project build with [Truffle](http://truffleframework.com) framework. 6 | 7 | Purpose of this implementation is to provide a good starting point for anyone who wants to use and develop fungible tokens on the Ethereum blockchain. Instead of re-implementing the ERC-20 yourself you can use this code which has gone through multiple audits and we hope it will be extensively used by the community in the future. 8 | 9 | ## Structure 10 | 11 | Since this is a Truffle project, you will find all tokens in `contracts/tokens/` directory. 12 | 13 | ## Requirements 14 | 15 | * NodeJS 9.0+ recommended. 16 | * Windows, Linux or Mac OS X. 17 | 18 | ## Installation 19 | 20 | ### NPM 21 | 22 | This is an [NPM](https://www.npmjs.com/package/@0xcert/ethereum-erc20) module for [Truffle](http://truffleframework.com) framework. In order to use it as a dependency in your Javascript project, you must install it through the `npm` command: 23 | 24 | ``` 25 | $ npm install @0xcert/ethereum-erc20 26 | ``` 27 | 28 | ### Source 29 | 30 | Clone the repository and install the required `npm` dependencies: 31 | 32 | ``` 33 | $ git clone git@github.com:0xcert/ethereum-erc20.git 34 | $ cd ethereum-erc20 35 | $ npm install 36 | ``` 37 | 38 | Make sure that everything has been set up correctly: 39 | 40 | ``` 41 | $ npm run test 42 | ``` 43 | 44 | ## Usage 45 | 46 | ### NPM 47 | 48 | To interact with package's contracts within JavaScript code, you simply need to require that package's .json files: 49 | 50 | ```js 51 | const contract = require("@0xcert/ethereum-erc20/build/contracts/Token.json"); 52 | console.log(contract); 53 | ``` 54 | 55 | ### Source 56 | 57 | #### Creating smart contract 58 | 59 | The easiest way to start is to create a new file under `contracts/tokens/` (e.g. `MyToken.sol`): 60 | 61 | ```sol 62 | pragma solidity ^0.4.24; 63 | 64 | import "../tokens/Token.sol"; 65 | 66 | contract MyToken is Token { 67 | 68 | constructor() 69 | public 70 | { 71 | tokenName = "My Token"; 72 | tokenSymbol = "MTK"; 73 | tokenDecimals = 18; 74 | tokenTotalSupply = 100000000000000000000000000; 75 | balances[msg.sender] = totalTokenSupply; // Give the owner of the contract the whole balance 76 | } 77 | } 78 | ``` 79 | 80 | That's it. Let's compile the contract: 81 | 82 | ``` 83 | $ npm run compile 84 | ``` 85 | 86 | The easiest way to deploy it locally and start interacting with the contract (minting and transferring tokens) is to deploy it on your personal (local) blockchain using [Ganache](http://truffleframework.com/ganache/). Follow the steps in the Truffle documentation which are described [here](http://truffleframework.com/docs/getting_started/project#alternative-migrating-with-ganache). 87 | 88 | ## Contributing 89 | 90 | See [CONTRIBUTING.md](./CONTRIBUTING.md) for how to help out. 91 | 92 | ## Licence 93 | 94 | See [LICENSE](./LICENSE) for details. 95 | -------------------------------------------------------------------------------- /contracts/tokens/Token.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "@0xcert/ethereum-utils/contracts/math/SafeMath.sol"; 4 | import "./ERC20.sol"; 5 | 6 | /** 7 | * @title ERC20 standard token implementation. 8 | * @dev Standard ERC20 token. This contract follows the implementation at https://goo.gl/mLbAPJ. 9 | */ 10 | contract Token is 11 | ERC20 12 | { 13 | using SafeMath for uint256; 14 | 15 | /** 16 | * Token name. 17 | */ 18 | string internal tokenName; 19 | 20 | /** 21 | * Token symbol. 22 | */ 23 | string internal tokenSymbol; 24 | 25 | /** 26 | * Number of decimals. 27 | */ 28 | uint8 internal tokenDecimals; 29 | 30 | /** 31 | * Total supply of tokens. 32 | */ 33 | uint256 internal tokenTotalSupply; 34 | 35 | /** 36 | * Balance information map. 37 | */ 38 | mapping (address => uint256) internal balances; 39 | 40 | /** 41 | * Token allowance mapping. 42 | */ 43 | mapping (address => mapping (address => uint256)) internal allowed; 44 | 45 | /** 46 | * @dev Trigger when tokens are transferred, including zero value transfers. 47 | */ 48 | event Transfer( 49 | address indexed _from, 50 | address indexed _to, 51 | uint256 _value 52 | ); 53 | 54 | /** 55 | * @dev Trigger on any successful call to approve(address _spender, uint256 _value). 56 | */ 57 | event Approval( 58 | address indexed _owner, 59 | address indexed _spender, 60 | uint256 _value 61 | ); 62 | 63 | /** 64 | * @dev Returns the name of the token. 65 | */ 66 | function name() 67 | external 68 | view 69 | returns (string _name) 70 | { 71 | _name = tokenName; 72 | } 73 | 74 | /** 75 | * @dev Returns the symbol of the token. 76 | */ 77 | function symbol() 78 | external 79 | view 80 | returns (string _symbol) 81 | { 82 | _symbol = tokenSymbol; 83 | } 84 | 85 | /** 86 | * @dev Returns the number of decimals the token uses. 87 | */ 88 | function decimals() 89 | external 90 | view 91 | returns (uint8 _decimals) 92 | { 93 | _decimals = tokenDecimals; 94 | } 95 | 96 | /** 97 | * @dev Returns the total token supply. 98 | */ 99 | function totalSupply() 100 | external 101 | view 102 | returns (uint256 _totalSupply) 103 | { 104 | _totalSupply = tokenTotalSupply; 105 | } 106 | 107 | /** 108 | * @dev Returns the account balance of another account with address _owner. 109 | * @param _owner The address from which the balance will be retrieved. 110 | */ 111 | function balanceOf( 112 | address _owner 113 | ) 114 | external 115 | view 116 | returns (uint256 _balance) 117 | { 118 | _balance = balances[_owner]; 119 | } 120 | 121 | /** 122 | * @dev Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The 123 | * function SHOULD throw if the _from account balance does not have enough tokens to spend. 124 | * @param _to The address of the recipient. 125 | * @param _value The amount of token to be transferred. 126 | */ 127 | function transfer( 128 | address _to, 129 | uint256 _value 130 | ) 131 | public 132 | returns (bool _success) 133 | { 134 | require(_value <= balances[msg.sender]); 135 | 136 | balances[msg.sender] = balances[msg.sender].sub(_value); 137 | balances[_to] = balances[_to].add(_value); 138 | 139 | emit Transfer(msg.sender, _to, _value); 140 | _success = true; 141 | } 142 | 143 | /** 144 | * @dev Allows _spender to withdraw from your account multiple times, up to the _value amount. If 145 | * this function is called again it overwrites the current allowance with _value. 146 | * @param _spender The address of the account able to transfer the tokens. 147 | * @param _value The amount of tokens to be approved for transfer. 148 | */ 149 | function approve( 150 | address _spender, 151 | uint256 _value 152 | ) 153 | public 154 | returns (bool _success) 155 | { 156 | require((_value == 0) || (allowed[msg.sender][_spender] == 0)); 157 | 158 | allowed[msg.sender][_spender] = _value; 159 | 160 | emit Approval(msg.sender, _spender, _value); 161 | _success = true; 162 | } 163 | 164 | /** 165 | * @dev Returns the amount which _spender is still allowed to withdraw from _owner. 166 | * @param _owner The address of the account owning tokens. 167 | * @param _spender The address of the account able to transfer the tokens. 168 | */ 169 | function allowance( 170 | address _owner, 171 | address _spender 172 | ) 173 | external 174 | view 175 | returns (uint256 _remaining) 176 | { 177 | _remaining = allowed[_owner][_spender]; 178 | } 179 | 180 | /** 181 | * @dev Transfers _value amount of tokens from address _from to address _to, and MUST fire the 182 | * Transfer event. 183 | * @param _from The address of the sender. 184 | * @param _to The address of the recipient. 185 | * @param _value The amount of token to be transferred. 186 | */ 187 | function transferFrom( 188 | address _from, 189 | address _to, 190 | uint256 _value 191 | ) 192 | public 193 | returns (bool _success) 194 | { 195 | require(_value <= balances[_from]); 196 | require(_value <= allowed[_from][msg.sender]); 197 | 198 | balances[_from] = balances[_from].sub(_value); 199 | balances[_to] = balances[_to].add(_value); 200 | allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value); 201 | 202 | emit Transfer(_from, _to, _value); 203 | _success = true; 204 | } 205 | 206 | } 207 | -------------------------------------------------------------------------------- /test/tokenks/Token.test.js: -------------------------------------------------------------------------------- 1 | const Token = artifacts.require('TokenMock'); 2 | const assertRevert = require('../../node_modules/@0xcert/ethereum-utils/test/helpers/assertRevert'); 3 | 4 | contract('erc/Token', (accounts) => { 5 | let token; 6 | const owner = accounts[0]; 7 | const recipient = accounts[1]; 8 | const allowedAccount = accounts[2]; 9 | const tokenTotalSupply = new web3.BigNumber('3e+26'); 10 | const tokenName = "Mock Token"; 11 | const tokenSymbol = "MCK"; 12 | const tokenDecimals = "18"; 13 | const ownerSupply = new web3.BigNumber('3e+26'); 14 | 15 | // To send the right amount of tokens, taking in account number of decimals. 16 | const decimalsMul = new web3.BigNumber('1e+18'); 17 | 18 | beforeEach(async () => { 19 | token = await Token.new(); 20 | }); 21 | 22 | it('has correct totalSupply after construction', async () => { 23 | const actualSupply = await token.totalSupply(); 24 | assert.equal(actualSupply.toString(), tokenTotalSupply.toString()); 25 | }); 26 | 27 | it('has correct token name after construction', async () => { 28 | const actualName = await token.name(); 29 | assert.equal(actualName, tokenName); 30 | }); 31 | 32 | it('has correct token symbol after construction', async () => { 33 | const actualSymbol = await token.symbol(); 34 | assert.equal(actualSymbol, tokenSymbol); 35 | }); 36 | 37 | it('has correct token decimals after construction', async () => { 38 | const actualDecimals = await token.decimals(); 39 | assert.equal(actualDecimals.toString(), tokenDecimals); 40 | }); 41 | 42 | it('has correct owner token balance after construction', async () => { 43 | const actualBalance = await token.balanceOf(owner); 44 | assert.equal(actualBalance.toString(), ownerSupply.toString()); 45 | }); 46 | 47 | it('recipient and sender have correct balances after transfer', async () => { 48 | const tokenAmount = decimalsMul.mul(100); 49 | await token.transfer(recipient, tokenAmount); 50 | const actualSenderBalance = await token.balanceOf(owner); 51 | const actualRecipientBalance = await token.balanceOf(recipient); 52 | assert.equal(actualSenderBalance.toString(), ownerSupply.minus(tokenAmount).toString()); 53 | assert.equal(actualRecipientBalance.toString(), tokenAmount.toString()); 54 | }); 55 | 56 | it('emits Transfer event on transfer', async () => { 57 | const tokenAmount = decimalsMul.mul(100); 58 | const { logs } = await token.transfer(recipient, tokenAmount); 59 | const event = logs.find(e => e.event === 'Transfer'); 60 | assert.notEqual(event, undefined); 61 | }); 62 | 63 | it('throws when trying to transfer more than available balance', async () => { 64 | const moreThanBalance = tokenTotalSupply.plus(1); 65 | await assertRevert(token.transfer(recipient, moreThanBalance)); 66 | }); 67 | 68 | it('returns the correct allowance amount after approval', async () => { 69 | const tokenAmount = decimalsMul.mul(100); 70 | await token.approve(recipient, tokenAmount); 71 | const actualAllowance = await token.allowance(owner, recipient); 72 | assert.equal(actualAllowance.toString(), tokenAmount.toString()); 73 | }); 74 | 75 | it('emits Approval event after approval', async () => { 76 | const tokenAmount = decimalsMul.mul(100); 77 | const { logs } = await token.approve(recipient, tokenAmount); 78 | const event = logs.find(e => e.event === 'Approval'); 79 | assert.notEqual(event, undefined); 80 | }); 81 | 82 | it('reverts if owner wants to reset allowance before setting it to 0 first', async () => { 83 | const tokenAmount = decimalsMul.mul(100); 84 | const newTokenAmount = decimalsMul.mul(50); 85 | await token.approve(recipient, tokenAmount); 86 | await assertRevert(token.approve(recipient, newTokenAmount)); 87 | }); 88 | 89 | it('successfully resets allowance', async () => { 90 | const tokenAmount = decimalsMul.mul(100); 91 | const newTokenAmount = decimalsMul.mul(50); 92 | await token.approve(recipient, tokenAmount); 93 | await token.approve(recipient, 0); 94 | await token.approve(recipient, newTokenAmount); 95 | const actualAllowance = await token.allowance(owner, recipient); 96 | assert.equal(actualAllowance.toString(), newTokenAmount.toString()); 97 | }); 98 | 99 | it('returns correct balances after transfering from another account', async () => { 100 | const tokenAmount = decimalsMul.mul(100); 101 | await token.approve(allowedAccount, tokenAmount); 102 | await token.transferFrom(owner, recipient, tokenAmount, { from: allowedAccount }); 103 | const balanceOwner = await token.balanceOf(owner); 104 | const balanceRecipient = await token.balanceOf(recipient); 105 | const balanceAllowedAcc = await token.balanceOf(allowedAccount); 106 | assert.equal(balanceOwner.toString(), ownerSupply.minus(tokenAmount).toString()); 107 | assert.equal(balanceAllowedAcc.toNumber(), 0); 108 | assert.equal(balanceRecipient.toNumber(), tokenAmount.toString()); 109 | }); 110 | 111 | it('emits Transfer event on transferFrom', async () => { 112 | const tokenAmount = decimalsMul.mul(100); 113 | await token.approve(allowedAccount, tokenAmount); 114 | const { logs } = await token.transferFrom(owner, recipient, tokenAmount, 115 | { from: allowedAccount }); 116 | const event = logs.find(e => e.event === 'Transfer'); 117 | assert.notEqual(event, undefined); 118 | }); 119 | 120 | it('throws when trying to transferFrom more than allowed amount', async () => { 121 | const tokenAmountAllowed = decimalsMul.mul(99); 122 | const tokenAmount = decimalsMul.mul(100); 123 | await token.approve(allowedAccount, tokenAmountAllowed); 124 | await assertRevert(token.transferFrom(owner, recipient, tokenAmount, { from: accounts[1] })); 125 | }); 126 | 127 | it('throws an error when trying to transferFrom more than _from has', async () => { 128 | await token.approve(allowedAccount, ownerSupply.plus(1)); 129 | await assertRevert(token.transferFrom(owner, recipient, ownerSupply.plus(1), 130 | { from: allowedAccount})); 131 | }); 132 | 133 | }); 134 | --------------------------------------------------------------------------------