├── .nvmrc ├── dist ├── .gitkeep └── ServiceReceiver.dist.sol ├── analysis ├── .gitkeep ├── uml │ ├── .gitkeep │ ├── ServiceReceiver.svg │ ├── StandardBEP20.svg │ ├── SimpleBEP20.svg │ └── BurnableBEP20.svg ├── control-flow │ ├── .gitkeep │ ├── CommonBEP20.png │ ├── SimpleBEP20.png │ ├── BurnableBEP20.png │ ├── MintableBEP20.png │ ├── StandardBEP20.png │ └── ServiceReceiver.png ├── description-table │ ├── .gitkeep │ ├── ServiceReceiver.md │ ├── StandardBEP20.md │ ├── BurnableBEP20.md │ ├── SimpleBEP20.md │ ├── MintableBEP20.md │ └── CommonBEP20.md └── inheritance-tree │ ├── .gitkeep │ ├── CommonBEP20.png │ ├── SimpleBEP20.png │ ├── BurnableBEP20.png │ ├── MintableBEP20.png │ ├── StandardBEP20.png │ └── ServiceReceiver.png ├── migrations └── .gitkeep ├── .gitattributes ├── .eslintignore ├── scripts ├── coverage.sh ├── compile.sh ├── flat.sh ├── analyze.sh └── test.sh ├── bs-config.json ├── .solcover.js ├── .editorconfig ├── web-console ├── package.json ├── index.html └── js │ └── app.js ├── contracts ├── mocks │ ├── ServicePayerMock.sol │ ├── BEP20Mock.sol │ └── ERC20Mock.sol ├── service │ ├── ServicePayer.sol │ └── ServiceReceiver.sol ├── token │ └── BEP20 │ │ ├── StandardBEP20.sol │ │ ├── BurnableBEP20.sol │ │ ├── SimpleBEP20.sol │ │ ├── lib │ │ ├── BEP20Capped.sol │ │ ├── BEP20Burnable.sol │ │ ├── BEP20Mintable.sol │ │ ├── IBEP20.sol │ │ └── BEP20.sol │ │ ├── MintableBEP20.sol │ │ └── CommonBEP20.sol └── utils │ └── GeneratorCopyright.sol ├── .solhint.json ├── hardhat.config.js ├── test ├── utils │ └── GeneratorCopyright.behaviour.js ├── token │ └── BEP20 │ │ ├── behaviours │ │ ├── BEP20Capped.behaviour.js │ │ ├── BEP20Mintable.behaviour.js │ │ ├── BEP20Burnable.behaviour.js │ │ └── BEP20.behaviour.js │ │ ├── StandardBEP20.test.js │ │ ├── SimpleBEP20.test.js │ │ ├── BurnableBEP20.test.js │ │ ├── MintableBEP20.test.js │ │ └── CommonBEP20.test.js ├── access │ └── Ownable.behavior.js └── service │ ├── ServicePayer.test.js │ └── ServiceReceiver.test.js ├── .gitignore ├── truffle-config.js ├── LICENSE ├── .eslintrc ├── .github └── workflows │ └── ci.yml ├── package.json └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 14 2 | -------------------------------------------------------------------------------- /dist/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /analysis/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /analysis/uml/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /migrations/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /analysis/control-flow/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /analysis/description-table/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /analysis/inheritance-tree/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | cache 3 | node_modules 4 | web-console 5 | -------------------------------------------------------------------------------- /scripts/coverage.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | npm run hardhat:compile 4 | npm run hardhat:coverage 5 | -------------------------------------------------------------------------------- /bs-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "server": { 3 | "baseDir": ["./web-console", "./build/contracts"] 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /analysis/control-flow/CommonBEP20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdgo/bep20-generator/HEAD/analysis/control-flow/CommonBEP20.png -------------------------------------------------------------------------------- /analysis/control-flow/SimpleBEP20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdgo/bep20-generator/HEAD/analysis/control-flow/SimpleBEP20.png -------------------------------------------------------------------------------- /analysis/control-flow/BurnableBEP20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdgo/bep20-generator/HEAD/analysis/control-flow/BurnableBEP20.png -------------------------------------------------------------------------------- /analysis/control-flow/MintableBEP20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdgo/bep20-generator/HEAD/analysis/control-flow/MintableBEP20.png -------------------------------------------------------------------------------- /analysis/control-flow/StandardBEP20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdgo/bep20-generator/HEAD/analysis/control-flow/StandardBEP20.png -------------------------------------------------------------------------------- /analysis/control-flow/ServiceReceiver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdgo/bep20-generator/HEAD/analysis/control-flow/ServiceReceiver.png -------------------------------------------------------------------------------- /analysis/inheritance-tree/CommonBEP20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdgo/bep20-generator/HEAD/analysis/inheritance-tree/CommonBEP20.png -------------------------------------------------------------------------------- /analysis/inheritance-tree/SimpleBEP20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdgo/bep20-generator/HEAD/analysis/inheritance-tree/SimpleBEP20.png -------------------------------------------------------------------------------- /analysis/inheritance-tree/BurnableBEP20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdgo/bep20-generator/HEAD/analysis/inheritance-tree/BurnableBEP20.png -------------------------------------------------------------------------------- /analysis/inheritance-tree/MintableBEP20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdgo/bep20-generator/HEAD/analysis/inheritance-tree/MintableBEP20.png -------------------------------------------------------------------------------- /analysis/inheritance-tree/StandardBEP20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdgo/bep20-generator/HEAD/analysis/inheritance-tree/StandardBEP20.png -------------------------------------------------------------------------------- /analysis/inheritance-tree/ServiceReceiver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markdgo/bep20-generator/HEAD/analysis/inheritance-tree/ServiceReceiver.png -------------------------------------------------------------------------------- /scripts/compile.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | if [ "$SOLC_NIGHTLY" = true ]; then 4 | docker pull ethereum/solc:nightly 5 | fi 6 | 7 | npx truffle compile --all 8 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | norpc: true, 3 | testCommand: 'npm run hardhat:test', 4 | compileCommand: 'npm run hardhat:compile', 5 | skipFiles: [ 6 | 'mocks' 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | end_of_line = lf 9 | indent_size = 2 10 | indent_style = space 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | -------------------------------------------------------------------------------- /scripts/flat.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | for contract in "SimpleBEP20" "StandardBEP20" "BurnableBEP20" "MintableBEP20" "CommonBEP20" 4 | do 5 | npx truffle-flattener contracts/token/BEP20/$contract.sol > dist/$contract.dist.sol 6 | done 7 | 8 | npx truffle-flattener contracts/service/ServiceReceiver.sol > dist/ServiceReceiver.dist.sol 9 | -------------------------------------------------------------------------------- /web-console/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dapp-src", 3 | "version": "1.0.0", 4 | "description": "", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "", 9 | "license": "MIT", 10 | "dependencies": { 11 | "truffle-contract": "3.0.7", 12 | "web3": "0.20.6" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /contracts/mocks/ServicePayerMock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "../service/ServicePayer.sol"; 6 | 7 | // mock class using ServicePayer 8 | contract ServicePayerMock is ServicePayer { 9 | 10 | constructor (address payable feeReceiver) ServicePayer(feeReceiver, "ServicePayerMock") payable {} 11 | } 12 | -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:recommended", 3 | "rules": { 4 | "mark-callable-contracts": "off", 5 | "no-empty-blocks": "off", 6 | "compiler-version": ["error", "^0.7.0"], 7 | "private-vars-leading-underscore": "error", 8 | "reason-string": "off", 9 | "func-visibility": ["error", { "ignoreConstructors": true }] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /contracts/service/ServicePayer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "./ServiceReceiver.sol"; 6 | 7 | /** 8 | * @title ServicePayer 9 | * @dev Implementation of the ServicePayer 10 | */ 11 | abstract contract ServicePayer { 12 | 13 | constructor (address payable receiver, string memory serviceName) payable { 14 | ServiceReceiver(receiver).pay{value: msg.value}(serviceName); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /contracts/mocks/BEP20Mock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "../token/BEP20/lib/BEP20.sol"; 6 | 7 | // mock class using BEP20 8 | contract BEP20Mock is BEP20 { 9 | 10 | constructor ( 11 | string memory name, 12 | string memory symbol, 13 | address initialAccount, 14 | uint256 initialBalance 15 | ) BEP20(name, symbol) { 16 | _mint(initialAccount, initialBalance); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /contracts/mocks/ERC20Mock.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | // mock class using ERC20 8 | contract ERC20Mock is ERC20 { 9 | 10 | constructor ( 11 | string memory name, 12 | string memory symbol, 13 | address initialAccount, 14 | uint256 initialBalance 15 | ) ERC20(name, symbol) { 16 | _mint(initialAccount, initialBalance); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /scripts/analyze.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | for contract in "SimpleBEP20" "StandardBEP20" "BurnableBEP20" "MintableBEP20" "CommonBEP20" "ServiceReceiver" 4 | do 5 | npx surya inheritance dist/$contract.dist.sol | dot -Tpng > analysis/inheritance-tree/$contract.png 6 | 7 | npx surya graph dist/$contract.dist.sol | dot -Tpng > analysis/control-flow/$contract.png 8 | 9 | npx surya mdreport analysis/description-table/$contract.md dist/$contract.dist.sol 10 | 11 | npx sol2uml dist/$contract.dist.sol -o analysis/uml/$contract.svg 12 | done 13 | -------------------------------------------------------------------------------- /web-console/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Smart Contract - Console 8 | 9 | 10 | Smart Contract - Console 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require('chai/register-should'); 2 | require('@nomiclabs/hardhat-ganache'); 3 | require('@nomiclabs/hardhat-truffle5'); 4 | require('solidity-coverage'); 5 | 6 | module.exports = { 7 | defaultNetwork: 'hardhat', 8 | networks: { 9 | coverage: { 10 | url: 'http://127.0.0.1:8555', 11 | gas: 0xfffffffffff, 12 | gasPrice: 0x01, 13 | }, 14 | }, 15 | solidity: { 16 | version: '0.7.6', 17 | settings: { 18 | optimizer: { 19 | enabled: true, 20 | runs: 200, 21 | }, 22 | }, 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /test/utils/GeneratorCopyright.behaviour.js: -------------------------------------------------------------------------------- 1 | function shouldBehaveLikeGeneratorCopyright (version) { 2 | describe('should have', function () { 3 | const _builtOn = { 4 | generator: 'https://hikecoder.github.io/bep20-generator', 5 | }; 6 | 7 | it('a generator value', async function () { 8 | _builtOn.generator.should.be.equal(await this.instance.generator()); 9 | }); 10 | 11 | it('a version value', async function () { 12 | version.should.be.equal(await this.instance.version()); 13 | }); 14 | }); 15 | } 16 | 17 | module.exports = { 18 | shouldBehaveLikeGeneratorCopyright, 19 | }; 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | 4 | # Logs 5 | logs 6 | *.log 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | scTopics 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | coverage.json 17 | coverageEnv 18 | 19 | # temporary artifact from solidity-coverage 20 | allFiredEvents 21 | .coverage_artifacts 22 | .coverage_cache 23 | .coverage_contracts 24 | 25 | # node-waf configuration 26 | .lock-wscript 27 | 28 | # Dependency directory 29 | node_modules 30 | 31 | # Debug log from npm 32 | npm-debug.log 33 | 34 | # local env variables 35 | .env 36 | 37 | # truffle build directory 38 | /build 39 | 40 | # Hardhat files 41 | /artifacts 42 | /cache 43 | 44 | # macOS 45 | .DS_Store 46 | 47 | # truffle 48 | .node-xmlhttprequest-* 49 | 50 | # npm package 51 | package-lock.json 52 | 53 | # IntelliJ IDE 54 | .idea 55 | -------------------------------------------------------------------------------- /contracts/token/BEP20/StandardBEP20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "./lib/BEP20.sol"; 6 | 7 | import "../../service/ServicePayer.sol"; 8 | 9 | /** 10 | * @title StandardBEP20 11 | * @dev Implementation of the StandardBEP20 12 | */ 13 | contract StandardBEP20 is BEP20, ServicePayer { 14 | 15 | constructor ( 16 | string memory name, 17 | string memory symbol, 18 | uint8 decimals, 19 | uint256 initialBalance, 20 | address payable feeReceiver 21 | ) 22 | BEP20(name, symbol) 23 | ServicePayer(feeReceiver, "StandardBEP20") 24 | payable 25 | { 26 | require(initialBalance > 0, "StandardBEP20: supply cannot be zero"); 27 | 28 | _setupDecimals(decimals); 29 | _mint(_msgSender(), initialBalance); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /contracts/token/BEP20/BurnableBEP20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "./lib/BEP20Burnable.sol"; 6 | 7 | import "../../service/ServicePayer.sol"; 8 | 9 | /** 10 | * @title BurnableBEP20 11 | * @dev Implementation of the BurnableBEP20 12 | */ 13 | contract BurnableBEP20 is BEP20Burnable, ServicePayer { 14 | 15 | constructor ( 16 | string memory name, 17 | string memory symbol, 18 | uint8 decimals, 19 | uint256 initialBalance, 20 | address payable feeReceiver 21 | ) 22 | BEP20(name, symbol) 23 | ServicePayer(feeReceiver, "BurnableBEP20") 24 | payable 25 | { 26 | require(initialBalance > 0, "BurnableBEP20: supply cannot be zero"); 27 | 28 | _setupDecimals(decimals); 29 | _mint(_msgSender(), initialBalance); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | require('chai/register-should'); 2 | 3 | const solcStable = { 4 | version: '0.7.6', 5 | settings: { 6 | optimizer: { 7 | enabled: true, 8 | runs: 200, 9 | }, 10 | }, 11 | }; 12 | 13 | const solcNightly = { 14 | version: 'nightly', 15 | docker: true, 16 | }; 17 | 18 | const useSolcNightly = process.env.SOLC_NIGHTLY === 'true'; 19 | 20 | module.exports = { 21 | networks: { 22 | development: { 23 | host: 'localhost', 24 | port: 8545, 25 | network_id: '*', // eslint-disable-line camelcase 26 | }, 27 | coverage: { 28 | host: 'localhost', 29 | network_id: '*', // eslint-disable-line camelcase 30 | port: 8555, 31 | gas: 0xfffffffffff, 32 | gasPrice: 0x01, 33 | }, 34 | }, 35 | compilers: { 36 | solc: useSolcNightly ? solcNightly : solcStable, 37 | }, 38 | plugins: ['solidity-coverage'], 39 | }; 40 | -------------------------------------------------------------------------------- /contracts/utils/GeneratorCopyright.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | /** 6 | * @title GeneratorCopyright 7 | * @author BEP20 Generator (https://hikecoder.github.io/bep20-generator) 8 | * @dev Implementation of the GeneratorCopyright 9 | */ 10 | contract GeneratorCopyright { 11 | 12 | string private constant _GENERATOR = "https://hikecoder.github.io/bep20-generator"; 13 | string private _version; 14 | 15 | constructor (string memory version_) { 16 | _version = version_; 17 | } 18 | 19 | /** 20 | * @dev Returns the token generator tool. 21 | */ 22 | function generator() public pure returns (string memory) { 23 | return _GENERATOR; 24 | } 25 | 26 | /** 27 | * @dev Returns the token generator version. 28 | */ 29 | function version() public view returns (string memory) { 30 | return _version; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /contracts/token/BEP20/SimpleBEP20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "./lib/BEP20.sol"; 6 | 7 | import "../../service/ServicePayer.sol"; 8 | import "../../utils/GeneratorCopyright.sol"; 9 | 10 | /** 11 | * @title SimpleBEP20 12 | * @author BEP20 Generator (https://hikecoder.github.io/bep20-generator) 13 | * @dev Implementation of the SimpleBEP20 14 | */ 15 | contract SimpleBEP20 is BEP20, ServicePayer, GeneratorCopyright("v1.0.0") { 16 | 17 | constructor ( 18 | string memory name, 19 | string memory symbol, 20 | uint256 initialBalance, 21 | address payable feeReceiver 22 | ) 23 | BEP20(name, symbol) 24 | ServicePayer(feeReceiver, "SimpleBEP20") 25 | payable 26 | { 27 | require(initialBalance > 0, "SimpleBEP20: supply cannot be zero"); 28 | 29 | _mint(_msgSender(), initialBalance); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Vittorio Minacori (https://github.com/hikecoder) 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 | -------------------------------------------------------------------------------- /test/token/BEP20/behaviours/BEP20Capped.behaviour.js: -------------------------------------------------------------------------------- 1 | const { expectRevert } = require('@openzeppelin/test-helpers'); 2 | 3 | const { expect } = require('chai'); 4 | 5 | function shouldBehaveLikeBEP20Capped (cap, [minter, other]) { 6 | describe('totalSupply', function () { 7 | const from = minter; 8 | 9 | it('should start with the correct cap', async function () { 10 | expect(await this.token.cap()).to.be.bignumber.equal(cap); 11 | }); 12 | 13 | it('should mint when amount is less than cap', async function () { 14 | await this.token.mint(other, cap.subn(1), { from }); 15 | expect(await this.token.totalSupply()).to.be.bignumber.equal(cap.subn(1)); 16 | }); 17 | 18 | it('should fail to mint if the amount exceeds the cap', async function () { 19 | await this.token.mint(other, cap.subn(1), { from }); 20 | await expectRevert(this.token.mint(other, 2, { from }), 'BEP20Capped: cap exceeded'); 21 | }); 22 | 23 | it('should fail to mint after cap is reached', async function () { 24 | await this.token.mint(other, cap, { from }); 25 | await expectRevert(this.token.mint(other, 1, { from }), 'BEP20Capped: cap exceeded'); 26 | }); 27 | }); 28 | } 29 | 30 | module.exports = { 31 | shouldBehaveLikeBEP20Capped, 32 | }; 33 | -------------------------------------------------------------------------------- /contracts/service/ServiceReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | 7 | /** 8 | * @title ServiceReceiver 9 | * @dev Implementation of the ServiceReceiver 10 | */ 11 | contract ServiceReceiver is Ownable { 12 | 13 | mapping (bytes32 => uint256) private _prices; 14 | 15 | event Created(string serviceName, address indexed serviceAddress); 16 | 17 | function pay(string memory serviceName) public payable { 18 | require(msg.value == _prices[_toBytes32(serviceName)], "ServiceReceiver: incorrect price"); 19 | 20 | emit Created(serviceName, _msgSender()); 21 | } 22 | 23 | function getPrice(string memory serviceName) public view returns (uint256) { 24 | return _prices[_toBytes32(serviceName)]; 25 | } 26 | 27 | function setPrice(string memory serviceName, uint256 amount) public onlyOwner { 28 | _prices[_toBytes32(serviceName)] = amount; 29 | } 30 | 31 | function withdraw(uint256 amount) public onlyOwner { 32 | payable(owner()).transfer(amount); 33 | } 34 | 35 | function _toBytes32(string memory serviceName) private pure returns (bytes32) { 36 | return keccak256(abi.encode(serviceName)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/token/BEP20/lib/BEP20Capped.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "./BEP20.sol"; 6 | 7 | /** 8 | * @dev Extension of {BEP20} that adds a cap to the supply of tokens. 9 | */ 10 | abstract contract BEP20Capped is BEP20 { 11 | using SafeMath for uint256; 12 | 13 | uint256 private _cap; 14 | 15 | /** 16 | * @dev Sets the value of the `cap`. This value is immutable, it can only be 17 | * set once during construction. 18 | */ 19 | constructor (uint256 cap_) { 20 | require(cap_ > 0, "BEP20Capped: cap is 0"); 21 | _cap = cap_; 22 | } 23 | 24 | /** 25 | * @dev Returns the cap on the token's total supply. 26 | */ 27 | function cap() public view returns (uint256) { 28 | return _cap; 29 | } 30 | 31 | /** 32 | * @dev See {BEP20-_beforeTokenTransfer}. 33 | * 34 | * Requirements: 35 | * 36 | * - minted tokens must not cause the total supply to go over the cap. 37 | */ 38 | function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual override { 39 | super._beforeTokenTransfer(from, to, amount); 40 | 41 | if (from == address(0)) { // When minting tokens 42 | require(totalSupply().add(amount) <= _cap, "BEP20Capped: cap exceeded"); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /contracts/token/BEP20/lib/BEP20Burnable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "@openzeppelin/contracts/GSN/Context.sol"; 6 | 7 | import "./BEP20.sol"; 8 | 9 | /** 10 | * @dev Extension of {BEP20} that allows token holders to destroy both their own 11 | * tokens and those that they have an allowance for, in a way that can be 12 | * recognized off-chain (via event analysis). 13 | */ 14 | abstract contract BEP20Burnable is Context, BEP20 { 15 | using SafeMath for uint256; 16 | 17 | /** 18 | * @dev Destroys `amount` tokens from the caller. 19 | * 20 | * See {BEP20-_burn}. 21 | */ 22 | function burn(uint256 amount) public virtual { 23 | _burn(_msgSender(), amount); 24 | } 25 | 26 | /** 27 | * @dev Destroys `amount` tokens from `account`, deducting from the caller's 28 | * allowance. 29 | * 30 | * See {BEP20-_burn} and {BEP20-allowance}. 31 | * 32 | * Requirements: 33 | * 34 | * - the caller must have allowance for ``accounts``'s tokens of at least 35 | * `amount`. 36 | */ 37 | function burnFrom(address account, uint256 amount) public virtual { 38 | uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "BEP20: burn amount exceeds allowance"); 39 | 40 | _approve(account, _msgSender(), decreasedAllowance); 41 | _burn(account, amount); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /analysis/description-table/ServiceReceiver.md: -------------------------------------------------------------------------------- 1 | ## Sūrya's Description Report 2 | 3 | ### Files Description Table 4 | 5 | 6 | | File Name | SHA-1 Hash | 7 | |-------------|--------------| 8 | | dist/ServiceReceiver.dist.sol | c82d86212412d090a68f0d78b2f291b5adb29e30 | 9 | 10 | 11 | ### Contracts Description Table 12 | 13 | 14 | | Contract | Type | Bases | | | 15 | |:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| 16 | | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | 17 | |||||| 18 | | **Context** | Implementation | ||| 19 | | └ | _msgSender | Internal 🔒 | | | 20 | | └ | _msgData | Internal 🔒 | | | 21 | |||||| 22 | | **Ownable** | Implementation | Context ||| 23 | | └ | | Public ❗️ | 🛑 |NO❗️ | 24 | | └ | owner | Public ❗️ | |NO❗️ | 25 | | └ | renounceOwnership | Public ❗️ | 🛑 | onlyOwner | 26 | | └ | transferOwnership | Public ❗️ | 🛑 | onlyOwner | 27 | |||||| 28 | | **ServiceReceiver** | Implementation | Ownable ||| 29 | | └ | pay | Public ❗️ | 💵 |NO❗️ | 30 | | └ | getPrice | Public ❗️ | |NO❗️ | 31 | | └ | setPrice | Public ❗️ | 🛑 | onlyOwner | 32 | | └ | withdraw | Public ❗️ | 🛑 | onlyOwner | 33 | | └ | _toBytes32 | Private 🔐 | | | 34 | 35 | 36 | ### Legend 37 | 38 | | Symbol | Meaning | 39 | |:--------:|-----------| 40 | | 🛑 | Function can modify state | 41 | | 💵 | Function is payable | 42 | -------------------------------------------------------------------------------- /web-console/js/app.js: -------------------------------------------------------------------------------- 1 | /* global Web3, TruffleContract */ 2 | 3 | const App = { 4 | web3Provider: null, 5 | contracts: [], 6 | sc: {}, 7 | load: function (contracts) { 8 | this.contracts = contracts; 9 | return this.initWeb3(); 10 | }, 11 | initWeb3: async function () { 12 | if (typeof window.ethereum !== 'undefined') { 13 | console.log('injected web3'); 14 | this.web3Provider = window.ethereum; 15 | await this.web3Provider.enable(); 16 | } else if (typeof window.web3 !== 'undefined') { 17 | console.log('injected web3 (legacy'); 18 | this.web3Provider = window.web3.currentProvider; 19 | } else { 20 | console.log('provided web3'); 21 | this.web3Provider = new Web3.providers.HttpProvider('https://127.0.0.1:9545'); 22 | window.web3 = new Web3(this.web3Provider); 23 | } 24 | 25 | return this.initContracts(); 26 | }, 27 | initContracts: function () { 28 | this.contracts.forEach((contractName) => { 29 | this.getContract(contractName); 30 | }); 31 | }, 32 | getContract: async function (contractName) { 33 | fetch(`${contractName}.json`) 34 | .then((response) => response.json()) 35 | .then((contract) => { 36 | this.sc[contractName] = TruffleContract(contract); 37 | this.sc[contractName].setProvider(this.web3Provider); 38 | }) 39 | .catch(error => console.log(error)); 40 | }, 41 | }; 42 | 43 | App.load(['SimpleBEP20', 'StandardBEP20', 'ServiceReceiver']); 44 | -------------------------------------------------------------------------------- /contracts/token/BEP20/MintableBEP20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "./lib/BEP20Mintable.sol"; 6 | 7 | import "../../service/ServicePayer.sol"; 8 | 9 | /** 10 | * @title MintableBEP20 11 | * @dev Implementation of the MintableBEP20 12 | */ 13 | contract MintableBEP20 is BEP20Mintable, ServicePayer { 14 | 15 | constructor ( 16 | string memory name, 17 | string memory symbol, 18 | uint8 decimals, 19 | uint256 initialBalance, 20 | address payable feeReceiver 21 | ) 22 | BEP20(name, symbol) 23 | ServicePayer(feeReceiver, "MintableBEP20") 24 | payable 25 | { 26 | _setupDecimals(decimals); 27 | _mint(_msgSender(), initialBalance); 28 | } 29 | 30 | /** 31 | * @dev Function to mint tokens. 32 | * 33 | * NOTE: restricting access to owner only. See {BEP20Mintable-mint}. 34 | * 35 | * @param account The address that will receive the minted tokens 36 | * @param amount The amount of tokens to mint 37 | */ 38 | function _mint(address account, uint256 amount) internal override onlyOwner { 39 | super._mint(account, amount); 40 | } 41 | 42 | /** 43 | * @dev Function to stop minting new tokens. 44 | * 45 | * NOTE: restricting access to owner only. See {BEP20Mintable-finishMinting}. 46 | */ 47 | function _finishMinting() internal override onlyOwner { 48 | super._finishMinting(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /test/access/Ownable.behavior.js: -------------------------------------------------------------------------------- 1 | const { constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); 2 | const { ZERO_ADDRESS } = constants; 3 | 4 | function shouldBehaveLikeOwnable (owner, [other]) { 5 | describe('as an ownable', function () { 6 | it('should have an owner', async function () { 7 | expect(await this.ownable.owner()).to.equal(owner); 8 | }); 9 | 10 | it('changes owner after transfer', async function () { 11 | const receipt = await this.ownable.transferOwnership(other, { from: owner }); 12 | expectEvent(receipt, 'OwnershipTransferred'); 13 | 14 | expect(await this.ownable.owner()).to.equal(other); 15 | }); 16 | 17 | it('should prevent non-owners from transferring', async function () { 18 | await expectRevert( 19 | this.ownable.transferOwnership(other, { from: other }), 20 | 'Ownable: caller is not the owner', 21 | ); 22 | }); 23 | 24 | it('should guard access against stuck state', async function () { 25 | await expectRevert( 26 | this.ownable.transferOwnership(ZERO_ADDRESS, { from: owner }), 27 | 'Ownable: new owner is the zero address', 28 | ); 29 | }); 30 | 31 | it('loses owner after renouncement', async function () { 32 | const receipt = await this.ownable.renounceOwnership({ from: owner }); 33 | expectEvent(receipt, 'OwnershipTransferred'); 34 | 35 | expect(await this.ownable.owner()).to.equal(ZERO_ADDRESS); 36 | }); 37 | 38 | it('should prevent non-owners from renouncement', async function () { 39 | await expectRevert( 40 | this.ownable.renounceOwnership({ from: other }), 41 | 'Ownable: caller is not the owner', 42 | ); 43 | }); 44 | }); 45 | } 46 | 47 | module.exports = { 48 | shouldBehaveLikeOwnable, 49 | }; 50 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends" : [ 3 | "standard", 4 | "plugin:promise/recommended", 5 | ], 6 | "plugins": [ 7 | "mocha-no-only", 8 | "promise", 9 | ], 10 | "env": { 11 | "browser" : true, 12 | "node" : true, 13 | "mocha" : true, 14 | "jest" : true, 15 | }, 16 | "globals" : { 17 | "artifacts": false, 18 | "contract": false, 19 | "assert": false, 20 | "web3": false, 21 | }, 22 | "rules": { 23 | 24 | // Strict mode 25 | "strict": ["error", "global"], 26 | 27 | // Code style 28 | "array-bracket-spacing": ["off"], 29 | "camelcase": ["error", {"properties": "always"}], 30 | "comma-dangle": ["error", "always-multiline"], 31 | "comma-spacing": ["error", {"before": false, "after": true}], 32 | "dot-notation": ["error", {"allowKeywords": true, "allowPattern": ""}], 33 | "eol-last": ["error", "always"], 34 | "eqeqeq": ["error", "smart"], 35 | "generator-star-spacing": ["error", "before"], 36 | "indent": ["error", 2], 37 | "linebreak-style": ["error", "unix"], 38 | "max-len": ["error", 120, 2], 39 | "no-debugger": "off", 40 | "no-dupe-args": "error", 41 | "no-dupe-keys": "error", 42 | "no-mixed-spaces-and-tabs": ["error", "smart-tabs"], 43 | "no-redeclare": ["error", {"builtinGlobals": true}], 44 | "no-trailing-spaces": ["error", { "skipBlankLines": false }], 45 | "no-undef": "error", 46 | "no-use-before-define": "off", 47 | "no-var": "error", 48 | "object-curly-spacing": ["error", "always"], 49 | "prefer-const": "error", 50 | "quotes": ["error", "single"], 51 | "semi": ["error", "always"], 52 | "space-before-function-paren": ["error", "always"], 53 | 54 | "mocha-no-only/mocha-no-only": ["error"], 55 | 56 | "promise/always-return": "off", 57 | "promise/avoid-new": "off", 58 | }, 59 | "parserOptions": { 60 | "ecmaVersion": 2018 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /contracts/token/BEP20/lib/BEP20Mintable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "../lib/BEP20.sol"; 6 | 7 | /** 8 | * @title BEP20Mintable 9 | * @dev Implementation of the BEP20Mintable. Extension of {BEP20} that adds a minting behaviour. 10 | */ 11 | abstract contract BEP20Mintable is BEP20 { 12 | 13 | // indicates if minting is finished 14 | bool private _mintingFinished = false; 15 | 16 | /** 17 | * @dev Emitted during finish minting 18 | */ 19 | event MintFinished(); 20 | 21 | /** 22 | * @dev Tokens can be minted only before minting finished. 23 | */ 24 | modifier canMint() { 25 | require(!_mintingFinished, "BEP20Mintable: minting is finished"); 26 | _; 27 | } 28 | 29 | /** 30 | * @return if minting is finished or not. 31 | */ 32 | function mintingFinished() public view returns (bool) { 33 | return _mintingFinished; 34 | } 35 | 36 | /** 37 | * @dev Function to mint tokens. 38 | * 39 | * WARNING: it allows everyone to mint new tokens. Access controls MUST be defined in derived contracts. 40 | * 41 | * @param account The address that will receive the minted tokens 42 | * @param amount The amount of tokens to mint 43 | */ 44 | function mint(address account, uint256 amount) public canMint { 45 | _mint(account, amount); 46 | } 47 | 48 | /** 49 | * @dev Function to stop minting new tokens. 50 | * 51 | * WARNING: it allows everyone to finish minting. Access controls MUST be defined in derived contracts. 52 | */ 53 | function finishMinting() public canMint { 54 | _finishMinting(); 55 | } 56 | 57 | /** 58 | * @dev Function to stop minting new tokens. 59 | */ 60 | function _finishMinting() internal virtual { 61 | _mintingFinished = true; 62 | 63 | emit MintFinished(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /contracts/token/BEP20/CommonBEP20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | import "./lib/BEP20Capped.sol"; 7 | import "./lib/BEP20Mintable.sol"; 8 | import "./lib/BEP20Burnable.sol"; 9 | 10 | import "../../service/ServicePayer.sol"; 11 | 12 | /** 13 | * @title CommonBEP20 14 | * @dev Implementation of the CommonBEP20 15 | */ 16 | contract CommonBEP20 is BEP20Capped, BEP20Mintable, BEP20Burnable, ServicePayer { 17 | 18 | constructor ( 19 | string memory name, 20 | string memory symbol, 21 | uint8 decimals, 22 | uint256 cap, 23 | uint256 initialBalance, 24 | address payable feeReceiver 25 | ) 26 | BEP20(name, symbol) 27 | BEP20Capped(cap) 28 | ServicePayer(feeReceiver, "CommonBEP20") 29 | payable 30 | { 31 | _setupDecimals(decimals); 32 | _mint(_msgSender(), initialBalance); 33 | } 34 | 35 | /** 36 | * @dev Function to mint tokens. 37 | * 38 | * NOTE: restricting access to owner only. See {BEP20Mintable-mint}. 39 | * 40 | * @param account The address that will receive the minted tokens 41 | * @param amount The amount of tokens to mint 42 | */ 43 | function _mint(address account, uint256 amount) internal override onlyOwner { 44 | super._mint(account, amount); 45 | } 46 | 47 | /** 48 | * @dev Function to stop minting new tokens. 49 | * 50 | * NOTE: restricting access to owner only. See {BEP20Mintable-finishMinting}. 51 | */ 52 | function _finishMinting() internal override onlyOwner { 53 | super._finishMinting(); 54 | } 55 | 56 | /** 57 | * @dev See {BEP20-_beforeTokenTransfer}. See {BEP20Capped-_beforeTokenTransfer}. 58 | */ 59 | function _beforeTokenTransfer(address from, address to, uint256 amount) internal override(BEP20, BEP20Capped) { 60 | super._beforeTokenTransfer(from, to, amount); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: {} 5 | pull_request: {} 6 | release: 7 | types: 8 | - created 9 | 10 | jobs: 11 | lint: 12 | name: Lint 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Setup Code 16 | uses: actions/checkout@v2 17 | 18 | - name: Setup Node 19 | uses: actions/setup-node@v1 20 | with: 21 | node-version: '14.x' 22 | 23 | - name: Install dependencies 24 | run: npm install 25 | 26 | - name: Linter 27 | run: npm run lint 28 | 29 | test: 30 | name: Test 31 | runs-on: ubuntu-latest 32 | steps: 33 | - name: Setup Code 34 | uses: actions/checkout@v2 35 | 36 | - name: Setup Node 37 | uses: actions/setup-node@v1 38 | with: 39 | node-version: '14.x' 40 | 41 | - name: Install dependencies 42 | run: npm install 43 | 44 | - name: Test 45 | run: npm test 46 | 47 | coverage: 48 | name: Coverage 49 | runs-on: ubuntu-latest 50 | steps: 51 | - name: Setup Code 52 | uses: actions/checkout@v2 53 | 54 | - name: Setup Node 55 | uses: actions/setup-node@v1 56 | with: 57 | node-version: '14.x' 58 | 59 | - name: Install dependencies 60 | run: npm install 61 | 62 | - name: Coverage 63 | run: npm run coverage 64 | 65 | - name: Post to Coveralls 66 | uses: coverallsapp/github-action@master 67 | with: 68 | github-token: ${{ secrets.GITHUB_TOKEN }} 69 | 70 | nightly_build: 71 | name: Nightly Build 72 | runs-on: ubuntu-latest 73 | 74 | steps: 75 | - name: Setup Code 76 | uses: actions/checkout@v2 77 | 78 | - name: Setup Node 79 | uses: actions/setup-node@v1 80 | with: 81 | node-version: '14.x' 82 | 83 | - name: Install dependencies 84 | run: npm install 85 | 86 | - name: Nightly Build 87 | run: npm run truffle:test 88 | env: 89 | SOLC_NIGHTLY: true 90 | -------------------------------------------------------------------------------- /test/service/ServicePayer.test.js: -------------------------------------------------------------------------------- 1 | const { balance, BN, constants, ether, expectRevert } = require('@openzeppelin/test-helpers'); 2 | const { ZERO_ADDRESS } = constants; 3 | 4 | const { expect } = require('chai'); 5 | 6 | const ServicePayer = artifacts.require('ServicePayerMock'); 7 | const ServiceReceiver = artifacts.require('ServiceReceiver'); 8 | 9 | contract('ServicePayer', function ([owner, thirdParty]) { 10 | const fee = ether('0.1'); 11 | 12 | context('ServicePayer behaviours', function () { 13 | beforeEach(async function () { 14 | this.serviceReceiver = await ServiceReceiver.new({ from: owner }); 15 | 16 | await this.serviceReceiver.setPrice('ServicePayerMock', fee); 17 | }); 18 | 19 | it('requires a non-zero fee', async function () { 20 | await expectRevert( 21 | ServicePayer.new( 22 | this.serviceReceiver.address, 23 | { 24 | from: owner, 25 | value: new BN(0), 26 | }, 27 | ), 28 | 'ServiceReceiver: incorrect price', 29 | ); 30 | }); 31 | 32 | it('requires a ServiceReceiver', async function () { 33 | await expectRevert.unspecified( 34 | ServicePayer.new(ZERO_ADDRESS, { from: owner, value: fee }), 35 | ); 36 | 37 | await expectRevert.unspecified( 38 | ServicePayer.new(thirdParty, { from: owner, value: fee }), 39 | ); 40 | }); 41 | 42 | it('fail with incorrect price', async function () { 43 | await expectRevert( 44 | ServicePayer.new( 45 | this.serviceReceiver.address, 46 | { 47 | from: owner, 48 | value: fee.add(ether('1')), 49 | }, 50 | ), 51 | 'ServiceReceiver: incorrect price', 52 | ); 53 | }); 54 | 55 | it('transfer fee to receiver', async function () { 56 | const initBalance = await balance.current(this.serviceReceiver.address); 57 | 58 | await ServicePayer.new( 59 | this.serviceReceiver.address, 60 | { 61 | from: owner, 62 | value: fee, 63 | }, 64 | ); 65 | 66 | const newBalance = (await balance.current(this.serviceReceiver.address)); 67 | 68 | expect(newBalance).to.be.bignumber.equal(initBalance.add(fee)); 69 | }); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Exit script as soon as a command fails. 4 | set -o errexit 5 | 6 | # Executes cleanup function at script exit. 7 | trap cleanup EXIT 8 | 9 | cleanup() { 10 | # Kill the ganache instance that we started (if we started one and if it's still running). 11 | if [ -n "$ganache_pid" ] && ps -p $ganache_pid > /dev/null; then 12 | kill -9 $ganache_pid 13 | fi 14 | } 15 | 16 | ganache_port=8545 17 | 18 | ganache_running() { 19 | nc -z localhost "$ganache_port" 20 | } 21 | 22 | start_ganache() { 23 | # We define 10 accounts with balance 1M ether, needed for high-value tests. 24 | local accounts=( 25 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501200,1000000000000000000000000" 26 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501201,1000000000000000000000000" 27 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501202,1000000000000000000000000" 28 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501203,1000000000000000000000000" 29 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501204,1000000000000000000000000" 30 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501205,1000000000000000000000000" 31 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501206,1000000000000000000000000" 32 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501207,1000000000000000000000000" 33 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501208,1000000000000000000000000" 34 | --account="0x2bdd21761a483f71054e14f5b827213567971c676928d9a1808cbfa4b7501209,1000000000000000000000000" 35 | ) 36 | 37 | npx ganache-cli --gasLimit 0xfffffffffff --port "$ganache_port" "${accounts[@]}" > /dev/null & 38 | 39 | ganache_pid=$! 40 | 41 | echo "Waiting for ganache to launch on port "$ganache_port"..." 42 | 43 | while ! ganache_running; do 44 | sleep 0.1 # wait for 1/10 of the second before check again 45 | done 46 | 47 | echo "Ganache launched!" 48 | } 49 | 50 | if ganache_running; then 51 | echo "Using existing ganache instance" 52 | else 53 | echo "Starting our own ganache instance" 54 | start_ganache 55 | fi 56 | 57 | npx truffle version 58 | 59 | npx truffle test "$@" 60 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bep20-generator", 3 | "version": "1.0.0", 4 | "description": "The new Smart Contract Generator for BEP20 Token.", 5 | "files": [ 6 | "contracts", 7 | "test" 8 | ], 9 | "scripts": { 10 | "dev": "lite-server", 11 | "truffle:console": "truffle develop", 12 | "truffle:compile": "scripts/compile.sh", 13 | "truffle:test": "npm run truffle:compile && scripts/test.sh", 14 | "hardhat:console": "hardhat console", 15 | "hardhat:compile": "hardhat compile", 16 | "hardhat:test": "hardhat test", 17 | "hardhat:coverage": "hardhat coverage --network coverage", 18 | "test": "npm run hardhat:test", 19 | "coverage": "scripts/coverage.sh", 20 | "clean": "rm -rf coverage", 21 | "profile": "npm run clean && npm run coverage && open coverage/index.html", 22 | "lint": "npm run lint:js && npm run lint:sol", 23 | "lint:fix": "npm run lint:js:fix", 24 | "lint:js": "eslint .", 25 | "lint:js:fix": "eslint . --fix", 26 | "lint:sol": "solhint --max-warnings 0 \"contracts/**/*.sol\"", 27 | "flat": "scripts/flat.sh", 28 | "analyze": "scripts/analyze.sh" 29 | }, 30 | "keywords": [ 31 | "solidity", 32 | "bsc", 33 | "smart", 34 | "contracts", 35 | "token", 36 | "bep20" 37 | ], 38 | "repository": { 39 | "type": "git", 40 | "url": "https://github.com/hikecoder/bep20-generator.git" 41 | }, 42 | "homepage": "https://hikecoder.github.io/bep20-generator", 43 | "bugs": { 44 | "url": "https://github.com/hikecoder/bep20-generator/issues" 45 | }, 46 | "author": "Vittorio Minacori", 47 | "contributors": [ 48 | { 49 | "name": "Vittorio Minacori", 50 | "url": "https://github.com/hikecoder" 51 | } 52 | ], 53 | "license": "MIT", 54 | "devDependencies": { 55 | "@nomiclabs/hardhat-ganache": "^2.0.0", 56 | "@nomiclabs/hardhat-truffle5": "^2.0.0", 57 | "@nomiclabs/hardhat-web3": "^2.0.0", 58 | "@openzeppelin/test-helpers": "^0.5.9", 59 | "chai": "^4.2.0", 60 | "eslint": "^7.16.0", 61 | "eslint-config-standard": "^16.0.2", 62 | "eslint-plugin-import": "^2.22.1", 63 | "eslint-plugin-mocha-no-only": "^1.1.1", 64 | "eslint-plugin-node": "^11.1.0", 65 | "eslint-plugin-promise": "^4.2.1", 66 | "eslint-plugin-standard": "^5.0.0", 67 | "ganache-cli": "^6.12.1", 68 | "hardhat": "^2.0.6", 69 | "lite-server": "^2.6.1", 70 | "sol2uml": "^1.1.21", 71 | "solhint": "^3.3.2", 72 | "solidity-coverage": "^0.7.13", 73 | "surya": "^0.4.1", 74 | "truffle": "5.1.49", 75 | "truffle-flattener": "^1.5.0" 76 | }, 77 | "dependencies": { 78 | "@openzeppelin/contracts": "3.3.0-solc-0.7" 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /test/token/BEP20/StandardBEP20.test.js: -------------------------------------------------------------------------------- 1 | const { BN, ether, expectRevert } = require('@openzeppelin/test-helpers'); 2 | 3 | const { shouldBehaveLikeBEP20 } = require('./behaviours/BEP20.behaviour'); 4 | 5 | const StandardBEP20 = artifacts.require('StandardBEP20'); 6 | const ServiceReceiver = artifacts.require('ServiceReceiver'); 7 | 8 | contract('StandardBEP20', function ([owner, other, thirdParty]) { 9 | const _name = 'StandardBEP20'; 10 | const _symbol = 'BEP20'; 11 | const _decimals = new BN(8); 12 | const _initialSupply = new BN(100000000); 13 | 14 | const fee = ether('0.1'); 15 | 16 | beforeEach(async function () { 17 | this.serviceReceiver = await ServiceReceiver.new({ from: owner }); 18 | await this.serviceReceiver.setPrice('StandardBEP20', fee); 19 | }); 20 | 21 | context('creating valid token', function () { 22 | describe('as a StandardBEP20', function () { 23 | describe('without initial supply', function () { 24 | it('should fail', async function () { 25 | await expectRevert( 26 | StandardBEP20.new( 27 | _name, 28 | _symbol, 29 | _decimals, 30 | 0, 31 | this.serviceReceiver.address, 32 | { 33 | from: owner, 34 | value: fee, 35 | }, 36 | ), 37 | 'StandardBEP20: supply cannot be zero', 38 | ); 39 | }); 40 | }); 41 | 42 | describe('with initial supply', function () { 43 | beforeEach(async function () { 44 | this.token = await StandardBEP20.new( 45 | _name, 46 | _symbol, 47 | _decimals, 48 | _initialSupply, 49 | this.serviceReceiver.address, 50 | { 51 | from: owner, 52 | value: fee, 53 | }, 54 | ); 55 | }); 56 | 57 | describe('once deployed', function () { 58 | it('total supply should be equal to initial supply', async function () { 59 | (await this.token.totalSupply()).should.be.bignumber.equal(_initialSupply); 60 | }); 61 | 62 | it('owner balance should be equal to initial supply', async function () { 63 | (await this.token.balanceOf(owner)).should.be.bignumber.equal(_initialSupply); 64 | }); 65 | }); 66 | }); 67 | }); 68 | }); 69 | 70 | context('StandardBEP20 token behaviours', function () { 71 | beforeEach(async function () { 72 | this.token = await StandardBEP20.new( 73 | _name, 74 | _symbol, 75 | _decimals, 76 | _initialSupply, 77 | this.serviceReceiver.address, 78 | { 79 | from: owner, 80 | value: fee, 81 | }, 82 | ); 83 | }); 84 | 85 | context('like a BEP20', function () { 86 | shouldBehaveLikeBEP20(_name, _symbol, _decimals, _initialSupply, [owner, other, thirdParty]); 87 | }); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /test/token/BEP20/SimpleBEP20.test.js: -------------------------------------------------------------------------------- 1 | const { BN, expectRevert } = require('@openzeppelin/test-helpers'); 2 | 3 | const { shouldBehaveLikeBEP20 } = require('./behaviours/BEP20.behaviour'); 4 | 5 | const { shouldBehaveLikeGeneratorCopyright } = require('../../utils/GeneratorCopyright.behaviour'); 6 | 7 | const SimpleBEP20 = artifacts.require('SimpleBEP20'); 8 | const ServiceReceiver = artifacts.require('ServiceReceiver'); 9 | 10 | contract('SimpleBEP20', function ([owner, other, thirdParty]) { 11 | const _name = 'SimpleBEP20'; 12 | const _symbol = 'BEP20'; 13 | const _decimals = new BN(18); 14 | const _initialSupply = new BN(100000000); 15 | 16 | const fee = 0; 17 | 18 | const version = 'v1.0.0'; 19 | 20 | beforeEach(async function () { 21 | this.serviceReceiver = await ServiceReceiver.new({ from: owner }); 22 | // not to set any price means it doesn't require any fee 23 | // await this.serviceReceiver.setPrice('SimpleBEP20', fee); 24 | }); 25 | 26 | context('creating valid token', function () { 27 | describe('without initial supply', function () { 28 | it('should fail', async function () { 29 | await expectRevert( 30 | SimpleBEP20.new( 31 | _name, 32 | _symbol, 33 | 0, 34 | this.serviceReceiver.address, 35 | { 36 | from: owner, 37 | value: fee, 38 | }, 39 | ), 40 | 'SimpleBEP20: supply cannot be zero', 41 | ); 42 | }); 43 | }); 44 | 45 | describe('with initial supply', function () { 46 | beforeEach(async function () { 47 | this.token = await SimpleBEP20.new( 48 | _name, 49 | _symbol, 50 | _initialSupply, 51 | this.serviceReceiver.address, 52 | { 53 | from: owner, 54 | value: fee, 55 | }, 56 | ); 57 | }); 58 | 59 | describe('once deployed', function () { 60 | it('total supply should be equal to initial supply', async function () { 61 | (await this.token.totalSupply()).should.be.bignumber.equal(_initialSupply); 62 | }); 63 | 64 | it('owner balance should be equal to initial supply', async function () { 65 | (await this.token.balanceOf(owner)).should.be.bignumber.equal(_initialSupply); 66 | }); 67 | }); 68 | }); 69 | }); 70 | 71 | context('SimpleBEP20 token behaviours', function () { 72 | beforeEach(async function () { 73 | this.token = await SimpleBEP20.new( 74 | _name, 75 | _symbol, 76 | _initialSupply, 77 | this.serviceReceiver.address, 78 | { 79 | from: owner, 80 | value: fee, 81 | }, 82 | ); 83 | }); 84 | 85 | context('like a BEP20', function () { 86 | shouldBehaveLikeBEP20(_name, _symbol, _decimals, _initialSupply, [owner, other, thirdParty]); 87 | }); 88 | 89 | context('like a GeneratorCopyright', function () { 90 | beforeEach(async function () { 91 | this.instance = this.token; 92 | }); 93 | 94 | shouldBehaveLikeGeneratorCopyright(version); 95 | }); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /test/token/BEP20/BurnableBEP20.test.js: -------------------------------------------------------------------------------- 1 | const { BN, ether, expectRevert } = require('@openzeppelin/test-helpers'); 2 | 3 | const { shouldBehaveLikeBEP20 } = require('./behaviours/BEP20.behaviour'); 4 | const { shouldBehaveLikeBEP20Burnable } = require('./behaviours/BEP20Burnable.behaviour'); 5 | 6 | const BurnableBEP20 = artifacts.require('BurnableBEP20'); 7 | const ServiceReceiver = artifacts.require('ServiceReceiver'); 8 | 9 | contract('BurnableBEP20', function ([owner, other, thirdParty]) { 10 | const _name = 'BurnableBEP20'; 11 | const _symbol = 'BEP20'; 12 | const _decimals = new BN(8); 13 | const _initialSupply = new BN(100000000); 14 | 15 | const fee = ether('0.1'); 16 | 17 | beforeEach(async function () { 18 | this.serviceReceiver = await ServiceReceiver.new({ from: owner }); 19 | await this.serviceReceiver.setPrice('BurnableBEP20', fee); 20 | }); 21 | 22 | context('creating valid token', function () { 23 | describe('as a BurnableBEP20', function () { 24 | describe('without initial supply', function () { 25 | it('should fail', async function () { 26 | await expectRevert( 27 | BurnableBEP20.new( 28 | _name, 29 | _symbol, 30 | _decimals, 31 | 0, 32 | this.serviceReceiver.address, 33 | { 34 | from: owner, 35 | value: fee, 36 | }, 37 | ), 38 | 'BurnableBEP20: supply cannot be zero', 39 | ); 40 | }); 41 | }); 42 | 43 | describe('with initial supply', function () { 44 | beforeEach(async function () { 45 | this.token = await BurnableBEP20.new( 46 | _name, 47 | _symbol, 48 | _decimals, 49 | _initialSupply, 50 | this.serviceReceiver.address, 51 | { 52 | from: owner, 53 | value: fee, 54 | }, 55 | ); 56 | }); 57 | 58 | describe('once deployed', function () { 59 | it('total supply should be equal to initial supply', async function () { 60 | (await this.token.totalSupply()).should.be.bignumber.equal(_initialSupply); 61 | }); 62 | 63 | it('owner balance should be equal to initial supply', async function () { 64 | (await this.token.balanceOf(owner)).should.be.bignumber.equal(_initialSupply); 65 | }); 66 | }); 67 | }); 68 | }); 69 | }); 70 | 71 | context('BurnableBEP20 token behaviours', function () { 72 | beforeEach(async function () { 73 | this.token = await BurnableBEP20.new( 74 | _name, 75 | _symbol, 76 | _decimals, 77 | _initialSupply, 78 | this.serviceReceiver.address, 79 | { 80 | from: owner, 81 | value: fee, 82 | }, 83 | ); 84 | }); 85 | 86 | context('like a BEP20', function () { 87 | shouldBehaveLikeBEP20(_name, _symbol, _decimals, _initialSupply, [owner, other, thirdParty]); 88 | }); 89 | 90 | context('like a BEP20Burnable', function () { 91 | shouldBehaveLikeBEP20Burnable(_initialSupply, [owner, thirdParty]); 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /analysis/description-table/StandardBEP20.md: -------------------------------------------------------------------------------- 1 | ## Sūrya's Description Report 2 | 3 | ### Files Description Table 4 | 5 | 6 | | File Name | SHA-1 Hash | 7 | |-------------|--------------| 8 | | dist/StandardBEP20.dist.sol | 23219f5850842afedd8f7e908d03573fa9be6daa | 9 | 10 | 11 | ### Contracts Description Table 12 | 13 | 14 | | Contract | Type | Bases | | | 15 | |:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| 16 | | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | 17 | |||||| 18 | | **Context** | Implementation | ||| 19 | | └ | _msgSender | Internal 🔒 | | | 20 | | └ | _msgData | Internal 🔒 | | | 21 | |||||| 22 | | **Ownable** | Implementation | Context ||| 23 | | └ | | Public ❗️ | 🛑 |NO❗️ | 24 | | └ | owner | Public ❗️ | |NO❗️ | 25 | | └ | renounceOwnership | Public ❗️ | 🛑 | onlyOwner | 26 | | └ | transferOwnership | Public ❗️ | 🛑 | onlyOwner | 27 | |||||| 28 | | **SafeMath** | Library | ||| 29 | | └ | add | Internal 🔒 | | | 30 | | └ | sub | Internal 🔒 | | | 31 | | └ | sub | Internal 🔒 | | | 32 | | └ | mul | Internal 🔒 | | | 33 | | └ | div | Internal 🔒 | | | 34 | | └ | div | Internal 🔒 | | | 35 | | └ | mod | Internal 🔒 | | | 36 | | └ | mod | Internal 🔒 | | | 37 | |||||| 38 | | **IBEP20** | Interface | ||| 39 | | └ | name | External ❗️ | |NO❗️ | 40 | | └ | symbol | External ❗️ | |NO❗️ | 41 | | └ | decimals | External ❗️ | |NO❗️ | 42 | | └ | totalSupply | External ❗️ | |NO❗️ | 43 | | └ | balanceOf | External ❗️ | |NO❗️ | 44 | | └ | getOwner | External ❗️ | |NO❗️ | 45 | | └ | transfer | External ❗️ | 🛑 |NO❗️ | 46 | | └ | transferFrom | External ❗️ | 🛑 |NO❗️ | 47 | | └ | approve | External ❗️ | 🛑 |NO❗️ | 48 | | └ | allowance | External ❗️ | |NO❗️ | 49 | |||||| 50 | | **BEP20** | Implementation | Ownable, IBEP20 ||| 51 | | └ | | Public ❗️ | 🛑 |NO❗️ | 52 | | └ | name | Public ❗️ | |NO❗️ | 53 | | └ | symbol | Public ❗️ | |NO❗️ | 54 | | └ | decimals | Public ❗️ | |NO❗️ | 55 | | └ | totalSupply | Public ❗️ | |NO❗️ | 56 | | └ | balanceOf | Public ❗️ | |NO❗️ | 57 | | └ | getOwner | Public ❗️ | |NO❗️ | 58 | | └ | transfer | Public ❗️ | 🛑 |NO❗️ | 59 | | └ | transferFrom | Public ❗️ | 🛑 |NO❗️ | 60 | | └ | approve | Public ❗️ | 🛑 |NO❗️ | 61 | | └ | allowance | Public ❗️ | |NO❗️ | 62 | | └ | increaseAllowance | Public ❗️ | 🛑 |NO❗️ | 63 | | └ | decreaseAllowance | Public ❗️ | 🛑 |NO❗️ | 64 | | └ | _transfer | Internal 🔒 | 🛑 | | 65 | | └ | _mint | Internal 🔒 | 🛑 | | 66 | | └ | _burn | Internal 🔒 | 🛑 | | 67 | | └ | _approve | Internal 🔒 | 🛑 | | 68 | | └ | _setupDecimals | Internal 🔒 | 🛑 | | 69 | | └ | _beforeTokenTransfer | Internal 🔒 | 🛑 | | 70 | |||||| 71 | | **ServiceReceiver** | Implementation | Ownable ||| 72 | | └ | pay | Public ❗️ | 💵 |NO❗️ | 73 | | └ | getPrice | Public ❗️ | |NO❗️ | 74 | | └ | setPrice | Public ❗️ | 🛑 | onlyOwner | 75 | | └ | withdraw | Public ❗️ | 🛑 | onlyOwner | 76 | | └ | _toBytes32 | Private 🔐 | | | 77 | |||||| 78 | | **ServicePayer** | Implementation | ||| 79 | | └ | | Public ❗️ | 💵 |NO❗️ | 80 | |||||| 81 | | **StandardBEP20** | Implementation | BEP20, ServicePayer ||| 82 | | └ | | Public ❗️ | 💵 | BEP20 ServicePayer | 83 | 84 | 85 | ### Legend 86 | 87 | | Symbol | Meaning | 88 | |:--------:|-----------| 89 | | 🛑 | Function can modify state | 90 | | 💵 | Function is payable | 91 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BEP20 Token Generator 2 | 3 | [![CI](https://github.com/defipro/bep20-generator/workflows/CI/badge.svg?branch=master)](https://github.com/defipro/bep20-generator/actions/) 4 | [![Coverage Status](https://coveralls.io/repos/github/defipro/bep20-generator/badge.svg?branch=master)](https://coveralls.io/github/defipro/bep20-generator?branch=master) 5 | [![MIT licensed](https://img.shields.io/github/license/defipro/bep20-generator.svg)](https://github.com/defipro/bep20-generator/blob/master/LICENSE) 6 | 7 | The new Smart Contract Generator for BEP20 Token. 8 | 9 | ## Try it 10 | 11 | [https://defipro.github.io/bep20-generator](https://defipro.github.io/bep20-generator) 12 | 13 | 14 | ## Development 15 | 16 | 17 | ### Install dependencies 18 | 19 | ```bash 20 | npm install 21 | ``` 22 | 23 | 24 | ### Usage (using Truffle) 25 | 26 | Open the Truffle console 27 | 28 | ```bash 29 | npm run truffle:console 30 | ``` 31 | 32 | 33 | #### Compile 34 | 35 | ```bash 36 | npm run truffle:compile 37 | ``` 38 | 39 | 40 | #### Test 41 | 42 | ```bash 43 | npm run truffle:test 44 | ``` 45 | 46 | 47 | ### Usage (using Hardhat) 48 | 49 | Open the Hardhat console 50 | 51 | ```bash 52 | npm run hardhat:console 53 | ``` 54 | 55 | 56 | #### Compile 57 | 58 | ```bash 59 | npm run hardhat:compile 60 | ``` 61 | 62 | 63 | #### Test 64 | 65 | ```bash 66 | npm run hardhat:test 67 | ``` 68 | 69 | 70 | ### Code Coverage 71 | 72 | ```bash 73 | npm run hardhat:coverage 74 | ``` 75 | 76 | 77 | ## Linter 78 | 79 | Use Solhint 80 | 81 | ```bash 82 | npm run lint:sol 83 | ``` 84 | 85 | Use ESLint 86 | 87 | ```bash 88 | npm run lint:js 89 | ``` 90 | 91 | Use ESLint and fix 92 | 93 | ```bash 94 | npm run lint:fix 95 | ``` 96 | 97 | 98 | ## Flattener 99 | 100 | This allow to flatten the code into a single file 101 | 102 | Edit `scripts/flat.sh` to add your contracts 103 | 104 | ```bash 105 | npm run flat 106 | ``` 107 | 108 | 109 | ## Analysis 110 | 111 | Note: it is better to analyze the flattened code to have a bigger overview on the entire codebase. So run the flattener first. 112 | 113 | ### Describe 114 | 115 | The `describe` command shows a summary of the contracts and methods in the files provided 116 | 117 | ```bash 118 | npx surya describe dist/StandardBEP20.dist.sol 119 | ``` 120 | 121 | ### Dependencies 122 | 123 | The `dependencies` command outputs the c3-linearization of a given contract's inheirtance graph. Contracts will be listed starting with most-derived, ie. if the same function is defined in more than one contract, the solidity compiler will use the definition in whichever contract is listed first. 124 | 125 | ```bash 126 | npx surya dependencies StandardBEP20 dist/StandardBEP20.dist.sol 127 | ``` 128 | ### Generate Report 129 | 130 | Edit `scripts/analyze.sh` to add your contracts 131 | 132 | ```bash 133 | npm run analyze 134 | ``` 135 | 136 | The `inheritance` command outputs a DOT-formatted graph of the inheritance tree. 137 | 138 | The `graph` command outputs a DOT-formatted graph of the control flow. 139 | 140 | The `mdreport` command creates a markdown description report with tables comprising information about the system's files, contracts and their functions. 141 | 142 | The `sol2uml` generates UML class diagram from Solidity contracts. 143 | 144 | 145 | ## License 146 | 147 | Code released under the [MIT License](https://github.com/defipro/bep20-generator/blob/master/LICENSE). 148 | 149 | -------------------------------------------------------------------------------- /contracts/token/BEP20/lib/IBEP20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | /** 6 | * @dev Interface of the BEP standard. 7 | */ 8 | interface IBEP20 { 9 | 10 | /** 11 | * @dev Returns the token name. 12 | */ 13 | function name() external view returns (string memory); 14 | 15 | /** 16 | * @dev Returns the token symbol. 17 | */ 18 | function symbol() external view returns (string memory); 19 | 20 | /** 21 | * @dev Returns the token decimals. 22 | */ 23 | function decimals() external view returns (uint8); 24 | 25 | /** 26 | * @dev Returns the amount of tokens in existence. 27 | */ 28 | function totalSupply() external view returns (uint256); 29 | 30 | /** 31 | * @dev Returns the amount of tokens owned by `account`. 32 | */ 33 | function balanceOf(address account) external view returns (uint256); 34 | 35 | /** 36 | * @dev Returns the token owner. 37 | */ 38 | function getOwner() external view returns (address); 39 | 40 | /** 41 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 42 | * 43 | * Returns a boolean value indicating whether the operation succeeded. 44 | * 45 | * Emits a {Transfer} event. 46 | */ 47 | function transfer(address recipient, uint256 amount) external returns (bool); 48 | 49 | /** 50 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 51 | * allowance mechanism. `amount` is then deducted from the caller's 52 | * allowance. 53 | * 54 | * Returns a boolean value indicating whether the operation succeeded. 55 | * 56 | * Emits a {Transfer} event. 57 | */ 58 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 59 | 60 | /** 61 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 62 | * 63 | * Returns a boolean value indicating whether the operation succeeded. 64 | * 65 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 66 | * that someone may use both the old and the new allowance by unfortunate 67 | * transaction ordering. One possible solution to mitigate this race 68 | * condition is to first reduce the spender's allowance to 0 and set the 69 | * desired value afterwards: 70 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 71 | * 72 | * Emits an {Approval} event. 73 | */ 74 | function approve(address spender, uint256 amount) external returns (bool); 75 | 76 | /** 77 | * @dev Returns the remaining number of tokens that `spender` will be 78 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 79 | * zero by default. 80 | * 81 | * This value changes when {approve} or {transferFrom} are called. 82 | */ 83 | function allowance(address _owner, address spender) external view returns (uint256); 84 | 85 | /** 86 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 87 | * another (`to`). 88 | * 89 | * Note that `value` may be zero. 90 | */ 91 | event Transfer(address indexed from, address indexed to, uint256 value); 92 | 93 | /** 94 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 95 | * a call to {approve}. `value` is the new allowance. 96 | */ 97 | event Approval(address indexed owner, address indexed spender, uint256 value); 98 | } 99 | -------------------------------------------------------------------------------- /analysis/description-table/BurnableBEP20.md: -------------------------------------------------------------------------------- 1 | ## Sūrya's Description Report 2 | 3 | ### Files Description Table 4 | 5 | 6 | | File Name | SHA-1 Hash | 7 | |-------------|--------------| 8 | | dist/BurnableBEP20.dist.sol | c6b9515f052b612a8cef28ce27d93603b37d7526 | 9 | 10 | 11 | ### Contracts Description Table 12 | 13 | 14 | | Contract | Type | Bases | | | 15 | |:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| 16 | | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | 17 | |||||| 18 | | **Context** | Implementation | ||| 19 | | └ | _msgSender | Internal 🔒 | | | 20 | | └ | _msgData | Internal 🔒 | | | 21 | |||||| 22 | | **Ownable** | Implementation | Context ||| 23 | | └ | | Public ❗️ | 🛑 |NO❗️ | 24 | | └ | owner | Public ❗️ | |NO❗️ | 25 | | └ | renounceOwnership | Public ❗️ | 🛑 | onlyOwner | 26 | | └ | transferOwnership | Public ❗️ | 🛑 | onlyOwner | 27 | |||||| 28 | | **SafeMath** | Library | ||| 29 | | └ | add | Internal 🔒 | | | 30 | | └ | sub | Internal 🔒 | | | 31 | | └ | sub | Internal 🔒 | | | 32 | | └ | mul | Internal 🔒 | | | 33 | | └ | div | Internal 🔒 | | | 34 | | └ | div | Internal 🔒 | | | 35 | | └ | mod | Internal 🔒 | | | 36 | | └ | mod | Internal 🔒 | | | 37 | |||||| 38 | | **IBEP20** | Interface | ||| 39 | | └ | name | External ❗️ | |NO❗️ | 40 | | └ | symbol | External ❗️ | |NO❗️ | 41 | | └ | decimals | External ❗️ | |NO❗️ | 42 | | └ | totalSupply | External ❗️ | |NO❗️ | 43 | | └ | balanceOf | External ❗️ | |NO❗️ | 44 | | └ | getOwner | External ❗️ | |NO❗️ | 45 | | └ | transfer | External ❗️ | 🛑 |NO❗️ | 46 | | └ | transferFrom | External ❗️ | 🛑 |NO❗️ | 47 | | └ | approve | External ❗️ | 🛑 |NO❗️ | 48 | | └ | allowance | External ❗️ | |NO❗️ | 49 | |||||| 50 | | **BEP20** | Implementation | Ownable, IBEP20 ||| 51 | | └ | | Public ❗️ | 🛑 |NO❗️ | 52 | | └ | name | Public ❗️ | |NO❗️ | 53 | | └ | symbol | Public ❗️ | |NO❗️ | 54 | | └ | decimals | Public ❗️ | |NO❗️ | 55 | | └ | totalSupply | Public ❗️ | |NO❗️ | 56 | | └ | balanceOf | Public ❗️ | |NO❗️ | 57 | | └ | getOwner | Public ❗️ | |NO❗️ | 58 | | └ | transfer | Public ❗️ | 🛑 |NO❗️ | 59 | | └ | transferFrom | Public ❗️ | 🛑 |NO❗️ | 60 | | └ | approve | Public ❗️ | 🛑 |NO❗️ | 61 | | └ | allowance | Public ❗️ | |NO❗️ | 62 | | └ | increaseAllowance | Public ❗️ | 🛑 |NO❗️ | 63 | | └ | decreaseAllowance | Public ❗️ | 🛑 |NO❗️ | 64 | | └ | _transfer | Internal 🔒 | 🛑 | | 65 | | └ | _mint | Internal 🔒 | 🛑 | | 66 | | └ | _burn | Internal 🔒 | 🛑 | | 67 | | └ | _approve | Internal 🔒 | 🛑 | | 68 | | └ | _setupDecimals | Internal 🔒 | 🛑 | | 69 | | └ | _beforeTokenTransfer | Internal 🔒 | 🛑 | | 70 | |||||| 71 | | **BEP20Burnable** | Implementation | Context, BEP20 ||| 72 | | └ | burn | Public ❗️ | 🛑 |NO❗️ | 73 | | └ | burnFrom | Public ❗️ | 🛑 |NO❗️ | 74 | |||||| 75 | | **ServiceReceiver** | Implementation | Ownable ||| 76 | | └ | pay | Public ❗️ | 💵 |NO❗️ | 77 | | └ | getPrice | Public ❗️ | |NO❗️ | 78 | | └ | setPrice | Public ❗️ | 🛑 | onlyOwner | 79 | | └ | withdraw | Public ❗️ | 🛑 | onlyOwner | 80 | | └ | _toBytes32 | Private 🔐 | | | 81 | |||||| 82 | | **ServicePayer** | Implementation | ||| 83 | | └ | | Public ❗️ | 💵 |NO❗️ | 84 | |||||| 85 | | **BurnableBEP20** | Implementation | BEP20Burnable, ServicePayer ||| 86 | | └ | | Public ❗️ | 💵 | BEP20 ServicePayer | 87 | 88 | 89 | ### Legend 90 | 91 | | Symbol | Meaning | 92 | |:--------:|-----------| 93 | | 🛑 | Function can modify state | 94 | | 💵 | Function is payable | 95 | -------------------------------------------------------------------------------- /analysis/description-table/SimpleBEP20.md: -------------------------------------------------------------------------------- 1 | ## Sūrya's Description Report 2 | 3 | ### Files Description Table 4 | 5 | 6 | | File Name | SHA-1 Hash | 7 | |-------------|--------------| 8 | | dist/SimpleBEP20.dist.sol | 5cc3eb3c9ca4a92f761b3fdb28076859e60844e3 | 9 | 10 | 11 | ### Contracts Description Table 12 | 13 | 14 | | Contract | Type | Bases | | | 15 | |:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| 16 | | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | 17 | |||||| 18 | | **Context** | Implementation | ||| 19 | | └ | _msgSender | Internal 🔒 | | | 20 | | └ | _msgData | Internal 🔒 | | | 21 | |||||| 22 | | **Ownable** | Implementation | Context ||| 23 | | └ | | Public ❗️ | 🛑 |NO❗️ | 24 | | └ | owner | Public ❗️ | |NO❗️ | 25 | | └ | renounceOwnership | Public ❗️ | 🛑 | onlyOwner | 26 | | └ | transferOwnership | Public ❗️ | 🛑 | onlyOwner | 27 | |||||| 28 | | **SafeMath** | Library | ||| 29 | | └ | add | Internal 🔒 | | | 30 | | └ | sub | Internal 🔒 | | | 31 | | └ | sub | Internal 🔒 | | | 32 | | └ | mul | Internal 🔒 | | | 33 | | └ | div | Internal 🔒 | | | 34 | | └ | div | Internal 🔒 | | | 35 | | └ | mod | Internal 🔒 | | | 36 | | └ | mod | Internal 🔒 | | | 37 | |||||| 38 | | **IBEP20** | Interface | ||| 39 | | └ | name | External ❗️ | |NO❗️ | 40 | | └ | symbol | External ❗️ | |NO❗️ | 41 | | └ | decimals | External ❗️ | |NO❗️ | 42 | | └ | totalSupply | External ❗️ | |NO❗️ | 43 | | └ | balanceOf | External ❗️ | |NO❗️ | 44 | | └ | getOwner | External ❗️ | |NO❗️ | 45 | | └ | transfer | External ❗️ | 🛑 |NO❗️ | 46 | | └ | transferFrom | External ❗️ | 🛑 |NO❗️ | 47 | | └ | approve | External ❗️ | 🛑 |NO❗️ | 48 | | └ | allowance | External ❗️ | |NO❗️ | 49 | |||||| 50 | | **BEP20** | Implementation | Ownable, IBEP20 ||| 51 | | └ | | Public ❗️ | 🛑 |NO❗️ | 52 | | └ | name | Public ❗️ | |NO❗️ | 53 | | └ | symbol | Public ❗️ | |NO❗️ | 54 | | └ | decimals | Public ❗️ | |NO❗️ | 55 | | └ | totalSupply | Public ❗️ | |NO❗️ | 56 | | └ | balanceOf | Public ❗️ | |NO❗️ | 57 | | └ | getOwner | Public ❗️ | |NO❗️ | 58 | | └ | transfer | Public ❗️ | 🛑 |NO❗️ | 59 | | └ | transferFrom | Public ❗️ | 🛑 |NO❗️ | 60 | | └ | approve | Public ❗️ | 🛑 |NO❗️ | 61 | | └ | allowance | Public ❗️ | |NO❗️ | 62 | | └ | increaseAllowance | Public ❗️ | 🛑 |NO❗️ | 63 | | └ | decreaseAllowance | Public ❗️ | 🛑 |NO❗️ | 64 | | └ | _transfer | Internal 🔒 | 🛑 | | 65 | | └ | _mint | Internal 🔒 | 🛑 | | 66 | | └ | _burn | Internal 🔒 | 🛑 | | 67 | | └ | _approve | Internal 🔒 | 🛑 | | 68 | | └ | _setupDecimals | Internal 🔒 | 🛑 | | 69 | | └ | _beforeTokenTransfer | Internal 🔒 | 🛑 | | 70 | |||||| 71 | | **ServiceReceiver** | Implementation | Ownable ||| 72 | | └ | pay | Public ❗️ | 💵 |NO❗️ | 73 | | └ | getPrice | Public ❗️ | |NO❗️ | 74 | | └ | setPrice | Public ❗️ | 🛑 | onlyOwner | 75 | | └ | withdraw | Public ❗️ | 🛑 | onlyOwner | 76 | | └ | _toBytes32 | Private 🔐 | | | 77 | |||||| 78 | | **ServicePayer** | Implementation | ||| 79 | | └ | | Public ❗️ | 💵 |NO❗️ | 80 | |||||| 81 | | **GeneratorCopyright** | Implementation | ||| 82 | | └ | | Public ❗️ | 🛑 |NO❗️ | 83 | | └ | generator | Public ❗️ | |NO❗️ | 84 | | └ | version | Public ❗️ | |NO❗️ | 85 | |||||| 86 | | **SimpleBEP20** | Implementation | BEP20, ServicePayer, GeneratorCopyright ||| 87 | | └ | | Public ❗️ | 💵 | BEP20 ServicePayer | 88 | 89 | 90 | ### Legend 91 | 92 | | Symbol | Meaning | 93 | |:--------:|-----------| 94 | | 🛑 | Function can modify state | 95 | | 💵 | Function is payable | 96 | -------------------------------------------------------------------------------- /analysis/description-table/MintableBEP20.md: -------------------------------------------------------------------------------- 1 | ## Sūrya's Description Report 2 | 3 | ### Files Description Table 4 | 5 | 6 | | File Name | SHA-1 Hash | 7 | |-------------|--------------| 8 | | dist/MintableBEP20.dist.sol | 4872a4fea53e96deb9c99b5a9d3c25b988c65220 | 9 | 10 | 11 | ### Contracts Description Table 12 | 13 | 14 | | Contract | Type | Bases | | | 15 | |:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| 16 | | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | 17 | |||||| 18 | | **Context** | Implementation | ||| 19 | | └ | _msgSender | Internal 🔒 | | | 20 | | └ | _msgData | Internal 🔒 | | | 21 | |||||| 22 | | **Ownable** | Implementation | Context ||| 23 | | └ | | Public ❗️ | 🛑 |NO❗️ | 24 | | └ | owner | Public ❗️ | |NO❗️ | 25 | | └ | renounceOwnership | Public ❗️ | 🛑 | onlyOwner | 26 | | └ | transferOwnership | Public ❗️ | 🛑 | onlyOwner | 27 | |||||| 28 | | **SafeMath** | Library | ||| 29 | | └ | add | Internal 🔒 | | | 30 | | └ | sub | Internal 🔒 | | | 31 | | └ | sub | Internal 🔒 | | | 32 | | └ | mul | Internal 🔒 | | | 33 | | └ | div | Internal 🔒 | | | 34 | | └ | div | Internal 🔒 | | | 35 | | └ | mod | Internal 🔒 | | | 36 | | └ | mod | Internal 🔒 | | | 37 | |||||| 38 | | **IBEP20** | Interface | ||| 39 | | └ | name | External ❗️ | |NO❗️ | 40 | | └ | symbol | External ❗️ | |NO❗️ | 41 | | └ | decimals | External ❗️ | |NO❗️ | 42 | | └ | totalSupply | External ❗️ | |NO❗️ | 43 | | └ | balanceOf | External ❗️ | |NO❗️ | 44 | | └ | getOwner | External ❗️ | |NO❗️ | 45 | | └ | transfer | External ❗️ | 🛑 |NO❗️ | 46 | | └ | transferFrom | External ❗️ | 🛑 |NO❗️ | 47 | | └ | approve | External ❗️ | 🛑 |NO❗️ | 48 | | └ | allowance | External ❗️ | |NO❗️ | 49 | |||||| 50 | | **BEP20** | Implementation | Ownable, IBEP20 ||| 51 | | └ | | Public ❗️ | 🛑 |NO❗️ | 52 | | └ | name | Public ❗️ | |NO❗️ | 53 | | └ | symbol | Public ❗️ | |NO❗️ | 54 | | └ | decimals | Public ❗️ | |NO❗️ | 55 | | └ | totalSupply | Public ❗️ | |NO❗️ | 56 | | └ | balanceOf | Public ❗️ | |NO❗️ | 57 | | └ | getOwner | Public ❗️ | |NO❗️ | 58 | | └ | transfer | Public ❗️ | 🛑 |NO❗️ | 59 | | └ | transferFrom | Public ❗️ | 🛑 |NO❗️ | 60 | | └ | approve | Public ❗️ | 🛑 |NO❗️ | 61 | | └ | allowance | Public ❗️ | |NO❗️ | 62 | | └ | increaseAllowance | Public ❗️ | 🛑 |NO❗️ | 63 | | └ | decreaseAllowance | Public ❗️ | 🛑 |NO❗️ | 64 | | └ | _transfer | Internal 🔒 | 🛑 | | 65 | | └ | _mint | Internal 🔒 | 🛑 | | 66 | | └ | _burn | Internal 🔒 | 🛑 | | 67 | | └ | _approve | Internal 🔒 | 🛑 | | 68 | | └ | _setupDecimals | Internal 🔒 | 🛑 | | 69 | | └ | _beforeTokenTransfer | Internal 🔒 | 🛑 | | 70 | |||||| 71 | | **BEP20Mintable** | Implementation | BEP20 ||| 72 | | └ | mintingFinished | Public ❗️ | |NO❗️ | 73 | | └ | mint | Public ❗️ | 🛑 | canMint | 74 | | └ | finishMinting | Public ❗️ | 🛑 | canMint | 75 | | └ | _finishMinting | Internal 🔒 | 🛑 | | 76 | |||||| 77 | | **ServiceReceiver** | Implementation | Ownable ||| 78 | | └ | pay | Public ❗️ | 💵 |NO❗️ | 79 | | └ | getPrice | Public ❗️ | |NO❗️ | 80 | | └ | setPrice | Public ❗️ | 🛑 | onlyOwner | 81 | | └ | withdraw | Public ❗️ | 🛑 | onlyOwner | 82 | | └ | _toBytes32 | Private 🔐 | | | 83 | |||||| 84 | | **ServicePayer** | Implementation | ||| 85 | | └ | | Public ❗️ | 💵 |NO❗️ | 86 | |||||| 87 | | **MintableBEP20** | Implementation | BEP20Mintable, ServicePayer ||| 88 | | └ | | Public ❗️ | 💵 | BEP20 ServicePayer | 89 | | └ | _mint | Internal 🔒 | 🛑 | onlyOwner | 90 | | └ | _finishMinting | Internal 🔒 | 🛑 | onlyOwner | 91 | 92 | 93 | ### Legend 94 | 95 | | Symbol | Meaning | 96 | |:--------:|-----------| 97 | | 🛑 | Function can modify state | 98 | | 💵 | Function is payable | 99 | -------------------------------------------------------------------------------- /test/token/BEP20/behaviours/BEP20Mintable.behaviour.js: -------------------------------------------------------------------------------- 1 | const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); 2 | const { ZERO_ADDRESS } = constants; 3 | 4 | function shouldBehaveLikeBEP20Mintable (initialBalance, [minter, thirdParty]) { 5 | describe('mint', function () { 6 | const initialSupply = new BN(initialBalance); 7 | const amount = new BN(50); 8 | 9 | const from = minter; 10 | 11 | context('behaviours', function () { 12 | it('rejects a null account', async function () { 13 | await expectRevert( 14 | this.token.mint(ZERO_ADDRESS, amount, { from: minter }), 15 | 'BEP20: mint to the zero address', 16 | ); 17 | }); 18 | 19 | describe('for a non null account', function () { 20 | beforeEach('minting', async function () { 21 | const { logs } = await this.token.mint(thirdParty, amount); 22 | this.logs = logs; 23 | }); 24 | 25 | it('increments totalSupply', async function () { 26 | const expectedSupply = initialSupply.add(amount); 27 | (await this.token.totalSupply()).should.be.bignumber.equal(expectedSupply); 28 | }); 29 | 30 | it('increments thirdParty balance', async function () { 31 | (await this.token.balanceOf(thirdParty)).should.be.bignumber.equal(amount); 32 | }); 33 | 34 | it('emits Transfer event', async function () { 35 | const event = expectEvent.inLogs(this.logs, 'Transfer', { 36 | from: ZERO_ADDRESS, 37 | to: thirdParty, 38 | }); 39 | 40 | event.args.value.should.be.bignumber.equal(amount); 41 | }); 42 | }); 43 | 44 | context('for a zero amount', function () { 45 | shouldMint(new BN(0)); 46 | }); 47 | 48 | context('for a non-zero amount', function () { 49 | shouldMint(amount); 50 | }); 51 | 52 | function shouldMint (amount) { 53 | beforeEach(async function () { 54 | ({ logs: this.logs } = await this.token.mint(thirdParty, amount, { from })); 55 | }); 56 | 57 | it('mints the requested amount', async function () { 58 | (await this.token.balanceOf(thirdParty)).should.be.bignumber.equal(amount); 59 | }); 60 | 61 | it('emits a transfer event', async function () { 62 | expectEvent.inLogs(this.logs, 'Transfer', { 63 | from: ZERO_ADDRESS, 64 | to: thirdParty, 65 | value: amount, 66 | }); 67 | }); 68 | } 69 | }); 70 | 71 | context('before finish minting', function () { 72 | it('mintingFinished should be false', async function () { 73 | (await this.token.mintingFinished()).should.be.equal(false); 74 | }); 75 | }); 76 | 77 | context('after finish minting', function () { 78 | beforeEach(async function () { 79 | ({ logs: this.logs } = await this.token.finishMinting({ from })); 80 | }); 81 | 82 | it('should emit MintFinished', async function () { 83 | expectEvent.inLogs(this.logs, 'MintFinished'); 84 | }); 85 | 86 | it('mintingFinished should be true', async function () { 87 | (await this.token.mintingFinished()).should.be.equal(true); 88 | }); 89 | 90 | it('cannot mint more tokens', async function () { 91 | await expectRevert( 92 | this.token.mint(thirdParty, 1, { from }), 93 | 'BEP20Mintable: minting is finished', 94 | ); 95 | }); 96 | 97 | it('cannot finish minting again', async function () { 98 | await expectRevert( 99 | this.token.finishMinting({ from }), 100 | 'BEP20Mintable: minting is finished', 101 | ); 102 | }); 103 | }); 104 | }); 105 | } 106 | 107 | module.exports = { 108 | shouldBehaveLikeBEP20Mintable, 109 | }; 110 | -------------------------------------------------------------------------------- /test/token/BEP20/behaviours/BEP20Burnable.behaviour.js: -------------------------------------------------------------------------------- 1 | const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); 2 | const { ZERO_ADDRESS } = constants; 3 | 4 | const { expect } = require('chai'); 5 | 6 | function shouldBehaveLikeBEP20Burnable (initialBalance, [burner, thirdParty]) { 7 | describe('burn', function () { 8 | describe('when the given amount is not greater than balance of the sender', function () { 9 | context('for a zero amount', function () { 10 | shouldBurn(new BN(0)); 11 | }); 12 | 13 | context('for a non-zero amount', function () { 14 | shouldBurn(new BN(100)); 15 | }); 16 | 17 | function shouldBurn (amount) { 18 | beforeEach(async function () { 19 | ({ logs: this.logs } = await this.token.burn(amount, { from: burner })); 20 | }); 21 | 22 | it('burns the requested amount', async function () { 23 | expect(await this.token.balanceOf(burner)).to.be.bignumber.equal(initialBalance.sub(amount)); 24 | }); 25 | 26 | it('emits a transfer event', async function () { 27 | expectEvent.inLogs(this.logs, 'Transfer', { 28 | from: burner, 29 | to: ZERO_ADDRESS, 30 | value: amount, 31 | }); 32 | }); 33 | } 34 | }); 35 | 36 | describe('when the given amount is greater than the balance of the sender', function () { 37 | const amount = initialBalance.addn(1); 38 | 39 | it('reverts', async function () { 40 | await expectRevert(this.token.burn(amount, { from: burner }), 41 | 'BEP20: burn amount exceeds balance', 42 | ); 43 | }); 44 | }); 45 | }); 46 | 47 | describe('burnFrom', function () { 48 | describe('on success', function () { 49 | context('for a zero amount', function () { 50 | shouldBurnFrom(new BN(0)); 51 | }); 52 | 53 | context('for a non-zero amount', function () { 54 | shouldBurnFrom(new BN(100)); 55 | }); 56 | 57 | function shouldBurnFrom (amount) { 58 | const originalAllowance = amount.muln(3); 59 | 60 | beforeEach(async function () { 61 | await this.token.approve(thirdParty, originalAllowance, { from: burner }); 62 | const { logs } = await this.token.burnFrom(burner, amount, { from: thirdParty }); 63 | this.logs = logs; 64 | }); 65 | 66 | it('burns the requested amount', async function () { 67 | expect(await this.token.balanceOf(burner)).to.be.bignumber.equal(initialBalance.sub(amount)); 68 | }); 69 | 70 | it('decrements allowance', async function () { 71 | expect(await this.token.allowance(burner, thirdParty)).to.be.bignumber.equal(originalAllowance.sub(amount)); 72 | }); 73 | 74 | it('emits a transfer event', async function () { 75 | expectEvent.inLogs(this.logs, 'Transfer', { 76 | from: burner, 77 | to: ZERO_ADDRESS, 78 | value: amount, 79 | }); 80 | }); 81 | } 82 | }); 83 | 84 | describe('when the given amount is greater than the balance of the sender', function () { 85 | const amount = initialBalance.addn(1); 86 | 87 | it('reverts', async function () { 88 | await this.token.approve(thirdParty, amount, { from: burner }); 89 | await expectRevert(this.token.burnFrom(burner, amount, { from: thirdParty }), 90 | 'BEP20: burn amount exceeds balance', 91 | ); 92 | }); 93 | }); 94 | 95 | describe('when the given amount is greater than the allowance', function () { 96 | const allowance = new BN(100); 97 | 98 | it('reverts', async function () { 99 | await this.token.approve(thirdParty, allowance, { from: burner }); 100 | await expectRevert(this.token.burnFrom(burner, allowance.addn(1), { from: thirdParty }), 101 | 'BEP20: burn amount exceeds allowance', 102 | ); 103 | }); 104 | }); 105 | }); 106 | } 107 | 108 | module.exports = { 109 | shouldBehaveLikeBEP20Burnable, 110 | }; 111 | -------------------------------------------------------------------------------- /test/token/BEP20/MintableBEP20.test.js: -------------------------------------------------------------------------------- 1 | const { BN, ether, expectRevert } = require('@openzeppelin/test-helpers'); 2 | 3 | const { shouldBehaveLikeBEP20 } = require('./behaviours/BEP20.behaviour'); 4 | const { shouldBehaveLikeBEP20Mintable } = require('./behaviours/BEP20Mintable.behaviour'); 5 | 6 | const MintableBEP20 = artifacts.require('MintableBEP20'); 7 | const ServiceReceiver = artifacts.require('ServiceReceiver'); 8 | 9 | contract('MintableBEP20', function ([owner, other, thirdParty]) { 10 | const _name = 'MintableBEP20'; 11 | const _symbol = 'BEP20'; 12 | const _decimals = new BN(8); 13 | const _initialSupply = new BN(100000000); 14 | 15 | const fee = ether('0.1'); 16 | 17 | beforeEach(async function () { 18 | this.serviceReceiver = await ServiceReceiver.new({ from: owner }); 19 | await this.serviceReceiver.setPrice('MintableBEP20', fee); 20 | }); 21 | 22 | context('creating valid token', function () { 23 | describe('as a MintableBEP20', function () { 24 | describe('without initial supply', function () { 25 | beforeEach(async function () { 26 | this.token = await MintableBEP20.new( 27 | _name, 28 | _symbol, 29 | _decimals, 30 | 0, 31 | this.serviceReceiver.address, 32 | { 33 | from: owner, 34 | value: fee, 35 | }, 36 | ); 37 | }); 38 | 39 | describe('once deployed', function () { 40 | it('total supply should be equal to zero', async function () { 41 | (await this.token.totalSupply()).should.be.bignumber.equal(new BN(0)); 42 | }); 43 | 44 | it('owner balance should be equal to zero', async function () { 45 | (await this.token.balanceOf(owner)).should.be.bignumber.equal(new BN(0)); 46 | }); 47 | }); 48 | }); 49 | 50 | describe('with initial supply', function () { 51 | beforeEach(async function () { 52 | this.token = await MintableBEP20.new( 53 | _name, 54 | _symbol, 55 | _decimals, 56 | _initialSupply, 57 | this.serviceReceiver.address, 58 | { 59 | from: owner, 60 | value: fee, 61 | }, 62 | ); 63 | }); 64 | 65 | describe('once deployed', function () { 66 | it('total supply should be equal to initial supply', async function () { 67 | (await this.token.totalSupply()).should.be.bignumber.equal(_initialSupply); 68 | }); 69 | 70 | it('owner balance should be equal to initial supply', async function () { 71 | (await this.token.balanceOf(owner)).should.be.bignumber.equal(_initialSupply); 72 | }); 73 | }); 74 | }); 75 | }); 76 | }); 77 | 78 | context('MintableBEP20 token behaviours', function () { 79 | beforeEach(async function () { 80 | this.token = await MintableBEP20.new( 81 | _name, 82 | _symbol, 83 | _decimals, 84 | _initialSupply, 85 | this.serviceReceiver.address, 86 | { 87 | from: owner, 88 | value: fee, 89 | }, 90 | ); 91 | }); 92 | 93 | context('like a BEP20', function () { 94 | shouldBehaveLikeBEP20(_name, _symbol, _decimals, _initialSupply, [owner, other, thirdParty]); 95 | }); 96 | 97 | context('like a BEP20Mintable', function () { 98 | shouldBehaveLikeBEP20Mintable(_initialSupply, [owner, thirdParty]); 99 | }); 100 | 101 | context('like a MintableBEP20', function () { 102 | describe('when the sender doesn\'t have minting permission', function () { 103 | const from = thirdParty; 104 | 105 | it('cannot mint', async function () { 106 | const amount = new BN(50); 107 | 108 | await expectRevert( 109 | this.token.mint(thirdParty, amount, { from }), 110 | 'Ownable: caller is not the owner', 111 | ); 112 | }); 113 | 114 | it('cannot finish minting', async function () { 115 | await expectRevert( 116 | this.token.finishMinting({ from }), 117 | 'Ownable: caller is not the owner', 118 | ); 119 | }); 120 | }); 121 | }); 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /analysis/description-table/CommonBEP20.md: -------------------------------------------------------------------------------- 1 | ## Sūrya's Description Report 2 | 3 | ### Files Description Table 4 | 5 | 6 | | File Name | SHA-1 Hash | 7 | |-------------|--------------| 8 | | dist/CommonBEP20.dist.sol | bdd3f4a8d6ccc008c46206ae1485e314ef06f6cb | 9 | 10 | 11 | ### Contracts Description Table 12 | 13 | 14 | | Contract | Type | Bases | | | 15 | |:----------:|:-------------------:|:----------------:|:----------------:|:---------------:| 16 | | └ | **Function Name** | **Visibility** | **Mutability** | **Modifiers** | 17 | |||||| 18 | | **Context** | Implementation | ||| 19 | | └ | _msgSender | Internal 🔒 | | | 20 | | └ | _msgData | Internal 🔒 | | | 21 | |||||| 22 | | **Ownable** | Implementation | Context ||| 23 | | └ | | Public ❗️ | 🛑 |NO❗️ | 24 | | └ | owner | Public ❗️ | |NO❗️ | 25 | | └ | renounceOwnership | Public ❗️ | 🛑 | onlyOwner | 26 | | └ | transferOwnership | Public ❗️ | 🛑 | onlyOwner | 27 | |||||| 28 | | **SafeMath** | Library | ||| 29 | | └ | add | Internal 🔒 | | | 30 | | └ | sub | Internal 🔒 | | | 31 | | └ | sub | Internal 🔒 | | | 32 | | └ | mul | Internal 🔒 | | | 33 | | └ | div | Internal 🔒 | | | 34 | | └ | div | Internal 🔒 | | | 35 | | └ | mod | Internal 🔒 | | | 36 | | └ | mod | Internal 🔒 | | | 37 | |||||| 38 | | **IBEP20** | Interface | ||| 39 | | └ | name | External ❗️ | |NO❗️ | 40 | | └ | symbol | External ❗️ | |NO❗️ | 41 | | └ | decimals | External ❗️ | |NO❗️ | 42 | | └ | totalSupply | External ❗️ | |NO❗️ | 43 | | └ | balanceOf | External ❗️ | |NO❗️ | 44 | | └ | getOwner | External ❗️ | |NO❗️ | 45 | | └ | transfer | External ❗️ | 🛑 |NO❗️ | 46 | | └ | transferFrom | External ❗️ | 🛑 |NO❗️ | 47 | | └ | approve | External ❗️ | 🛑 |NO❗️ | 48 | | └ | allowance | External ❗️ | |NO❗️ | 49 | |||||| 50 | | **BEP20** | Implementation | Ownable, IBEP20 ||| 51 | | └ | | Public ❗️ | 🛑 |NO❗️ | 52 | | └ | name | Public ❗️ | |NO❗️ | 53 | | └ | symbol | Public ❗️ | |NO❗️ | 54 | | └ | decimals | Public ❗️ | |NO❗️ | 55 | | └ | totalSupply | Public ❗️ | |NO❗️ | 56 | | └ | balanceOf | Public ❗️ | |NO❗️ | 57 | | └ | getOwner | Public ❗️ | |NO❗️ | 58 | | └ | transfer | Public ❗️ | 🛑 |NO❗️ | 59 | | └ | transferFrom | Public ❗️ | 🛑 |NO❗️ | 60 | | └ | approve | Public ❗️ | 🛑 |NO❗️ | 61 | | └ | allowance | Public ❗️ | |NO❗️ | 62 | | └ | increaseAllowance | Public ❗️ | 🛑 |NO❗️ | 63 | | └ | decreaseAllowance | Public ❗️ | 🛑 |NO❗️ | 64 | | └ | _transfer | Internal 🔒 | 🛑 | | 65 | | └ | _mint | Internal 🔒 | 🛑 | | 66 | | └ | _burn | Internal 🔒 | 🛑 | | 67 | | └ | _approve | Internal 🔒 | 🛑 | | 68 | | └ | _setupDecimals | Internal 🔒 | 🛑 | | 69 | | └ | _beforeTokenTransfer | Internal 🔒 | 🛑 | | 70 | |||||| 71 | | **BEP20Capped** | Implementation | BEP20 ||| 72 | | └ | | Public ❗️ | 🛑 |NO❗️ | 73 | | └ | cap | Public ❗️ | |NO❗️ | 74 | | └ | _beforeTokenTransfer | Internal 🔒 | 🛑 | | 75 | |||||| 76 | | **BEP20Mintable** | Implementation | BEP20 ||| 77 | | └ | mintingFinished | Public ❗️ | |NO❗️ | 78 | | └ | mint | Public ❗️ | 🛑 | canMint | 79 | | └ | finishMinting | Public ❗️ | 🛑 | canMint | 80 | | └ | _finishMinting | Internal 🔒 | 🛑 | | 81 | |||||| 82 | | **BEP20Burnable** | Implementation | Context, BEP20 ||| 83 | | └ | burn | Public ❗️ | 🛑 |NO❗️ | 84 | | └ | burnFrom | Public ❗️ | 🛑 |NO❗️ | 85 | |||||| 86 | | **ServiceReceiver** | Implementation | Ownable ||| 87 | | └ | pay | Public ❗️ | 💵 |NO❗️ | 88 | | └ | getPrice | Public ❗️ | |NO❗️ | 89 | | └ | setPrice | Public ❗️ | 🛑 | onlyOwner | 90 | | └ | withdraw | Public ❗️ | 🛑 | onlyOwner | 91 | | └ | _toBytes32 | Private 🔐 | | | 92 | |||||| 93 | | **ServicePayer** | Implementation | ||| 94 | | └ | | Public ❗️ | 💵 |NO❗️ | 95 | |||||| 96 | | **CommonBEP20** | Implementation | BEP20Capped, BEP20Mintable, BEP20Burnable, ServicePayer ||| 97 | | └ | | Public ❗️ | 💵 | BEP20 BEP20Capped ServicePayer | 98 | | └ | _mint | Internal 🔒 | 🛑 | onlyOwner | 99 | | └ | _finishMinting | Internal 🔒 | 🛑 | onlyOwner | 100 | | └ | _beforeTokenTransfer | Internal 🔒 | 🛑 | | 101 | 102 | 103 | ### Legend 104 | 105 | | Symbol | Meaning | 106 | |:--------:|-----------| 107 | | 🛑 | Function can modify state | 108 | | 💵 | Function is payable | 109 | -------------------------------------------------------------------------------- /test/service/ServiceReceiver.test.js: -------------------------------------------------------------------------------- 1 | const { balance, ether, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); 2 | 3 | const { expect } = require('chai'); 4 | 5 | const { shouldBehaveLikeOwnable } = require('../access/Ownable.behavior'); 6 | 7 | const ServiceReceiver = artifacts.require('ServiceReceiver'); 8 | 9 | contract('ServiceReceiver', function ([owner, thirdParty]) { 10 | const fee = ether('0.1'); 11 | 12 | context('ServiceReceiver behaviours', function () { 13 | beforeEach(async function () { 14 | this.serviceReceiver = await ServiceReceiver.new({ from: owner }); 15 | }); 16 | 17 | describe('set price', function () { 18 | context('when the sender is owner', function () { 19 | it('should set price', async function () { 20 | await this.serviceReceiver.setPrice('ServiceMock', fee, { from: owner }); 21 | 22 | (await this.serviceReceiver.getPrice('ServiceMock')).should.be.bignumber.equal(fee); 23 | }); 24 | }); 25 | 26 | context('when the sender is not owner', function () { 27 | it('reverts', async function () { 28 | await expectRevert( 29 | this.serviceReceiver.setPrice('ServiceMock', fee, { from: thirdParty }), 30 | 'Ownable: caller is not the owner', 31 | ); 32 | }); 33 | }); 34 | }); 35 | 36 | describe('pay', function () { 37 | context('with incorrect price', function () { 38 | it('reverts', async function () { 39 | await this.serviceReceiver.setPrice('ServiceMock', fee, { from: owner }); 40 | 41 | await expectRevert( 42 | this.serviceReceiver.pay( 43 | 'ServiceMock', 44 | { 45 | from: thirdParty, 46 | value: fee.add(ether('1')), 47 | }, 48 | ), 49 | 'ServiceReceiver: incorrect price', 50 | ); 51 | }); 52 | }); 53 | 54 | context('with correct price', function () { 55 | beforeEach(async function () { 56 | await this.serviceReceiver.setPrice('ServiceMock', fee, { from: owner }); 57 | }); 58 | 59 | it('emits a Created event', async function () { 60 | const { logs } = await this.serviceReceiver.pay('ServiceMock', { value: fee, from: thirdParty }); 61 | 62 | expectEvent.inLogs(logs, 'Created', { 63 | serviceName: 'ServiceMock', 64 | serviceAddress: thirdParty, 65 | }); 66 | }); 67 | 68 | it('transfer fee to receiver', async function () { 69 | const initBalance = await balance.current(this.serviceReceiver.address); 70 | 71 | await this.serviceReceiver.pay('ServiceMock', { value: fee, from: thirdParty }); 72 | 73 | const newBalance = (await balance.current(this.serviceReceiver.address)); 74 | 75 | expect(newBalance).to.be.bignumber.equal(initBalance.add(fee)); 76 | }); 77 | }); 78 | }); 79 | 80 | describe('withdraw', function () { 81 | beforeEach(async function () { 82 | await this.serviceReceiver.setPrice('ServiceMock', fee, { from: owner }); 83 | await this.serviceReceiver.pay('ServiceMock', { value: fee, from: thirdParty }); 84 | }); 85 | 86 | context('when the sender is owner', function () { 87 | it('should withdraw', async function () { 88 | const amount = ether('0.05'); 89 | 90 | const contractBalanceTracker = await balance.tracker(this.serviceReceiver.address); 91 | const ownerBalanceTracker = await balance.tracker(owner); 92 | 93 | await this.serviceReceiver.withdraw(amount, { from: owner, gasPrice: 0 }); 94 | 95 | expect(await contractBalanceTracker.delta()).to.be.bignumber.equal(amount.neg()); 96 | expect(await ownerBalanceTracker.delta()).to.be.bignumber.equal(amount); 97 | }); 98 | }); 99 | 100 | context('when the sender is not owner', function () { 101 | it('reverts', async function () { 102 | const amount = ether('0.05'); 103 | 104 | await expectRevert( 105 | this.serviceReceiver.withdraw(amount, { from: thirdParty }), 106 | 'Ownable: caller is not the owner', 107 | ); 108 | }); 109 | }); 110 | }); 111 | 112 | context('like a Ownable', function () { 113 | beforeEach(async function () { 114 | this.ownable = this.serviceReceiver; 115 | }); 116 | 117 | shouldBehaveLikeOwnable(owner, [thirdParty]); 118 | }); 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /dist/ServiceReceiver.dist.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | // File: @openzeppelin/contracts/GSN/Context.sol 4 | 5 | pragma solidity ^0.7.0; 6 | 7 | /* 8 | * @dev Provides information about the current execution context, including the 9 | * sender of the transaction and its data. While these are generally available 10 | * via msg.sender and msg.data, they should not be accessed in such a direct 11 | * manner, since when dealing with GSN meta-transactions the account sending and 12 | * paying for execution may not be the actual sender (as far as an application 13 | * is concerned). 14 | * 15 | * This contract is only required for intermediate, library-like contracts. 16 | */ 17 | abstract contract Context { 18 | function _msgSender() internal view virtual returns (address payable) { 19 | return msg.sender; 20 | } 21 | 22 | function _msgData() internal view virtual returns (bytes memory) { 23 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 24 | return msg.data; 25 | } 26 | } 27 | 28 | // File: @openzeppelin/contracts/access/Ownable.sol 29 | 30 | 31 | 32 | pragma solidity ^0.7.0; 33 | 34 | /** 35 | * @dev Contract module which provides a basic access control mechanism, where 36 | * there is an account (an owner) that can be granted exclusive access to 37 | * specific functions. 38 | * 39 | * By default, the owner account will be the one that deploys the contract. This 40 | * can later be changed with {transferOwnership}. 41 | * 42 | * This module is used through inheritance. It will make available the modifier 43 | * `onlyOwner`, which can be applied to your functions to restrict their use to 44 | * the owner. 45 | */ 46 | abstract contract Ownable is Context { 47 | address private _owner; 48 | 49 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 50 | 51 | /** 52 | * @dev Initializes the contract setting the deployer as the initial owner. 53 | */ 54 | constructor () { 55 | address msgSender = _msgSender(); 56 | _owner = msgSender; 57 | emit OwnershipTransferred(address(0), msgSender); 58 | } 59 | 60 | /** 61 | * @dev Returns the address of the current owner. 62 | */ 63 | function owner() public view returns (address) { 64 | return _owner; 65 | } 66 | 67 | /** 68 | * @dev Throws if called by any account other than the owner. 69 | */ 70 | modifier onlyOwner() { 71 | require(_owner == _msgSender(), "Ownable: caller is not the owner"); 72 | _; 73 | } 74 | 75 | /** 76 | * @dev Leaves the contract without owner. It will not be possible to call 77 | * `onlyOwner` functions anymore. Can only be called by the current owner. 78 | * 79 | * NOTE: Renouncing ownership will leave the contract without an owner, 80 | * thereby removing any functionality that is only available to the owner. 81 | */ 82 | function renounceOwnership() public virtual onlyOwner { 83 | emit OwnershipTransferred(_owner, address(0)); 84 | _owner = address(0); 85 | } 86 | 87 | /** 88 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 89 | * Can only be called by the current owner. 90 | */ 91 | function transferOwnership(address newOwner) public virtual onlyOwner { 92 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 93 | emit OwnershipTransferred(_owner, newOwner); 94 | _owner = newOwner; 95 | } 96 | } 97 | 98 | 99 | // File: contracts/service/ServiceReceiver.sol 100 | 101 | 102 | 103 | pragma solidity ^0.7.0; 104 | 105 | 106 | /** 107 | * @title ServiceReceiver 108 | * @dev Implementation of the ServiceReceiver 109 | */ 110 | contract ServiceReceiver is Ownable { 111 | 112 | mapping (bytes32 => uint256) private _prices; 113 | 114 | event Created(string serviceName, address indexed serviceAddress); 115 | 116 | function pay(string memory serviceName) public payable { 117 | require(msg.value == _prices[_toBytes32(serviceName)], "ServiceReceiver: incorrect price"); 118 | 119 | emit Created(serviceName, _msgSender()); 120 | } 121 | 122 | function getPrice(string memory serviceName) public view returns (uint256) { 123 | return _prices[_toBytes32(serviceName)]; 124 | } 125 | 126 | function setPrice(string memory serviceName, uint256 amount) public onlyOwner { 127 | _prices[_toBytes32(serviceName)] = amount; 128 | } 129 | 130 | function withdraw(uint256 amount) public onlyOwner { 131 | payable(owner()).transfer(amount); 132 | } 133 | 134 | function _toBytes32(string memory serviceName) private pure returns (bytes32) { 135 | return keccak256(abi.encode(serviceName)); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /test/token/BEP20/CommonBEP20.test.js: -------------------------------------------------------------------------------- 1 | const { BN, ether, expectRevert } = require('@openzeppelin/test-helpers'); 2 | 3 | const { shouldBehaveLikeBEP20 } = require('./behaviours/BEP20.behaviour'); 4 | const { shouldBehaveLikeBEP20Burnable } = require('./behaviours/BEP20Burnable.behaviour'); 5 | const { shouldBehaveLikeBEP20Capped } = require('./behaviours/BEP20Capped.behaviour'); 6 | const { shouldBehaveLikeBEP20Mintable } = require('./behaviours/BEP20Mintable.behaviour'); 7 | 8 | const CommonBEP20 = artifacts.require('CommonBEP20'); 9 | const ServiceReceiver = artifacts.require('ServiceReceiver'); 10 | 11 | contract('CommonBEP20', function ([owner, other, thirdParty]) { 12 | const _name = 'CommonBEP20'; 13 | const _symbol = 'BEP20'; 14 | const _decimals = new BN(8); 15 | const _cap = new BN(200000000); 16 | const _initialSupply = new BN(100000000); 17 | 18 | const fee = ether('0.1'); 19 | 20 | beforeEach(async function () { 21 | this.serviceReceiver = await ServiceReceiver.new({ from: owner }); 22 | await this.serviceReceiver.setPrice('CommonBEP20', fee); 23 | }); 24 | 25 | context('creating valid token', function () { 26 | describe('as a BEP20Capped', function () { 27 | it('requires a non-zero cap', async function () { 28 | await expectRevert( 29 | CommonBEP20.new( 30 | _name, 31 | _symbol, 32 | _decimals, 33 | 0, 34 | _initialSupply, 35 | this.serviceReceiver.address, 36 | { 37 | from: owner, 38 | value: fee, 39 | }, 40 | ), 41 | 'BEP20Capped: cap is 0', 42 | ); 43 | }); 44 | }); 45 | 46 | describe('as a CommonBEP20', function () { 47 | describe('without initial supply', function () { 48 | beforeEach(async function () { 49 | this.token = await CommonBEP20.new( 50 | _name, 51 | _symbol, 52 | _decimals, 53 | _cap, 54 | 0, 55 | this.serviceReceiver.address, 56 | { 57 | from: owner, 58 | value: fee, 59 | }, 60 | ); 61 | }); 62 | 63 | describe('once deployed', function () { 64 | it('total supply should be equal to zero', async function () { 65 | (await this.token.totalSupply()).should.be.bignumber.equal(new BN(0)); 66 | }); 67 | 68 | it('owner balance should be equal to zero', async function () { 69 | (await this.token.balanceOf(owner)).should.be.bignumber.equal(new BN(0)); 70 | }); 71 | }); 72 | }); 73 | 74 | describe('with initial supply', function () { 75 | beforeEach(async function () { 76 | this.token = await CommonBEP20.new( 77 | _name, 78 | _symbol, 79 | _decimals, 80 | _cap, 81 | _initialSupply, 82 | this.serviceReceiver.address, 83 | { 84 | from: owner, 85 | value: fee, 86 | }, 87 | ); 88 | }); 89 | 90 | describe('once deployed', function () { 91 | it('total supply should be equal to initial supply', async function () { 92 | (await this.token.totalSupply()).should.be.bignumber.equal(_initialSupply); 93 | }); 94 | 95 | it('owner balance should be equal to initial supply', async function () { 96 | (await this.token.balanceOf(owner)).should.be.bignumber.equal(_initialSupply); 97 | }); 98 | }); 99 | }); 100 | }); 101 | }); 102 | 103 | context('CommonBEP20 token behaviours', function () { 104 | beforeEach(async function () { 105 | this.token = await CommonBEP20.new( 106 | _name, 107 | _symbol, 108 | _decimals, 109 | _cap, 110 | _initialSupply, 111 | this.serviceReceiver.address, 112 | { 113 | from: owner, 114 | value: fee, 115 | }, 116 | ); 117 | }); 118 | 119 | context('like a BEP20', function () { 120 | shouldBehaveLikeBEP20(_name, _symbol, _decimals, _initialSupply, [owner, other, thirdParty]); 121 | }); 122 | 123 | context('like a BEP20Capped', function () { 124 | beforeEach(async function () { 125 | // NOTE: burning initial supply to test cap 126 | await this.token.burn(_initialSupply, { from: owner }); 127 | }); 128 | shouldBehaveLikeBEP20Capped(_cap, [owner, other]); 129 | }); 130 | 131 | context('like a BEP20Mintable', function () { 132 | shouldBehaveLikeBEP20Mintable(_initialSupply, [owner, thirdParty]); 133 | }); 134 | 135 | context('like a BEP20Burnable', function () { 136 | shouldBehaveLikeBEP20Burnable(_initialSupply, [owner, thirdParty]); 137 | }); 138 | 139 | context('like a CommonBEP20', function () { 140 | describe('when the sender doesn\'t have minting permission', function () { 141 | const from = thirdParty; 142 | 143 | it('cannot mint', async function () { 144 | const amount = new BN(50); 145 | 146 | await expectRevert( 147 | this.token.mint(thirdParty, amount, { from }), 148 | 'Ownable: caller is not the owner', 149 | ); 150 | }); 151 | 152 | it('cannot finish minting', async function () { 153 | await expectRevert( 154 | this.token.finishMinting({ from }), 155 | 'Ownable: caller is not the owner', 156 | ); 157 | }); 158 | }); 159 | }); 160 | }); 161 | }); 162 | -------------------------------------------------------------------------------- /analysis/uml/ServiceReceiver.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | UmlClassDiagram 11 | 12 | 13 | 14 | 0 15 | 16 | <<Abstract>> 17 | Context 18 | 19 | 20 | 21 | Internal: 22 |    _msgSender(): (payable: address) 23 |    _msgData(): bytes 24 | 25 | 26 | 27 | 1 28 | 29 | <<Abstract>> 30 | Ownable 31 | 32 | Private: 33 |   _owner: address 34 | 35 | Public: 36 |    <<event>> OwnershipTransferred(previousOwner: address, newOwner: address) 37 |    <<modifier>> onlyOwner() 38 |    constructor() 39 |    owner(): address 40 |    renounceOwnership() 41 |    transferOwnership(newOwner: address) 42 | 43 | 44 | 45 | 1->0 46 | 47 | 48 | 49 | 50 | 51 | 2 52 | 53 | ServiceReceiver 54 | 55 | Private: 56 |   _prices: mapping(bytes32=>uint256) 57 | 58 | Private: 59 |    _toBytes32(serviceName: string): bytes32 60 | Public: 61 |    <<payable>> pay(serviceName: string) 62 |    <<event>> Created(serviceName: string, serviceAddress: address) 63 |    getPrice(serviceName: string): uint256 64 |    setPrice(serviceName: string, amount: uint256) 65 |    withdraw(amount: uint256) 66 | 67 | 68 | 69 | 2->1 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /contracts/token/BEP20/lib/BEP20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.7.0; 4 | 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | import "@openzeppelin/contracts/math/SafeMath.sol"; 7 | 8 | import "./IBEP20.sol"; 9 | 10 | /** 11 | * @dev Implementation of the {IBEP20} interface. 12 | * 13 | * This implementation is agnostic to the way tokens are created. This means 14 | * that a supply mechanism has to be added in a derived contract using {_mint}. 15 | * 16 | * We have followed general OpenZeppelin guidelines: functions revert instead 17 | * of returning `false` on failure. This behavior is nonetheless conventional 18 | * and does not conflict with the expectations of BEP20 applications. 19 | * 20 | * Additionally, an {Approval} event is emitted on calls to {transferFrom}. 21 | * This allows applications to reconstruct the allowance for all accounts just 22 | * by listening to said events. Other implementations of the EIP may not emit 23 | * these events, as it isn't required by the specification. 24 | * 25 | * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} 26 | * functions have been added to mitigate the well-known issues around setting 27 | * allowances. See {IBEP20-approve}. 28 | */ 29 | contract BEP20 is Ownable, IBEP20 { 30 | using SafeMath for uint256; 31 | 32 | mapping (address => uint256) private _balances; 33 | 34 | mapping (address => mapping (address => uint256)) private _allowances; 35 | 36 | uint256 private _totalSupply; 37 | 38 | string private _name; 39 | string private _symbol; 40 | uint8 private _decimals; 41 | 42 | /** 43 | * @dev Sets the values for {name} and {symbol}, initializes {decimals} with 44 | * a default value of 18. 45 | * 46 | * To select a different value for {decimals}, use {_setupDecimals}. 47 | * 48 | * All three of these values are immutable: they can only be set once during 49 | * construction. 50 | */ 51 | constructor (string memory name_, string memory symbol_) { 52 | _name = name_; 53 | _symbol = symbol_; 54 | _decimals = 18; 55 | } 56 | 57 | /** 58 | * @dev Returns the name of the token. 59 | */ 60 | function name() public view override returns (string memory) { 61 | return _name; 62 | } 63 | 64 | /** 65 | * @dev Returns the symbol of the token, usually a shorter version of the 66 | * name. 67 | */ 68 | function symbol() public view override returns (string memory) { 69 | return _symbol; 70 | } 71 | 72 | /** 73 | * @dev Returns the number of decimals used to get its user representation. 74 | * For example, if `decimals` equals `2`, a balance of `505` tokens should 75 | * be displayed to a user as `5,05` (`505 / 10 ** 2`). 76 | * 77 | * Tokens usually opt for a value of 18, imitating the relationship between 78 | * Ether and Wei. This is the value {BEP20} uses, unless {_setupDecimals} is 79 | * called. 80 | * 81 | * NOTE: This information is only used for _display_ purposes: it in 82 | * no way affects any of the arithmetic of the contract, including 83 | * {IBEP20-balanceOf} and {IBEP20-transfer}. 84 | */ 85 | function decimals() public view override returns (uint8) { 86 | return _decimals; 87 | } 88 | 89 | /** 90 | * @dev See {IBEP20-totalSupply}. 91 | */ 92 | function totalSupply() public view override returns (uint256) { 93 | return _totalSupply; 94 | } 95 | 96 | /** 97 | * @dev See {IBEP20-balanceOf}. 98 | */ 99 | function balanceOf(address account) public view override returns (uint256) { 100 | return _balances[account]; 101 | } 102 | 103 | /** 104 | * @dev See {IBEP20-getOwner}. 105 | */ 106 | function getOwner() public view override returns (address) { 107 | return owner(); 108 | } 109 | 110 | /** 111 | * @dev See {IBEP20-transfer}. 112 | * 113 | * Requirements: 114 | * 115 | * - `recipient` cannot be the zero address. 116 | * - the caller must have a balance of at least `amount`. 117 | */ 118 | function transfer(address recipient, uint256 amount) public virtual override returns (bool) { 119 | _transfer(_msgSender(), recipient, amount); 120 | return true; 121 | } 122 | 123 | /** 124 | * @dev See {IBEP20-transferFrom}. 125 | * 126 | * Emits an {Approval} event indicating the updated allowance. This is not 127 | * required by the EIP. See the note at the beginning of {BEP20}. 128 | * 129 | * Requirements: 130 | * 131 | * - `sender` and `recipient` cannot be the zero address. 132 | * - `sender` must have a balance of at least `amount`. 133 | * - the caller must have allowance for ``sender``'s tokens of at least 134 | * `amount`. 135 | */ 136 | function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { 137 | _transfer(sender, recipient, amount); 138 | _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "BEP20: transfer amount exceeds allowance")); 139 | return true; 140 | } 141 | 142 | /** 143 | * @dev See {IBEP20-approve}. 144 | * 145 | * Requirements: 146 | * 147 | * - `spender` cannot be the zero address. 148 | */ 149 | function approve(address spender, uint256 amount) public virtual override returns (bool) { 150 | _approve(_msgSender(), spender, amount); 151 | return true; 152 | } 153 | 154 | /** 155 | * @dev See {IBEP20-allowance}. 156 | */ 157 | function allowance(address owner, address spender) public view virtual override returns (uint256) { 158 | return _allowances[owner][spender]; 159 | } 160 | 161 | /** 162 | * @dev Atomically increases the allowance granted to `spender` by the caller. 163 | * 164 | * This is an alternative to {approve} that can be used as a mitigation for 165 | * problems described in {IBEP20-approve}. 166 | * 167 | * Emits an {Approval} event indicating the updated allowance. 168 | * 169 | * Requirements: 170 | * 171 | * - `spender` cannot be the zero address. 172 | */ 173 | function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { 174 | _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); 175 | return true; 176 | } 177 | 178 | /** 179 | * @dev Atomically decreases the allowance granted to `spender` by the caller. 180 | * 181 | * This is an alternative to {approve} that can be used as a mitigation for 182 | * problems described in {IBEP20-approve}. 183 | * 184 | * Emits an {Approval} event indicating the updated allowance. 185 | * 186 | * Requirements: 187 | * 188 | * - `spender` cannot be the zero address. 189 | * - `spender` must have allowance for the caller of at least 190 | * `subtractedValue`. 191 | */ 192 | function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { 193 | _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "BEP20: decreased allowance below zero")); 194 | return true; 195 | } 196 | 197 | /** 198 | * @dev Moves tokens `amount` from `sender` to `recipient`. 199 | * 200 | * This is internal function is equivalent to {transfer}, and can be used to 201 | * e.g. implement automatic token fees, slashing mechanisms, etc. 202 | * 203 | * Emits a {Transfer} event. 204 | * 205 | * Requirements: 206 | * 207 | * - `sender` cannot be the zero address. 208 | * - `recipient` cannot be the zero address. 209 | * - `sender` must have a balance of at least `amount`. 210 | */ 211 | function _transfer(address sender, address recipient, uint256 amount) internal virtual { 212 | require(sender != address(0), "BEP20: transfer from the zero address"); 213 | require(recipient != address(0), "BEP20: transfer to the zero address"); 214 | 215 | _beforeTokenTransfer(sender, recipient, amount); 216 | 217 | _balances[sender] = _balances[sender].sub(amount, "BEP20: transfer amount exceeds balance"); 218 | _balances[recipient] = _balances[recipient].add(amount); 219 | emit Transfer(sender, recipient, amount); 220 | } 221 | 222 | /** @dev Creates `amount` tokens and assigns them to `account`, increasing 223 | * the total supply. 224 | * 225 | * Emits a {Transfer} event with `from` set to the zero address. 226 | * 227 | * Requirements: 228 | * 229 | * - `to` cannot be the zero address. 230 | */ 231 | function _mint(address account, uint256 amount) internal virtual { 232 | require(account != address(0), "BEP20: mint to the zero address"); 233 | 234 | _beforeTokenTransfer(address(0), account, amount); 235 | 236 | _totalSupply = _totalSupply.add(amount); 237 | _balances[account] = _balances[account].add(amount); 238 | emit Transfer(address(0), account, amount); 239 | } 240 | 241 | /** 242 | * @dev Destroys `amount` tokens from `account`, reducing the 243 | * total supply. 244 | * 245 | * Emits a {Transfer} event with `to` set to the zero address. 246 | * 247 | * Requirements: 248 | * 249 | * - `account` cannot be the zero address. 250 | * - `account` must have at least `amount` tokens. 251 | */ 252 | function _burn(address account, uint256 amount) internal virtual { 253 | require(account != address(0), "BEP20: burn from the zero address"); 254 | 255 | _beforeTokenTransfer(account, address(0), amount); 256 | 257 | _balances[account] = _balances[account].sub(amount, "BEP20: burn amount exceeds balance"); 258 | _totalSupply = _totalSupply.sub(amount); 259 | emit Transfer(account, address(0), amount); 260 | } 261 | 262 | /** 263 | * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. 264 | * 265 | * This internal function is equivalent to `approve`, and can be used to 266 | * e.g. set automatic allowances for certain subsystems, etc. 267 | * 268 | * Emits an {Approval} event. 269 | * 270 | * Requirements: 271 | * 272 | * - `owner` cannot be the zero address. 273 | * - `spender` cannot be the zero address. 274 | */ 275 | function _approve(address owner, address spender, uint256 amount) internal virtual { 276 | require(owner != address(0), "BEP20: approve from the zero address"); 277 | require(spender != address(0), "BEP20: approve to the zero address"); 278 | 279 | _allowances[owner][spender] = amount; 280 | emit Approval(owner, spender, amount); 281 | } 282 | 283 | /** 284 | * @dev Sets {decimals} to a value other than the default one of 18. 285 | * 286 | * WARNING: This function should only be called from the constructor. Most 287 | * applications that interact with token contracts will not expect 288 | * {decimals} to ever change, and may work incorrectly if it does. 289 | */ 290 | function _setupDecimals(uint8 decimals_) internal { 291 | _decimals = decimals_; 292 | } 293 | 294 | /** 295 | * @dev Hook that is called before any transfer of tokens. This includes 296 | * minting and burning. 297 | * 298 | * Calling conditions: 299 | * 300 | * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens 301 | * will be to transferred to `to`. 302 | * - when `from` is zero, `amount` tokens will be minted for `to`. 303 | * - when `to` is zero, `amount` of ``from``'s tokens will be burned. 304 | * - `from` and `to` are never both zero. 305 | * 306 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. 307 | */ 308 | function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } 309 | } 310 | -------------------------------------------------------------------------------- /test/token/BEP20/behaviours/BEP20.behaviour.js: -------------------------------------------------------------------------------- 1 | const { BN, constants, expectEvent, expectRevert } = require('@openzeppelin/test-helpers'); 2 | const { ZERO_ADDRESS } = constants; 3 | 4 | const { shouldBehaveLikeOwnable } = require('../../../access/Ownable.behavior'); 5 | 6 | function shouldBehaveLikeBEP20 (name, symbol, decimals, initialBalance, [owner, other, thirdParty]) { 7 | it('has a name', async function () { 8 | (await this.token.name()).should.be.equal(name); 9 | }); 10 | 11 | it('has a symbol', async function () { 12 | (await this.token.symbol()).should.be.equal(symbol); 13 | }); 14 | 15 | it('has an amount of decimals', async function () { 16 | (await this.token.decimals()).should.be.bignumber.equal(decimals); 17 | }); 18 | 19 | describe('total supply', function () { 20 | it('returns the total amount of tokens', async function () { 21 | (await this.token.totalSupply()).should.be.bignumber.equal(initialBalance); 22 | }); 23 | }); 24 | 25 | describe('balanceOf', function () { 26 | describe('when the requested account has no tokens', function () { 27 | it('returns zero', async function () { 28 | (await this.token.balanceOf(other)).should.be.bignumber.equal(new BN(0)); 29 | }); 30 | }); 31 | 32 | describe('when the requested account has some tokens', function () { 33 | it('returns the total amount of tokens', async function () { 34 | (await this.token.balanceOf(owner)).should.be.bignumber.equal(initialBalance); 35 | }); 36 | }); 37 | }); 38 | 39 | describe('getOwner', function () { 40 | it('returns the token owner', async function () { 41 | expect(await this.token.getOwner()).to.equal(owner); 42 | }); 43 | }); 44 | 45 | describe('transfer', function () { 46 | describe('when the recipient is not the zero address', function () { 47 | const to = thirdParty; 48 | 49 | describe('when the sender does not have enough balance', function () { 50 | const amount = initialBalance.addn(1); 51 | 52 | it('reverts', async function () { 53 | await expectRevert( 54 | this.token.transfer(to, amount, { from: owner }), 55 | 'BEP20: transfer amount exceeds balance', 56 | ); 57 | }); 58 | }); 59 | 60 | describe('when the sender has enough balance', function () { 61 | const amount = initialBalance; 62 | 63 | it('transfers the requested amount', async function () { 64 | await this.token.transfer(to, amount, { from: owner }); 65 | 66 | (await this.token.balanceOf(owner)).should.be.bignumber.equal(new BN(0)); 67 | 68 | (await this.token.balanceOf(to)).should.be.bignumber.equal(amount); 69 | }); 70 | 71 | it('emits a transfer event', async function () { 72 | const { logs } = await this.token.transfer(to, amount, { from: owner }); 73 | 74 | expectEvent.inLogs(logs, 'Transfer', { 75 | from: owner, 76 | to: to, 77 | value: amount, 78 | }); 79 | }); 80 | }); 81 | }); 82 | 83 | describe('when the recipient is the zero address', function () { 84 | const to = ZERO_ADDRESS; 85 | 86 | it('reverts', async function () { 87 | await expectRevert( 88 | this.token.transfer(to, initialBalance, { from: owner }), 89 | 'BEP20: transfer to the zero address', 90 | ); 91 | }); 92 | }); 93 | }); 94 | 95 | describe('approve', function () { 96 | describe('when the spender is not the zero address', function () { 97 | const spender = thirdParty; 98 | 99 | describe('when the sender has enough balance', function () { 100 | const amount = initialBalance; 101 | 102 | it('emits an approval event', async function () { 103 | const { logs } = await this.token.approve(spender, amount, { from: owner }); 104 | 105 | expectEvent.inLogs(logs, 'Approval', { 106 | owner: owner, 107 | spender: spender, 108 | value: amount, 109 | }); 110 | }); 111 | 112 | describe('when there was no approved amount before', function () { 113 | it('approves the requested amount', async function () { 114 | await this.token.approve(spender, amount, { from: owner }); 115 | 116 | (await this.token.allowance(owner, spender)).should.be.bignumber.equal(amount); 117 | }); 118 | }); 119 | 120 | describe('when the spender had an approved amount', function () { 121 | beforeEach(async function () { 122 | await this.token.approve(spender, 1, { from: owner }); 123 | }); 124 | 125 | it('approves the requested amount and replaces the previous one', async function () { 126 | await this.token.approve(spender, amount, { from: owner }); 127 | 128 | (await this.token.allowance(owner, spender)).should.be.bignumber.equal(amount); 129 | }); 130 | }); 131 | }); 132 | 133 | describe('when the sender does not have enough balance', function () { 134 | const amount = initialBalance.addn(1); 135 | 136 | it('emits an approval event', async function () { 137 | const { logs } = await this.token.approve(spender, amount, { from: owner }); 138 | 139 | expectEvent.inLogs(logs, 'Approval', { 140 | owner: owner, 141 | spender: spender, 142 | value: amount, 143 | }); 144 | }); 145 | 146 | describe('when there was no approved amount before', function () { 147 | it('approves the requested amount', async function () { 148 | await this.token.approve(spender, amount, { from: owner }); 149 | 150 | (await this.token.allowance(owner, spender)).should.be.bignumber.equal(amount); 151 | }); 152 | }); 153 | 154 | describe('when the spender had an approved amount', function () { 155 | beforeEach(async function () { 156 | await this.token.approve(spender, 1, { from: owner }); 157 | }); 158 | 159 | it('approves the requested amount and replaces the previous one', async function () { 160 | await this.token.approve(spender, amount, { from: owner }); 161 | 162 | (await this.token.allowance(owner, spender)).should.be.bignumber.equal(amount); 163 | }); 164 | }); 165 | }); 166 | }); 167 | 168 | describe('when the spender is the zero address', function () { 169 | const amount = initialBalance; 170 | const spender = ZERO_ADDRESS; 171 | 172 | it('reverts', async function () { 173 | await expectRevert( 174 | this.token.approve(spender, amount, { from: owner }), 175 | 'BEP20: approve to the zero address', 176 | ); 177 | }); 178 | }); 179 | }); 180 | 181 | describe('transfer from', function () { 182 | const spender = thirdParty; 183 | 184 | describe('when the recipient is not the zero address', function () { 185 | const to = other; 186 | 187 | describe('when the spender has enough approved balance', function () { 188 | beforeEach(async function () { 189 | await this.token.approve(spender, initialBalance, { from: owner }); 190 | }); 191 | 192 | describe('when the owner has enough balance', function () { 193 | const amount = initialBalance; 194 | 195 | it('transfers the requested amount', async function () { 196 | await this.token.transferFrom(owner, to, amount, { from: spender }); 197 | 198 | (await this.token.balanceOf(owner)).should.be.bignumber.equal(new BN(0)); 199 | 200 | (await this.token.balanceOf(to)).should.be.bignumber.equal(amount); 201 | }); 202 | 203 | it('decreases the spender allowance', async function () { 204 | await this.token.transferFrom(owner, to, amount, { from: spender }); 205 | 206 | (await this.token.allowance(owner, spender)).should.be.bignumber.equal(new BN(0)); 207 | }); 208 | 209 | it('emits a transfer event', async function () { 210 | const { logs } = await this.token.transferFrom(owner, to, amount, { from: spender }); 211 | 212 | expectEvent.inLogs(logs, 'Transfer', { 213 | from: owner, 214 | to: to, 215 | value: amount, 216 | }); 217 | }); 218 | }); 219 | 220 | describe('when the owner does not have enough balance', function () { 221 | const amount = initialBalance.addn(1); 222 | 223 | it('reverts', async function () { 224 | await expectRevert( 225 | this.token.transferFrom(owner, to, amount, { from: spender }), 226 | 'BEP20: transfer amount exceeds balance', 227 | ); 228 | }); 229 | }); 230 | }); 231 | 232 | describe('when the spender does not have enough approved balance', function () { 233 | beforeEach(async function () { 234 | await this.token.approve(spender, 99, { from: owner }); 235 | }); 236 | 237 | describe('when the owner has enough balance', function () { 238 | const amount = initialBalance; 239 | 240 | it('reverts', async function () { 241 | await expectRevert( 242 | this.token.transferFrom(owner, to, amount, { from: spender }), 243 | 'BEP20: transfer amount exceeds allowance', 244 | ); 245 | }); 246 | }); 247 | 248 | describe('when the owner does not have enough balance', function () { 249 | const amount = initialBalance.addn(1); 250 | 251 | it('reverts', async function () { 252 | await expectRevert( 253 | this.token.transferFrom(owner, to, amount, { from: spender }), 254 | 'BEP20: transfer amount exceeds balance', 255 | ); 256 | }); 257 | }); 258 | }); 259 | }); 260 | 261 | describe('when the recipient is the zero address', function () { 262 | const amount = initialBalance; 263 | const to = ZERO_ADDRESS; 264 | 265 | beforeEach(async function () { 266 | await this.token.approve(spender, amount, { from: owner }); 267 | }); 268 | 269 | it('reverts', async function () { 270 | await expectRevert( 271 | this.token.transferFrom(owner, to, amount, { from: spender }), 272 | 'BEP20: transfer to the zero address', 273 | ); 274 | }); 275 | }); 276 | }); 277 | 278 | describe('decrease allowance', function () { 279 | describe('when the spender is not the zero address', function () { 280 | const spender = thirdParty; 281 | 282 | function shouldDecreaseApproval (amount) { 283 | describe('when there was no approved amount before', function () { 284 | it('reverts', async function () { 285 | await expectRevert( 286 | this.token.decreaseAllowance(spender, amount, { from: owner }), 287 | 'BEP20: decreased allowance below zero', 288 | ); 289 | }); 290 | }); 291 | 292 | describe('when the spender had an approved amount', function () { 293 | const approvedAmount = amount; 294 | 295 | beforeEach(async function () { 296 | ({ logs: this.logs } = await this.token.approve(spender, approvedAmount, { from: owner })); 297 | }); 298 | 299 | it('emits an approval event', async function () { 300 | const { logs } = await this.token.decreaseAllowance(spender, approvedAmount, { from: owner }); 301 | 302 | expectEvent.inLogs(logs, 'Approval', { 303 | owner: owner, 304 | spender: spender, 305 | value: new BN(0), 306 | }); 307 | }); 308 | 309 | it('decreases the spender allowance subtracting the requested amount', async function () { 310 | await this.token.decreaseAllowance(spender, approvedAmount.subn(1), { from: owner }); 311 | 312 | (await this.token.allowance(owner, spender)).should.be.bignumber.equal(new BN(1)); 313 | }); 314 | 315 | it('sets the allowance to zero when all allowance is removed', async function () { 316 | await this.token.decreaseAllowance(spender, approvedAmount, { from: owner }); 317 | (await this.token.allowance(owner, spender)).should.be.bignumber.equal(new BN(0)); 318 | }); 319 | 320 | it('reverts when more than the full allowance is removed', async function () { 321 | await expectRevert( 322 | this.token.decreaseAllowance(spender, approvedAmount.addn(1), { from: owner }), 323 | 'BEP20: decreased allowance below zero', 324 | ); 325 | }); 326 | }); 327 | } 328 | 329 | describe('when the sender has enough balance', function () { 330 | const amount = initialBalance; 331 | 332 | shouldDecreaseApproval(amount); 333 | }); 334 | 335 | describe('when the sender does not have enough balance', function () { 336 | const amount = initialBalance.addn(1); 337 | 338 | shouldDecreaseApproval(amount); 339 | }); 340 | }); 341 | 342 | describe('when the spender is the zero address', function () { 343 | const amount = initialBalance; 344 | const spender = ZERO_ADDRESS; 345 | 346 | it('reverts', async function () { 347 | await expectRevert( 348 | this.token.decreaseAllowance(spender, amount, { from: owner }), 349 | 'BEP20: decreased allowance below zero', 350 | ); 351 | }); 352 | }); 353 | }); 354 | 355 | describe('increase allowance', function () { 356 | const amount = initialBalance; 357 | 358 | describe('when the spender is not the zero address', function () { 359 | const spender = thirdParty; 360 | 361 | describe('when the sender has enough balance', function () { 362 | it('emits an approval event', async function () { 363 | const { logs } = await this.token.increaseAllowance(spender, amount, { from: owner }); 364 | 365 | expectEvent.inLogs(logs, 'Approval', { 366 | owner: owner, 367 | spender: spender, 368 | value: amount, 369 | }); 370 | }); 371 | 372 | describe('when there was no approved amount before', function () { 373 | it('approves the requested amount', async function () { 374 | await this.token.increaseAllowance(spender, amount, { from: owner }); 375 | 376 | (await this.token.allowance(owner, spender)).should.be.bignumber.equal(amount); 377 | }); 378 | }); 379 | 380 | describe('when the spender had an approved amount', function () { 381 | beforeEach(async function () { 382 | await this.token.approve(spender, 1, { from: owner }); 383 | }); 384 | 385 | it('increases the spender allowance adding the requested amount', async function () { 386 | await this.token.increaseAllowance(spender, amount, { from: owner }); 387 | 388 | (await this.token.allowance(owner, spender)).should.be.bignumber.equal(amount.addn(1)); 389 | }); 390 | }); 391 | }); 392 | 393 | describe('when the sender does not have enough balance', function () { 394 | const amount = initialBalance.addn(1); 395 | 396 | it('emits an approval event', async function () { 397 | const { logs } = await this.token.increaseAllowance(spender, amount, { from: owner }); 398 | 399 | expectEvent.inLogs(logs, 'Approval', { 400 | owner: owner, 401 | spender: spender, 402 | value: amount, 403 | }); 404 | }); 405 | 406 | describe('when there was no approved amount before', function () { 407 | it('approves the requested amount', async function () { 408 | await this.token.increaseAllowance(spender, amount, { from: owner }); 409 | 410 | (await this.token.allowance(owner, spender)).should.be.bignumber.equal(amount); 411 | }); 412 | }); 413 | 414 | describe('when the spender had an approved amount', function () { 415 | beforeEach(async function () { 416 | await this.token.approve(spender, 1, { from: owner }); 417 | }); 418 | 419 | it('increases the spender allowance adding the requested amount', async function () { 420 | await this.token.increaseAllowance(spender, amount, { from: owner }); 421 | 422 | (await this.token.allowance(owner, spender)).should.be.bignumber.equal(amount.addn(1)); 423 | }); 424 | }); 425 | }); 426 | }); 427 | 428 | describe('when the spender is the zero address', function () { 429 | const spender = ZERO_ADDRESS; 430 | 431 | it('reverts', async function () { 432 | await expectRevert( 433 | this.token.increaseAllowance(spender, amount, { from: owner }), 434 | 'BEP20: approve to the zero address', 435 | ); 436 | }); 437 | }); 438 | }); 439 | 440 | context('like a Ownable', function () { 441 | beforeEach(async function () { 442 | this.ownable = this.token; 443 | }); 444 | 445 | shouldBehaveLikeOwnable(owner, [thirdParty]); 446 | }); 447 | } 448 | 449 | module.exports = { 450 | shouldBehaveLikeBEP20, 451 | }; 452 | -------------------------------------------------------------------------------- /analysis/uml/StandardBEP20.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | UmlClassDiagram 11 | 12 | 13 | 14 | 0 15 | 16 | <<Abstract>> 17 | Context 18 | 19 | 20 | 21 | Internal: 22 |    _msgSender(): (payable: address) 23 |    _msgData(): bytes 24 | 25 | 26 | 27 | 1 28 | 29 | <<Abstract>> 30 | Ownable 31 | 32 | Private: 33 |   _owner: address 34 | 35 | Public: 36 |    <<event>> OwnershipTransferred(previousOwner: address, newOwner: address) 37 |    <<modifier>> onlyOwner() 38 |    constructor() 39 |    owner(): address 40 |    renounceOwnership() 41 |    transferOwnership(newOwner: address) 42 | 43 | 44 | 45 | 1->0 46 | 47 | 48 | 49 | 50 | 51 | 2 52 | 53 | <<Library>> 54 | SafeMath 55 | 56 | 57 | 58 | Internal: 59 |    add(a: uint256, b: uint256): uint256 60 |    sub(a: uint256, b: uint256): uint256 61 |    sub(a: uint256, b: uint256, errorMessage: string): uint256 62 |    mul(a: uint256, b: uint256): uint256 63 |    div(a: uint256, b: uint256): uint256 64 |    div(a: uint256, b: uint256, errorMessage: string): uint256 65 |    mod(a: uint256, b: uint256): uint256 66 |    mod(a: uint256, b: uint256, errorMessage: string): uint256 67 | 68 | 69 | 70 | 3 71 | 72 | <<Interface>> 73 | IBEP20 74 | 75 | 76 | 77 | External: 78 |     name(): string 79 |     symbol(): string 80 |     decimals(): uint8 81 |     totalSupply(): uint256 82 |     balanceOf(account: address): uint256 83 |     getOwner(): address 84 |     transfer(recipient: address, amount: uint256): bool 85 |     transferFrom(sender: address, recipient: address, amount: uint256): bool 86 |     approve(spender: address, amount: uint256): bool 87 |     allowance(_owner: address, spender: address): uint256 88 | Public: 89 |    <<event>> Transfer(from: address, to: address, value: uint256) 90 |    <<event>> Approval(owner: address, spender: address, value: uint256) 91 | 92 | 93 | 94 | 4 95 | 96 | BEP20 97 | 98 | Private: 99 |   _balances: mapping(address=>uint256) 100 |   _allowances: mapping(address=>mapping(address=>uint256)) 101 |   _totalSupply: uint256 102 |   _name: string 103 |   _symbol: string 104 |   _decimals: uint8 105 | 106 | Internal: 107 |    _transfer(sender: address, recipient: address, amount: uint256) 108 |    _mint(account: address, amount: uint256) 109 |    _burn(account: address, amount: uint256) 110 |    _approve(owner: address, spender: address, amount: uint256) 111 |    _setupDecimals(decimals_: uint8) 112 |    _beforeTokenTransfer(from: address, to: address, amount: uint256) 113 | Public: 114 |    constructor(name_: string, symbol_: string) 115 |    name(): string 116 |    symbol(): string 117 |    decimals(): uint8 118 |    totalSupply(): uint256 119 |    balanceOf(account: address): uint256 120 |    getOwner(): address 121 |    transfer(recipient: address, amount: uint256): bool 122 |    transferFrom(sender: address, recipient: address, amount: uint256): bool 123 |    approve(spender: address, amount: uint256): bool 124 |    allowance(owner: address, spender: address): uint256 125 |    increaseAllowance(spender: address, addedValue: uint256): bool 126 |    decreaseAllowance(spender: address, subtractedValue: uint256): bool 127 | 128 | 129 | 130 | 4->1 131 | 132 | 133 | 134 | 135 | 136 | 4->2 137 | 138 | 139 | 140 | 141 | 142 | 4->3 143 | 144 | 145 | 146 | 147 | 148 | 5 149 | 150 | ServiceReceiver 151 | 152 | Private: 153 |   _prices: mapping(bytes32=>uint256) 154 | 155 | Private: 156 |    _toBytes32(serviceName: string): bytes32 157 | Public: 158 |    <<payable>> pay(serviceName: string) 159 |    <<event>> Created(serviceName: string, serviceAddress: address) 160 |    getPrice(serviceName: string): uint256 161 |    setPrice(serviceName: string, amount: uint256) 162 |    withdraw(amount: uint256) 163 | 164 | 165 | 166 | 5->1 167 | 168 | 169 | 170 | 171 | 172 | 6 173 | 174 | <<Abstract>> 175 | ServicePayer 176 | 177 | 178 | 179 | Public: 180 |    constructor(receiver: address, serviceName: string) 181 | 182 | 183 | 184 | 7 185 | 186 | StandardBEP20 187 | 188 | 189 | 190 | Public: 191 |    constructor(name: string, symbol: string, decimals: uint8, initialBalance: uint256, feeReceiver: address) 192 | 193 | 194 | 195 | 7->4 196 | 197 | 198 | 199 | 200 | 201 | 7->6 202 | 203 | 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /analysis/uml/SimpleBEP20.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | UmlClassDiagram 11 | 12 | 13 | 14 | 0 15 | 16 | <<Abstract>> 17 | Context 18 | 19 | 20 | 21 | Internal: 22 |    _msgSender(): (payable: address) 23 |    _msgData(): bytes 24 | 25 | 26 | 27 | 1 28 | 29 | <<Abstract>> 30 | Ownable 31 | 32 | Private: 33 |   _owner: address 34 | 35 | Public: 36 |    <<event>> OwnershipTransferred(previousOwner: address, newOwner: address) 37 |    <<modifier>> onlyOwner() 38 |    constructor() 39 |    owner(): address 40 |    renounceOwnership() 41 |    transferOwnership(newOwner: address) 42 | 43 | 44 | 45 | 1->0 46 | 47 | 48 | 49 | 50 | 51 | 2 52 | 53 | <<Library>> 54 | SafeMath 55 | 56 | 57 | 58 | Internal: 59 |    add(a: uint256, b: uint256): uint256 60 |    sub(a: uint256, b: uint256): uint256 61 |    sub(a: uint256, b: uint256, errorMessage: string): uint256 62 |    mul(a: uint256, b: uint256): uint256 63 |    div(a: uint256, b: uint256): uint256 64 |    div(a: uint256, b: uint256, errorMessage: string): uint256 65 |    mod(a: uint256, b: uint256): uint256 66 |    mod(a: uint256, b: uint256, errorMessage: string): uint256 67 | 68 | 69 | 70 | 3 71 | 72 | <<Interface>> 73 | IBEP20 74 | 75 | 76 | 77 | External: 78 |     name(): string 79 |     symbol(): string 80 |     decimals(): uint8 81 |     totalSupply(): uint256 82 |     balanceOf(account: address): uint256 83 |     getOwner(): address 84 |     transfer(recipient: address, amount: uint256): bool 85 |     transferFrom(sender: address, recipient: address, amount: uint256): bool 86 |     approve(spender: address, amount: uint256): bool 87 |     allowance(_owner: address, spender: address): uint256 88 | Public: 89 |    <<event>> Transfer(from: address, to: address, value: uint256) 90 |    <<event>> Approval(owner: address, spender: address, value: uint256) 91 | 92 | 93 | 94 | 4 95 | 96 | BEP20 97 | 98 | Private: 99 |   _balances: mapping(address=>uint256) 100 |   _allowances: mapping(address=>mapping(address=>uint256)) 101 |   _totalSupply: uint256 102 |   _name: string 103 |   _symbol: string 104 |   _decimals: uint8 105 | 106 | Internal: 107 |    _transfer(sender: address, recipient: address, amount: uint256) 108 |    _mint(account: address, amount: uint256) 109 |    _burn(account: address, amount: uint256) 110 |    _approve(owner: address, spender: address, amount: uint256) 111 |    _setupDecimals(decimals_: uint8) 112 |    _beforeTokenTransfer(from: address, to: address, amount: uint256) 113 | Public: 114 |    constructor(name_: string, symbol_: string) 115 |    name(): string 116 |    symbol(): string 117 |    decimals(): uint8 118 |    totalSupply(): uint256 119 |    balanceOf(account: address): uint256 120 |    getOwner(): address 121 |    transfer(recipient: address, amount: uint256): bool 122 |    transferFrom(sender: address, recipient: address, amount: uint256): bool 123 |    approve(spender: address, amount: uint256): bool 124 |    allowance(owner: address, spender: address): uint256 125 |    increaseAllowance(spender: address, addedValue: uint256): bool 126 |    decreaseAllowance(spender: address, subtractedValue: uint256): bool 127 | 128 | 129 | 130 | 4->1 131 | 132 | 133 | 134 | 135 | 136 | 4->2 137 | 138 | 139 | 140 | 141 | 142 | 4->3 143 | 144 | 145 | 146 | 147 | 148 | 5 149 | 150 | ServiceReceiver 151 | 152 | Private: 153 |   _prices: mapping(bytes32=>uint256) 154 | 155 | Private: 156 |    _toBytes32(serviceName: string): bytes32 157 | Public: 158 |    <<payable>> pay(serviceName: string) 159 |    <<event>> Created(serviceName: string, serviceAddress: address) 160 |    getPrice(serviceName: string): uint256 161 |    setPrice(serviceName: string, amount: uint256) 162 |    withdraw(amount: uint256) 163 | 164 | 165 | 166 | 5->1 167 | 168 | 169 | 170 | 171 | 172 | 6 173 | 174 | <<Abstract>> 175 | ServicePayer 176 | 177 | 178 | 179 | Public: 180 |    constructor(receiver: address, serviceName: string) 181 | 182 | 183 | 184 | 7 185 | 186 | GeneratorCopyright 187 | 188 | Private: 189 |   _GENERATOR: string 190 |   _version: string 191 | 192 | Public: 193 |    constructor(version_: string) 194 |    generator(): string 195 |    version(): string 196 | 197 | 198 | 199 | 8 200 | 201 | SimpleBEP20 202 | 203 | 204 | 205 | Public: 206 |    constructor(name: string, symbol: string, initialBalance: uint256, feeReceiver: address) 207 | 208 | 209 | 210 | 8->4 211 | 212 | 213 | 214 | 215 | 216 | 8->6 217 | 218 | 219 | 220 | 221 | 222 | 8->7 223 | 224 | 225 | 226 | 227 | 228 | -------------------------------------------------------------------------------- /analysis/uml/BurnableBEP20.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | UmlClassDiagram 11 | 12 | 13 | 14 | 0 15 | 16 | <<Abstract>> 17 | Context 18 | 19 | 20 | 21 | Internal: 22 |    _msgSender(): (payable: address) 23 |    _msgData(): bytes 24 | 25 | 26 | 27 | 1 28 | 29 | <<Abstract>> 30 | Ownable 31 | 32 | Private: 33 |   _owner: address 34 | 35 | Public: 36 |    <<event>> OwnershipTransferred(previousOwner: address, newOwner: address) 37 |    <<modifier>> onlyOwner() 38 |    constructor() 39 |    owner(): address 40 |    renounceOwnership() 41 |    transferOwnership(newOwner: address) 42 | 43 | 44 | 45 | 1->0 46 | 47 | 48 | 49 | 50 | 51 | 2 52 | 53 | <<Library>> 54 | SafeMath 55 | 56 | 57 | 58 | Internal: 59 |    add(a: uint256, b: uint256): uint256 60 |    sub(a: uint256, b: uint256): uint256 61 |    sub(a: uint256, b: uint256, errorMessage: string): uint256 62 |    mul(a: uint256, b: uint256): uint256 63 |    div(a: uint256, b: uint256): uint256 64 |    div(a: uint256, b: uint256, errorMessage: string): uint256 65 |    mod(a: uint256, b: uint256): uint256 66 |    mod(a: uint256, b: uint256, errorMessage: string): uint256 67 | 68 | 69 | 70 | 3 71 | 72 | <<Interface>> 73 | IBEP20 74 | 75 | 76 | 77 | External: 78 |     name(): string 79 |     symbol(): string 80 |     decimals(): uint8 81 |     totalSupply(): uint256 82 |     balanceOf(account: address): uint256 83 |     getOwner(): address 84 |     transfer(recipient: address, amount: uint256): bool 85 |     transferFrom(sender: address, recipient: address, amount: uint256): bool 86 |     approve(spender: address, amount: uint256): bool 87 |     allowance(_owner: address, spender: address): uint256 88 | Public: 89 |    <<event>> Transfer(from: address, to: address, value: uint256) 90 |    <<event>> Approval(owner: address, spender: address, value: uint256) 91 | 92 | 93 | 94 | 4 95 | 96 | BEP20 97 | 98 | Private: 99 |   _balances: mapping(address=>uint256) 100 |   _allowances: mapping(address=>mapping(address=>uint256)) 101 |   _totalSupply: uint256 102 |   _name: string 103 |   _symbol: string 104 |   _decimals: uint8 105 | 106 | Internal: 107 |    _transfer(sender: address, recipient: address, amount: uint256) 108 |    _mint(account: address, amount: uint256) 109 |    _burn(account: address, amount: uint256) 110 |    _approve(owner: address, spender: address, amount: uint256) 111 |    _setupDecimals(decimals_: uint8) 112 |    _beforeTokenTransfer(from: address, to: address, amount: uint256) 113 | Public: 114 |    constructor(name_: string, symbol_: string) 115 |    name(): string 116 |    symbol(): string 117 |    decimals(): uint8 118 |    totalSupply(): uint256 119 |    balanceOf(account: address): uint256 120 |    getOwner(): address 121 |    transfer(recipient: address, amount: uint256): bool 122 |    transferFrom(sender: address, recipient: address, amount: uint256): bool 123 |    approve(spender: address, amount: uint256): bool 124 |    allowance(owner: address, spender: address): uint256 125 |    increaseAllowance(spender: address, addedValue: uint256): bool 126 |    decreaseAllowance(spender: address, subtractedValue: uint256): bool 127 | 128 | 129 | 130 | 4->1 131 | 132 | 133 | 134 | 135 | 136 | 4->2 137 | 138 | 139 | 140 | 141 | 142 | 4->3 143 | 144 | 145 | 146 | 147 | 148 | 5 149 | 150 | <<Abstract>> 151 | BEP20Burnable 152 | 153 | 154 | 155 | Public: 156 |    burn(amount: uint256) 157 |    burnFrom(account: address, amount: uint256) 158 | 159 | 160 | 161 | 5->0 162 | 163 | 164 | 165 | 166 | 167 | 5->2 168 | 169 | 170 | 171 | 172 | 173 | 5->4 174 | 175 | 176 | 177 | 178 | 179 | 6 180 | 181 | ServiceReceiver 182 | 183 | Private: 184 |   _prices: mapping(bytes32=>uint256) 185 | 186 | Private: 187 |    _toBytes32(serviceName: string): bytes32 188 | Public: 189 |    <<payable>> pay(serviceName: string) 190 |    <<event>> Created(serviceName: string, serviceAddress: address) 191 |    getPrice(serviceName: string): uint256 192 |    setPrice(serviceName: string, amount: uint256) 193 |    withdraw(amount: uint256) 194 | 195 | 196 | 197 | 6->1 198 | 199 | 200 | 201 | 202 | 203 | 7 204 | 205 | <<Abstract>> 206 | ServicePayer 207 | 208 | 209 | 210 | Public: 211 |    constructor(receiver: address, serviceName: string) 212 | 213 | 214 | 215 | 8 216 | 217 | BurnableBEP20 218 | 219 | 220 | 221 | Public: 222 |    constructor(name: string, symbol: string, decimals: uint8, initialBalance: uint256, feeReceiver: address) 223 | 224 | 225 | 226 | 8->5 227 | 228 | 229 | 230 | 231 | 232 | 8->7 233 | 234 | 235 | 236 | 237 | 238 | --------------------------------------------------------------------------------