├── .env.example ├── .gitattributes ├── .gitignore ├── .openzeppelin └── project.json ├── LICENSE ├── README.md ├── contracts ├── ITokenReceiver.sol └── InjectiveToken.sol ├── networks.js ├── package-lock.json ├── package.json └── src └── index.js /.env.example: -------------------------------------------------------------------------------- 1 | DEV_MNEMONIC="concert load couple harbor equip island argue ramp clarify fence smart topic" 2 | ALCHEMY_ROPSTEN_ENDPOINT="https://eth-ropsten.alchemyapi.io/jsonrpc/{insert-api-key}" 3 | ALCHEMY_RINKEBY_ENDPOINT="https://eth-rinkeby.alchemyapi.io/jsonrpc/{insert-api-key}" 4 | ALCHEMY_KOVAN_ENDPOINT="https://eth-rinkeby.alchemyapi.io/jsonrpc/{insert-api-key}" 5 | MATIC_TESTNET_ENDPOINT="https://testnetv3.matic.network" 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # build folder 9 | build/ 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # Directory for instrumented libs generated by jscoverage/JSCover 18 | lib-cov 19 | 20 | # Coverage directory used by tools like istanbul 21 | coverage 22 | 23 | # nyc test coverage 24 | .nyc_output 25 | 26 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 27 | .grunt 28 | 29 | # Bower dependency directory (https://bower.io/) 30 | bower_components 31 | 32 | # node-waf configuration 33 | .lock-wscript 34 | 35 | # Compiled binary addons (https://nodejs.org/api/addons.html) 36 | build/Release 37 | 38 | # Dependency directories 39 | node_modules/ 40 | jspm_packages/ 41 | 42 | # TypeScript v1 declaration files 43 | typings/ 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | 63 | # next.js build output 64 | .next 65 | 66 | .openzeppelin/.session 67 | .openzeppelin/dev-* 68 | .openzeppelin/rinkeby* 69 | .openzeppelin/kovan* 70 | .idea/ 71 | temp/ 72 | 73 | .DS_Store 74 | -------------------------------------------------------------------------------- /.openzeppelin/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifestVersion": "2.2", 3 | "contracts": { 4 | "InjectiveToken": "InjectiveToken" 5 | }, 6 | "dependencies": {}, 7 | "name": "injective-token-contract", 8 | "version": "1.0.0", 9 | "compiler": { 10 | "manager": "openzeppelin", 11 | "solcVersion": "0.5.13", 12 | "compilerSettings": { 13 | "optimizer": { 14 | "enabled": true, 15 | "runs": "200" 16 | } 17 | } 18 | }, 19 | "telemetryOptIn": false 20 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 InjectiveLabs 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Injective Token ERC-20 Contract 2 | The Injective (INJ) ERC-20 token contract is created with the [OpenZeppelin Contracts Ethereum Package](https://github.com/OpenZeppelin/openzeppelin-contracts-ethereum-package). 3 | 4 | It is deployed on Ethereum Mainnet at [`0xe28b3b32b6c345a34ff64674606124dd5aceca30`](https://etherscan.io/address/0xe28b3b32b6c345a34ff64674606124dd5aceca30#code). 5 | 6 | It is also deployed on the Kovan Testnet at [`0xb952e28597fab7a4a6aac6d326afd517f3bfbed4`](https://kovan.etherscan.io/address/0xb952e28597fab7a4a6aac6d326afd517f3bfbed4). 7 | 8 | The token is an upgradable ERC20 token implementing the IERC20 interface. 9 | 10 | ## Installation 11 | 12 | To begin with the project you need to install a usual NodeJS environment and yarn. You must also install the Open Zeppelin SDK CLI and the Ganache CLI: 13 | 14 | ```bash 15 | $ npm install -g @openzeppelin/cli ganache-cli 16 | ``` 17 | 18 | After that, you can start fetching vendored dependencies: 19 | 20 | ```bash 21 | $ npm install 22 | ``` 23 | 24 | To deploy the Injective Token to your development network, initialize your local Ganache instance: 25 | 26 | ```bash 27 | $ ganache-cli --deterministic 28 | ``` 29 | 30 | Then in a separate Terminal tab, initialize the Open Zeppelin SDK project: 31 | ```bash 32 | $ oz init 33 | ? Welcome to the OpenZeppelin SDK! Choose a name for your project injective-token 34 | ? Initial project version 1.0.0 35 | Project initialized. Write a new contract in the contracts folder and run 'openzeppelin create' to deploy it. 36 | ``` 37 | 38 | 39 | Then you can easily deploy the Injective Token Contract with the following steps: 40 | 41 | ```bash 42 | $ oz create 43 | ? Pick a contract to instantiate InjectiveToken 44 | ? Pick a network development 45 | All contracts are up to date 46 | ? Do you want to call a function on the instance after creating it? Yes 47 | ? Select which function * initialize(name: string, symbol: string, decimals: uint8, initialSupply: uint256, initialHolder: address, minters: address[], 48 | pausers: address[]) 49 | ? name (string): InjectiveToken 50 | ? symbol (string): INJ 51 | ? decimals (uint8): 18 52 | ? initialSupply (uint256): 100e18 53 | ? initialHolder (address): 0x5409ED021D9299bf6814279A6A1411A7e866A631 54 | ? minters (address[]): 55 | ? pausers (address[]): 56 | ✓ Instance created at 0x74341e87b1c4dB7D5ED95F92b37509F2525A7A90 57 | 0x74341e87b1c4dB7D5ED95F92b37509F2525A7A90 58 | ``` 59 | 60 | This creates an instance of the [`InjectiveToken`](https://github.com/InjectiveLabs/injective-token-contract/blob/master/contracts/InjectiveToken.sol) at `0x74341e87b1c4dB7D5ED95F92b37509F2525A7A90` (the deployed address will be different in each deployment). 61 | -------------------------------------------------------------------------------- /contracts/ITokenReceiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.13; 2 | 3 | interface ITokenReceiver { 4 | function receiveApproval(address _from, uint256 _value, address _token, bytes calldata _data) 5 | external; 6 | } 7 | -------------------------------------------------------------------------------- /contracts/InjectiveToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.13; 2 | 3 | import "@openzeppelin/upgrades/contracts/Initializable.sol"; 4 | import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Pausable.sol"; 5 | import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Detailed.sol"; 6 | import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Mintable.sol"; 7 | import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20Burnable.sol"; 8 | import "./ITokenReceiver.sol"; 9 | 10 | contract InjectiveToken is Initializable, ERC20Pausable, ERC20Detailed, ERC20Mintable, ERC20Burnable { 11 | function initialize( 12 | string memory name, string memory symbol, uint8 decimals, uint256 initialSupply, address initialHolder, 13 | address[] memory minters, address[] memory pausers 14 | ) public initializer { 15 | ERC20Detailed.initialize(name, symbol, decimals); 16 | 17 | // Mint the initial supply 18 | _mint(initialHolder, initialSupply); 19 | 20 | // Initialize the minter and pauser roles, and renounce them 21 | ERC20Mintable.initialize(address(this)); 22 | _removeMinter(address(this)); 23 | 24 | ERC20Pausable.initialize(address(this)); 25 | _removePauser(address(this)); 26 | 27 | // Add the requested minters and pausers (this can be done after renouncing since 28 | // these are the internal calls) 29 | for (uint256 i = 0; i < minters.length; ++i) { 30 | _addMinter(minters[i]); 31 | } 32 | 33 | for (uint256 i = 0; i < pausers.length; ++i) { 34 | _addPauser(pausers[i]); 35 | } 36 | } 37 | 38 | // https://medium.com/official-amulet/paying-for-services-with-erc20-tokens-6c4313114128 39 | function approveAndCall( 40 | address _spender, 41 | uint256 _value, 42 | bytes memory _data 43 | ) 44 | public 45 | returns (bool success) 46 | { 47 | require(isContract(_spender), "ERR_NON_EXISTING"); 48 | super.increaseAllowance(_spender, _value); 49 | emit Approval(msg.sender, _spender, _value); 50 | ITokenReceiver(_spender).receiveApproval(msg.sender, _value, address(this), _data); 51 | return true; 52 | } 53 | 54 | // approveAndCallProxy is the same as approveAndCall, 55 | // but also takes proxy address as an argument, that means 56 | // approval can be issued for one contract (spender), but 57 | // the call will be made on another contract (proxy). 58 | // 59 | // This pattern allows upgradeable token spenders. 60 | function approveAndCallProxy( 61 | address _spender, 62 | address _proxy, 63 | uint256 _value, 64 | bytes memory _data 65 | ) 66 | public 67 | returns (bool success) 68 | { 69 | require(isContract(_spender), "ERR_NON_EXISTING"); 70 | require(isContract(_proxy), "ERR_NON_EXISTING"); 71 | super.increaseAllowance(_spender, _value); 72 | emit Approval(msg.sender, _spender, _value); 73 | ITokenReceiver(_proxy).receiveApproval(msg.sender, _value, address(this), _data); 74 | return true; 75 | } 76 | 77 | function isContract(address _addr) private view returns (bool){ 78 | uint32 size; 79 | assembly { 80 | size := extcodesize(_addr) 81 | } 82 | return (size > 0); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /networks.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | const HDWalletProvider = require('@truffle/hdwallet-provider'); 4 | const infuraProjectId = process.env.INFURA_PROJECT_ID; 5 | const infuraRopstenEndpoint = "https://ropsten.infura.io/v3/" + infuraProjectId; 6 | const infuraRinkebyEndpoint = "https://rinkeby.infura.io/v3/" + infuraProjectId; 7 | const alchemyRopstenEndpoint = process.env.ALCHEMY_ROPSTEN_ENDPOINT; 8 | const alchemyRinkebyEndpoint = process.env.ALCHEMY_RINKEBY_ENDPOINT; 9 | const alchemyKovanEndpoint = process.env.ALCHEMY_KOVAN_ENDPOINT; 10 | const maticTestnetEndpoint = process.env.MATIC_TESTNET_ENDPOINT; 11 | 12 | module.exports = { 13 | networks: { 14 | development: { 15 | protocol: 'http', 16 | host: 'localhost', 17 | port: 8545, 18 | gas: 5000000, 19 | gasPrice: 5e9, 20 | networkId: '*', 21 | }, 22 | ropsten: { 23 | provider: () => new HDWalletProvider(process.env.DEV_MNEMONIC, alchemyRopstenEndpoint ? alchemyRopstenEndpoint : infuraRopstenEndpoint), 24 | networkId: 3, // Ropsten's id 25 | }, 26 | rinkeby: { 27 | provider: () => new HDWalletProvider(process.env.DEV_MNEMONIC, alchemyRinkebyEndpoint ? alchemyRinkebyEndpoint : infuraRinkebyEndpoint), 28 | networkId: 4, // Rinkeby's id 29 | }, 30 | kovan: { 31 | provider: () => new HDWalletProvider(process.env.DEV_MNEMONIC, alchemyKovanEndpoint), 32 | networkId: 42, // Kovan's id 33 | }, 34 | matic: { 35 | provider: () => new HDWalletProvider(process.env.DEV_MNEMONIC, maticTestnetEndpoint), 36 | networkId: 15001, // Matic Tesnet id 37 | }, 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "injective-token", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "networks.js", 6 | "dependencies": { 7 | "@openzeppelin/contracts-ethereum-package": "^2.4.0", 8 | "@openzeppelin/upgrades": "^2.6.0", 9 | "web3": "^1.2.6" 10 | }, 11 | "devDependencies": { 12 | "@truffle/hdwallet-provider": "^1.0.23", 13 | "dotenv": "^8.2.0" 14 | }, 15 | "scripts": { 16 | "test": "echo \"Error: no test specified\" && exit 1" 17 | }, 18 | "keywords": [], 19 | "author": "", 20 | "license": "ISC" 21 | } 22 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/InjectiveLabs/injective-token-contract/05ee5fa6ad6a0f8461e75ef5abacfd5c39e92f5a/src/index.js --------------------------------------------------------------------------------