├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── packages └── chain │ ├── .example.env │ ├── audit │ ├── Audit_report_and_info.md │ ├── Buzzar_final_audit_report.pdf │ ├── Swarm_first_audit_report_draft.pdf │ └── audit_findings_and_responses.md │ ├── contracts │ ├── Curve.sol │ ├── Curve_test.sol │ ├── Eth_broker.sol │ ├── I_Curve.sol │ ├── I_Token.sol │ ├── I_router_02.sol │ ├── Mock_dai.sol │ ├── Mock_router.sol │ └── Token.sol │ ├── hardhat.config.ts │ ├── package.json │ ├── scripts │ └── deploy.ts │ ├── test │ ├── broker.test.js │ ├── curve.test.js │ ├── curve_calc.test.js │ ├── curve_pre_mint.test.js │ ├── settings.test.js │ └── token.test.js │ ├── tsconfig.json │ ├── tslint.json │ └── typings │ └── index.d.ts ├── tsconfig.json └── yarn.lock /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - 'v*.*.*' 9 | pull_request: 10 | branches: 11 | - '**' 12 | 13 | 14 | jobs: 15 | check: 16 | runs-on: ubuntu-latest 17 | defaults: 18 | run: 19 | working-directory: ./packages/chain 20 | 21 | steps: 22 | - uses: actions/checkout@v2 23 | with: 24 | fetch-depth: 0 25 | 26 | - name: Setup Node.js 27 | uses: actions/setup-node@v1 28 | with: 29 | node-version: '14' 30 | 31 | # Restore the yarn cache 32 | - uses: c-hive/gha-yarn-cache@v1 33 | 34 | - name: Yarn install 35 | run: yarn install 36 | 37 | - name: Build 38 | run: yarn hardhat compile 39 | 40 | - name: Test 41 | run: yarn hardhat test 42 | 43 | - name: Release 44 | uses: softprops/action-gh-release@v1 45 | if: startsWith(github.ref, 'refs/tags/') 46 | with: 47 | files: | 48 | artifacts/contracts/Curve.sol/Curve.json 49 | artifacts/contracts/Eth_broker.sol/Eth_broker.json 50 | artifacts/contracts/Token.sol/Token.json 51 | env: 52 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 53 | 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # APP 2 | 3 | app/node_modules/ 4 | app/src/interfaces/ 5 | app/build/ 6 | app/yarn.lock 7 | app/yarn-error.log 8 | app/.env 9 | app/.DS_Store 10 | app/package-lock.json 11 | app/debug.log 12 | 13 | # CHAIN 14 | 15 | chain/node_modules/ 16 | chain/artifacts/ 17 | chain/cache/ 18 | chain/.yarn/ 19 | chain/yarn.lock 20 | chain/yarn-error.log 21 | chain/.env 22 | chain/.DS_Store 23 | chain/package-lock.json 24 | 25 | packages/chain/node_modules/ 26 | packages/chain/artifacts/ 27 | packages/chain/cache/ 28 | packages/chain/.yarn/ 29 | packages/chain/yarn.lock 30 | packages/chain/yarn-error.log 31 | packages/chain/.env 32 | packages/chain/.DS_Store 33 | packages/chain/package-lock.json 34 | 35 | # YARN 36 | 37 | .yarn/* 38 | !.yarn/cache 39 | !.yarn/patches 40 | !.yarn/plugins 41 | !.yarn/releases 42 | !.yarn/sdks 43 | !.yarn/versions 44 | .pnp.* 45 | .yarn/cache/ 46 | .yarn/releases/ 47 | 48 | node_modules/ 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 The Bzzaar Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Swarm nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bzzzar Hardhat Migration 2 | 3 | This repo has been set up to move the Bzzzar contracts from an Etherlime development environment to Hardhat, as Etherlime is no longer under active development. This was necessitated due to changes to the Eth Broker contract which caused the contract to longer pass its test suite in the Etherlime environment. 4 | 5 | This branch of the repo still has failing tests in the Eth Broker test suite. There was a desire for separate PRs for migrating to Hardhat and fixing the Eth Broker tests. As a result, in this branch the tests and deploy script have been migrated to Hardhat, but broken tests have note been fixed. 6 | 7 | ### Setup 8 | 9 | Install the relevant packages with: 10 | ``` 11 | yarn 12 | ``` 13 | 14 | ### Running the Tests 15 | 16 | To run all of the tests: 17 | 18 | run 19 | ``` 20 | yarn test:chain 21 | ``` 22 | 23 | The command for running only the Eth Broker tests is: 24 | ``` 25 | yarn test:chain ./packages/chain/test/broker.test.js 26 | ``` 27 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bzzaar-contracts", 3 | "description": "customized environment for web3 development", 4 | "version": "0.0.3", 5 | "private": true, 6 | "workspaces": { 7 | "packages": [ 8 | "packages/*" 9 | ], 10 | "nohoist": [ 11 | "**/webpack-dev-server", 12 | "**/react-scripts" 13 | ] 14 | }, 15 | "scripts": { 16 | "chain": "yarn workspace @bzzaar-contracts/chain chain", 17 | "start": "yarn workspace @bzzaar-contracts/app start", 18 | "build": "yarn workspace @bzzaar-contracts/app build", 19 | "eject": "yarn workspace @bzzaar-contracts/app eject", 20 | "compile": "yarn workspace @bzzaar-contracts/chain compile", 21 | "test:chain": "yarn workspace @bzzaar-contracts/chain test", 22 | "deploy": "yarn workspace @bzzaar-contracts/chain deploy" 23 | }, 24 | "devDependencies": { 25 | "ts-node": "^9.1.1" 26 | }, 27 | "dependencies": { 28 | "hardhat": "2.6.4" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/chain/.example.env: -------------------------------------------------------------------------------- 1 | # This is a template for the environment variables you'll need for 2 | # deployment on various networks 3 | 4 | # To use, copy this file, rename it .env, and fill in the values 5 | # Everything will be interpreted as a string by JavaScript even though 6 | # you should not wrap values in quotation marks 7 | 8 | # Infura endpoints should be passed in WITHOUT the rest of the url 9 | # Private keys should be pasted WITHOUT the 0x prefix 10 | 11 | # Example 12 | EXAMPLE_INFURA_KEY=582dabbadbeef8... 13 | EXAMPLE_DEPLOYER_PRIV_KEY=deadbeef01EEdf972aBB... 14 | 15 | # Rinkeby 16 | RINKEBY_INFURA_KEY= 17 | RINKEBY_DEPLOYER_PRIV_KEY= 18 | 19 | # mainnet 20 | MAINNET_INFURA_KEY= 21 | MAINNET_DEPLOYER_PRIV_KEY= 22 | 23 | # Kovan 24 | KOVAN_INFURA_KEY= 25 | KOVAN_DEPLOYER_PRIV_KEY= 26 | 27 | # Ropsten 28 | ROPSTEN_INFURA_KEY= 29 | ROPSTEN_DEPLOYER_PRIV_KEY= 30 | 31 | # Goerli 32 | GOERLI_INFURA_KEY= 33 | GOERLI_DEPLOYER_PRIV_KEY= 34 | 35 | # xDai 36 | XDAI_DEPLOYER_PRIV_KEY= 37 | 38 | # This repo is configured with a plugin for verifying contracts 39 | # An Etherscan API key is needed for this 40 | 41 | ETHERSCAN_API_KEY= 42 | 43 | ################################### 44 | # ADDITIONAL DEPLOYMENT VARS # 45 | ################################### 46 | 47 | # BZZ bonding curve set up variables 48 | BZZ_DEPLOYED_MAINNET= 49 | ROUTER_ADDRESS_GOERLI= 50 | DAI_ADDRESS_MAINNET=0x6b175474e89094c44da98b954eedeac495271d0f 51 | OWNER_ADDRESS= 52 | 53 | # TESTING SET UP 54 | # Mock DAI (for testing) set up 55 | COLLATERAL_TOKEN_NAME= 56 | COLLATERAL_TOKEN_SYMBOL= 57 | COLLATERAL_TOKEN_DECIMAL=18 58 | 59 | # BZZ token set up variables 60 | TOKEN_NAME= 61 | TOKEN_SYMBOL= 62 | # BZZ token has 16 decimals. DO NOT CHANGE THIS! This will break the bonding curve 63 | TOKEN_DECIMAL=16 64 | # BZZ needs this limit for the curve. DO NOT CHANGE THIS! This will break the bonding curve 65 | TOKEN_CAP=1250000000000000000000000 66 | 67 | # Testing address (NB: GETS THE BZZ ON TESTNET DEPLOYMENT) 68 | ADDRESS_OF_TESTER= -------------------------------------------------------------------------------- /packages/chain/audit/Audit_report_and_info.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

BZZ Smart Contract Ecosystem

4 |

Audit Information

5 |
6 | 7 | --- 8 | 9 | # Index 10 | 11 | #### [Audit Information](#audit-information) 12 | 13 | ### Additional documentation 14 | 15 | #### [< `README`](../README.md) 16 | #### [~ Audit Report and Info](./Audit_report_and_info.md) 17 | #### [> Admin Permissions and Risks](../docs/admin_permissions_and_risks.md) 18 | #### [> Token Contract](../docs/token_contract.md) 19 | #### [> Curve Contract](../docs/curve_contract.md) 20 | #### [> ETH Broker Contract](../docs/eth_broker_contract.md) 21 | 22 | 23 | # Audit Information 24 | 25 | An audit was performed on this repository by [QuantStamp](https://quantstamp.com/). 26 | 27 | To read the first (draft) audit, please click below: 28 | 29 | #### [> First Audit Report](./Swarm_first_audit_report_draft.pdf) 30 | 31 | To see the status of each of the audit issues, please see: 32 | 33 | #### [> Audit Findings & Responses](./audit_findings_and_responses.md) 34 | 35 | To see the final audit report after the above fixes and acknowledges, check the final audit: 36 | 37 | #### [> Final Audit Report](./Buzzar_final_audit_report.pdf) 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /packages/chain/audit/Buzzar_final_audit_report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethersphere/bzzaar-contracts/a6a5d4d90329ad89ed4a318f16c03c54f54a6794/packages/chain/audit/Buzzar_final_audit_report.pdf -------------------------------------------------------------------------------- /packages/chain/audit/Swarm_first_audit_report_draft.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethersphere/bzzaar-contracts/a6a5d4d90329ad89ed4a318f16c03c54f54a6794/packages/chain/audit/Swarm_first_audit_report_draft.pdf -------------------------------------------------------------------------------- /packages/chain/audit/audit_findings_and_responses.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |

BZZ Smart Contract Ecosystem

4 |

Audit Findings & Responses

5 |
6 | 7 | --- 8 | 9 | # Audit Findings & Responses 10 | 11 | Issue | Status | Project Notes 12 | |:----|:------:|:-------------| 13 | | QSP-1 | Resolved | Additional checks were added to `Eth_broker.sol` to ensure no blank addresses (`0x0`) can be entered. 14 | | QSP-2 | Resolved | Flattered file contained artifacts of older versions of the `Eth_portal.sol` and `Curve.sol` contracts. Files have been removed. `Eth_broker.sol` and `Curve.sol` have reentrancy guards. 15 | | QSP-3 | Acknowledged | `increaseAllowance` and `decreaseAllowance` are available on the `Token.sol` contract. App developers have been informed to use increase and decrease over approve. 16 | | QSP-4 | Acknowledged | Should Ether be forcibly sent to the contract a user on mint or burn may receive more Ether than expected. This does not affect any logic nor UX negatively. 17 | | QSP-5 | Resolved | Checks have been added. See QSP-2. 18 | | QSP-6 | Resolved | Updated visibility of `Curve.sol` `public` functions to `external`. 19 | | QSP-7 | Resolved | Additional `require`'s added for `approve` and `mintTo` calls. `require` not added to Uniswap calls as they handle failure through `revert`. 20 | | QSP-8 | Resolved | Variables mentioned are actually intended on being `constant`. Declaration has been updated to `constant`. `openMarketSupply` has been renamed to make it more clear as `_MARKET_OPENING_SUPPLY`. 21 | | QSP-9 | Resolved | Additional documentation was added to clear up the understanding of the `_helper` function in the `Curve.sol`. 22 | | QSP-10 | Resolved | Changes `Eth_broker.sol` `redeem` function to use `dai_.balanceOf(address(this))` instead of `_minDaiSellValue`. 23 | | QSP-11 | Resolved | Variables where artifacts of outdated math implementation. They have been removed. 24 | | QSP-12 | Resolved | See QSP-2. 25 | | QSP-13 | Resolved | See QSP-2. Additional unused return value of `_mint` in `Curve.sol` has also been removed and tests updated. 26 | | Code Documentation | Resolved | Mentioned documentation has been updated to reflect code functionally. 27 | | Adherence to Best Practices | Resolved | Common functionality in `Curve` `mint` and `mintTo` moved to an internal function `_commonMint`. Same thing for `Eth_broker` `mint` and `mintTo`. Variable naming between the `Curve` and `Broker` have been updated to be more consistent. 28 | 29 | ### Commit Hash 30 | 31 | Phase (Delivery Date, YYYY-MM-DD) | Commit Hash 32 | |:--------------------|:-----------| 33 | | Initial Audit (2021-02-10) | 9a9a0ae71f1294faa76c12642809159361820ea3 | 34 | | Final Audit (2021-02-24) | dc28d883e496759eb2115e05a705fd714ae8473b 35 | 36 | ### Status Reference 37 | 38 | | Status | Definition | 39 | |:-------|:-----------| 40 | | Unresolved | Acknowledged the existence of the risk, and decided to accept it without engaging in special efforts to control it. | 41 | | Acknowledged | The issue remains in the code but is a result of an intentional business or design decision. As such, it is supposed to be addressed outside the programmatic means, such as: 1) comments, documentation, README, FAQ; 2) business processes; 3) analyses showing that the issue shall have no negative consequences in practice (e.g., gas analysis, deployment settings). | 42 | | Resolved | Adjusted program implementation, requirements or constraints to eliminate the risk. | 43 | | Mitigated | Implemented actions to minimize the impact or likelihood of the risk. | 44 | 45 | -------------------------------------------------------------------------------- /packages/chain/contracts/Curve.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.0; 2 | 3 | import "@openzeppelin/contracts/ownership/Ownable.sol"; 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | import "@openzeppelin/contracts/math/SafeMath.sol"; 6 | import "./I_Token.sol"; 7 | import "./I_Curve.sol"; 8 | 9 | contract Curve is Ownable, I_Curve { 10 | using SafeMath for uint256; 11 | // The instance of the token this curve controls (has mint rights to) 12 | I_Token internal bzz_; 13 | // The instance of the collateral token that is used to buy and sell tokens 14 | IERC20 internal dai_; 15 | // Stores if the curve has been initialised 16 | bool internal init_; 17 | // The active state of the curve (false after emergency shutdown) 18 | bool internal active_; 19 | // Mutex guard for state modifying functions 20 | uint256 private status_; 21 | // States for the guard 22 | uint256 private constant _NOT_ENTERED = 1; 23 | uint256 private constant _ENTERED = 2; 24 | 25 | // ------------------------------------------------------------------------- 26 | // Events 27 | // ------------------------------------------------------------------------- 28 | 29 | // Emitted when tokens are minted 30 | event mintTokens( 31 | address indexed buyer, // The address of the buyer 32 | uint256 amount, // The amount of bonded tokens to mint 33 | uint256 pricePaid, // The price in collateral tokens 34 | uint256 maxSpend // The max amount of collateral to spend 35 | ); 36 | // Emitted when tokens are minted 37 | event mintTokensTo( 38 | address indexed buyer, // The address of the buyer 39 | address indexed receiver, // The address of the receiver of the tokens 40 | uint256 amount, // The amount of bonded tokens to mint 41 | uint256 pricePaid, // The price in collateral tokens 42 | uint256 maxSpend // The max amount of collateral to spend 43 | ); 44 | // Emitted when tokens are burnt 45 | event burnTokens( 46 | address indexed seller, // The address of the seller 47 | uint256 amount, // The amount of bonded tokens to sell 48 | uint256 rewardReceived, // The collateral tokens received 49 | uint256 minReward // The min collateral reward for tokens 50 | ); 51 | // Emitted when the curve is permanently shut down 52 | event shutDownOccurred(address indexed owner); 53 | 54 | // ------------------------------------------------------------------------- 55 | // Modifiers 56 | // ------------------------------------------------------------------------- 57 | 58 | /** 59 | * @notice Requires the curve to be initialised and active. 60 | */ 61 | modifier isActive() { 62 | require(active_ && init_, "Curve inactive"); 63 | _; 64 | } 65 | 66 | /** 67 | * @notice Protects against re-entrancy attacks 68 | */ 69 | modifier mutex() { 70 | require(status_ != _ENTERED, "ReentrancyGuard: reentrant call"); 71 | // Any calls to nonReentrant after this point will fail 72 | status_ = _ENTERED; 73 | // Function executes 74 | _; 75 | // Status set to not entered 76 | status_ = _NOT_ENTERED; 77 | } 78 | 79 | // ------------------------------------------------------------------------- 80 | // Constructor 81 | // ------------------------------------------------------------------------- 82 | 83 | constructor(address _bondedToken, address _collateralToken) public Ownable() { 84 | bzz_ = I_Token(_bondedToken); 85 | dai_ = IERC20(_collateralToken); 86 | status_ = _NOT_ENTERED; 87 | } 88 | 89 | // ------------------------------------------------------------------------- 90 | // View functions 91 | // ------------------------------------------------------------------------- 92 | 93 | /** 94 | * @notice This function is only callable after the curve contract has been 95 | * initialized. 96 | * @param _amount The amount of tokens a user wants to buy 97 | * @return uint256 The cost to buy the _amount of tokens in the collateral 98 | * currency (see collateral token). 99 | */ 100 | function buyPrice(uint256 _amount) 101 | public 102 | view 103 | isActive() 104 | returns (uint256 collateralRequired) 105 | { 106 | collateralRequired = _mint(_amount, bzz_.totalSupply()); 107 | return collateralRequired; 108 | } 109 | 110 | /** 111 | * @notice This function is only callable after the curve contract has been 112 | * initialized. 113 | * @param _amount The amount of tokens a user wants to sell 114 | * @return collateralReward The reward for selling the _amount of tokens in the 115 | * collateral currency (see collateral token). 116 | */ 117 | function sellReward(uint256 _amount) 118 | public 119 | view 120 | isActive() 121 | returns (uint256 collateralReward) 122 | { 123 | (collateralReward, ) = _withdraw(_amount, bzz_.totalSupply()); 124 | return collateralReward; 125 | } 126 | 127 | /** 128 | * @return If the curve is both active and initialised. 129 | */ 130 | function isCurveActive() public view returns (bool) { 131 | if (active_ && init_) { 132 | return true; 133 | } 134 | return false; 135 | } 136 | 137 | /** 138 | * @param _initialSupply The expected initial supply the bonded token 139 | * will have. 140 | * @return The required collateral amount (DAI) to initialise the curve. 141 | */ 142 | function requiredCollateral(uint256 _initialSupply) 143 | public 144 | view 145 | returns (uint256) 146 | { 147 | return _initializeCurve(_initialSupply); 148 | } 149 | 150 | /** 151 | * @return The address of the bonded token (BZZ). 152 | */ 153 | function bondedToken() external view returns (address) { 154 | return address(bzz_); 155 | } 156 | 157 | /** 158 | * @return The address of the collateral token (DAI) 159 | */ 160 | function collateralToken() external view returns (address) { 161 | return address(dai_); 162 | } 163 | 164 | // ------------------------------------------------------------------------- 165 | // State modifying functions 166 | // ------------------------------------------------------------------------- 167 | 168 | /** 169 | * @notice This function initializes the curve contract, and ensure the 170 | * curve has the required permissions on the token contract needed 171 | * to function. 172 | */ 173 | function init() external { 174 | // Checks the curve has not already been initialized 175 | require(!init_, "Curve is init"); 176 | // Checks the curve has the correct permissions on the given token 177 | require(bzz_.isMinter(address(this)), "Curve is not minter"); 178 | // Gets the total supply of the token 179 | uint256 initialSupply = bzz_.totalSupply(); 180 | // The curve requires that the initial supply is at least the expected 181 | // open market supply 182 | require( 183 | initialSupply >= _MARKET_OPENING_SUPPLY, 184 | "Curve equation requires pre-mint" 185 | ); 186 | // Gets the price for the current supply 187 | uint256 price = _initializeCurve(initialSupply); 188 | // Requires the transfer for the collateral needed to back fill for the 189 | // minted supply 190 | require( 191 | dai_.transferFrom(msg.sender, address(this), price), 192 | "Failed to collateralized the curve" 193 | ); 194 | // Sets the Curve to being active and initialised 195 | active_ = true; 196 | init_ = true; 197 | } 198 | 199 | /** 200 | * @param _amount The amount of tokens (BZZ) the user wants to buy. 201 | * @param _maxCollateralSpend The max amount of collateral (DAI) the user is 202 | * willing to spend in order to buy the _amount of tokens. 203 | * @return The status of the mint. Note that should the total cost of the 204 | * purchase exceed the _maxCollateralSpend the transaction will revert. 205 | */ 206 | function mint( 207 | uint256 _amount, 208 | uint256 _maxCollateralSpend 209 | ) 210 | external 211 | isActive() 212 | mutex() 213 | returns (bool success) 214 | { 215 | // Gets the price for the amount of tokens 216 | uint256 price = _commonMint(_amount, _maxCollateralSpend, msg.sender); 217 | // Emitting event with all important info 218 | emit mintTokens( 219 | msg.sender, 220 | _amount, 221 | price, 222 | _maxCollateralSpend 223 | ); 224 | // Returning that the mint executed successfully 225 | return true; 226 | } 227 | 228 | /** 229 | * @param _amount The amount of tokens (BZZ) the user wants to buy. 230 | * @param _maxCollateralSpend The max amount of collateral (DAI) the user is 231 | * willing to spend in order to buy the _amount of tokens. 232 | * @param _to The address to send the tokens to. 233 | * @return The status of the mint. Note that should the total cost of the 234 | * purchase exceed the _maxCollateralSpend the transaction will revert. 235 | */ 236 | function mintTo( 237 | uint256 _amount, 238 | uint256 _maxCollateralSpend, 239 | address _to 240 | ) 241 | external 242 | isActive() 243 | mutex() 244 | returns (bool success) 245 | { 246 | // Gets the price for the amount of tokens 247 | uint256 price = _commonMint(_amount, _maxCollateralSpend, _to); 248 | // Emitting event with all important info 249 | emit mintTokensTo( 250 | msg.sender, 251 | _to, 252 | _amount, 253 | price, 254 | _maxCollateralSpend 255 | ); 256 | // Returning that the mint executed successfully 257 | return true; 258 | } 259 | 260 | /** 261 | * @param _amount The amount of tokens (BZZ) the user wants to sell. 262 | * @param _minCollateralReward The min amount of collateral (DAI) the user is 263 | * willing to receive for their tokens. 264 | * @return The status of the burn. Note that should the total reward of the 265 | * burn be below the _minCollateralReward the transaction will revert. 266 | */ 267 | function redeem(uint256 _amount, uint256 _minCollateralReward) 268 | external 269 | isActive() 270 | mutex() 271 | returns (bool success) 272 | { 273 | // Gets the reward for the amount of tokens 274 | uint256 reward = sellReward(_amount); 275 | // Checks the reward has not slipped below the min amount the user 276 | // wishes to receive. 277 | require(reward >= _minCollateralReward, "Reward under min sell"); 278 | // Burns the number of tokens (fails - no bool return) 279 | bzz_.burnFrom(msg.sender, _amount); 280 | // Transfers the reward from the curve to the collateral token 281 | require( 282 | dai_.transfer(msg.sender, reward), 283 | "Transferring collateral failed" 284 | ); 285 | // Emitting event with all important info 286 | emit burnTokens( 287 | msg.sender, 288 | _amount, 289 | reward, 290 | _minCollateralReward 291 | ); 292 | // Returning that the burn executed successfully 293 | return true; 294 | } 295 | 296 | /** 297 | * @notice Shuts down the curve, disabling buying, selling and both price 298 | * functions. Can only be called by the owner. Will renounce the 299 | * minter role on the bonded token. 300 | */ 301 | function shutDown() external onlyOwner() { 302 | // Removes the curve as a minter on the token 303 | bzz_.renounceMinter(); 304 | // Irreversibly shuts down the curve 305 | active_ = false; 306 | // Emitting address of owner who shut down curve permanently 307 | emit shutDownOccurred(msg.sender); 308 | } 309 | 310 | // ------------------------------------------------------------------------- 311 | // Internal functions 312 | // ------------------------------------------------------------------------- 313 | 314 | /** 315 | * @param _amount The amount of tokens (BZZ) the user wants to buy. 316 | * @param _maxCollateralSpend The max amount of collateral (DAI) the user is 317 | * willing to spend in order to buy the _amount of tokens. 318 | * @param _to The address to send the tokens to. 319 | * @return uint256 The price the user has paid for buying the _amount of 320 | * BUZZ tokens. 321 | */ 322 | function _commonMint( 323 | uint256 _amount, 324 | uint256 _maxCollateralSpend, 325 | address _to 326 | ) 327 | internal 328 | returns(uint256) 329 | { 330 | // Gets the price for the amount of tokens 331 | uint256 price = buyPrice(_amount); 332 | // Checks the price has not risen above the max amount the user wishes 333 | // to spend. 334 | require(price <= _maxCollateralSpend, "Price exceeds max spend"); 335 | // Transfers the price of tokens in the collateral token to the curve 336 | require( 337 | dai_.transferFrom(msg.sender, address(this), price), 338 | "Transferring collateral failed" 339 | ); 340 | // Mints the user their tokens 341 | require(bzz_.mint(_to, _amount), "Minting tokens failed"); 342 | // Returns the price the user will pay for buy 343 | return price; 344 | } 345 | 346 | // ------------------------------------------------------------------------- 347 | // Curve mathematical functions 348 | 349 | uint256 internal constant _BZZ_SCALE = 1e16; 350 | uint256 internal constant _N = 5; 351 | uint256 internal constant _MARKET_OPENING_SUPPLY = 62500000 * _BZZ_SCALE; 352 | // Equation for curve: 353 | 354 | /** 355 | * @param x The supply to calculate at. 356 | * @return x^32/_MARKET_OPENING_SUPPLY^5 357 | * @dev Calculates the 32 power of `x` (`x` squared 5 times) times a 358 | * constant. Each time it squares the function it divides by the 359 | * `_MARKET_OPENING_SUPPLY` so when `x` = `_MARKET_OPENING_SUPPLY` 360 | * it doesn't change `x`. 361 | * 362 | * `c*x^32` | `c` is chosen in such a way that 363 | * `_MARKET_OPENING_SUPPLY` is the fixed point of the helper 364 | * function. 365 | * 366 | * The division by `_MARKET_OPENING_SUPPLY` also helps avoid an 367 | * overflow. 368 | * 369 | * The `_helper` function is separate to the `_primitiveFunction` 370 | * as we modify `x`. 371 | */ 372 | function _helper(uint256 x) internal view returns (uint256) { 373 | for (uint256 index = 1; index <= _N; index++) { 374 | x = (x.mul(x)).div(_MARKET_OPENING_SUPPLY); 375 | } 376 | return x; 377 | } 378 | 379 | /** 380 | * @param s The supply point being calculated for. 381 | * @return The amount of DAI required for the requested amount of BZZ (s). 382 | * @dev `s` is being added because it is the linear term in the 383 | * polynomial (this ensures no free BUZZ tokens). 384 | * 385 | * primitive function equation: s + c*s^32. 386 | * 387 | * See the helper function for the definition of `c`. 388 | * 389 | * Converts from something measured in BZZ (1e16) to dai atomic 390 | * units 1e18. 391 | */ 392 | function _primitiveFunction(uint256 s) internal view returns (uint256) { 393 | return s.add(_helper(s)); 394 | } 395 | 396 | /** 397 | * @param _supply The number of tokens that exist. 398 | * @return uint256 The price for the next token up the curve. 399 | */ 400 | function _spotPrice(uint256 _supply) internal view returns (uint256) { 401 | return (_primitiveFunction(_supply.add(1)).sub(_primitiveFunction(_supply))); 402 | } 403 | 404 | /** 405 | * @param _amount The amount of tokens to be minted 406 | * @param _currentSupply The current supply of tokens 407 | * @return uint256 The cost for the tokens 408 | * @return uint256 The price being paid per token 409 | */ 410 | function _mint(uint256 _amount, uint256 _currentSupply) 411 | internal 412 | view 413 | returns (uint256) 414 | { 415 | uint256 deltaR = _primitiveFunction(_currentSupply.add(_amount)).sub( 416 | _primitiveFunction(_currentSupply)); 417 | return deltaR; 418 | } 419 | 420 | /** 421 | * @param _amount The amount of tokens to be sold 422 | * @param _currentSupply The current supply of tokens 423 | * @return uint256 The reward for the tokens 424 | * @return uint256 The price being received per token 425 | */ 426 | function _withdraw(uint256 _amount, uint256 _currentSupply) 427 | internal 428 | view 429 | returns (uint256, uint256) 430 | { 431 | assert(_currentSupply - _amount > 0); 432 | uint256 deltaR = _primitiveFunction(_currentSupply).sub( 433 | _primitiveFunction(_currentSupply.sub(_amount))); 434 | uint256 realized_price = deltaR.div(_amount); 435 | return (deltaR, realized_price); 436 | } 437 | 438 | /** 439 | * @param _initial_supply The supply the curve is going to start with. 440 | * @return initial_reserve How much collateral is needed to collateralized 441 | * the bonding curve. 442 | * @return price The price being paid per token (averaged). 443 | */ 444 | function _initializeCurve(uint256 _initial_supply) 445 | internal 446 | view 447 | returns (uint256 price) 448 | { 449 | price = _mint(_initial_supply, 0); 450 | return price; 451 | } 452 | } 453 | -------------------------------------------------------------------------------- /packages/chain/contracts/Curve_test.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.0; 2 | 3 | import "./Curve.sol"; 4 | 5 | contract curve_test is Curve { 6 | constructor(address _token, address _collateralToken) 7 | public 8 | Curve(_token, _collateralToken) 9 | {} 10 | 11 | function helper(uint256 _x) public view returns (uint256) { 12 | return _helper(_x); 13 | } 14 | 15 | function primitiveFunction(uint256 _s) public view returns (uint256) { 16 | return _primitiveFunction(_s); 17 | } 18 | 19 | function spotPrice(uint256 _supply) public view returns (uint256) { 20 | return _spotPrice(_supply); 21 | } 22 | 23 | function mathMint(uint256 _amount, uint256 _currentSupply) 24 | public 25 | view 26 | returns (uint256) 27 | { 28 | return _mint(_amount, _currentSupply); 29 | } 30 | 31 | function withdraw(uint256 _amount, uint256 _currentSupply) 32 | public 33 | view 34 | returns (uint256, uint256) 35 | { 36 | return _withdraw(_amount, _currentSupply); 37 | } 38 | 39 | function initializeCurve(uint256 _initialSupply) 40 | public 41 | view 42 | returns (uint256) 43 | { 44 | return _initializeCurve(_initialSupply); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/chain/contracts/Eth_broker.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.0; 2 | 3 | import "./I_Curve.sol"; 4 | import "./I_router_02.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | contract Eth_broker { 8 | // The instance of the curve 9 | I_Curve internal curve_; 10 | // The instance of the Uniswap router 11 | I_router_02 internal router_; 12 | // The instance of the DAI token 13 | IERC20 internal dai_; 14 | // The address for the underlying token address 15 | IERC20 internal bzz_; 16 | // Mutex guard for state modifying functions 17 | uint256 private status_; 18 | // States for the guard 19 | uint256 private constant _NOT_ENTERED = 1; 20 | uint256 private constant _ENTERED = 2; 21 | 22 | // ------------------------------------------------------------------------- 23 | // Events 24 | // ------------------------------------------------------------------------- 25 | 26 | // Emitted when tokens are minted 27 | event mintTokensWithEth( 28 | address indexed buyer, // The address of the buyer 29 | uint256 amount, // The amount of bonded tokens to mint 30 | uint256 priceForTokensDai, // The price in DAI for the token amount 31 | uint256 EthTradedForDai, // The ETH amount sold for DAI 32 | uint256 maxSpendDai // The max amount of DAI to spend 33 | ); 34 | // Emitted when tokens are minted 35 | event mintTokensToWithEth( 36 | address indexed buyer, // The address of the buyer 37 | address indexed receiver, // The address of the receiver of the tokens 38 | uint256 amount, // The amount of bonded tokens to mint 39 | uint256 priceForTokensDai, // The price in DAI for the token amount 40 | uint256 EthTradedForDai, // The ETH amount sold for DAI 41 | uint256 maxSpendDai // The max amount of DAI to spend 42 | ); 43 | // Emitted when tokens are burnt 44 | event burnTokensWithEth( 45 | address indexed seller, // The address of the seller 46 | uint256 amount, // The amount of bonded tokens to burn 47 | uint256 rewardReceivedDai, // The amount of DAI received for selling 48 | uint256 ethReceivedForDai, // How much ETH the DAI was traded for 49 | uint256 minRewardDai // The min amount of DAI to sell for 50 | ); 51 | 52 | // ------------------------------------------------------------------------- 53 | // Modifiers 54 | // ------------------------------------------------------------------------- 55 | 56 | /** 57 | * @notice Checks if the curve is active (may be redundant). 58 | */ 59 | modifier isCurveActive() { 60 | require(curve_.isCurveActive(), "Curve inactive"); 61 | _; 62 | } 63 | 64 | /** 65 | * @notice Protects against re-entrancy attacks 66 | */ 67 | modifier mutex() { 68 | require(status_ != _ENTERED, "ReentrancyGuard: reentrant call"); 69 | // Any calls to non Reentrant after this point will fail 70 | status_ = _ENTERED; 71 | // Function executes 72 | _; 73 | // Status set to not entered 74 | status_ = _NOT_ENTERED; 75 | } 76 | 77 | // ------------------------------------------------------------------------- 78 | // Constructor 79 | // ------------------------------------------------------------------------- 80 | 81 | constructor( 82 | address _bzzCurve, 83 | address _collateralToken, 84 | address _router02 85 | ) 86 | public 87 | { 88 | require( 89 | _bzzCurve != address(0) && 90 | _collateralToken != address(0) && 91 | _router02 != address(0), 92 | "Addresses of contracts cannot be 0x address" 93 | ); 94 | curve_ = I_Curve(_bzzCurve); 95 | dai_ = IERC20(_collateralToken); 96 | router_ = I_router_02(_router02); 97 | // Setting the address of the underlying token (BZZ) 98 | bzz_ = IERC20(curve_.bondedToken()); 99 | } 100 | 101 | // ------------------------------------------------------------------------- 102 | // View functions 103 | // ------------------------------------------------------------------------- 104 | 105 | /** 106 | * @notice This function is only callable after the curve contract has been 107 | * initialized. 108 | * @param _amount The amount of tokens a user wants to buy 109 | * @return uint256 The cost to buy the _amount of tokens in ETH 110 | */ 111 | function buyPrice(uint256 _amount) 112 | public 113 | view 114 | isCurveActive() 115 | returns (uint256) 116 | { 117 | // Getting the current DAI cost for token amount 118 | uint256 daiCost = curve_.buyPrice(_amount); 119 | // Returning the required ETH to buy DAI amount 120 | return router_.getAmountsIn( 121 | daiCost, 122 | getPath(true) 123 | )[0]; 124 | } 125 | 126 | /** 127 | * @notice This function is only callable after the curve contract has been 128 | * initialized. 129 | * @param _amount The amount of tokens a user wants to sell 130 | * @return uint256 The reward for selling the _amount of tokens in ETH 131 | */ 132 | function sellReward(uint256 _amount) 133 | public 134 | view 135 | isCurveActive() 136 | returns (uint256) 137 | { 138 | // Getting the current DAI reward for token amount 139 | uint256 daiReward = curve_.sellReward(_amount); 140 | // Returning the ETH reward for token sale 141 | return router_.getAmountsIn( 142 | daiReward, 143 | getPath(true) 144 | )[0]; 145 | } 146 | 147 | /** 148 | * @param _daiAmount The amount of dai to be traded for ETH 149 | * @return uint256 The amount of ETH that can be traded for given the 150 | * dai amount 151 | */ 152 | function sellRewardDai(uint256 _daiAmount) 153 | public 154 | view 155 | isCurveActive() 156 | returns (uint256) 157 | { 158 | // Returning the ETH reward for the dai amount 159 | return router_.getAmountsIn( 160 | _daiAmount, 161 | getPath(true) 162 | )[0]; 163 | } 164 | 165 | /** 166 | * @param _buy If the path is for a buy or sell transaction 167 | * @return address[] The path for the transaction 168 | */ 169 | function getPath(bool _buy) public view returns(address[] memory) { 170 | address[] memory buyPath = new address[](2); 171 | if(_buy) { 172 | buyPath[0] = router_.WETH(); 173 | buyPath[1] = address(dai_); 174 | } else { 175 | buyPath[0] = address(dai_); 176 | buyPath[1] = router_.WETH(); 177 | } 178 | 179 | return buyPath; 180 | } 181 | 182 | /** 183 | * @return Gets the current time 184 | */ 185 | function getTime() public view returns(uint256) { 186 | return now; 187 | } 188 | 189 | // ------------------------------------------------------------------------- 190 | // State modifying functions 191 | // ------------------------------------------------------------------------- 192 | 193 | /** 194 | * @param _tokenAmount The amount of BZZ tokens the user would like to 195 | * buy from the curve. 196 | * @param _maxDaiSpendAmount The max amount of collateral (DAI) the user 197 | * is willing to spend to buy the amount of tokens. 198 | * @param _deadline Unix timestamp after which the transaction will 199 | * revert. - Taken from Uniswap documentation: 200 | * https://uniswap.org/docs/v2/smart-contracts/router02/#swapethforexacttokens 201 | * @return bool If the transaction completed successfully. 202 | * @notice Before this function is called the caller does not need to 203 | * approve the spending of anything. Please assure that the amount 204 | * of ETH sent with this transaction is sufficient by first calling 205 | * `buyPrice` with the same token amount. Add your % slippage to 206 | * the max dai spend amount (you can get the expected amount by 207 | * calling `buyPrice` on the curve contract). 208 | */ 209 | function mint( 210 | uint256 _tokenAmount, 211 | uint256 _maxDaiSpendAmount, 212 | uint _deadline 213 | ) 214 | external 215 | payable 216 | isCurveActive() 217 | mutex() 218 | returns (bool) 219 | { 220 | (uint256 daiNeeded, uint256 ethReceived) = _commonMint( 221 | _tokenAmount, 222 | _maxDaiSpendAmount, 223 | _deadline, 224 | msg.sender 225 | ); 226 | // Emitting event with all important info 227 | emit mintTokensWithEth( 228 | msg.sender, 229 | _tokenAmount, 230 | daiNeeded, 231 | ethReceived, 232 | _maxDaiSpendAmount 233 | ); 234 | // Returning that the mint executed successfully 235 | return true; 236 | } 237 | 238 | /** 239 | * @param _tokenAmount The amount of BZZ tokens the user would like to 240 | * buy from the curve. 241 | * @param _maxDaiSpendAmount The max amount of collateral (DAI) the user 242 | * is willing to spend to buy the amount of tokens. 243 | * @param _deadline Unix timestamp after which the transaction will 244 | * revert. - Taken from Uniswap documentation: 245 | * https://uniswap.org/docs/v2/smart-contracts/router02/#swapethforexacttokens 246 | * @return bool If the transaction completed successfully. 247 | * @notice Before this function is called the caller does not need to 248 | * approve the spending of anything. Please assure that the amount 249 | * of ETH sent with this transaction is sufficient by first calling 250 | * `buyPrice` with the same token amount. Add your % slippage to 251 | * the max dai spend amount (you can get the expected amount by 252 | * calling `buyPrice` on the curve contract). 253 | */ 254 | function mintTo( 255 | uint256 _tokenAmount, 256 | uint256 _maxDaiSpendAmount, 257 | uint _deadline, 258 | address _to 259 | ) 260 | external 261 | payable 262 | isCurveActive() 263 | mutex() 264 | returns (bool) 265 | { 266 | (uint256 daiNeeded, uint256 ethReceived) = _commonMint( 267 | _tokenAmount, 268 | _maxDaiSpendAmount, 269 | _deadline, 270 | _to 271 | ); 272 | // Emitting event with all important info 273 | emit mintTokensToWithEth( 274 | msg.sender, 275 | _to, 276 | _tokenAmount, 277 | daiNeeded, 278 | ethReceived, 279 | _maxDaiSpendAmount 280 | ); 281 | // Returning that the mint executed successfully 282 | return true; 283 | } 284 | 285 | /** 286 | * @notice The user needs to have approved this contract as a spender of 287 | * of the desired `_tokenAmount` to sell before calling this 288 | * transaction. Failure to do so will result in the call reverting. 289 | * This function is payable to receive ETH from internal calls. 290 | * Please do not send ETH to this function. 291 | * @param _tokenAmount The amount of BZZ tokens the user would like to 292 | * sell. 293 | * @param _minDaiSellValue The min value of collateral (DAI) the user 294 | * is willing to sell their tokens for. 295 | * @param _deadline Unix timestamp after which the transaction will 296 | * revert. - Taken from Uniswap documentation: 297 | * https://uniswap.org/docs/v2/smart-contracts/router02/#swapexacttokensforeth 298 | * @return bool If the transaction completed successfully. 299 | */ 300 | function redeem( 301 | uint256 _tokenAmount, 302 | uint256 _minDaiSellValue, 303 | uint _deadline 304 | ) 305 | external 306 | payable 307 | isCurveActive() 308 | mutex() 309 | returns (bool) 310 | { 311 | // Gets the current reward for selling the amount of tokens 312 | uint256 daiReward = curve_.sellReward(_tokenAmount); 313 | // The check that the dai reward amount is not more than the min sell 314 | // amount happens in the curve. 315 | 316 | // Transferring _amount of tokens into this contract 317 | require( 318 | bzz_.transferFrom( 319 | msg.sender, 320 | address(this), 321 | _tokenAmount 322 | ), 323 | "Transferring BZZ failed" 324 | ); 325 | // Approving the curve as a spender 326 | require( 327 | bzz_.approve( 328 | address(curve_), 329 | _tokenAmount 330 | ), 331 | "BZZ approve failed" 332 | ); 333 | // Selling tokens for DAI 334 | require( 335 | curve_.redeem( 336 | _tokenAmount, 337 | daiReward 338 | ), 339 | "Curve burn failed" 340 | ); 341 | // Getting expected ETH for DAI 342 | uint256 ethMin = sellRewardDai(_minDaiSellValue); 343 | // Approving the router as a spender 344 | require( 345 | dai_.approve( 346 | address(router_), 347 | daiReward 348 | ), 349 | "DAI approve failed" 350 | ); 351 | // Selling DAI received for ETH 352 | router_.swapExactTokensForETH( 353 | daiReward, 354 | ethMin, 355 | getPath(false), 356 | msg.sender, 357 | _deadline 358 | ); 359 | // Emitting event with all important info 360 | emit burnTokensWithEth( 361 | msg.sender, 362 | _tokenAmount, 363 | daiReward, 364 | ethMin, 365 | _minDaiSellValue 366 | ); 367 | // Returning that the burn executed successfully 368 | return true; 369 | } 370 | 371 | function() external payable { 372 | require( 373 | msg.sender == address(router_), 374 | "ETH not accepted outside router" 375 | ); 376 | } 377 | 378 | 379 | // ------------------------------------------------------------------------- 380 | // Internal functions 381 | // ------------------------------------------------------------------------- 382 | 383 | /** 384 | * @param _tokenAmount The amount of BZZ tokens the user would like to 385 | * buy from the curve. 386 | * @param _maxDaiSpendAmount The max amount of collateral (DAI) the user 387 | * is willing to spend to buy the amount of tokens. 388 | * @param _deadline Unix timestamp after which the transaction will 389 | * revert. - Taken from Uniswap documentation: 390 | * https://uniswap.org/docs/v2/smart-contracts/router02/#swapethforexacttokens 391 | * @return uint256 The dai needed to buy the tokens. 392 | * @return uint256 The Ether received from the user for the trade. 393 | * @notice Before this function is called the caller does not need to 394 | * approve the spending of anything. Please assure that the amount 395 | * of ETH sent with this transaction is sufficient by first calling 396 | * `buyPrice` with the same token amount. Add your % slippage to 397 | * the max dai spend amount (you can get the expected amount by 398 | * calling `buyPrice` on the curve contract). 399 | */ 400 | function _commonMint( 401 | uint256 _tokenAmount, 402 | uint256 _maxDaiSpendAmount, 403 | uint _deadline, 404 | address _to 405 | ) 406 | internal 407 | returns( 408 | uint256 daiNeeded, 409 | uint256 ethReceived 410 | ) 411 | { 412 | // Getting the exact needed amount of DAI for desired token amount 413 | daiNeeded = curve_.buyPrice(_tokenAmount); 414 | // Checking that this amount is not more than the max spend amount 415 | require( 416 | _maxDaiSpendAmount >= daiNeeded, 417 | "DAI required for trade above max" 418 | ); 419 | // Swapping sent ETH for exact amount of DAI needed 420 | router_.swapETHForExactTokens.value(msg.value)( 421 | daiNeeded, 422 | getPath(true), 423 | address(this), 424 | _deadline 425 | ); 426 | // Getting the amount of ETH received 427 | ethReceived = address(this).balance; 428 | // Approving the curve as a spender 429 | require( 430 | dai_.approve(address(curve_), daiNeeded), 431 | "DAI approve failed" 432 | ); 433 | // Buying tokens (BZZ) against the curve 434 | require( 435 | curve_.mintTo(_tokenAmount, daiNeeded, _to), 436 | "BZZ mintTo failed" 437 | ); 438 | // Refunding user excess ETH 439 | msg.sender.transfer(ethReceived); 440 | } 441 | } -------------------------------------------------------------------------------- /packages/chain/contracts/I_Curve.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.0; 2 | 3 | /** 4 | * @title Interface Curve 5 | * @notice This contract acts as an interface to the curve contract. For 6 | * documentation please see the curve smart contract. 7 | */ 8 | interface I_Curve { 9 | 10 | // ------------------------------------------------------------------------- 11 | // View functions 12 | // ------------------------------------------------------------------------- 13 | 14 | /** 15 | * @notice This function is only callable after the curve contract has been 16 | * initialized. 17 | * @param _amount The amount of tokens a user wants to buy 18 | * @return uint256 The cost to buy the _amount of tokens in the collateral 19 | * currency (see collateral token). 20 | */ 21 | function buyPrice(uint256 _amount) 22 | external 23 | view 24 | returns (uint256 collateralRequired); 25 | 26 | /** 27 | * @notice This function is only callable after the curve contract has been 28 | * initialized. 29 | * @param _amount The amount of tokens a user wants to sell 30 | * @return collateralReward The reward for selling the _amount of tokens in the 31 | * collateral currency (see collateral token). 32 | */ 33 | function sellReward(uint256 _amount) 34 | external 35 | view 36 | returns (uint256 collateralReward); 37 | 38 | /** 39 | * @return If the curve is both active and initialised. 40 | */ 41 | function isCurveActive() external view returns (bool); 42 | 43 | /** 44 | * @return The address of the collateral token (DAI) 45 | */ 46 | function collateralToken() external view returns (address); 47 | 48 | /** 49 | * @return The address of the bonded token (BZZ). 50 | */ 51 | function bondedToken() external view returns (address); 52 | 53 | /** 54 | * @return The required collateral amount (DAI) to initialise the curve. 55 | */ 56 | function requiredCollateral(uint256 _initialSupply) 57 | external 58 | view 59 | returns (uint256); 60 | 61 | // ------------------------------------------------------------------------- 62 | // State modifying functions 63 | // ------------------------------------------------------------------------- 64 | 65 | /** 66 | * @notice This function initializes the curve contract, and ensure the 67 | * curve has the required permissions on the token contract needed 68 | * to function. 69 | */ 70 | function init() external; 71 | 72 | /** 73 | * @param _amount The amount of tokens (BZZ) the user wants to buy. 74 | * @param _maxCollateralSpend The max amount of collateral (DAI) the user is 75 | * willing to spend in order to buy the _amount of tokens. 76 | * @return The status of the mint. Note that should the total cost of the 77 | * purchase exceed the _maxCollateralSpend the transaction will revert. 78 | */ 79 | function mint(uint256 _amount, uint256 _maxCollateralSpend) 80 | external 81 | returns (bool success); 82 | 83 | /** 84 | * @param _amount The amount of tokens (BZZ) the user wants to buy. 85 | * @param _maxCollateralSpend The max amount of collateral (DAI) the user is 86 | * willing to spend in order to buy the _amount of tokens. 87 | * @param _to The address to send the tokens to. 88 | * @return The status of the mint. Note that should the total cost of the 89 | * purchase exceed the _maxCollateralSpend the transaction will revert. 90 | */ 91 | function mintTo( 92 | uint256 _amount, 93 | uint256 _maxCollateralSpend, 94 | address _to 95 | ) 96 | external 97 | returns (bool success); 98 | 99 | /** 100 | * @param _amount The amount of tokens (BZZ) the user wants to sell. 101 | * @param _minCollateralReward The min amount of collateral (DAI) the user is 102 | * willing to receive for their tokens. 103 | * @return The status of the burn. Note that should the total reward of the 104 | * burn be below the _minCollateralReward the transaction will revert. 105 | */ 106 | function redeem(uint256 _amount, uint256 _minCollateralReward) 107 | external 108 | returns (bool success); 109 | 110 | /** 111 | * @notice Shuts down the curve, disabling buying, selling and both price 112 | * functions. Can only be called by the owner. Will renounce the 113 | * minter role on the bonded token. 114 | */ 115 | function shutDown() external; 116 | } 117 | -------------------------------------------------------------------------------- /packages/chain/contracts/I_Token.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.0; 2 | 3 | /** 4 | * @title Interface Token 5 | * @notice Allows the Curve contract to interact with the token contract 6 | * without importing the entire smart contract. For documentation 7 | * please see the token contract: 8 | * https://gitlab.com/linumlabs/swarm-token 9 | * @dev This is not a full interface of the token, but instead a partial 10 | * interface covering only the functions that are needed by the curve. 11 | */ 12 | interface I_Token { 13 | // ------------------------------------------------------------------------- 14 | // IERC20 functions 15 | // ------------------------------------------------------------------------- 16 | 17 | function totalSupply() external view returns (uint256); 18 | 19 | function balanceOf(address account) external view returns (uint256); 20 | 21 | function transfer(address recipient, uint256 amount) 22 | external 23 | returns (bool); 24 | 25 | function allowance(address owner, address spender) 26 | external 27 | view 28 | returns (uint256); 29 | 30 | function approve(address spender, uint256 amount) external returns (bool); 31 | 32 | function transferFrom( 33 | address sender, 34 | address recipient, 35 | uint256 amount 36 | ) external returns (bool); 37 | 38 | // ------------------------------------------------------------------------- 39 | // ERC20 functions 40 | // ------------------------------------------------------------------------- 41 | 42 | function increaseAllowance(address spender, uint256 addedValue) 43 | external 44 | returns (bool); 45 | 46 | function decreaseAllowance(address spender, uint256 subtractedValue) 47 | external 48 | returns (bool); 49 | 50 | // ------------------------------------------------------------------------- 51 | // ERC20 Detailed 52 | // ------------------------------------------------------------------------- 53 | 54 | function name() external view returns (string memory); 55 | 56 | function symbol() external view returns (string memory); 57 | 58 | function decimals() external view returns (uint8); 59 | 60 | // ------------------------------------------------------------------------- 61 | // Burnable functions 62 | // ------------------------------------------------------------------------- 63 | 64 | function burn(uint256 amount) external; 65 | 66 | function burnFrom(address account, uint256 amount) external; 67 | 68 | // ------------------------------------------------------------------------- 69 | // Mintable functions 70 | // ------------------------------------------------------------------------- 71 | 72 | function isMinter(address account) external view returns (bool); 73 | 74 | function addMinter(address account) external; 75 | 76 | function renounceMinter() external; 77 | 78 | function mint(address account, uint256 amount) external returns (bool); 79 | 80 | // ------------------------------------------------------------------------- 81 | // Capped functions 82 | // ------------------------------------------------------------------------- 83 | 84 | function cap() external view returns (uint256); 85 | } 86 | -------------------------------------------------------------------------------- /packages/chain/contracts/I_router_02.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.0; 2 | 3 | /** 4 | * Please note that this interface was created as IUniswapV2Router02 uses 5 | * Solidity >= 0.6.2, and the BZZ infastructure uses 0.5.0. 6 | */ 7 | interface I_router_02 { 8 | // Views & Pure 9 | function WETH() external pure returns (address); 10 | 11 | function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts); 12 | 13 | function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts); 14 | 15 | // State modifying 16 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) 17 | external 18 | payable 19 | returns (uint[] memory amounts); 20 | 21 | function swapExactTokensForETH( 22 | uint amountIn, 23 | uint amountOutMin, 24 | address[] calldata path, 25 | address to, 26 | uint deadline 27 | ) 28 | external 29 | returns (uint[] memory amounts); 30 | } -------------------------------------------------------------------------------- /packages/chain/contracts/Mock_dai.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.0; 2 | 3 | /** 4 | MOCK CONTRACT - DO NOT AUDIT 5 | This ERC20 token contract is solely for testing purposes, and is acting 6 | as a mock for the ERC20 token dai. 7 | 8 | This contract allows individuals to mint tokens freely for testing 9 | purposes. 10 | */ 11 | 12 | /* 13 | * @dev Provides information about the current execution context, including the 14 | * sender of the transaction and its data. While these are generally available 15 | * via msg.sender and msg.data, they should not be accessed in such a direct 16 | * manner, since when dealing with GSN meta-transactions the account sending and 17 | * paying for execution may not be the actual sender (as far as an application 18 | * is concerned). 19 | * 20 | * This contract is only required for intermediate, library-like contracts. 21 | */ 22 | contract Context { 23 | // Empty internal constructor, to prevent people from mistakenly deploying 24 | // an instance of this contract, which should be used via inheritance. 25 | constructor () internal { } 26 | // solhint-disable-previous-line no-empty-blocks 27 | 28 | function _msgSender() internal view returns (address payable) { 29 | return msg.sender; 30 | } 31 | 32 | function _msgData() internal view returns (bytes memory) { 33 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 34 | return msg.data; 35 | } 36 | } 37 | 38 | /** 39 | * @dev Interface of the ERC20 standard as defined in the EIP. Does not include 40 | * the optional functions; to access them see {ERC20Detailed}. 41 | */ 42 | interface IERC20 { 43 | /** 44 | * @dev Returns the amount of tokens in existence. 45 | */ 46 | function totalSupply() external view returns (uint256); 47 | 48 | /** 49 | * @dev Returns the amount of tokens owned by `account`. 50 | */ 51 | function balanceOf(address account) external view returns (uint256); 52 | 53 | /** 54 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 55 | * 56 | * Returns a boolean value indicating whether the operation succeeded. 57 | * 58 | * Emits a {Transfer} event. 59 | */ 60 | function transfer(address recipient, uint256 amount) external returns (bool); 61 | 62 | /** 63 | * @dev Returns the remaining number of tokens that `spender` will be 64 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 65 | * zero by default. 66 | * 67 | * This value changes when {approve} or {transferFrom} are called. 68 | */ 69 | function allowance(address owner, address spender) external view returns (uint256); 70 | 71 | /** 72 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 73 | * 74 | * Returns a boolean value indicating whether the operation succeeded. 75 | * 76 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 77 | * that someone may use both the old and the new allowance by unfortunate 78 | * transaction ordering. One possible solution to mitigate this race 79 | * condition is to first reduce the spender's allowance to 0 and set the 80 | * desired value afterwards: 81 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 82 | * 83 | * Emits an {Approval} event. 84 | */ 85 | function approve(address spender, uint256 amount) external returns (bool); 86 | 87 | /** 88 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 89 | * allowance mechanism. `amount` is then deducted from the caller's 90 | * allowance. 91 | * 92 | * Returns a boolean value indicating whether the operation succeeded. 93 | * 94 | * Emits a {Transfer} event. 95 | */ 96 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 97 | 98 | /** 99 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 100 | * another (`to`). 101 | * 102 | * Note that `value` may be zero. 103 | */ 104 | event Transfer(address indexed from, address indexed to, uint256 value); 105 | 106 | /** 107 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 108 | * a call to {approve}. `value` is the new allowance. 109 | */ 110 | event Approval(address indexed owner, address indexed spender, uint256 value); 111 | } 112 | 113 | /** 114 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 115 | * checks. 116 | * 117 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 118 | * in bugs, because programmers usually assume that an overflow raises an 119 | * error, which is the standard behavior in high level programming languages. 120 | * `SafeMath` restores this intuition by reverting the transaction when an 121 | * operation overflows. 122 | * 123 | * Using this library instead of the unchecked operations eliminates an entire 124 | * class of bugs, so it's recommended to use it always. 125 | */ 126 | library SafeMath { 127 | /** 128 | * @dev Returns the addition of two unsigned integers, reverting on 129 | * overflow. 130 | * 131 | * Counterpart to Solidity's `+` operator. 132 | * 133 | * Requirements: 134 | * - Addition cannot overflow. 135 | */ 136 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 137 | uint256 c = a + b; 138 | require(c >= a, "SafeMath: addition overflow"); 139 | 140 | return c; 141 | } 142 | 143 | /** 144 | * @dev Returns the subtraction of two unsigned integers, reverting on 145 | * overflow (when the result is negative). 146 | * 147 | * Counterpart to Solidity's `-` operator. 148 | * 149 | * Requirements: 150 | * - Subtraction cannot overflow. 151 | */ 152 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 153 | return sub(a, b, "SafeMath: subtraction overflow"); 154 | } 155 | 156 | /** 157 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on 158 | * overflow (when the result is negative). 159 | * 160 | * Counterpart to Solidity's `-` operator. 161 | * 162 | * Requirements: 163 | * - Subtraction cannot overflow. 164 | * 165 | * _Available since v2.4.0._ 166 | */ 167 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 168 | require(b <= a, errorMessage); 169 | uint256 c = a - b; 170 | 171 | return c; 172 | } 173 | 174 | /** 175 | * @dev Returns the multiplication of two unsigned integers, reverting on 176 | * overflow. 177 | * 178 | * Counterpart to Solidity's `*` operator. 179 | * 180 | * Requirements: 181 | * - Multiplication cannot overflow. 182 | */ 183 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 184 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 185 | // benefit is lost if 'b' is also tested. 186 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 187 | if (a == 0) { 188 | return 0; 189 | } 190 | 191 | uint256 c = a * b; 192 | require(c / a == b, "SafeMath: multiplication overflow"); 193 | 194 | return c; 195 | } 196 | 197 | /** 198 | * @dev Returns the integer division of two unsigned integers. Reverts on 199 | * division by zero. The result is rounded towards zero. 200 | * 201 | * Counterpart to Solidity's `/` operator. Note: this function uses a 202 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 203 | * uses an invalid opcode to revert (consuming all remaining gas). 204 | * 205 | * Requirements: 206 | * - The divisor cannot be zero. 207 | */ 208 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 209 | return div(a, b, "SafeMath: division by zero"); 210 | } 211 | 212 | /** 213 | * @dev Returns the integer division of two unsigned integers. Reverts with custom message on 214 | * division by zero. The result is rounded towards zero. 215 | * 216 | * Counterpart to Solidity's `/` operator. Note: this function uses a 217 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 218 | * uses an invalid opcode to revert (consuming all remaining gas). 219 | * 220 | * Requirements: 221 | * - The divisor cannot be zero. 222 | * 223 | * _Available since v2.4.0._ 224 | */ 225 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 226 | // Solidity only automatically asserts when dividing by 0 227 | require(b > 0, errorMessage); 228 | uint256 c = a / b; 229 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 230 | 231 | return c; 232 | } 233 | 234 | /** 235 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 236 | * Reverts when dividing by zero. 237 | * 238 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 239 | * opcode (which leaves remaining gas untouched) while Solidity uses an 240 | * invalid opcode to revert (consuming all remaining gas). 241 | * 242 | * Requirements: 243 | * - The divisor cannot be zero. 244 | */ 245 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 246 | return mod(a, b, "SafeMath: modulo by zero"); 247 | } 248 | 249 | /** 250 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 251 | * Reverts with custom message when dividing by zero. 252 | * 253 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 254 | * opcode (which leaves remaining gas untouched) while Solidity uses an 255 | * invalid opcode to revert (consuming all remaining gas). 256 | * 257 | * Requirements: 258 | * - The divisor cannot be zero. 259 | * 260 | * _Available since v2.4.0._ 261 | */ 262 | function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 263 | require(b != 0, errorMessage); 264 | return a % b; 265 | } 266 | } 267 | 268 | /** 269 | * @dev Implementation of the {IERC20} interface. 270 | * 271 | * This implementation is agnostic to the way tokens are created. This means 272 | * that a supply mechanism has to be added in a derived contract using {_mint}. 273 | * For a generic mechanism see {ERC20Mintable}. 274 | * 275 | * TIP: For a detailed writeup see our guide 276 | * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How 277 | * to implement supply mechanisms]. 278 | * 279 | * We have followed general OpenZeppelin guidelines: functions revert instead 280 | * of returning `false` on failure. This behavior is nonetheless conventional 281 | * and does not conflict with the expectations of ERC20 applications. 282 | * 283 | * Additionally, an {Approval} event is emitted on calls to {transferFrom}. 284 | * This allows applications to reconstruct the allowance for all accounts just 285 | * by listening to said events. Other implementations of the EIP may not emit 286 | * these events, as it isn't required by the specification. 287 | * 288 | * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} 289 | * functions have been added to mitigate the well-known issues around setting 290 | * allowances. See {IERC20-approve}. 291 | */ 292 | contract ERC20 is Context, IERC20 { 293 | using SafeMath for uint256; 294 | 295 | mapping (address => uint256) private _balances; 296 | 297 | mapping (address => mapping (address => uint256)) private _allowances; 298 | 299 | uint256 private _totalSupply; 300 | 301 | /** 302 | * @dev See {IERC20-totalSupply}. 303 | */ 304 | function totalSupply() public view returns (uint256) { 305 | return _totalSupply; 306 | } 307 | 308 | /** 309 | * @dev See {IERC20-balanceOf}. 310 | */ 311 | function balanceOf(address account) public view returns (uint256) { 312 | return _balances[account]; 313 | } 314 | 315 | /** 316 | * @dev See {IERC20-transfer}. 317 | * 318 | * Requirements: 319 | * 320 | * - `recipient` cannot be the zero address. 321 | * - the caller must have a balance of at least `amount`. 322 | */ 323 | function transfer(address recipient, uint256 amount) public returns (bool) { 324 | _transfer(_msgSender(), recipient, amount); 325 | return true; 326 | } 327 | 328 | /** 329 | * @dev See {IERC20-allowance}. 330 | */ 331 | function allowance(address owner, address spender) public view returns (uint256) { 332 | return _allowances[owner][spender]; 333 | } 334 | 335 | /** 336 | * @dev See {IERC20-approve}. 337 | * 338 | * Requirements: 339 | * 340 | * - `spender` cannot be the zero address. 341 | */ 342 | function approve(address spender, uint256 amount) public returns (bool) { 343 | _approve(_msgSender(), spender, amount); 344 | return true; 345 | } 346 | 347 | /** 348 | * @dev See {IERC20-transferFrom}. 349 | * 350 | * Emits an {Approval} event indicating the updated allowance. This is not 351 | * required by the EIP. See the note at the beginning of {ERC20}; 352 | * 353 | * Requirements: 354 | * - `sender` and `recipient` cannot be the zero address. 355 | * - `sender` must have a balance of at least `amount`. 356 | * - the caller must have allowance for `sender`'s tokens of at least 357 | * `amount`. 358 | */ 359 | function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { 360 | _transfer(sender, recipient, amount); 361 | _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); 362 | return true; 363 | } 364 | 365 | /** 366 | * @dev Atomically increases the allowance granted to `spender` by the caller. 367 | * 368 | * This is an alternative to {approve} that can be used as a mitigation for 369 | * problems described in {IERC20-approve}. 370 | * 371 | * Emits an {Approval} event indicating the updated allowance. 372 | * 373 | * Requirements: 374 | * 375 | * - `spender` cannot be the zero address. 376 | */ 377 | function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { 378 | _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); 379 | return true; 380 | } 381 | 382 | /** 383 | * @dev Atomically decreases the allowance granted to `spender` by the caller. 384 | * 385 | * This is an alternative to {approve} that can be used as a mitigation for 386 | * problems described in {IERC20-approve}. 387 | * 388 | * Emits an {Approval} event indicating the updated allowance. 389 | * 390 | * Requirements: 391 | * 392 | * - `spender` cannot be the zero address. 393 | * - `spender` must have allowance for the caller of at least 394 | * `subtractedValue`. 395 | */ 396 | function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { 397 | _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); 398 | return true; 399 | } 400 | 401 | /** 402 | * @dev Moves tokens `amount` from `sender` to `recipient`. 403 | * 404 | * This is internal function is equivalent to {transfer}, and can be used to 405 | * e.g. implement automatic token fees, slashing mechanisms, etc. 406 | * 407 | * Emits a {Transfer} event. 408 | * 409 | * Requirements: 410 | * 411 | * - `sender` cannot be the zero address. 412 | * - `recipient` cannot be the zero address. 413 | * - `sender` must have a balance of at least `amount`. 414 | */ 415 | function _transfer(address sender, address recipient, uint256 amount) internal { 416 | require(sender != address(0), "ERC20: transfer from the zero address"); 417 | require(recipient != address(0), "ERC20: transfer to the zero address"); 418 | 419 | _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); 420 | _balances[recipient] = _balances[recipient].add(amount); 421 | emit Transfer(sender, recipient, amount); 422 | } 423 | 424 | /** @dev Creates `amount` tokens and assigns them to `account`, increasing 425 | * the total supply. 426 | * 427 | * Emits a {Transfer} event with `from` set to the zero address. 428 | * 429 | * Requirements 430 | * 431 | * - `to` cannot be the zero address. 432 | */ 433 | function _mint(address account, uint256 amount) internal { 434 | require(account != address(0), "ERC20: mint to the zero address"); 435 | 436 | _totalSupply = _totalSupply.add(amount); 437 | _balances[account] = _balances[account].add(amount); 438 | emit Transfer(address(0), account, amount); 439 | } 440 | 441 | /** 442 | * @dev Destroys `amount` tokens from `account`, reducing the 443 | * total supply. 444 | * 445 | * Emits a {Transfer} event with `to` set to the zero address. 446 | * 447 | * Requirements 448 | * 449 | * - `account` cannot be the zero address. 450 | * - `account` must have at least `amount` tokens. 451 | */ 452 | function _burn(address account, uint256 amount) internal { 453 | require(account != address(0), "ERC20: burn from the zero address"); 454 | 455 | _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); 456 | _totalSupply = _totalSupply.sub(amount); 457 | emit Transfer(account, address(0), amount); 458 | } 459 | 460 | /** 461 | * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. 462 | * 463 | * This is internal function is equivalent to `approve`, and can be used to 464 | * e.g. set automatic allowances for certain subsystems, etc. 465 | * 466 | * Emits an {Approval} event. 467 | * 468 | * Requirements: 469 | * 470 | * - `owner` cannot be the zero address. 471 | * - `spender` cannot be the zero address. 472 | */ 473 | function _approve(address owner, address spender, uint256 amount) internal { 474 | require(owner != address(0), "ERC20: approve from the zero address"); 475 | require(spender != address(0), "ERC20: approve to the zero address"); 476 | 477 | _allowances[owner][spender] = amount; 478 | emit Approval(owner, spender, amount); 479 | } 480 | 481 | /** 482 | * @dev Destroys `amount` tokens from `account`.`amount` is then deducted 483 | * from the caller's allowance. 484 | * 485 | * See {_burn} and {_approve}. 486 | */ 487 | function _burnFrom(address account, uint256 amount) internal { 488 | _burn(account, amount); 489 | _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance")); 490 | } 491 | } 492 | 493 | /** 494 | * @dev Optional functions from the ERC20 standard. 495 | */ 496 | contract ERC20Detailed is IERC20 { 497 | string private _name; 498 | string private _symbol; 499 | uint8 private _decimals; 500 | 501 | /** 502 | * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of 503 | * these values are immutable: they can only be set once during 504 | * construction. 505 | */ 506 | constructor (string memory name, string memory symbol, uint8 decimals) public { 507 | _name = name; 508 | _symbol = symbol; 509 | _decimals = decimals; 510 | } 511 | 512 | /** 513 | * @dev Returns the name of the token. 514 | */ 515 | function name() public view returns (string memory) { 516 | return _name; 517 | } 518 | 519 | /** 520 | * @dev Returns the symbol of the token, usually a shorter version of the 521 | * name. 522 | */ 523 | function symbol() public view returns (string memory) { 524 | return _symbol; 525 | } 526 | 527 | /** 528 | * @dev Returns the number of decimals used to get its user representation. 529 | * For example, if `decimals` equals `2`, a balance of `505` tokens should 530 | * be displayed to a user as `5,05` (`505 / 10 ** 2`). 531 | * 532 | * Tokens usually opt for a value of 18, imitating the relationship between 533 | * Ether and Wei. 534 | * 535 | * NOTE: This information is only used for _display_ purposes: it in 536 | * no way affects any of the arithmetic of the contract, including 537 | * {IERC20-balanceOf} and {IERC20-transfer}. 538 | */ 539 | function decimals() public view returns (uint8) { 540 | return _decimals; 541 | } 542 | } 543 | 544 | /** 545 | * @notice This contract is for the Swarm BZZ token. This contract inherits 546 | * from all the above imported contracts indirectly through the 547 | * implemented contracts. ERC20Capped is Mintable, Burnable is an ERC20 548 | */ 549 | contract Mock_dai is ERC20Detailed, ERC20 { 550 | /** 551 | * @dev Initialises all the inherited smart contracts 552 | */ 553 | constructor( 554 | string memory _name, 555 | string memory _symbol, 556 | uint8 _decimals 557 | ) 558 | ERC20() 559 | ERC20Detailed( 560 | _name, 561 | _symbol, 562 | _decimals 563 | ) 564 | public 565 | { 566 | 567 | } 568 | 569 | function mint(uint256 _amount) public returns(bool) { 570 | _mint(msg.sender, _amount); 571 | } 572 | } -------------------------------------------------------------------------------- /packages/chain/contracts/Mock_router.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.0; 2 | 3 | import "./I_router_02.sol"; 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | contract Mock_router is I_router_02 { 7 | address internal weth_; 8 | 9 | constructor(address _weth) public payable { 10 | weth_ = _weth; 11 | } 12 | 13 | function WETH() external pure returns (address) { 14 | return 0xacDdD0dBa07959Be810f6cd29E41b127b29E4A8a; 15 | } 16 | 17 | function getAmountsOut(uint256 amountIn, address[] calldata path) 18 | external 19 | view 20 | returns (uint256[] memory amounts) 21 | { 22 | require( 23 | path[0] != address(0), 24 | "Valid path is required" 25 | ); 26 | amounts = new uint256[](2); 27 | amounts[0] = amountIn; // Eth for DAI 28 | amounts[1] = 0; 29 | } 30 | 31 | function getAmountsIn(uint256 amountOut, address[] calldata path) 32 | external 33 | view 34 | returns (uint256[] memory amounts) 35 | { 36 | require( 37 | path[0] != address(0), 38 | "Valid path is required" 39 | ); 40 | // AmountOut DAI 41 | amounts = new uint256[](2); 42 | amounts[0] = (amountOut*17)/10000; // Eth for DAI 43 | amounts[1] = 0; 44 | } 45 | 46 | // State modifying 47 | function swapETHForExactTokens( 48 | uint256 amountOut, // DAI 49 | address[] calldata path, 50 | address to, 51 | uint256 deadline 52 | ) external payable returns (uint256[] memory amounts) { 53 | // Getting the eth amount from the internal calculation 54 | amounts = this.getAmountsIn(amountOut, path); 55 | // Ensureing the user has sent enough ETH for their desired amountOut 56 | require(msg.value >= amounts[0], "Insufficient Eth sent"); 57 | require( 58 | deadline != 0, 59 | "Invalid deadline" 60 | ); 61 | // Sending the user the required amount of DAI 62 | // Note that this contract will need to be seeded with collateral 63 | IERC20(path[1]).transfer(to, amountOut); 64 | 65 | return(amounts); 66 | } 67 | 68 | function swapExactTokensForETH( 69 | uint256 amountIn, // DAI amount (pre-approved) 70 | uint256 amountOutMin, // Min ETH for DAI amount 71 | address[] calldata path, 72 | address to, 73 | uint256 deadline 74 | ) external returns (uint256[] memory amounts) { 75 | // Transfering DAI to this router 76 | IERC20(path[0]).transferFrom( 77 | msg.sender, 78 | address(this), 79 | amountIn 80 | ); 81 | amounts = this.getAmountsIn(amountIn, path); 82 | // Checks rates user will get within min range 83 | require( 84 | amounts[0] >= amountOutMin, 85 | "Receivable ETH less than min" 86 | ); 87 | require( 88 | to != address(0), 89 | "Mock needs to == msg.sender" 90 | ); 91 | require( 92 | deadline != 0, 93 | "Invalid deadline" 94 | ); 95 | // Sending user ETH amount 96 | to.call.value(amounts[0])(""); 97 | 98 | return(amounts); 99 | } 100 | 101 | function() external payable {} 102 | } 103 | -------------------------------------------------------------------------------- /packages/chain/contracts/Token.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.0; 2 | 3 | // FLAT - OpenZeppelin Smart Contracts 4 | /** 5 | * @notice Below is all the required smart contracts from the OpenZeppelin 6 | * library needed for the Token contract. This is the inheritance 7 | * tree of the token: 8 | * 9 | * Token 10 | * |--ERC20Detailed 11 | * | |--IERC20 12 | * |--ERC20Capped 13 | * | |--ERC20Mintable 14 | * | |--MinterRoll 15 | * | | |--Context 16 | * | | |--Roles 17 | * | |--ERC20 18 | * | |--IERC20 19 | * | |--Context 20 | * | |--SafeMath 21 | * |--ERC20Burnable 22 | * | |--Context 23 | * | |--ERC20 24 | * | |--IERC20 25 | * | |--Context 26 | * | |--SafeMath 27 | */ 28 | 29 | /* 30 | * @dev Provides information about the current execution context, including the 31 | * sender of the transaction and its data. While these are generally available 32 | * via msg.sender and msg.data, they should not be accessed in such a direct 33 | * manner, since when dealing with GSN meta-transactions the account sending and 34 | * paying for execution may not be the actual sender (as far as an application 35 | * is concerned). 36 | * 37 | * This contract is only required for intermediate, library-like contracts. 38 | */ 39 | contract Context { 40 | // Empty internal constructor, to prevent people from mistakenly deploying 41 | // an instance of this contract, which should be used via inheritance. 42 | constructor () internal { } 43 | // solhint-disable-previous-line no-empty-blocks 44 | 45 | function _msgSender() internal view returns (address payable) { 46 | return msg.sender; 47 | } 48 | 49 | function _msgData() internal view returns (bytes memory) { 50 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 51 | return msg.data; 52 | } 53 | } 54 | 55 | /** 56 | * @dev Interface of the ERC20 standard as defined in the EIP. Does not include 57 | * the optional functions; to access them see {ERC20Detailed}. 58 | */ 59 | interface IERC20 { 60 | /** 61 | * @dev Returns the amount of tokens in existence. 62 | */ 63 | function totalSupply() external view returns (uint256); 64 | 65 | /** 66 | * @dev Returns the amount of tokens owned by `account`. 67 | */ 68 | function balanceOf(address account) external view returns (uint256); 69 | 70 | /** 71 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 72 | * 73 | * Returns a boolean value indicating whether the operation succeeded. 74 | * 75 | * Emits a {Transfer} event. 76 | */ 77 | function transfer(address recipient, uint256 amount) external returns (bool); 78 | 79 | /** 80 | * @dev Returns the remaining number of tokens that `spender` will be 81 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 82 | * zero by default. 83 | * 84 | * This value changes when {approve} or {transferFrom} are called. 85 | */ 86 | function allowance(address owner, address spender) external view returns (uint256); 87 | 88 | /** 89 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 90 | * 91 | * Returns a boolean value indicating whether the operation succeeded. 92 | * 93 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 94 | * that someone may use both the old and the new allowance by unfortunate 95 | * transaction ordering. One possible solution to mitigate this race 96 | * condition is to first reduce the spender's allowance to 0 and set the 97 | * desired value afterwards: 98 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 99 | * 100 | * Emits an {Approval} event. 101 | */ 102 | function approve(address spender, uint256 amount) external returns (bool); 103 | 104 | /** 105 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 106 | * allowance mechanism. `amount` is then deducted from the caller's 107 | * allowance. 108 | * 109 | * Returns a boolean value indicating whether the operation succeeded. 110 | * 111 | * Emits a {Transfer} event. 112 | */ 113 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 114 | 115 | /** 116 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 117 | * another (`to`). 118 | * 119 | * Note that `value` may be zero. 120 | */ 121 | event Transfer(address indexed from, address indexed to, uint256 value); 122 | 123 | /** 124 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 125 | * a call to {approve}. `value` is the new allowance. 126 | */ 127 | event Approval(address indexed owner, address indexed spender, uint256 value); 128 | } 129 | 130 | /** 131 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 132 | * checks. 133 | * 134 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 135 | * in bugs, because programmers usually assume that an overflow raises an 136 | * error, which is the standard behavior in high level programming languages. 137 | * `SafeMath` restores this intuition by reverting the transaction when an 138 | * operation overflows. 139 | * 140 | * Using this library instead of the unchecked operations eliminates an entire 141 | * class of bugs, so it's recommended to use it always. 142 | */ 143 | library SafeMath { 144 | /** 145 | * @dev Returns the addition of two unsigned integers, reverting on 146 | * overflow. 147 | * 148 | * Counterpart to Solidity's `+` operator. 149 | * 150 | * Requirements: 151 | * - Addition cannot overflow. 152 | */ 153 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 154 | uint256 c = a + b; 155 | require(c >= a, "SafeMath: addition overflow"); 156 | 157 | return c; 158 | } 159 | 160 | /** 161 | * @dev Returns the subtraction of two unsigned integers, reverting on 162 | * overflow (when the result is negative). 163 | * 164 | * Counterpart to Solidity's `-` operator. 165 | * 166 | * Requirements: 167 | * - Subtraction cannot overflow. 168 | */ 169 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 170 | return sub(a, b, "SafeMath: subtraction overflow"); 171 | } 172 | 173 | /** 174 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on 175 | * overflow (when the result is negative). 176 | * 177 | * Counterpart to Solidity's `-` operator. 178 | * 179 | * Requirements: 180 | * - Subtraction cannot overflow. 181 | * 182 | * _Available since v2.4.0._ 183 | */ 184 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 185 | require(b <= a, errorMessage); 186 | uint256 c = a - b; 187 | 188 | return c; 189 | } 190 | 191 | /** 192 | * @dev Returns the multiplication of two unsigned integers, reverting on 193 | * overflow. 194 | * 195 | * Counterpart to Solidity's `*` operator. 196 | * 197 | * Requirements: 198 | * - Multiplication cannot overflow. 199 | */ 200 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 201 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 202 | // benefit is lost if 'b' is also tested. 203 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 204 | if (a == 0) { 205 | return 0; 206 | } 207 | 208 | uint256 c = a * b; 209 | require(c / a == b, "SafeMath: multiplication overflow"); 210 | 211 | return c; 212 | } 213 | 214 | /** 215 | * @dev Returns the integer division of two unsigned integers. Reverts on 216 | * division by zero. The result is rounded towards zero. 217 | * 218 | * Counterpart to Solidity's `/` operator. Note: this function uses a 219 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 220 | * uses an invalid opcode to revert (consuming all remaining gas). 221 | * 222 | * Requirements: 223 | * - The divisor cannot be zero. 224 | */ 225 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 226 | return div(a, b, "SafeMath: division by zero"); 227 | } 228 | 229 | /** 230 | * @dev Returns the integer division of two unsigned integers. Reverts with custom message on 231 | * division by zero. The result is rounded towards zero. 232 | * 233 | * Counterpart to Solidity's `/` operator. Note: this function uses a 234 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 235 | * uses an invalid opcode to revert (consuming all remaining gas). 236 | * 237 | * Requirements: 238 | * - The divisor cannot be zero. 239 | * 240 | * _Available since v2.4.0._ 241 | */ 242 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 243 | // Solidity only automatically asserts when dividing by 0 244 | require(b > 0, errorMessage); 245 | uint256 c = a / b; 246 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 247 | 248 | return c; 249 | } 250 | 251 | /** 252 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 253 | * Reverts when dividing by zero. 254 | * 255 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 256 | * opcode (which leaves remaining gas untouched) while Solidity uses an 257 | * invalid opcode to revert (consuming all remaining gas). 258 | * 259 | * Requirements: 260 | * - The divisor cannot be zero. 261 | */ 262 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 263 | return mod(a, b, "SafeMath: modulo by zero"); 264 | } 265 | 266 | /** 267 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 268 | * Reverts with custom message when dividing by zero. 269 | * 270 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 271 | * opcode (which leaves remaining gas untouched) while Solidity uses an 272 | * invalid opcode to revert (consuming all remaining gas). 273 | * 274 | * Requirements: 275 | * - The divisor cannot be zero. 276 | * 277 | * _Available since v2.4.0._ 278 | */ 279 | function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 280 | require(b != 0, errorMessage); 281 | return a % b; 282 | } 283 | } 284 | 285 | /** 286 | * @dev Implementation of the {IERC20} interface. 287 | * 288 | * This implementation is agnostic to the way tokens are created. This means 289 | * that a supply mechanism has to be added in a derived contract using {_mint}. 290 | * For a generic mechanism see {ERC20Mintable}. 291 | * 292 | * TIP: For a detailed writeup see our guide 293 | * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How 294 | * to implement supply mechanisms]. 295 | * 296 | * We have followed general OpenZeppelin guidelines: functions revert instead 297 | * of returning `false` on failure. This behavior is nonetheless conventional 298 | * and does not conflict with the expectations of ERC20 applications. 299 | * 300 | * Additionally, an {Approval} event is emitted on calls to {transferFrom}. 301 | * This allows applications to reconstruct the allowance for all accounts just 302 | * by listening to said events. Other implementations of the EIP may not emit 303 | * these events, as it isn't required by the specification. 304 | * 305 | * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} 306 | * functions have been added to mitigate the well-known issues around setting 307 | * allowances. See {IERC20-approve}. 308 | */ 309 | contract ERC20 is Context, IERC20 { 310 | using SafeMath for uint256; 311 | 312 | mapping (address => uint256) private _balances; 313 | 314 | mapping (address => mapping (address => uint256)) private _allowances; 315 | 316 | uint256 private _totalSupply; 317 | 318 | /** 319 | * @dev See {IERC20-totalSupply}. 320 | */ 321 | function totalSupply() public view returns (uint256) { 322 | return _totalSupply; 323 | } 324 | 325 | /** 326 | * @dev See {IERC20-balanceOf}. 327 | */ 328 | function balanceOf(address account) public view returns (uint256) { 329 | return _balances[account]; 330 | } 331 | 332 | /** 333 | * @dev See {IERC20-transfer}. 334 | * 335 | * Requirements: 336 | * 337 | * - `recipient` cannot be the zero address. 338 | * - the caller must have a balance of at least `amount`. 339 | */ 340 | function transfer(address recipient, uint256 amount) public returns (bool) { 341 | _transfer(_msgSender(), recipient, amount); 342 | return true; 343 | } 344 | 345 | /** 346 | * @dev See {IERC20-allowance}. 347 | */ 348 | function allowance(address owner, address spender) public view returns (uint256) { 349 | return _allowances[owner][spender]; 350 | } 351 | 352 | /** 353 | * @dev See {IERC20-approve}. 354 | * 355 | * Requirements: 356 | * 357 | * - `spender` cannot be the zero address. 358 | */ 359 | function approve(address spender, uint256 amount) public returns (bool) { 360 | _approve(_msgSender(), spender, amount); 361 | return true; 362 | } 363 | 364 | /** 365 | * @dev See {IERC20-transferFrom}. 366 | * 367 | * Emits an {Approval} event indicating the updated allowance. This is not 368 | * required by the EIP. See the note at the beginning of {ERC20}; 369 | * 370 | * Requirements: 371 | * - `sender` and `recipient` cannot be the zero address. 372 | * - `sender` must have a balance of at least `amount`. 373 | * - the caller must have allowance for `sender`'s tokens of at least 374 | * `amount`. 375 | */ 376 | function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { 377 | _transfer(sender, recipient, amount); 378 | _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); 379 | return true; 380 | } 381 | 382 | /** 383 | * @dev Atomically increases the allowance granted to `spender` by the caller. 384 | * 385 | * This is an alternative to {approve} that can be used as a mitigation for 386 | * problems described in {IERC20-approve}. 387 | * 388 | * Emits an {Approval} event indicating the updated allowance. 389 | * 390 | * Requirements: 391 | * 392 | * - `spender` cannot be the zero address. 393 | */ 394 | function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { 395 | _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); 396 | return true; 397 | } 398 | 399 | /** 400 | * @dev Atomically decreases the allowance granted to `spender` by the caller. 401 | * 402 | * This is an alternative to {approve} that can be used as a mitigation for 403 | * problems described in {IERC20-approve}. 404 | * 405 | * Emits an {Approval} event indicating the updated allowance. 406 | * 407 | * Requirements: 408 | * 409 | * - `spender` cannot be the zero address. 410 | * - `spender` must have allowance for the caller of at least 411 | * `subtractedValue`. 412 | */ 413 | function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { 414 | _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); 415 | return true; 416 | } 417 | 418 | /** 419 | * @dev Moves tokens `amount` from `sender` to `recipient`. 420 | * 421 | * This is internal function is equivalent to {transfer}, and can be used to 422 | * e.g. implement automatic token fees, slashing mechanisms, etc. 423 | * 424 | * Emits a {Transfer} event. 425 | * 426 | * Requirements: 427 | * 428 | * - `sender` cannot be the zero address. 429 | * - `recipient` cannot be the zero address. 430 | * - `sender` must have a balance of at least `amount`. 431 | */ 432 | function _transfer(address sender, address recipient, uint256 amount) internal { 433 | require(sender != address(0), "ERC20: transfer from the zero address"); 434 | require(recipient != address(0), "ERC20: transfer to the zero address"); 435 | 436 | _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); 437 | _balances[recipient] = _balances[recipient].add(amount); 438 | emit Transfer(sender, recipient, amount); 439 | } 440 | 441 | /** @dev Creates `amount` tokens and assigns them to `account`, increasing 442 | * the total supply. 443 | * 444 | * Emits a {Transfer} event with `from` set to the zero address. 445 | * 446 | * Requirements 447 | * 448 | * - `to` cannot be the zero address. 449 | */ 450 | function _mint(address account, uint256 amount) internal { 451 | require(account != address(0), "ERC20: mint to the zero address"); 452 | 453 | _totalSupply = _totalSupply.add(amount); 454 | _balances[account] = _balances[account].add(amount); 455 | emit Transfer(address(0), account, amount); 456 | } 457 | 458 | /** 459 | * @dev Destroys `amount` tokens from `account`, reducing the 460 | * total supply. 461 | * 462 | * Emits a {Transfer} event with `to` set to the zero address. 463 | * 464 | * Requirements 465 | * 466 | * - `account` cannot be the zero address. 467 | * - `account` must have at least `amount` tokens. 468 | */ 469 | function _burn(address account, uint256 amount) internal { 470 | require(account != address(0), "ERC20: burn from the zero address"); 471 | 472 | _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); 473 | _totalSupply = _totalSupply.sub(amount); 474 | emit Transfer(account, address(0), amount); 475 | } 476 | 477 | /** 478 | * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. 479 | * 480 | * This is internal function is equivalent to `approve`, and can be used to 481 | * e.g. set automatic allowances for certain subsystems, etc. 482 | * 483 | * Emits an {Approval} event. 484 | * 485 | * Requirements: 486 | * 487 | * - `owner` cannot be the zero address. 488 | * - `spender` cannot be the zero address. 489 | */ 490 | function _approve(address owner, address spender, uint256 amount) internal { 491 | require(owner != address(0), "ERC20: approve from the zero address"); 492 | require(spender != address(0), "ERC20: approve to the zero address"); 493 | 494 | _allowances[owner][spender] = amount; 495 | emit Approval(owner, spender, amount); 496 | } 497 | 498 | /** 499 | * @dev Destroys `amount` tokens from `account`.`amount` is then deducted 500 | * from the caller's allowance. 501 | * 502 | * See {_burn} and {_approve}. 503 | */ 504 | function _burnFrom(address account, uint256 amount) internal { 505 | _burn(account, amount); 506 | _approve(account, _msgSender(), _allowances[account][_msgSender()].sub(amount, "ERC20: burn amount exceeds allowance")); 507 | } 508 | } 509 | 510 | /** 511 | * @title Roles 512 | * @dev Library for managing addresses assigned to a Role. 513 | */ 514 | library Roles { 515 | struct Role { 516 | mapping (address => bool) bearer; 517 | } 518 | 519 | /** 520 | * @dev Give an account access to this role. 521 | */ 522 | function add(Role storage role, address account) internal { 523 | require(!has(role, account), "Roles: account already has role"); 524 | role.bearer[account] = true; 525 | } 526 | 527 | /** 528 | * @dev Remove an account's access to this role. 529 | */ 530 | function remove(Role storage role, address account) internal { 531 | require(has(role, account), "Roles: account does not have role"); 532 | role.bearer[account] = false; 533 | } 534 | 535 | /** 536 | * @dev Check if an account has this role. 537 | * @return bool 538 | */ 539 | function has(Role storage role, address account) internal view returns (bool) { 540 | require(account != address(0), "Roles: account is the zero address"); 541 | return role.bearer[account]; 542 | } 543 | } 544 | 545 | 546 | contract MinterRole is Context { 547 | using Roles for Roles.Role; 548 | 549 | event MinterAdded(address indexed account); 550 | event MinterRemoved(address indexed account); 551 | 552 | Roles.Role private _minters; 553 | 554 | constructor () internal { 555 | _addMinter(_msgSender()); 556 | } 557 | 558 | modifier onlyMinter() { 559 | require(isMinter(_msgSender()), "MinterRole: caller does not have the Minter role"); 560 | _; 561 | } 562 | 563 | function isMinter(address account) public view returns (bool) { 564 | return _minters.has(account); 565 | } 566 | 567 | function addMinter(address account) public onlyMinter { 568 | _addMinter(account); 569 | } 570 | 571 | function renounceMinter() public { 572 | _removeMinter(_msgSender()); 573 | } 574 | 575 | function _addMinter(address account) internal { 576 | _minters.add(account); 577 | emit MinterAdded(account); 578 | } 579 | 580 | function _removeMinter(address account) internal { 581 | _minters.remove(account); 582 | emit MinterRemoved(account); 583 | } 584 | } 585 | 586 | /** 587 | * @dev Extension of {ERC20} that adds a set of accounts with the {MinterRole}, 588 | * which have permission to mint (create) new tokens as they see fit. 589 | * 590 | * At construction, the deployer of the contract is the only minter. 591 | */ 592 | contract ERC20Mintable is ERC20, MinterRole { 593 | /** 594 | * @dev See {ERC20-_mint}. 595 | * 596 | * Requirements: 597 | * 598 | * - the caller must have the {MinterRole}. 599 | */ 600 | function mint(address account, uint256 amount) public onlyMinter returns (bool) { 601 | _mint(account, amount); 602 | return true; 603 | } 604 | } 605 | 606 | 607 | /** 608 | * @dev Extension of {ERC20} that allows token holders to destroy both their own 609 | * tokens and those that they have an allowance for, in a way that can be 610 | * recognized off-chain (via event analysis). 611 | */ 612 | contract ERC20Burnable is Context, ERC20, MinterRole { 613 | /** 614 | * @dev Destroys `amount` tokens from the caller. 615 | * 616 | * See {ERC20-_burn}. 617 | */ 618 | function burn(uint256 amount) public onlyMinter { 619 | _burn(_msgSender(), amount); 620 | } 621 | 622 | /** 623 | * @dev See {ERC20-_burnFrom}. 624 | */ 625 | function burnFrom(address account, uint256 amount) public onlyMinter { 626 | _burnFrom(account, amount); 627 | } 628 | } 629 | 630 | /** 631 | * @dev Extension of {ERC20Mintable} that adds a cap to the supply of tokens. 632 | */ 633 | contract ERC20Capped is ERC20Mintable { 634 | uint256 private _cap; 635 | 636 | /** 637 | * @dev Sets the value of the `cap`. This value is immutable, it can only be 638 | * set once during construction. 639 | */ 640 | constructor (uint256 cap) public { 641 | require(cap > 0, "ERC20Capped: cap is 0"); 642 | _cap = cap; 643 | } 644 | 645 | /** 646 | * @dev Returns the cap on the token's total supply. 647 | */ 648 | function cap() public view returns (uint256) { 649 | return _cap; 650 | } 651 | 652 | /** 653 | * @dev See {ERC20Mintable-mint}. 654 | * 655 | * Requirements: 656 | * 657 | * - `value` must not cause the total supply to go over the cap. 658 | */ 659 | function _mint(address account, uint256 value) internal { 660 | require(totalSupply().add(value) <= _cap, "ERC20Capped: cap exceeded"); 661 | super._mint(account, value); 662 | } 663 | } 664 | 665 | /** 666 | * @dev Optional functions from the ERC20 standard. 667 | */ 668 | contract ERC20Detailed is IERC20 { 669 | string private _name; 670 | string private _symbol; 671 | uint8 private _decimals; 672 | 673 | /** 674 | * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of 675 | * these values are immutable: they can only be set once during 676 | * construction. 677 | */ 678 | constructor (string memory name, string memory symbol, uint8 decimals) public { 679 | _name = name; 680 | _symbol = symbol; 681 | _decimals = decimals; 682 | } 683 | 684 | /** 685 | * @dev Returns the name of the token. 686 | */ 687 | function name() public view returns (string memory) { 688 | return _name; 689 | } 690 | 691 | /** 692 | * @dev Returns the symbol of the token, usually a shorter version of the 693 | * name. 694 | */ 695 | function symbol() public view returns (string memory) { 696 | return _symbol; 697 | } 698 | 699 | /** 700 | * @dev Returns the number of decimals used to get its user representation. 701 | * For example, if `decimals` equals `2`, a balance of `505` tokens should 702 | * be displayed to a user as `5,05` (`505 / 10 ** 2`). 703 | * 704 | * Tokens usually opt for a value of 18, imitating the relationship between 705 | * Ether and Wei. 706 | * 707 | * NOTE: This information is only used for _display_ purposes: it in 708 | * no way affects any of the arithmetic of the contract, including 709 | * {IERC20-balanceOf} and {IERC20-transfer}. 710 | */ 711 | function decimals() public view returns (uint8) { 712 | return _decimals; 713 | } 714 | } 715 | 716 | /** 717 | * @notice This contract is for the Swarm BZZ token. This contract inherits 718 | * from all the above imported contracts indirectly through the 719 | * implemented contracts. ERC20Capped is Mintable, Burnable is an ERC20 720 | */ 721 | contract Token is ERC20Detailed, ERC20Capped, ERC20Burnable { 722 | /** 723 | * @dev Initialises all the inherited smart contracts 724 | */ 725 | constructor( 726 | string memory _name, 727 | string memory _symbol, 728 | uint8 _decimals, 729 | uint256 _cap 730 | ) 731 | ERC20() 732 | ERC20Detailed( 733 | _name, 734 | _symbol, 735 | _decimals 736 | ) 737 | ERC20Capped( 738 | _cap 739 | ) 740 | ERC20Mintable() 741 | ERC20Burnable() 742 | public 743 | { 744 | 745 | } 746 | } -------------------------------------------------------------------------------- /packages/chain/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import "@nomiclabs/hardhat-ethers"; 2 | import "@nomiclabs/hardhat-etherscan"; 3 | import "@nomiclabs/hardhat-waffle"; 4 | // import "hardhat-typechain"; 5 | import { HardhatUserConfig } from 'hardhat/types'; 6 | import * as dotenv from "dotenv"; 7 | 8 | dotenv.config(); 9 | 10 | 11 | /** 12 | * @type import('hardhat/config').HardhatUserConfig 13 | */ 14 | 15 | const config: HardhatUserConfig = { 16 | solidity: { 17 | compilers: [{ version: "0.5.0", settings: {} }], 18 | }, 19 | // typechain: { 20 | // outDir: "src/types", 21 | // target: "ethers-v5", 22 | // }, 23 | defaultNetwork: "hardhat", 24 | networks: { 25 | hardhat: { 26 | accounts: { 27 | accountsBalance: "1000000000000000000000000" 28 | } 29 | }, 30 | localhost: { 31 | url: "http://localhost:8545", 32 | /* 33 | notice no env vars here? it will just use account 0 of the hardhat node to deploy 34 | (you can put in a mnemonic here to set the deployer locally) 35 | */ 36 | }, 37 | // networks without env vars set need to be commented out or they'll crash the script 38 | // so only uncomment if the .env has been set 39 | // rinkeby: { 40 | // url: `https://rinkeby.infura.io/v3/${process.env.RINKEBY_INFURA_KEY}`, 41 | // accounts: [`${process.env.RINKEBY_DEPLOYER_PRIV_KEY}`], 42 | // }, 43 | // mainnet: { 44 | // url: `https://mainnet.infura.io/v3/${process.env.MAINNET_INFURA_KEY}`, 45 | // accounts: [`${process.env.MAINNET_DEPLOYER_PRIV_KEY}`], 46 | // }, 47 | // kovan: { 48 | // url: `https://kovan.infura.io/v3/${process.env.KOVAN_INFURA_KEY}`, 49 | // accounts: [`${process.env.KOVAN_DEPLOYER_PRIV_KEY}`], 50 | // }, 51 | // ropsten: { 52 | // url: `https://ropsten.infura.io/v3/${process.env.ROPSTEN_INFURA_KEY}`, 53 | // accounts: [`${process.env.ROPSTEN_DEPLOYER_PRIV_KEY}`], 54 | // }, 55 | // goerli: { 56 | // url: `https://goerli.infura.io/v3/${process.env.GOERLI_INFURA_KEY}`, 57 | // accounts: [`${process.env.GOERLI_DEPLOYER_PRIV_KEY}`], 58 | // }, 59 | // xdai: { 60 | // url: 'https://dai.poa.network', 61 | // gasPrice: 1000000000, 62 | // accounts: [`${process.env.XDAI_DEPLOYER_PRIV_KEY}`], 63 | // }, 64 | }, 65 | etherscan: { 66 | apiKey: process.env.ETHERSCAN_API_KEY 67 | }, 68 | // mocha options can be set here 69 | mocha: { 70 | // timeout: "300s", 71 | }, 72 | }; 73 | 74 | export default config; -------------------------------------------------------------------------------- /packages/chain/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bzzaar-contracts/chain", 3 | "version": "0.0.3", 4 | "description": "custom Hardhat blockchain dev environment", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "dependencies": { 8 | "@nomiclabs/hardhat-ethers": "^2.0.2", 9 | "@nomiclabs/hardhat-waffle": "^2.0.1", 10 | "@openzeppelin/contracts": "2.5.0", 11 | "@types/chai": "^4.2.14", 12 | "@types/cli-progress": "^3.9.0", 13 | "@types/jest": "^26.0.16", 14 | "chai": "^4.3.4", 15 | "chalk": "^4.1.0", 16 | "cli-progress": "^3.9.0", 17 | "cross-env": "7.0.3", 18 | "dotenv": "^8.2.0", 19 | "ethereum-waffle": "^3.4.0", 20 | "ethers": "^5.4.7", 21 | "hardhat": "^2.6.1", 22 | "hardhat-typechain": "^0.3.3", 23 | "progress": "^2.0.3", 24 | "ts-node": "^9.1.0", 25 | "typescript": "^4.1.2", 26 | "yargs": "^16.2.0" 27 | }, 28 | "devDependencies": { 29 | "@nomiclabs/hardhat-etherscan": "^2.1.1", 30 | "@types/cli-progress": "^3.9.0", 31 | "@types/node": "^14.14.11", 32 | "@types/progress": "^2.0.3" 33 | }, 34 | "scripts": { 35 | "chain": "hardhat node", 36 | "test": "hardhat test", 37 | "compile": "hardhat compile", 38 | "deploy": "hardhat run scripts/deploy.ts", 39 | "accounts": "hardhat accounts", 40 | "balance": "hardhat balance", 41 | "generate": "hardhat generate", 42 | "account": "hardhat account" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/chain/scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | import hre, { ethers } from "hardhat"; 2 | import "@nomiclabs/hardhat-etherscan"; 3 | import chalk from "chalk"; 4 | import fs from "fs"; 5 | import { Contract } from "ethers"; 6 | import ProgressBar from "progress"; 7 | 8 | interface DeploymentObject { 9 | name: string; 10 | address: string; 11 | args: any; 12 | contract: Contract; 13 | } 14 | 15 | // custom `deploy` in order to make verifying easier 16 | const deploy = async (contractName: string, _args: any[] = [], overrides = {}, libraries = {}) => { 17 | console.log(` 🛰 Deploying: ${contractName}`); 18 | 19 | const contractArgs: any = _args || []; 20 | const stringifiedArgs = JSON.stringify(contractArgs); 21 | const contractArtifacts = await ethers.getContractFactory(contractName,{libraries: libraries}); 22 | const contract = await contractArtifacts.deploy(...contractArgs, overrides); 23 | const contractAddress = contract.address; 24 | fs.writeFileSync(`artifacts/${contractName}.address`, contractAddress); 25 | fs.writeFileSync(`artifacts/${contractName}.args`, stringifiedArgs); 26 | 27 | // tslint:disable-next-line: no-console 28 | console.log("Deploying", chalk.cyan(contractName), "contract to", chalk.magenta(contractAddress)); 29 | 30 | await contract.deployed(); 31 | 32 | const deployed: DeploymentObject = { name: contractName, address: contractAddress, args: contractArgs, contract }; 33 | 34 | return deployed 35 | } 36 | 37 | const pause = (time: number) => new Promise(resolve => setTimeout(resolve, time)); 38 | 39 | const verifiableNetwork = ["mainnet", "ropsten", "rinkeby", "goerli", "kovan"]; 40 | 41 | async function main() { 42 | const network = process.env.HARDHAT_NETWORK === undefined ? "localhost" : process.env.HARDHAT_NETWORK; 43 | 44 | // tslint:disable-next-line: no-console 45 | console.log("🚀 Deploying to", chalk.magenta(network), "!"); 46 | if( 47 | network === "localhost" || 48 | network === "hardhat" 49 | ) { 50 | const [deployer] = await ethers.getSigners(); 51 | 52 | // tslint:disable-next-line: no-console 53 | console.log( 54 | chalk.cyan("deploying contracts with the account:"), 55 | chalk.green(deployer.address) 56 | ); 57 | 58 | // tslint:disable-next-line: no-console 59 | console.log("Account balance:", (await deployer.getBalance()).toString()); 60 | } 61 | 62 | // this array stores the data for contract verification 63 | let contracts: DeploymentObject[] = []; 64 | 65 | // In order to set scripts for certain nets (rinkeby, mainnet), use the 66 | // network variable. For example, if you want to set conditions that are 67 | // only triggered in a mainnet deployment: 68 | // if(network === "mainnet"){ 69 | // // set logic here 70 | // } 71 | 72 | // See README in this directory for more detailed instructions about using this script! 73 | 74 | // In order to deploy, do NOT use the standard ethers.getContractFactory pattern - 75 | // the deploy() function will take care of that for you. Just follow the example 76 | // with "Token" below. 77 | // The deploy() function returns a "DeploymentObject", a custom type that makes 78 | // auto-verification easier. It contains the name, address, arguments, and Contract 79 | // of the contract being deployed. It follows that if you'd want to include logic 80 | // calling the mint function on the Token contract that you'd call `token.contact.mint()` 81 | 82 | // some notes on the deploy function: 83 | // - arguments should be passed in an array after the contract name 84 | // args need to be formatted properly for verification to pass 85 | // see: https://hardhat.org/plugins/nomiclabs-hardhat-etherscan.html#complex-arguments 86 | // example: await deploy("Token", ["Test", "TST"]); 87 | // - custom ethers parameters like gasLimit go in an object after that 88 | // EVEN IF THERE ARE NO ARGS (put an empty array for the args) 89 | // example: await deploy("Token", [], { gasLimit: 300000 }); 90 | // - libraries can be added by address after that 91 | // example: await deploy("Token", [], {}, { "SafeMath": "0x..."}); 92 | 93 | let curve: DeploymentObject; 94 | let bzzToken: DeploymentObject; 95 | let mockDai: DeploymentObject; 96 | let ethBroker: DeploymentObject; 97 | 98 | switch(network) { 99 | case "mainnet": 100 | curve = await deploy("Curve", [ 101 | process.env.BZZ_DEPLOYED_MAINNET, 102 | process.env.DAI_ADDRESS_MAINNET 103 | ]); 104 | contracts.push(curve); 105 | 106 | console.log("\nBonding curve deployed"); 107 | 108 | await curve.contract.transferOwnership(process.env.OWNER_ADDRESS); 109 | 110 | console.log(">>>\tBZZ Curve deployed to mainnet\n\n" + 111 | "Please follow the below steps for a successful ecosystem setup:\n\n" + 112 | "\t1. Add the BZZ Curve as a minter on the BZZ token\n\t" + 113 | "2. Pre-minted at least the minimum number of tokens (62500000 1e16)\n\t" + 114 | "3. Ensure the init address has sufficient collateral to initialise\n\t (call `requiredCollateral` on the BZZ curve)\n\t" + 115 | "4. Approve the BZZ curve as a spender of the required collateral amount\n\t" + 116 | "5. Call the init function and the curve will be operational"); 117 | 118 | break; 119 | 120 | case "goerli": 121 | mockDai = await deploy("Mock_dai", [ 122 | process.env.COLLATERAL_TOKEN_NAME, 123 | process.env.COLLATERAL_TOKEN_SYMBOL, 124 | process.env.COLLATERAL_TOKEN_DECIMAL 125 | ]); 126 | contracts.push(mockDai); 127 | 128 | bzzToken = await deploy("Token", [ 129 | process.env.TOKEN_NAME, 130 | process.env.TOKEN_SYMBOL, 131 | process.env.TOKEN_DECIMAL, 132 | process.env.TOKEN_CAP 133 | ]); 134 | contracts.push(bzzToken); 135 | 136 | curve = await deploy("Curve", [ 137 | bzzToken.address, 138 | mockDai.address 139 | ]); 140 | contracts.push(curve); 141 | 142 | ethBroker = await deploy("Eth_broker", [ 143 | curve.address, 144 | mockDai.address, 145 | process.env.ROUTER_ADDRESS_GOERLI 146 | ]) 147 | 148 | await curve.contract.transferOwnership(process.env.OWNER_ADDRESS); 149 | 150 | console.log("\nOwnership transferred to owner address") 151 | 152 | await (await bzzToken.contract.addMinter(curve.address)).wait(); 153 | 154 | console.log("\nCurve added as minter"); 155 | 156 | // Minting the pre-mint tokens to the pre-mint owner 157 | await (await bzzToken.contract.mint( 158 | process.env.ADDRESS_OF_TESTER, 159 | ethers.utils.parseUnits("62500000", 16) 160 | )).wait(); 161 | 162 | console.log("\nBZZ token pre-mint completed"); 163 | 164 | let requiredCollateralDai = await curve.contract.requiredCollateral( 165 | ethers.utils.parseUnits("62500000", 16) 166 | ); 167 | 168 | console.log("\nSuccessfully queried for required collateral amount"); 169 | 170 | // This is the amount of required collateral for the curve 171 | await (await mockDai.contract.mint( 172 | requiredCollateralDai 173 | )).wait(); 174 | 175 | console.log("\nRequired collateral has been minted"); 176 | 177 | // Approving the curve as a spender of the required amount 178 | await (await mockDai.contract.approve( 179 | curve.address, 180 | requiredCollateralDai 181 | )).wait(); 182 | 183 | console.log("\nCurve has been approved as spender for required collateral"); 184 | 185 | // Minting mock DAI for tester 186 | await (await mockDai.contract.mint(ethers.utils.parseUnits("2000000", 18))).wait() 187 | // Transferring to tester 188 | await (await mockDai.contract.transfer( 189 | process.env.ADDRESS_OF_TESTER, 190 | ethers.utils.parseUnits("1000000", 18) 191 | )).wait() 192 | 193 | console.log("\nTester has been minted gDAI"); 194 | 195 | // Initialising the curve 196 | await (await curve.contract.init()).wait(); 197 | 198 | console.log("\nCurve has been initialised"); 199 | 200 | console.table([ 201 | { Contract: "Token", Address: bzzToken.address }, 202 | { Contract: "Mock Dai", Address: mockDai.address }, 203 | { Contract: "Curve", Address: curve.address }, 204 | { Contract: "Broker", Address: ethBroker.address } 205 | ]); 206 | 207 | break; 208 | 209 | default: 210 | mockDai = await deploy("Mock_dai", [ 211 | process.env.COLLATERAL_TOKEN_NAME, 212 | process.env.COLLATERAL_TOKEN_SYMBOL, 213 | process.env.COLLATERAL_TOKEN_DECIMAL 214 | ]); 215 | contracts.push(mockDai); 216 | 217 | bzzToken = await deploy("Token", [ 218 | process.env.TOKEN_NAME, 219 | process.env.TOKEN_SYMBOL, 220 | process.env.TOKEN_DECIMAL, 221 | process.env.TOKEN_CAP 222 | ]); 223 | contracts.push(bzzToken); 224 | 225 | curve = await deploy("Curve", [ 226 | bzzToken.address, 227 | mockDai.address 228 | ]); 229 | contracts.push(curve); 230 | 231 | const router = await deploy("Mock_router", [ 232 | "0xacDdD0dBa07959Be810f6cd29E41b127b29E4A8a" 233 | ]); 234 | contracts.push(router); 235 | 236 | ethBroker = await deploy("Eth_broker", [ 237 | curve.address, 238 | mockDai.address, 239 | router.address 240 | ]); 241 | 242 | // Adding the curve as a minter on the token 243 | await (await bzzToken.contract.addMinter(curve.address)).wait(); 244 | 245 | console.log("\nCurve added as minter"); 246 | 247 | // Minting the pre-mint tokens to the pre-mint owner 248 | await (await bzzToken.contract.mint( 249 | process.env.ADDRESS_OF_TESTER, 250 | ethers.utils.parseUnits("62500000", 16) 251 | )).wait(); 252 | 253 | console.log("\nBZZ token pre-mint completed"); 254 | 255 | let requiredCollateral = await curve.contract.requiredCollateral( 256 | ethers.utils.parseUnits("62500000", 16) 257 | ); 258 | 259 | console.log("\nSuccessfully queried for required collateral amount"); 260 | 261 | // This is the amount of required collateral for the curve 262 | await (await mockDai.contract.mint( 263 | requiredCollateral 264 | )).wait(); 265 | 266 | console.log("\nRequired collateral has been minted"); 267 | 268 | // Approving the curve as a spender of the required amount 269 | await (await mockDai.contract.approve( 270 | curve.address, 271 | requiredCollateral 272 | )).wait(); 273 | 274 | console.log("\nCurve has been approved as spender for required collateral"); 275 | 276 | // Minting mock DAI for tester 277 | await (await mockDai.contract.mint(ethers.utils.parseUnits("2000000", 18))).wait() 278 | // Transfering to tester 279 | await (await mockDai.contract.transfer( 280 | process.env.ADDRESS_OF_TESTER, 281 | ethers.utils.parseUnits("1000000", 18) 282 | )).wait() 283 | 284 | console.log("\nTester has been minted test DAI"); 285 | 286 | // Transferring to Mock Router 287 | await (await mockDai.contract.transfer( 288 | router.address, 289 | ethers.utils.parseUnits("1000000", 18) 290 | )).wait() 291 | 292 | console.log("\nRouter has been pre-funded"); 293 | 294 | await (await curve.contract.init()).wait(); 295 | 296 | console.log("\nCurve has been initialised"); 297 | 298 | console.table([ 299 | { Contract: "Token", Address: bzzToken.address }, 300 | { Contract: "Mock Dai", Address: mockDai.address }, 301 | { Contract: "Curve", Address: curve.address }, 302 | { Contract: "Router", Address: router.address }, 303 | { Contract: "Broker", Address: ethBroker.address } 304 | ]); 305 | 306 | break; 307 | } 308 | 309 | // verification 310 | if( 311 | verifiableNetwork.includes(network) 312 | ) { 313 | let counter = 0; 314 | 315 | // tslint:disable-next-line: no-console 316 | console.log("Beginning Etherscan verification process...\n", 317 | chalk.yellow(`WARNING: The process will wait two minutes for Etherscan \nto update their backend before commencing, please wait \nand do not stop the terminal process...`) 318 | ); 319 | 320 | const bar = new ProgressBar('Etherscan update: [:bar] :percent :etas', { 321 | total: 50, 322 | complete: '\u2588', 323 | incomplete: '\u2591', 324 | }); 325 | // two minute timeout to let Etherscan update 326 | const timer = setInterval(() => { 327 | bar.tick(); 328 | if(bar.complete) { 329 | clearInterval(timer); 330 | } 331 | }, 2300); 332 | 333 | await pause(120000); 334 | 335 | // there may be some issues with contracts using libraries 336 | // if you experience problems, refer to https://hardhat.org/plugins/nomiclabs-hardhat-etherscan.html#providing-libraries-from-a-script-or-task 337 | // tslint:disable-next-line: no-console 338 | console.log(chalk.cyan("\n🔍 Running Etherscan verification...")); 339 | 340 | await Promise.all(contracts.map(async contract => { 341 | // tslint:disable-next-line: no-console 342 | console.log(`Verifying ${contract.name}...`); 343 | try { 344 | await hre.run("verify:verify", { 345 | address: contract.address, 346 | constructorArguments: contract.args 347 | }); 348 | // tslint:disable-next-line: no-console 349 | console.log(chalk.cyan(`✅ ${contract.name} verified!`)); 350 | } catch (error) { 351 | // tslint:disable-next-line: no-console 352 | console.log(error); 353 | } 354 | })); 355 | 356 | } 357 | } 358 | 359 | main() 360 | .then(() => process.exit(0)) 361 | .catch(error => { 362 | // tslint:disable-next-line: no-console 363 | console.error(error); 364 | process.exit(1); 365 | }); -------------------------------------------------------------------------------- /packages/chain/test/broker.test.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | const hre = require("hardhat"); 3 | const { expect, assert } = require("chai"); 4 | const { 5 | ethers, 6 | curve_abi, 7 | token_abi, 8 | mock_dai_abi, 9 | eth_broker_abi, 10 | mock_router_abi, 11 | pre_mint_sequence, 12 | tokenSettings, 13 | test_settings, 14 | } = require("./settings.test.js"); 15 | const { network } = require("hardhat"); 16 | 17 | describe("🤝 Broker tests", () => { 18 | let investor; 19 | let owner; 20 | let user; 21 | let user_two; 22 | 23 | let deployer; 24 | let tokenInstance; 25 | let curveInstance; 26 | let collateralInstance; 27 | let brokerInstance; 28 | let mockRouterInstance; 29 | let mockWethInstance; 30 | 31 | const provider = new ethers.providers.JsonRpcProvider(); 32 | 33 | beforeEach(async () => { 34 | const accounts = await ethers.getSigners(); 35 | owner = accounts[0]; 36 | investor = accounts[1]; 37 | user = accounts[2]; 38 | user_two = accounts[3]; 39 | 40 | const tokenArtifacts = await ethers.getContractFactory("Token"); 41 | tokenInstance = await tokenArtifacts.deploy( 42 | tokenSettings.bzz.name, 43 | tokenSettings.bzz.symbol, 44 | tokenSettings.bzz.decimals, 45 | tokenSettings.bzz.cap 46 | ); 47 | 48 | const collateralArtifacts = await ethers.getContractFactory("Mock_dai"); 49 | collateralInstance = await collateralArtifacts.deploy( 50 | tokenSettings.dai.name, 51 | tokenSettings.dai.symbol, 52 | tokenSettings.dai.decimals 53 | ); 54 | 55 | const curveArtifacts = await ethers.getContractFactory("Curve"); 56 | curveInstance = await curveArtifacts.deploy( 57 | tokenInstance.address, 58 | collateralInstance.address 59 | ); 60 | 61 | //------------------------------------------------------------------ 62 | // Setting up the curve pre-mint 63 | // For the pre-mint tests please see the pre-mint test file 64 | //------------------------------------------------------------------ 65 | 66 | // Minting the pre-mint tokens to the pre-mint owner 67 | await tokenInstance 68 | .connect(owner) 69 | .mint(investor.getAddress(), pre_mint_sequence.whole); 70 | // Adding the curve as a minter on the token 71 | await tokenInstance.connect(owner).addMinter(curveInstance.address); 72 | // Getting the required collateral for the pre-mint tokens 73 | let requiredCollateral = await curveInstance.requiredCollateral( 74 | pre_mint_sequence.whole 75 | ); 76 | // This is the amount of required collateral for the curve 77 | // 1 230 468 . 599 843 763 228 132 556 78 | // The owner is minting the required number of tokens in collateral (DAI) 79 | await collateralInstance.connect(owner).mint(requiredCollateral); 80 | // Approving the curve as a spender of the required amount 81 | await collateralInstance 82 | .connect(owner) 83 | .approve(curveInstance.address, requiredCollateral); 84 | // Initialising the curve 85 | await curveInstance.connect(owner).init(); 86 | 87 | //------------------------------------------------------------------ 88 | // Deploying the eth broker + mock router + mock WETH 89 | //------------------------------------------------------------------ 90 | 91 | const mockWethArtifacts = await ethers.getContractFactory("Mock_dai") 92 | mockWethInstance = await mockWethArtifacts.deploy( 93 | tokenSettings.weth.name, 94 | tokenSettings.weth.symbol, 95 | tokenSettings.weth.decimals 96 | ); 97 | 98 | const mockRouterArtifacts = await ethers.getContractFactory("Mock_router"); 99 | mockRouterInstance = await mockRouterArtifacts.deploy( 100 | mockWethInstance.address, 101 | ); 102 | // priv key for account 0 of Hardhat 103 | const ownerWallet = new ethers.Wallet( 104 | "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", 105 | provider 106 | ); 107 | await ownerWallet.sendTransaction({ 108 | to: mockRouterInstance.address, 109 | value: test_settings.eth_broker.eth.seed_eth_amount 110 | }); 111 | 112 | // Minting DAI to seed the router 113 | await collateralInstance 114 | .connect(user) 115 | .mint(test_settings.eth_broker.eth.seed_eth_amount); 116 | // Sending seed DAI to router 117 | await collateralInstance 118 | .connect(user) 119 | .transfer( 120 | mockRouterInstance.address, 121 | test_settings.eth_broker.eth.seed_eth_amount 122 | ); 123 | 124 | const brokerArtifacts = await ethers.getContractFactory("Eth_broker"); 125 | brokerInstance = await brokerArtifacts.deploy( 126 | curveInstance.address, 127 | collateralInstance.address, 128 | mockRouterInstance.address 129 | ); 130 | }); 131 | 132 | describe("Mock router tests", () => { 133 | /** 134 | * Tests that the getAmountsIn on the mocked router works as expected 135 | * against the hardcoded conversion rate given. 136 | */ 137 | it("Gets Amounts In returns correct values", async () => { 138 | let ethValue = await mockRouterInstance.getAmountsIn( 139 | test_settings.eth_broker.dai.almost_one_eth, 140 | [mockWethInstance.address, collateralInstance.address] 141 | ); 142 | 143 | // Testing expected behaviour 144 | assert.equal( 145 | ethValue[0].toString(), 146 | test_settings.eth_broker.eth.almost_one_eth, 147 | "DAI to ETH conversion incorrect against expected" 148 | ); 149 | }); 150 | /** 151 | * The WETH address of the mocked WETH is correct 152 | */ 153 | it("get weth address expected", async () => { 154 | let wethAddress = await mockRouterInstance.WETH(); 155 | 156 | // Testing expected behaviour 157 | assert.equal( 158 | wethAddress, 159 | "0xacDdD0dBa07959Be810f6cd29E41b127b29E4A8a", 160 | "Weth address in mock router incorrect" 161 | ); 162 | }); 163 | /** 164 | * Ensures that the ETH to DAI router functionality is mocked correctly, 165 | * and that the balances of involved addresses changes correctly. 166 | */ 167 | it("Swap ETH for exact tokens (DAI) test", async () => { 168 | // let balanceOfUserInEthBefore = await ??? 169 | let balanceOfUserDaiBefore = await collateralInstance.balanceOf( 170 | user.address 171 | ); 172 | // Getting the current time 173 | let time = await brokerInstance.getTime(); 174 | // Swapping ETH for an exact amount of tokens (DAI) 175 | await mockRouterInstance 176 | .connect(user) 177 | .swapETHForExactTokens( 178 | test_settings.eth_broker.dai.almost_one_eth, 179 | [ 180 | mockWethInstance.address, 181 | collateralInstance.address, 182 | ], 183 | user.address, 184 | time, 185 | { value: "0x" + test_settings.eth_broker.eth.almost_one_eth } 186 | ); 187 | 188 | let balanceOfUserDaiAfter = await collateralInstance.balanceOf( 189 | user.address 190 | ); 191 | 192 | // Testing expected behaviour 193 | assert.equal( 194 | balanceOfUserDaiBefore.toString(), 195 | 0, 196 | "User started with incorrect DAI balance" 197 | ); 198 | assert.equal( 199 | balanceOfUserDaiAfter.toString(), 200 | test_settings.eth_broker.dai.almost_one_eth, 201 | "User does not have DAI balance after trade" 202 | ); 203 | }); 204 | /** 205 | * Ensures that the DAI to ETH router functionality is mocked correctly, 206 | * and that the balances of the involved addresses changes correctly. 207 | */ 208 | it("Swap exact tokens (DAI) for ETH test", async () => { 209 | let mockRouterEthBalance = await provider.getBalance( 210 | mockRouterInstance.address 211 | ); 212 | 213 | await collateralInstance 214 | .connect(user) 215 | .mint(test_settings.eth_broker.dai.almost_one_eth); 216 | let balanceOfUserDAI = await collateralInstance.balanceOf( 217 | user.address 218 | ); 219 | 220 | // approving router as spender 221 | await collateralInstance 222 | .connect(user) 223 | .approve( 224 | mockRouterInstance.address, 225 | test_settings.eth_broker.dai.almost_one_eth 226 | ); 227 | // Getting the current time 228 | let time = await brokerInstance.getTime(); 229 | // Swapping the exact DAI amount for ETH 230 | await mockRouterInstance 231 | .connect(user) 232 | .swapExactTokensForETH( 233 | test_settings.eth_broker.dai.almost_one_eth, 234 | test_settings.eth_broker.eth.almost_one_eth, 235 | [ 236 | collateralInstance.address, 237 | mockWethInstance.address, 238 | ], 239 | user.address, 240 | time + 100 241 | ); 242 | 243 | let mockRouterEthBalanceAfter = await provider.getBalance( 244 | mockRouterInstance.address 245 | ); 246 | let balanceOfUserDaiAfter = await collateralInstance.balanceOf( 247 | user.address 248 | ); 249 | 250 | // Testing expected behaviour 251 | // expect(mockRouterEthBalance.toString()).to.equal(test_settings.eth_broker.eth.seed_eth_amount.toString()); 252 | assert.equal( 253 | mockRouterEthBalance.toString(), 254 | test_settings.eth_broker.eth.seed_eth_amount.toString(), 255 | "Eth balance of broker is incorrect" 256 | ); 257 | // expect(mockRouterEthBalanceAfter.toString()).to.equal(test_settings.eth_broker.eth.mock_router_eth_balance_after_swap.toString()); 258 | assert.equal( 259 | mockRouterEthBalanceAfter.toString(), 260 | test_settings.eth_broker.eth.mock_router_eth_balance_after_swap.toString(), 261 | "Eth balance of broker is incorrect after swap" 262 | ); 263 | // expect(balanceOfUserDAI.toString()).to.equal(test_settings.eth_broker.dai.almost_one_eth); 264 | assert.equal( 265 | balanceOfUserDAI.toString(), 266 | test_settings.eth_broker.dai.almost_one_eth, 267 | "User started with incorrect DAI balance" 268 | ); 269 | // expect(balanceOfUserDaiAfter.toString()).to.equal("0"); 270 | assert.equal( 271 | balanceOfUserDaiAfter.toString(), 272 | 0, 273 | "User incorrectly has DAI balance after trade" 274 | ); 275 | }); 276 | }); 277 | 278 | describe("broker view tests", () => { 279 | /** 280 | * Ensures that the buy price for the specified number of bzz is as 281 | * expected given the mocked conversion rate. 282 | */ 283 | it("buy price expected", async () => { 284 | let buyPrice = await brokerInstance.buyPrice(test_settings.bzz.buyAmount); 285 | 286 | // expect(buyPrice.toString()).to.equal(test_settings.eth_broker.eth.buy_price); 287 | assert.equal( 288 | buyPrice.toString(), 289 | test_settings.eth_broker.eth.buy_price, 290 | "buy price unexpected" 291 | ); 292 | }); 293 | /** 294 | * Ensures that the sell reward for the specified number of bzz is 295 | * as expected given the mocked conversion rate. 296 | */ 297 | it("sell reward expected", async () => { 298 | let sellRewardAmount = await brokerInstance.sellReward( 299 | test_settings.bzz.buyAmount 300 | ); 301 | 302 | // Testing expected behaviour 303 | // expect(sellRewardAmount.toString()).to.equal(test_settings.eth_broker.eth.sell_reward); 304 | assert.equal( 305 | sellRewardAmount.toString(), 306 | test_settings.eth_broker.eth.sell_reward, 307 | "sell reward unexpected" 308 | ); 309 | }); 310 | /** 311 | * Tests that the direct DAI to ETH conversion produces expected results 312 | * given the hardcoded conversion rate within the mock router. 313 | */ 314 | it("sell reward dai expected", async () => { 315 | let sellRewardAmount = await brokerInstance.sellRewardDai( 316 | test_settings.eth_broker.dai.almost_one_eth 317 | ); 318 | 319 | // Testing expected behaviour 320 | // expect(sellRewardAmount.toString()).to.equal(test_settings.eth_broker.eth.almost_one_eth); 321 | assert.equal( 322 | sellRewardAmount.toString(), 323 | test_settings.eth_broker.eth.almost_one_eth, 324 | "DAI to ETH amount unexpected" 325 | ); 326 | }); 327 | /** 328 | * The trade path generator correctly generates a path in both the buy 329 | * and sell directions. 330 | */ 331 | it("Get path expected", async () => { 332 | let buyPath = await brokerInstance.getPath(true); 333 | let sellPath = await brokerInstance.getPath(false); 334 | let hardCodedWethAddress = "0xacDdD0dBa07959Be810f6cd29E41b127b29E4A8a"; 335 | 336 | // Testing expected behaviour 337 | // expect(buyPath[0]).to.equal(hardCodedWethAddress); 338 | assert.equal( 339 | buyPath[0], 340 | hardCodedWethAddress, 341 | "WETH address in trade route incorrect" 342 | ); 343 | // expect(sellPath[1]).to.equal(hardCodedWethAddress); 344 | assert.equal( 345 | sellPath[1], 346 | hardCodedWethAddress, 347 | "WETH address in trade route incorrect" 348 | ); 349 | // expect(buyPath[1]).to.equal(collateralInstance.address); 350 | assert.equal( 351 | buyPath[1], 352 | collateralInstance.address, 353 | "DAI address in trade route incorrect" 354 | ); 355 | // expect(sellPath[0]).to.equal(collateralInstance.address); 356 | assert.equal( 357 | sellPath[0], 358 | collateralInstance.address, 359 | "DAI address in trade route incorrect" 360 | ); 361 | }); 362 | /** 363 | * Testing that the network returns a valid time. 364 | */ 365 | it("get time works as expected", async () => { 366 | let time = await brokerInstance.getTime(); 367 | 368 | // expect(time.toString()).to.not.equal("0"); 369 | assert.notEqual(time.toString(), 0, "Time has not be correctly relayed"); 370 | }); 371 | }); 372 | 373 | describe("broker tests", () => { 374 | /** 375 | * The max dai spend slippage check is honoured 376 | */ 377 | it("mint slippage check", async () => { 378 | 379 | await collateralInstance.connect(owner).mint(ethers.utils.parseUnits("1000000")); 380 | 381 | let time = await brokerInstance.getTime(); 382 | // Testing expected behaviour 383 | await expect(brokerInstance.connect(owner).mint( 384 | test_settings.bzz.buyAmount, 385 | test_settings.dai.sellReward, 386 | time, 387 | { value: "0x" + test_settings.eth_broker.eth.buy_price } 388 | )).to.be.revertedWith(test_settings.errors.dai_slippage) 389 | }); 390 | /** 391 | * Ensures that all the various involved parties balances change as 392 | * expected. The following balances: ETH, DAI, BZZ are checked on the 393 | * following involved entities: user, curve, mock router, broker. The 394 | * BZZ tokens supply change is also checked. 395 | */ 396 | it("mint balance checks", async () => { 397 | let time = await brokerInstance.getTime(); 398 | 399 | let userDaiBalance = await collateralInstance.balanceOf( 400 | user.address 401 | ); 402 | let userBzzBalance = await tokenInstance.balanceOf(user.address); 403 | let userEthBalance = await provider.getBalance(user.address); 404 | let brokerDaiBalance = await collateralInstance.balanceOf( 405 | brokerInstance.address 406 | ); 407 | let brokerBzzBalance = await tokenInstance.balanceOf( 408 | brokerInstance.address 409 | ); 410 | let brokerEthBalance = await provider.getBalance( 411 | brokerInstance.address 412 | ); 413 | let curveDaiBalance = await collateralInstance.balanceOf( 414 | curveInstance.address 415 | ); 416 | let mockRouterDaiBalance = await collateralInstance.balanceOf( 417 | mockRouterInstance.address 418 | ); 419 | let mockRouterBzzBalance = await tokenInstance.balanceOf( 420 | mockRouterInstance.address 421 | ); 422 | let mockRouterEthBalance = await provider.getBalance( 423 | mockRouterInstance.address 424 | ); 425 | let tokenSupply = await tokenInstance.totalSupply(); 426 | let daiCost = await curveInstance.buyPrice(test_settings.bzz.buyAmount); 427 | let ethCost = await brokerInstance.sellRewardDai(daiCost); 428 | let buyPrice = await brokerInstance.buyPrice(test_settings.bzz.buyAmount); 429 | 430 | // Minting 1000 BZZ for 0.5611 ETH 431 | await brokerInstance 432 | .connect(user) 433 | .mint(test_settings.bzz.buyAmount, test_settings.dai.buyCost, time, { 434 | value: test_settings.eth_broker.eth.buy_price_encoded, 435 | }); 436 | 437 | let userDaiBalanceAfter = await collateralInstance.balanceOf( 438 | user.address 439 | ); 440 | let userBzzBalanceAfter = await tokenInstance.balanceOf( 441 | user.address 442 | ); 443 | let userEthBalanceAfter = await provider.getBalance(user.address); 444 | let brokerDaiBalanceAfter = await collateralInstance.balanceOf( 445 | brokerInstance.address 446 | ); 447 | let brokerBzzBalanceAfter = await tokenInstance.balanceOf( 448 | brokerInstance.address 449 | ); 450 | let brokerEthBalanceAfter = await provider.getBalance( 451 | brokerInstance.address 452 | ); 453 | let curveDaiBalanceAfter = await collateralInstance.balanceOf( 454 | curveInstance.address 455 | ); 456 | let mockRouterDaiBalanceAfter = await collateralInstance.balanceOf( 457 | mockRouterInstance.address 458 | ); 459 | let mockRouterBzzBalanceAfter = await tokenInstance.balanceOf( 460 | mockRouterInstance.address 461 | ); 462 | let mockRouterEthBalanceAfter = await provider.getBalance( 463 | mockRouterInstance.address 464 | ); 465 | let tokenSupplyAfter = await tokenInstance.totalSupply(); 466 | 467 | // Testing expected behaviour 468 | assert.equal( 469 | mockRouterEthBalance.toString(), 470 | test_settings.eth_broker.eth.seed_eth_amount, 471 | "Mock router ETH balance incorrect" 472 | ); 473 | 474 | assert.equal( 475 | mockRouterEthBalanceAfter.toString(), 476 | test_settings.eth_broker.eth.mock_router_eth_balance_after_mint, 477 | "Mock router ETH balance incorrect after mint" 478 | ); 479 | 480 | assert.notEqual( 481 | userEthBalance.toString(), 482 | userEthBalanceAfter.toString(), 483 | "User ETH balance does not change with mint" 484 | ); 485 | 486 | assert.equal( 487 | daiCost.toString(), 488 | test_settings.dai.buyCost, 489 | "DAI cost for token amount unexpected" 490 | ); 491 | 492 | assert.equal( 493 | ethCost.toString(), 494 | test_settings.eth_broker.eth.buy_price, 495 | "ETH cost for token amount unexpected" 496 | ); 497 | 498 | assert.equal( 499 | buyPrice.toString(), 500 | test_settings.eth_broker.eth.buy_price, 501 | "ETH (raw) cost for token amount unexpected" 502 | ); 503 | // user balances changes as expected with mint 504 | 505 | assert.equal(userDaiBalance.toString(), 0, "User starts without DAI"); 506 | 507 | assert.equal(userBzzBalance.toString(), 0, "User starts without BZZ"); 508 | 509 | assert.notEqual( 510 | userEthBalance.toString(), 511 | userEthBalanceAfter.toString(), 512 | "User ETH balance did not change with mint" 513 | ); 514 | 515 | assert.equal( 516 | userDaiBalanceAfter.toString(), 517 | 0, 518 | "User DAI balance incorrectly changed with eth mint" 519 | ); 520 | 521 | assert.equal( 522 | userBzzBalanceAfter.toString(), 523 | test_settings.bzz.buyAmount, 524 | "User BZZ balance did not increase with specified mint amount" 525 | ); 526 | // broker balance remains 0 on all assets 527 | assert.equal(0, brokerDaiBalance.toString(), "broker dai balance non 0"); 528 | 529 | assert.equal( 530 | brokerBzzBalance.toString(), 531 | brokerDaiBalance.toString(), 532 | "broker bzz balance non 0" 533 | ); 534 | 535 | assert.equal( 536 | brokerEthBalance.toString(), 537 | brokerDaiBalance.toString(), 538 | "broker eth balance non 0" 539 | ); 540 | 541 | assert.equal( 542 | brokerDaiBalanceAfter.toString(), 543 | brokerDaiBalance.toString(), 544 | "broker dai balance after non 0" 545 | ); 546 | 547 | assert.equal( 548 | brokerBzzBalanceAfter.toString(), 549 | brokerDaiBalance.toString(), 550 | "broker bzz balance after non 0" 551 | ); 552 | 553 | assert.equal( 554 | brokerEthBalanceAfter.toString(), 555 | brokerDaiBalance.toString(), 556 | "broker eth balance after non 0" 557 | ); 558 | // Curve DAI balances correct 559 | assert.equal( 560 | curveDaiBalance.toString(), 561 | pre_mint_sequence.dai.cost, 562 | "Curve DAI is not as expected before mint" 563 | ); 564 | 565 | assert.equal( 566 | curveDaiBalanceAfter.toString(), 567 | test_settings.dai.curve_collateral_after_buy, 568 | "Curve DAI balance did not increase with mint" 569 | ); 570 | // Token supply increases as expected 571 | assert.equal( 572 | tokenSupply.toString(), 573 | test_settings.eth_broker.bzz.initial_supply, 574 | "initial supply of bzz token unexpected" 575 | ); 576 | 577 | assert.equal( 578 | tokenSupplyAfter.toString(), 579 | test_settings.eth_broker.bzz.after_buy, 580 | "BZZ token supply after mint incorrect" 581 | ); 582 | // Mock router balances change as expected 583 | assert.equal( 584 | mockRouterBzzBalance.toString(), 585 | mockRouterBzzBalanceAfter.toString(), 586 | "Mock router BZZ balance incorrect (non 0)" 587 | ); 588 | 589 | assert.equal( 590 | mockRouterDaiBalance.toString(), 591 | test_settings.eth_broker.eth.seed_eth_amount, 592 | "mock router starts with incorrect dai balance" 593 | ); 594 | 595 | assert.equal( 596 | mockRouterDaiBalanceAfter.toString(), 597 | test_settings.eth_broker.dai.mock_router_dai_balance_after_mint, 598 | "mock router dai balance after buy incorrect" 599 | ); 600 | }); 601 | 602 | /** 603 | * Ensures that all the various involved parties balances change as 604 | * expected. The following balances: ETH, DAI, BZZ are checked on the 605 | * following involved entities: user, curve, mock router, broker. The 606 | * BZZ tokens supply change is also checked. 607 | */ 608 | it("mintTo balance checks", async () => { 609 | let time = await brokerInstance.getTime(); 610 | 611 | let userDaiBalance = await collateralInstance.balanceOf( 612 | user.address 613 | ); 614 | let userBzzBalance = await tokenInstance.balanceOf(user.address); 615 | let userEthBalance = await provider.getBalance(user.address); 616 | let userReceiverDaiBalance = await collateralInstance.balanceOf( 617 | user_two.address 618 | ); 619 | let userReceiverBzzBalance = await tokenInstance.balanceOf(user_two.address); 620 | let userReceiverEthBalance = await provider.getBalance(user_two.address); 621 | let brokerDaiBalance = await collateralInstance.balanceOf( 622 | brokerInstance.address 623 | ); 624 | let brokerBzzBalance = await tokenInstance.balanceOf( 625 | brokerInstance.address 626 | ); 627 | let brokerEthBalance = await provider.getBalance( 628 | brokerInstance.address 629 | ); 630 | let curveDaiBalance = await collateralInstance.balanceOf( 631 | curveInstance.address 632 | ); 633 | let mockRouterDaiBalance = await collateralInstance.balanceOf( 634 | mockRouterInstance.address 635 | ); 636 | let mockRouterBzzBalance = await tokenInstance.balanceOf( 637 | mockRouterInstance.address 638 | ); 639 | let mockRouterEthBalance = await provider.getBalance( 640 | mockRouterInstance.address 641 | ); 642 | let tokenSupply = await tokenInstance.totalSupply(); 643 | let daiCost = await curveInstance.buyPrice(test_settings.bzz.buyAmount); 644 | let ethCost = await brokerInstance.sellRewardDai(daiCost); 645 | let buyPrice = await brokerInstance.buyPrice(test_settings.bzz.buyAmount); 646 | 647 | // Minting 1000 BZZ for 0.5611 ETH 648 | await brokerInstance.connect(user).mintTo( 649 | test_settings.bzz.buyAmount, 650 | test_settings.dai.buyCost, 651 | time, 652 | user_two.address, 653 | { value: test_settings.eth_broker.eth.buy_price_encoded, } 654 | ); 655 | 656 | let userDaiBalanceAfter = await collateralInstance.balanceOf( 657 | user.address 658 | ); 659 | let userBzzBalanceAfter = await tokenInstance.balanceOf( 660 | user.address 661 | ); 662 | let userEthBalanceAfter = await provider.getBalance(user.address); 663 | 664 | let userReceiverDaiBalanceAfter = await collateralInstance.balanceOf( 665 | user_two.address 666 | ); 667 | let userReceiverBzzBalanceAfter = await tokenInstance.balanceOf(user_two.address); 668 | let brokerDaiBalanceAfter = await collateralInstance.balanceOf( 669 | brokerInstance.address 670 | ); 671 | let brokerBzzBalanceAfter = await tokenInstance.balanceOf( 672 | brokerInstance.address 673 | ); 674 | let brokerEthBalanceAfter = await provider.getBalance( 675 | brokerInstance.address 676 | ); 677 | let curveDaiBalanceAfter = await collateralInstance.balanceOf( 678 | curveInstance.address 679 | ); 680 | let mockRouterDaiBalanceAfter = await collateralInstance.balanceOf( 681 | mockRouterInstance.address 682 | ); 683 | let mockRouterBzzBalanceAfter = await tokenInstance.balanceOf( 684 | mockRouterInstance.address 685 | ); 686 | let mockRouterEthBalanceAfter = await provider.getBalance( 687 | mockRouterInstance.address 688 | ); 689 | let tokenSupplyAfter = await tokenInstance.totalSupply(); 690 | 691 | // Testing expected behaviour 692 | assert.equal( 693 | mockRouterEthBalance.toString(), 694 | test_settings.eth_broker.eth.seed_eth_amount, 695 | "Mock router ETH balance incorrect" 696 | ); 697 | 698 | assert.equal( 699 | mockRouterEthBalanceAfter.toString(), 700 | test_settings.eth_broker.eth.mock_router_eth_balance_after_mint, 701 | "Mock router ETH balance incorrect after mint" 702 | ); 703 | 704 | assert.notEqual( 705 | userEthBalance.toString(), 706 | userEthBalanceAfter.toString(), 707 | "User ETH balance does not change with mint" 708 | ); 709 | 710 | assert.equal( 711 | daiCost.toString(), 712 | test_settings.dai.buyCost, 713 | "DAI cost for token amount unexpected" 714 | ); 715 | 716 | assert.equal( 717 | ethCost.toString(), 718 | test_settings.eth_broker.eth.buy_price, 719 | "ETH cost for token amount unexpected" 720 | ); 721 | 722 | assert.equal( 723 | buyPrice.toString(), 724 | test_settings.eth_broker.eth.buy_price, 725 | "ETH (raw) cost for token amount unexpected" 726 | ); 727 | // user balances changes as expected with mint 728 | assert.equal(userDaiBalance.toString(), 0, "User starts without DAI"); 729 | 730 | assert.equal(userBzzBalance.toString(), 0, "User starts without BZZ"); 731 | 732 | assert.notEqual( 733 | userEthBalance.toString(), 734 | userEthBalanceAfter.toString(), 735 | "User ETH balance did not change with mint" 736 | ); 737 | 738 | assert.equal( 739 | userDaiBalanceAfter.toString(), 740 | 0, 741 | "User DAI balance incorrectly changed with eth mint" 742 | ); 743 | 744 | assert.equal( 745 | userBzzBalanceAfter.toString(), 746 | 0, 747 | "User BZZ balance did not increase with specified mint amount" 748 | ); 749 | // user receiver balances changes as expected with mint 750 | assert.equal(userReceiverDaiBalance.toString(), 0, "User starts without DAI"); 751 | 752 | assert.equal(userReceiverBzzBalance.toString(), 0, "User starts without BZZ"); 753 | 754 | assert.notEqual( 755 | userReceiverEthBalance.toString(), 756 | userEthBalanceAfter.toString(), 757 | "User ETH balance did not change with mint" 758 | ); 759 | 760 | assert.equal( 761 | userReceiverDaiBalanceAfter.toString(), 762 | 0, 763 | "User DAI balance incorrectly changed with eth mint" 764 | ); 765 | 766 | assert.equal( 767 | userReceiverBzzBalanceAfter.toString(), 768 | test_settings.bzz.buyAmount.toString(), 769 | "User BZZ balance did not increase with specified mint amount" 770 | ); 771 | // broker balance remains 0 on all assets 772 | assert.equal(0, brokerDaiBalance.toString(), "broker dai balance non 0"); 773 | 774 | assert.equal( 775 | brokerBzzBalance.toString(), 776 | brokerDaiBalance.toString(), 777 | "broker bzz balance non 0" 778 | ); 779 | 780 | assert.equal( 781 | brokerEthBalance.toString(), 782 | brokerDaiBalance.toString(), 783 | "broker eth balance non 0" 784 | ); 785 | 786 | assert.equal( 787 | brokerDaiBalanceAfter.toString(), 788 | brokerDaiBalance.toString(), 789 | "broker dai balance after non 0" 790 | ); 791 | 792 | assert.equal( 793 | brokerBzzBalanceAfter.toString(), 794 | brokerDaiBalance.toString(), 795 | "broker bzz balance after non 0" 796 | ); 797 | 798 | assert.equal( 799 | brokerEthBalanceAfter.toString(), 800 | brokerDaiBalance.toString(), 801 | "broker eth balance after non 0" 802 | ); 803 | // Curve DAI balances correct 804 | assert.equal( 805 | curveDaiBalance.toString(), 806 | pre_mint_sequence.dai.cost, 807 | "Curve DAI is not as expected before mint" 808 | ); 809 | 810 | assert.equal( 811 | curveDaiBalanceAfter.toString(), 812 | test_settings.dai.curve_collateral_after_buy, 813 | "Curve DAI balance did not increase with mint" 814 | ); 815 | // Token supply increases as expected 816 | assert.equal( 817 | tokenSupply.toString(), 818 | test_settings.eth_broker.bzz.initial_supply, 819 | "initial supply of bzz token unexpected" 820 | ); 821 | 822 | assert.equal( 823 | tokenSupplyAfter.toString(), 824 | test_settings.eth_broker.bzz.after_buy, 825 | "BZZ token supply after mint incorrect" 826 | ); 827 | // Mock router balances change as expected 828 | assert.equal( 829 | mockRouterBzzBalance.toString(), 830 | mockRouterBzzBalanceAfter.toString(), 831 | "Mock router BZZ balance incorrect (non 0)" 832 | ); 833 | 834 | assert.equal( 835 | mockRouterDaiBalance.toString(), 836 | test_settings.eth_broker.eth.seed_eth_amount, 837 | "mock router starts with incorrect dai balance" 838 | ); 839 | 840 | assert.equal( 841 | mockRouterDaiBalanceAfter.toString(), 842 | test_settings.eth_broker.dai.mock_router_dai_balance_after_mint, 843 | "mock router dai balance after buy incorrect" 844 | ); 845 | }); 846 | /** 847 | * Ensures that the transaction will fail without being approved 848 | */ 849 | it("burn fails without approval", async () => { 850 | let time = await brokerInstance.getTime(); 851 | 852 | await expect(brokerInstance 853 | .connect(user) 854 | .redeem( 855 | test_settings.bzz.buyAmount, 856 | test_settings.dai.sellReward, 857 | time 858 | )).to.be.revertedWith(test_settings.errors.transfer_failed); 859 | }); 860 | /** 861 | * Ensures that the burn function works as expected and all involved 862 | * parties (curve, mock router, broker, token) balances change correctly 863 | * in the various involved currencies (ETH, BZZ, DAI). 864 | */ 865 | it("burn balance checks", async () => { 866 | let time = await brokerInstance.getTime(); 867 | 868 | // User receives BZZ 869 | await tokenInstance 870 | .connect(investor) 871 | .transfer(user.address, test_settings.bzz.buyAmount); 872 | 873 | // approving the broker as a spender 874 | await tokenInstance 875 | .connect(user) 876 | .approve(brokerInstance.address, test_settings.bzz.buyAmount); 877 | 878 | let allowanceOfBroker = await tokenInstance.allowance( 879 | user.address, 880 | brokerInstance.address 881 | ); 882 | let userDaiBalance = await collateralInstance.balanceOf( 883 | user.address 884 | ); 885 | let userBzzBalance = await tokenInstance.balanceOf(user.address); 886 | let userEthBalance = await provider.getBalance(user.address); 887 | let brokerDaiBalance = await collateralInstance.balanceOf( 888 | brokerInstance.address 889 | ); 890 | let brokerBzzBalance = await tokenInstance.balanceOf( 891 | brokerInstance.address 892 | ); 893 | let brokerEthBalance = await provider.getBalance( 894 | brokerInstance.address 895 | ); 896 | let curveDaiBalance = await collateralInstance.balanceOf( 897 | curveInstance.address 898 | ); 899 | let mockRouterDaiBalance = await collateralInstance.balanceOf( 900 | mockRouterInstance.address 901 | ); 902 | let mockRouterBzzBalance = await tokenInstance.balanceOf( 903 | mockRouterInstance.address 904 | ); 905 | let mockRouterEthBalance = await provider.getBalance( 906 | mockRouterInstance.address 907 | ); 908 | let tokenSupply = await tokenInstance.totalSupply(); 909 | 910 | let daiCost = await curveInstance.sellReward(test_settings.bzz.buyAmount); 911 | let ethCost = await brokerInstance.sellRewardDai(daiCost); 912 | let buyPrice = await brokerInstance.sellReward( 913 | test_settings.bzz.buyAmount 914 | ); 915 | 916 | // Minting 1000 BZZ for 0.5611 ETH 917 | await brokerInstance.connect(user).redeem( 918 | test_settings.bzz.buyAmount, 919 | 10, 920 | time 921 | ); 922 | 923 | let userDaiBalanceAfter = await collateralInstance.balanceOf( 924 | user.address 925 | ); 926 | let userBzzBalanceAfter = await tokenInstance.balanceOf( 927 | user.address 928 | ); 929 | let userEthBalanceAfter = await provider.getBalance(user.address); 930 | let brokerDaiBalanceAfter = await collateralInstance.balanceOf( 931 | brokerInstance.address 932 | ); 933 | let brokerBzzBalanceAfter = await tokenInstance.balanceOf( 934 | brokerInstance.address 935 | ); 936 | let brokerEthBalanceAfter = await provider.getBalance( 937 | brokerInstance.address 938 | ); 939 | let curveDaiBalanceAfter = await collateralInstance.balanceOf( 940 | curveInstance.address 941 | ); 942 | let mockRouterDaiBalanceAfter = await collateralInstance.balanceOf( 943 | mockRouterInstance.address 944 | ); 945 | let mockRouterBzzBalanceAfter = await tokenInstance.balanceOf( 946 | mockRouterInstance.address 947 | ); 948 | let mockRouterEthBalanceAfter = await provider.getBalance( 949 | mockRouterInstance.address 950 | ); 951 | let tokenSupplyAfter = await tokenInstance.totalSupply(); 952 | 953 | // Testing expected behaviour 954 | assert.equal( 955 | allowanceOfBroker.toString(), 956 | test_settings.bzz.buyAmount.toString(), 957 | "broker allowance incorrect" 958 | ); 959 | // User balance in various currencies expected 960 | assert.equal(userDaiBalance.toString(), 0, "User DAI balance incorrect"); 961 | 962 | assert.equal( 963 | userBzzBalance.toString(), 964 | test_settings.bzz.buyAmount.toString(), 965 | "User BZZ balance incorrect" 966 | ); 967 | 968 | assert.notEqual( 969 | userEthBalance.toString(), 970 | 0, 971 | "User ETH balance incorrect" 972 | ); 973 | // broker balances are as expected 974 | assert.equal( 975 | brokerDaiBalance.toString(), 976 | 0, 977 | "broker incorrectly has a balance in DAI" 978 | ); 979 | 980 | assert.equal( 981 | brokerBzzBalance.toString(), 982 | 0, 983 | "broker incorrectly has a balance in BZZ" 984 | ); 985 | 986 | assert.equal( 987 | brokerEthBalance.toString(), 988 | 0, 989 | "broker incorrectly has a balance in ETH" 990 | ); 991 | // Curve has correct balance 992 | assert.equal( 993 | curveDaiBalance.toString(), 994 | pre_mint_sequence.dai.cost, 995 | "Curve has unexpected balance after pre-mint" 996 | ); 997 | // Router balances are as expected 998 | assert.equal( 999 | mockRouterDaiBalance.toString(), 1000 | test_settings.eth_broker.eth.seed_eth_amount.toString(), 1001 | "Mock router has incorrect DAI balance" 1002 | ); 1003 | 1004 | assert.equal( 1005 | mockRouterBzzBalance.toString(), 1006 | 0, 1007 | "Mock router has incorrect BZZ balance" 1008 | ); 1009 | 1010 | assert.equal( 1011 | mockRouterEthBalance.toString(), 1012 | test_settings.eth_broker.eth.seed_eth_amount.toString(), 1013 | "Mock router has incorrect ETH balance" 1014 | ); 1015 | // Testing that pricing & supply are as expected 1016 | assert.equal( 1017 | tokenSupply.toString(), 1018 | test_settings.eth_broker.bzz.initial_supply, 1019 | "BZZ current supply incorrect" 1020 | ); 1021 | 1022 | assert.equal( 1023 | daiCost.toString(), 1024 | test_settings.eth_broker.dai.buy_cost, 1025 | "DAI cost for token amount unexpected" 1026 | ); 1027 | 1028 | assert.equal( 1029 | ethCost.toString(), 1030 | test_settings.eth_broker.eth.sell_reward, 1031 | "ETH cost for token amount unexpected" 1032 | ); 1033 | 1034 | assert.equal( 1035 | buyPrice.toString(), 1036 | test_settings.eth_broker.eth.sell_reward, 1037 | "ETH (raw) cost for token amount unexpected" 1038 | ); 1039 | // User balance in various currencies expected after burn 1040 | assert.equal( 1041 | userDaiBalanceAfter.toString(), 1042 | 0, 1043 | "User incorrectly has left over DAI after burn" 1044 | ); 1045 | 1046 | assert.equal( 1047 | userBzzBalanceAfter.toString(), 1048 | 0, 1049 | "User incorrectly has left over BZZ after burn" 1050 | ); 1051 | 1052 | assert.notEqual( 1053 | userEthBalanceAfter.toString(), 1054 | userEthBalance.toString(), 1055 | "User ETH balance did not change with burn" 1056 | ); 1057 | // broker balances are as expected after burn 1058 | assert.equal( 1059 | brokerDaiBalanceAfter.toString(), 1060 | 0, 1061 | "broker incorrectly has a balance in DAI" 1062 | ); 1063 | 1064 | assert.equal( 1065 | brokerBzzBalanceAfter.toString(), 1066 | 0, 1067 | "broker incorrectly has a balance in BZZ" 1068 | ); 1069 | 1070 | assert.equal( 1071 | brokerEthBalanceAfter.toString(), 1072 | 0, 1073 | "broker incorrectly has a balance in ETH after burn" 1074 | ); 1075 | // Curve has correct balance 1076 | assert.equal( 1077 | curveDaiBalanceAfter.toString(), 1078 | test_settings.eth_broker.dai.curve_balance_after_burn, 1079 | "Curve has unexpected DAI balance after burn" 1080 | ); 1081 | // Router balances are as expected after burn 1082 | assert.equal( 1083 | mockRouterDaiBalanceAfter.toString(), 1084 | test_settings.eth_broker.dai.mock_router_dai_balance_after_burn.toString(), 1085 | "Mock router has incorrect DAI balance" 1086 | ); 1087 | 1088 | assert.equal( 1089 | mockRouterBzzBalanceAfter.toString(), 1090 | 0, 1091 | "Mock router has incorrect BZZ balance" 1092 | ); 1093 | 1094 | assert.equal( 1095 | mockRouterEthBalanceAfter.toString(), 1096 | test_settings.eth_broker.eth.mock_router_eth_balance_after_burn.toString(), 1097 | "Mock router has incorrect ETH balance" 1098 | ); 1099 | // Token supply on curve correctly affected by burn 1100 | assert.equal( 1101 | tokenSupplyAfter.toString(), 1102 | test_settings.eth_broker.bzz.after_burn, 1103 | "Total supply incorrectly affected by burn" 1104 | ); 1105 | }); 1106 | }); 1107 | }); 1108 | 1109 | -------------------------------------------------------------------------------- /packages/chain/test/curve_calc.test.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | const hre = require("hardhat"); 3 | const { expect } = require("chai"); 4 | const { 5 | ethers, 6 | curve_test_abi, 7 | token_abi, 8 | mock_dai_abi, 9 | pre_mint_sequence, 10 | tokenSettings, 11 | test_settings 12 | } = require("./settings.test.js"); 13 | 14 | describe("🧮 Curve Calculations Tests", () => { 15 | let investor; 16 | let owner; 17 | let user; 18 | let user_two; 19 | 20 | let deployer; 21 | let tokenInstance; 22 | let collateralInstance; 23 | let curveTestInstance; 24 | 25 | beforeEach(async () => { 26 | const accounts = await ethers.getSigners(); 27 | owner = accounts[0]; 28 | investor = accounts[1]; 29 | user = accounts[2]; 30 | user_two = accounts[3]; 31 | 32 | const accountSlice = accounts.slice(4,19); 33 | const lossaEther = ethers.utils.parseEther("9999.99"); 34 | for (let i = 0; i < accountSlice.length; i++) { 35 | await accountSlice[i].sendTransaction({ to: owner.address, value: lossaEther}) 36 | } 37 | 38 | const tokenArtifacts = await ethers.getContractFactory("Token"); 39 | tokenInstance = await tokenArtifacts.deploy( 40 | tokenSettings.bzz.name, 41 | tokenSettings.bzz.symbol, 42 | tokenSettings.bzz.decimals, 43 | tokenSettings.bzz.cap 44 | ); 45 | 46 | const collateralArtifacts = await ethers.getContractFactory("Mock_dai"); 47 | collateralInstance = await collateralArtifacts.deploy( 48 | tokenSettings.dai.name, 49 | tokenSettings.dai.symbol, 50 | tokenSettings.dai.decimals 51 | ); 52 | 53 | const curveTestArtifacts = await ethers.getContractFactory("curve_test"); 54 | curveTestInstance = await curveTestArtifacts.deploy( 55 | tokenInstance.address, 56 | collateralInstance.address 57 | ); 58 | }); 59 | 60 | describe('Curve pre-init tests', () => { 61 | /** 62 | * Testing that the cost for the pre-mint is consistent no matter where 63 | * you get the amount from (consistency between calculating functions) 64 | */ 65 | it("Pre-mint cost consistent", async () => { 66 | // Getting the cost of the initial tokens through the mint function 67 | let buyCost = await curveTestInstance.mathMint( 68 | pre_mint_sequence.whole, 69 | 0 70 | ); 71 | // Getting the cost of the initial tokens through the init function 72 | let initialCost = await curveTestInstance.initializeCurve( 73 | pre_mint_sequence.whole 74 | ); 75 | // Getting the primitive function for the initial supply (0) 76 | let primFuncAtZero = await curveTestInstance.primitiveFunction(0); 77 | // Getting the primitive function for the pre-mint supply 78 | let primFuncAtPreMint = await curveTestInstance.primitiveFunction( 79 | pre_mint_sequence.whole 80 | ); 81 | // Testing expected behaviour 82 | expect(buyCost.toString()).to.equal(initialCost.toString()); 83 | 84 | expect(buyCost.toString()).to.equal(primFuncAtPreMint.toString()); 85 | 86 | expect(initialCost.toString()).to.equal(pre_mint_sequence.dai.cost); 87 | 88 | expect(primFuncAtZero.toString()).to.equal("0"); 89 | }); 90 | /** 91 | * Testing that the price per token is never 0, even before the curve has 92 | * had it's pre-mint 93 | */ 94 | it("Spot price before init", async () => { 95 | // Getting the price of one token before the curve has been initialized 96 | let spotPriceAtStart = await curveTestInstance.spotPrice(0); 97 | // Testing expected behaviour 98 | expect(spotPriceAtStart.toString()).to.not.equal("0"); 99 | 100 | expect(spotPriceAtStart.toString()).to.equal("1"); 101 | }); 102 | }); 103 | 104 | describe('Curve post-init tests', () => { 105 | beforeEach(async () => { 106 | //------------------------------------------------------------------ 107 | // Setting up the curve pre-mint 108 | // For the pre-mint tests please see the pre-mint test file 109 | //------------------------------------------------------------------ 110 | 111 | // Minting the pre-mint tokens to the pre-mint owner 112 | await tokenInstance.connect(owner).mint( 113 | investor.address, 114 | pre_mint_sequence.whole 115 | ) 116 | // Adding the curve as a minter on the token 117 | await tokenInstance.connect(owner).addMinter( 118 | curveTestInstance.address 119 | ); 120 | // Getting the required collateral for the pre-mint tokens 121 | let requiredCollateral = await curveTestInstance.requiredCollateral( 122 | pre_mint_sequence.whole 123 | ); 124 | // This is the amount of required collateral for the curve 125 | // 1 230 468 . 599 843 763 228 132 556 126 | // The owner is minting the required number of tokens in collateral (DAI) 127 | await collateralInstance.connect(owner).mint( 128 | requiredCollateral 129 | ); 130 | // Approving the curve as a spender of the required amount 131 | await collateralInstance.connect(owner).approve( 132 | curveTestInstance.address, 133 | requiredCollateral 134 | ); 135 | // Initialising the curve 136 | await curveTestInstance.connect(owner).init(); 137 | }); 138 | /** 139 | * Testing the helper value is expected 140 | */ 141 | it("Helper is correct", async () => { 142 | // Getting the helper 143 | let helper = await curveTestInstance.helper(pre_mint_sequence.whole); 144 | // Testing expected behaviour 145 | expect(helper.toString()).to.equal(test_settings.helper_value); 146 | }); 147 | /** 148 | * Testing that after the curve has pre-minted that the price for each 149 | * token is expected 150 | */ 151 | it("Price at start of curve", async () => { 152 | // Getting the cost of the initial tokens through the mint function 153 | let buyCost = await curveTestInstance.mathMint( 154 | test_settings.bzz.buyAmount, 155 | pre_mint_sequence.whole, 156 | ); 157 | // Getting the primitive function for the initial supply (0) 158 | let primFuncAtZero = await curveTestInstance.primitiveFunction( 159 | pre_mint_sequence.whole 160 | ); 161 | // Getting the primitive function for the pre-mint supply 162 | let primFuncAtPreMint = await curveTestInstance.primitiveFunction( 163 | test_settings.bzz.supply_at_buy 164 | ); 165 | // Getting the price for one token at current supply 166 | let spotPricePostMint = await curveTestInstance.spotPrice(pre_mint_sequence.whole); 167 | // Testing expected behaviour 168 | expect(buyCost.toString()).to.equal(test_settings.dai.buyCost); 169 | 170 | expect(primFuncAtZero.toString()).to.equal(pre_mint_sequence.dai.cost); 171 | 172 | expect(primFuncAtPreMint.toString()).to.equal(test_settings.dai.curve_coll_at_prem); 173 | 174 | expect(spotPricePostMint.toString()).to.equal(test_settings.dai.one_cost); 175 | }); 176 | /** 177 | * Testing that after the curves pre-mint the sell price for each token 178 | * is expected 179 | */ 180 | it("Withdraw reward at start", async () => { 181 | // Getting the buy cost for 1000 tokens 182 | let buyCost = await curveTestInstance.buyPrice(test_settings.bzz.buyAmount); 183 | // Approving the curve as a spender of collateral 184 | await collateralInstance.connect(user).approve( 185 | curveTestInstance.address, 186 | buyCost 187 | ); 188 | // Minting the collateral tokens for the user 189 | await collateralInstance.connect(user).mint(buyCost); 190 | // Mints tokens 191 | await curveTestInstance.connect(user).mint( 192 | test_settings.bzz.buyAmount, 193 | buyCost 194 | ); 195 | let currentSupply = await tokenInstance.totalSupply(); 196 | // Getting the cost of the initial tokens through the mint function 197 | let sellRewardWithdraw = await curveTestInstance.withdraw( 198 | test_settings.bzz.buyAmount, 199 | currentSupply, 200 | ); 201 | // Testing expected behaviour 202 | expect(sellRewardWithdraw[0].toString()).to.equal(test_settings.dai.buyCost); 203 | }); 204 | }); 205 | }); -------------------------------------------------------------------------------- /packages/chain/test/curve_pre_mint.test.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | const hre = require("hardhat"); 3 | const { expect, assert } = require("chai"); 4 | const { 5 | ethers, 6 | curve_abi, 7 | token_abi, 8 | mock_dai_abi, 9 | pre_mint_sequence, 10 | tokenSettings, 11 | test_settings 12 | } = require("./settings.test.js"); 13 | 14 | describe('🍃 Curve pre-mint tests', () => { 15 | let investor; 16 | let owner; 17 | let user; 18 | let user_two; 19 | 20 | let deployer; 21 | let tokenInstance; 22 | let curveInstance; 23 | let collateralInstance; 24 | 25 | beforeEach(async () => { 26 | const accounts = await ethers.getSigners(); 27 | owner = accounts[0]; 28 | investor = accounts[1]; 29 | user = accounts[2]; 30 | user_two = accounts[3]; 31 | 32 | const tokenArtifacts = await ethers.getContractFactory("Token"); 33 | tokenInstance = await tokenArtifacts.deploy( 34 | tokenSettings.bzz.name, 35 | tokenSettings.bzz.symbol, 36 | tokenSettings.bzz.decimals, 37 | tokenSettings.bzz.cap 38 | ); 39 | 40 | const collateralArtifacts = await ethers.getContractFactory("Mock_dai"); 41 | collateralInstance = await collateralArtifacts.deploy( 42 | tokenSettings.dai.name, 43 | tokenSettings.dai.symbol, 44 | tokenSettings.dai.decimals 45 | ); 46 | 47 | const curveArtifacts = await ethers.getContractFactory("Curve"); 48 | curveInstance = await curveArtifacts.deploy( 49 | tokenInstance.address, 50 | collateralInstance.address 51 | ); 52 | }); 53 | 54 | describe('Curve initialisation tests', () => { 55 | /** 56 | * Testing the curves set up and connection to the token without the token 57 | * having minted anything (i.e a 0 `totalSupply()`). 58 | */ 59 | it("Can't set up curve with less than expected pre-mint", async() => { 60 | let isOwnerMinterBefore = await tokenInstance.isMinter(owner.address); 61 | let isCurveMinterBefore = await tokenInstance.isMinter( 62 | curveInstance.address 63 | ); 64 | let isCurveActiveBefore = await curveInstance.isCurveActive(); 65 | // Adding the curve as a minter on the token 66 | await tokenInstance.connect(owner).addMinter(curveInstance.address); 67 | let isCurveMinter = await tokenInstance.isMinter(curveInstance.address); 68 | let requiredCollateral = await curveInstance.requiredCollateral( 69 | pre_mint_sequence.whole 70 | ); 71 | // Initialising the curve (will fail without pre-mint) 72 | await expect(curveInstance.connect(user).init()) 73 | .to.be.revertedWith(test_settings.errors.curve_requires_pre_mint); 74 | let isCurveActiveAfter = await curveInstance.isCurveActive(); 75 | // Testing expected behaviour 76 | assert.equal( 77 | isOwnerMinterBefore, 78 | true, 79 | "Owner is not minter" 80 | ); 81 | assert.equal( 82 | isCurveMinterBefore, 83 | false, 84 | "Curve is minter before being set" 85 | ); 86 | assert.equal( 87 | isCurveActiveBefore, 88 | false, 89 | "Curve is active before activation" 90 | ); 91 | assert.equal( 92 | isCurveMinter, 93 | true, 94 | "Curve is not minter after being set" 95 | ); 96 | assert.equal( 97 | requiredCollateral.toString(), 98 | pre_mint_sequence.dai.cost, 99 | "Required collateral is incorrect" 100 | ); 101 | assert.equal( 102 | isCurveActiveAfter, 103 | false, 104 | "Curve is active after incorrect activation" 105 | ); 106 | }); 107 | /** 108 | * Testing the curves set up and connection to the token with the expected 109 | * pre-mint amount of tokens minted. 110 | */ 111 | it("Curve set up with pre-mint (exact)", async() => { 112 | let investorBalanceBeforeMint = await tokenInstance.balanceOf( 113 | investor.address 114 | ); 115 | // Minting the pre-mint tokens to the pre-mint owner 116 | await tokenInstance.connect(owner).mint( 117 | investor.address, 118 | pre_mint_sequence.whole 119 | ); 120 | let investorBalanceAfterMint = await tokenInstance.balanceOf( 121 | investor.address 122 | ); 123 | // Adding the curve as a minter on the token 124 | await tokenInstance.connect(owner).addMinter(curveInstance.address); 125 | // Getting the required collateral for the pre-mint tokens 126 | let requiredCollateral = await curveInstance.requiredCollateral( 127 | pre_mint_sequence.whole 128 | ); 129 | 130 | let ownerBalanceBefore = await collateralInstance.balanceOf(owner.address); 131 | // The owner is minting the required number of tokens in collateral (DAI) 132 | await collateralInstance.connect(owner).mint( 133 | requiredCollateral 134 | ); 135 | let ownerBalanceAfter = await collateralInstance.balanceOf(owner.address); 136 | // Approving the curve as a spender of the required amount 137 | await collateralInstance.connect(owner).approve( 138 | curveInstance.address, 139 | requiredCollateral 140 | ); 141 | let isCurveActiveBefore = await curveInstance.isCurveActive(); 142 | // Initialising the curve 143 | await curveInstance.connect(owner).init(); 144 | let isCurveActiveAfter = await curveInstance.isCurveActive(); 145 | let startingPrice = await curveInstance.buyPrice( 146 | test_settings.bzz.one 147 | ); 148 | // Testing expected behaviour 149 | assert.equal( 150 | investorBalanceBeforeMint.toString(), 151 | 0, 152 | "Pre-mine has balance before pre-mine" 153 | ); 154 | assert.equal( 155 | investorBalanceAfterMint.toString(), 156 | pre_mint_sequence.whole.toString(), 157 | "Pre-mine does not have balance after pre-mine" 158 | ); 159 | assert.equal( 160 | requiredCollateral.toString(), 161 | pre_mint_sequence.dai.cost, 162 | "Pre-mine does not cost expected amount" 163 | ); 164 | assert.equal( 165 | ownerBalanceBefore.toString(), 166 | 0, 167 | "Owner has collateral balance before mint" 168 | ); 169 | assert.equal( 170 | ownerBalanceAfter.toString(), 171 | requiredCollateral.toString(), 172 | "Owner has incorrect collateral balance after mint" 173 | ); 174 | assert.equal( 175 | isCurveActiveBefore, 176 | false, 177 | "Curve is active before activation" 178 | ); 179 | assert.equal( 180 | isCurveActiveAfter, 181 | true, 182 | "Curve is not active after activation" 183 | ); 184 | assert.equal( 185 | startingPrice.toString(), 186 | test_settings.dai.firstOneCost, 187 | "Starting price for token after pre-sale incorrect" 188 | ); 189 | }); 190 | /** 191 | * Testing the curves set up and connection to the token with more than the 192 | * expected pre-mint amount of tokens minted. 193 | */ 194 | it("Curve set up with pre-mint (above expected)", async() => { 195 | let investorBalanceBeforeMint = await tokenInstance.balanceOf( 196 | investor.address 197 | ); 198 | // Minting the pre-mint tokens to the pre-mint owner 199 | await tokenInstance.connect(owner).mint( 200 | investor.address, 201 | pre_mint_sequence.above_expected 202 | ); 203 | let investorBalanceAfterMint = await tokenInstance.balanceOf( 204 | investor.address 205 | ); 206 | // Adding the curve as a minter on the token 207 | await tokenInstance.connect(owner).addMinter(curveInstance.address); 208 | // Getting the required collateral for the pre-mint tokens 209 | let requiredCollateral = await curveInstance.requiredCollateral( 210 | pre_mint_sequence.above_expected 211 | ); 212 | let ownerBalanceBefore = await collateralInstance.balanceOf(owner.address); 213 | // The owner is minting the required number of tokens in collateral (DAI) 214 | await collateralInstance.connect(owner).mint( 215 | requiredCollateral 216 | ); 217 | let ownerBalanceAfter = await collateralInstance.balanceOf(owner.address); 218 | // Approving the curve as a spender of the required amount 219 | await collateralInstance.connect(owner).approve( 220 | curveInstance.address, 221 | requiredCollateral 222 | ); 223 | let isCurveActiveBefore = await curveInstance.isCurveActive(); 224 | // Initialising the curve 225 | await curveInstance.connect(owner).init(); 226 | let isCurveActiveAfter = await curveInstance.isCurveActive(); 227 | let startingPrice = await curveInstance.buyPrice( 228 | test_settings.bzz.one 229 | ); 230 | // Testing expected behaviour 231 | assert.equal( 232 | investorBalanceBeforeMint.toString(), 233 | 0, 234 | "Pre-mine has balance before pre-mine" 235 | ); 236 | assert.equal( 237 | investorBalanceAfterMint.toString(), 238 | pre_mint_sequence.above_expected.toString(), 239 | "Pre-mine does not have balance after pre-mine" 240 | ); 241 | assert.equal( 242 | requiredCollateral.toString(), 243 | pre_mint_sequence.dai.above_expected_cost, 244 | "Pre-mine does not cost expected amount" 245 | ); 246 | assert.equal( 247 | ownerBalanceBefore.toString(), 248 | 0, 249 | "Owner has collateral balance before mint" 250 | ); 251 | assert.equal( 252 | ownerBalanceAfter.toString(), 253 | requiredCollateral.toString(), 254 | "Owner has incorrect collateral balance after mint" 255 | ); 256 | assert.equal( 257 | isCurveActiveBefore, 258 | false, 259 | "Curve is active before activation" 260 | ); 261 | assert.equal( 262 | isCurveActiveAfter, 263 | true, 264 | "Curve is not active after activation" 265 | ); 266 | assert.equal( 267 | startingPrice.toString(), 268 | test_settings.dai.above_expected_firstOneCost, 269 | "Starting price for token after pre-sale incorrect" 270 | ); 271 | }); 272 | /** 273 | * Testing the ability to buy tokens (basic test) 274 | */ 275 | it("Can buy tokens", async() => { 276 | // Setting up curve 277 | //------------------------------------------------------------------ 278 | // Minting the pre-mint tokens to the pre-mint owner 279 | await tokenInstance.connect(owner).mint( 280 | investor.address, 281 | pre_mint_sequence.whole 282 | ); 283 | // Adding the curve as a minter on the token 284 | await tokenInstance.connect(owner).addMinter(curveInstance.address); 285 | // Getting the required collateral for the pre-mint tokens 286 | let requiredCollateral = await curveInstance.requiredCollateral( 287 | pre_mint_sequence.whole 288 | ); 289 | // The owner is minting the required number of tokens in collateral (DAI) 290 | await collateralInstance.connect(owner).mint( 291 | requiredCollateral 292 | ); 293 | // Approving the curve as a spender of the required amount 294 | await collateralInstance.connect(owner).approve( 295 | curveInstance.address, 296 | requiredCollateral 297 | ); 298 | // Initialising the curve 299 | await curveInstance.connect(owner).init(); 300 | //------------------------------------------------------------------ 301 | // Minting testing 302 | //------------------------------------------------------------------ 303 | let userCollateralBalanceBefore = await collateralInstance.balanceOf( 304 | user.address 305 | ); 306 | // Getting buy cost for buying `buyAmount` (see settings.test.js) 307 | let buyCost = await curveInstance.buyPrice( 308 | test_settings.bzz.buyAmount 309 | ); 310 | // User minting collateral tokens 311 | await collateralInstance.connect(user).mint( 312 | buyCost 313 | ); 314 | let userCollateralBalanceAfter = await collateralInstance.balanceOf( 315 | user.address 316 | ); 317 | // User approves the curve as a spender of the collateral token (DAI) 318 | await collateralInstance.connect(user).approve( 319 | curveInstance.address, 320 | buyCost 321 | ); 322 | let curveAllowance = await collateralInstance.allowance( 323 | user.address, 324 | curveInstance.address 325 | ); 326 | let userTokenBalanceBefore = await tokenInstance.balanceOf(user.address); 327 | // User mints tokens from the curve 328 | await curveInstance.connect(user).mint( 329 | test_settings.bzz.buyAmount, 330 | buyCost 331 | ); 332 | let userTokenBalanceAfter = await tokenInstance.balanceOf(user.address); 333 | let userCollateralBalanceAfterMint = await collateralInstance.balanceOf( 334 | user.address 335 | ); 336 | // Testing expected behaviour 337 | assert.equal( 338 | userCollateralBalanceBefore.toString(), 339 | 0, 340 | "User has incorrect collateral balance before mint" 341 | ); 342 | assert.equal( 343 | userCollateralBalanceAfter.toString(), 344 | test_settings.dai.buyCost, 345 | "User has incorrect collateral balance after mint" 346 | ); 347 | assert.equal( 348 | curveAllowance.toString(), 349 | test_settings.dai.buyCost, 350 | "Curve has incorrect allowance on user" 351 | ); 352 | assert.equal( 353 | userTokenBalanceBefore.toString(), 354 | 0, 355 | "User has incorrect token balance before mint" 356 | ); 357 | assert.equal( 358 | userTokenBalanceAfter.toString(), 359 | test_settings.bzz.buyAmount.toString(), 360 | "User has incorrect token balance after mint" 361 | ); 362 | assert.equal( 363 | userCollateralBalanceAfterMint.toString(), 364 | 0, 365 | "User has incorrect collateral balance after minting tokens" 366 | ); 367 | }); 368 | /** 369 | * Testing the ability to sell tokens (basic test) 370 | */ 371 | it("Can sell tokens", async() => { 372 | // Setting up curve 373 | //------------------------------------------------------------------ 374 | // Minting the pre-mint tokens to the pre-mint owner 375 | await tokenInstance.connect(owner).mint( 376 | investor.address, 377 | pre_mint_sequence.whole 378 | ); 379 | // Adding the curve as a minter on the token 380 | await tokenInstance.connect(owner).addMinter(curveInstance.address); 381 | // Getting the required collateral for the pre-mint tokens 382 | let requiredCollateral = await curveInstance.requiredCollateral( 383 | pre_mint_sequence.whole 384 | ); 385 | // The owner is minting the required number of tokens in collateral (DAI) 386 | await collateralInstance.connect(owner).mint( 387 | requiredCollateral 388 | ); 389 | // Approving the curve as a spender of the required amount 390 | await collateralInstance.connect(owner).approve( 391 | curveInstance.address, 392 | requiredCollateral 393 | ); 394 | // Initialising the curve 395 | await curveInstance.connect(owner).init(); 396 | //------------------------------------------------------------------ 397 | // Minting 398 | //------------------------------------------------------------------ 399 | // Getting buy cost for buying `buyAmount` (see settings.test.js) 400 | let buyCost = await curveInstance.buyPrice( 401 | test_settings.bzz.buyAmount 402 | ); 403 | // User minting collateral tokens 404 | await collateralInstance.connect(user).mint( 405 | buyCost 406 | ); 407 | // User approves the curve as a spender of the collateral token (DAI) 408 | await collateralInstance.connect(user).approve( 409 | curveInstance.address, 410 | buyCost 411 | ); 412 | // User mints tokens from the curve 413 | await curveInstance.connect(user).mint( 414 | test_settings.bzz.buyAmount, 415 | buyCost 416 | ); 417 | let userCollateralBalanceBefore = await collateralInstance.balanceOf( 418 | user.address 419 | ); 420 | let userTokenBalanceBefore = await tokenInstance.balanceOf( 421 | user.address 422 | ); 423 | //------------------------------------------------------------------ 424 | // Burning testing 425 | //------------------------------------------------------------------ 426 | let sellReward = await curveInstance.sellReward(test_settings.bzz.sellAmount); 427 | // Approving the curve to spend the sell amount of tokens 428 | await tokenInstance.connect(user).approve( 429 | curveInstance.address, 430 | test_settings.bzz.buyAmount 431 | ); 432 | let balanceOfCurve = await collateralInstance.balanceOf( 433 | curveInstance.address 434 | ); 435 | // User burns half the tokens they bought 436 | await curveInstance.connect(user).redeem( 437 | test_settings.bzz.sellAmount, 438 | sellReward 439 | ); 440 | let userCollateralBalanceAfter = await collateralInstance.balanceOf( 441 | user.address 442 | ); 443 | let userTokenBalanceAfter = await tokenInstance.balanceOf( 444 | user.address 445 | ); 446 | let balanceOfCurveAfter = await collateralInstance.balanceOf( 447 | curveInstance.address 448 | ); 449 | // Testing expected behaviour 450 | assert.equal( 451 | userCollateralBalanceBefore.toString(), 452 | 0, 453 | "User has collateral after buying tokens" 454 | ); 455 | assert.equal( 456 | sellReward.toString(), 457 | test_settings.dai.sellReward, 458 | "Sell reward is incorrect" 459 | ); 460 | assert.equal( 461 | balanceOfCurve.toString(), 462 | test_settings.dai.curve_collateral_after_buy, 463 | "Collateral owned by curve is incorrect" 464 | ); 465 | assert.equal( 466 | userCollateralBalanceAfter.toString(), 467 | test_settings.dai.sellReward, 468 | "User collateral balance incorrect after sell" 469 | ); 470 | assert.equal( 471 | userTokenBalanceAfter.toString(), 472 | test_settings.bzz.sellAmount.toString(), 473 | "User token balance is incorrect" 474 | ); 475 | assert.equal( 476 | balanceOfCurveAfter.toString(), 477 | test_settings.dai.curve_collateral_after_sell, 478 | "Curve collateral balance incorrect after sell" 479 | ); 480 | }); 481 | /** 482 | * Tests that the curve cannot be initialized multiple times 483 | */ 484 | it("Cannot double initialize", async() => { 485 | // Minting the pre-mint tokens to the pre-mint owner 486 | await tokenInstance.connect(owner).mint( 487 | investor.address, 488 | pre_mint_sequence.whole 489 | ); 490 | // Adding the curve as a minter on the token 491 | await tokenInstance.connect(owner).addMinter(curveInstance.address); 492 | // Getting the required collateral for the pre-mint tokens 493 | let requiredCollateral = await curveInstance.requiredCollateral( 494 | pre_mint_sequence.whole 495 | ); 496 | // The owner is minting the required number of tokens in collateral (DAI) 497 | await collateralInstance.connect(owner).mint( 498 | requiredCollateral 499 | ); 500 | // Approving the curve as a spender of the required amount 501 | await collateralInstance.connect(owner).approve( 502 | curveInstance.address, 503 | requiredCollateral 504 | ); 505 | // Initialising the curve 506 | await curveInstance.connect(owner).init(); 507 | // Testing expected behaviour 508 | await expect(curveInstance.connect(owner).init()) 509 | .to.be.revertedWith(test_settings.errors.init); 510 | }); 511 | /** 512 | * Tests that the curve cannot be initialized if the curve has 513 | * not been given minter rights on the token 514 | */ 515 | it("Cannot initialize if curve is not minter", async() => { 516 | // Minting the pre-mint tokens to the pre-mint owner 517 | await tokenInstance.connect(owner).mint( 518 | investor.address, 519 | pre_mint_sequence.whole 520 | ); 521 | // Getting the required collateral for the pre-mint tokens 522 | let requiredCollateral = await curveInstance.requiredCollateral( 523 | pre_mint_sequence.whole 524 | ); 525 | // The owner is minting the required number of tokens in collateral (DAI) 526 | await collateralInstance.connect(owner).mint( 527 | requiredCollateral 528 | ); 529 | // Approving the curve as a spender of the required amount 530 | await collateralInstance.connect(owner).approve( 531 | curveInstance.address, 532 | requiredCollateral 533 | ); 534 | // Testing expected behaviour 535 | await expect(curveInstance.connect(owner).init()) 536 | .to.be.revertedWith(test_settings.errors.minter_approval); 537 | }); 538 | }); 539 | }); 540 | 541 | -------------------------------------------------------------------------------- /packages/chain/test/settings.test.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("hardhat"); 2 | let curve_abi = require("../artifacts/contracts/Curve.sol/Curve.json"); 3 | let curve_test_abi = require("../artifacts/contracts/Curve_test.sol/curve_test.json"); 4 | let token_abi = require("../artifacts/contracts/Token.sol/Token.json"); 5 | let i_token_abi = require("../artifacts/contracts/I_Token.sol/I_Token.json"); 6 | let i_curve_abi = require("../artifacts/contracts/I_Curve.sol/I_Curve.json"); 7 | let mock_dai_abi = require("../artifacts/contracts/Mock_dai.sol/Mock_dai.json"); 8 | let eth_broker_abi = require("../artifacts/contracts/Eth_broker.sol/Eth_broker.json"); 9 | let mock_router_abi = require("../artifacts/contracts/Mock_router.sol/Mock_router.json"); 10 | 11 | const pre_mint_sequence = { 12 | // These amounts are each half of the open market supply 13 | first_half: ethers.utils.parseUnits("31250000", 16), 14 | second_half: ethers.utils.parseUnits("31250000", 16), 15 | // This amount is the full open market 16 | whole: ethers.utils.parseUnits("62500000", 16), 17 | // Just under the whole amount 18 | almost_whole: ethers.utils.parseUnits("624999999999999999999999", 0), 19 | // More than expected 20 | above_expected: ethers.utils.parseUnits("68300000", 16), 21 | token_balance_after_burn: "1", 22 | // The cost for the pre-mint tokens in collateral 23 | dai: { 24 | cost: "1250000000000000000000000", 25 | above_expected_cost: "11378029335573157356496046", 26 | almost_whole_cost: "164980161587107946530", 27 | balance_after_burn: "1249999999999999999999999", 28 | co: "32000007936001269000", 29 | }, 30 | }; 31 | 32 | const tokenSettings = { 33 | dai: { 34 | name: "DAI", 35 | symbol: "DAI", 36 | decimals: 18, 37 | }, 38 | bzz: { 39 | name: "BZZ", 40 | symbol: "BZZ", 41 | decimals: 16, 42 | cap: ethers.utils.parseUnits("1250000000000000000000000", 0), 43 | }, 44 | weth: { 45 | name: "WETH", 46 | symbol: "WETH", 47 | decimals: 18, 48 | }, 49 | }; 50 | 51 | const test_settings = { 52 | bzz: { 53 | one: ethers.utils.parseUnits("1", tokenSettings.bzz.decimals), 54 | buyAmount: ethers.utils.parseUnits("1000", tokenSettings.bzz.decimals), 55 | sellAmount: ethers.utils.parseUnits("500", tokenSettings.bzz.decimals), 56 | one_token: ethers.utils.parseUnits("000000000000000001", 0), 57 | preMint_tokenBalance: "624995000000000000000000", 58 | supply_at_buy: ethers.utils.parseUnits("626000", 18), 59 | }, 60 | dai: { 61 | buyCost: "330079372699073053579", 62 | firstOneCost: "330000079360012690", 63 | above_expected_firstOneCost: "5020849434941449787", 64 | sellReward: "165059531111780991856", 65 | sellReward_preMint: "164980161587107946530", 66 | sellReward_doubleMint: "165138938742513301487", 67 | user_two_collateral: "330238168905297915788", 68 | curve_collateral_after_buy: "1250330079372699073053579", 69 | curve_coll_at_prem: "1283806446221456618676654", 70 | one_cost: "33", 71 | curve_collateral_after_sell: "1250165019841587292061723", 72 | buy_cost_pre_mint: "330000003174400008253", 73 | buy_cost_token_burn: "330158761274565606157", 74 | max_supply_buy: "2684354560000000000000000000000000" 75 | }, 76 | token_supply_after_mint: ethers.utils.parseUnits( 77 | "658781236895288088986887", 78 | 0 79 | ), 80 | helper_value: "625000000000000000000000", 81 | large: { 82 | max_uint256: ethers.constants.MaxUint256, 83 | max_supply: ethers.utils.parseUnits("1.25", 25), 84 | just_under_max: ethers.utils.parseUnits("62500000", 16) 85 | }, 86 | errors: { 87 | zero_address: "0x0000000000000000000000000000000000000000", 88 | curve_requires_pre_mint: "Curve equation requires pre-mint", 89 | max_spend: "Price exceeds max spend", 90 | min_reward: "Reward under min sell", 91 | owner: "Ownable: caller is not the owner", 92 | inactive: "Curve inactive", 93 | init: "Curve is init", 94 | minter_approval: "Curve is not minter", 95 | dai_slippage: "DAI required for trade above max", 96 | bzz_transfer_fail: "Transferring of bzz failed", 97 | dai_sell_slippage: "DAI required for trade below min", 98 | not_approved: "Transferring BZZ failed", 99 | transfer_failed: "ERC20: transfer amount exceeds balance", 100 | safe_math_add: "SafeMath: addition overflow", 101 | safe_math_sub: "SafeMath: subtraction overflow", 102 | safe_math_mul: "SafeMath: multiplication overflow", 103 | safe_math_div_zero: "SafeMath: division by zero", 104 | minter_is_minter: "MinterRole: caller does not have the Minter role" 105 | }, 106 | eth_broker: { 107 | dai: { 108 | almost_one_eth: ethers.utils.parseUnits("588", 18), 109 | max_slippage_protect: "330079372699073053579", 110 | mock_router_dai_balance_after_mint: "9669920627300926946421", 111 | buy_cost: "329920652696127210368", 112 | curve_balance_after_burn: "1249670079347303872789632", 113 | mock_router_dai_balance_after_burn: "10329920652696127210368", 114 | }, 115 | eth: { 116 | almost_one_eth: "999600000000000000", 117 | seed_eth_amount: ethers.utils.parseUnits("10000", 18), 118 | buy_price: "561134933588424191", 119 | buy_price_encoded: "0x7C98D3FC399B5FF", 120 | sell_reward: "560865109583416257", 121 | user_balance_before: "191408831393027855447563427831363046221", 122 | user_balance_after_mint: "191408831393027853859903276258779284924", 123 | mock_router_eth_balance_after_burn: "9999439134890416583743", 124 | mock_router_eth_balance_after_swap: "9999000400000000000000", 125 | mock_router_eth_balance_after_mint: "10000561134933588424191" 126 | }, 127 | bzz: { 128 | initial_supply: "625000000000000000000000", 129 | after_buy: "625010000000000000000000", 130 | after_burn: "624990000000000000000000", 131 | }, 132 | }, 133 | }; 134 | 135 | var erc20 = { 136 | events: { 137 | transfer: "Transfer", 138 | approval: "Approval", 139 | minterAdded: "MinterAdded", 140 | minterRemoved: "MinterRemoved", 141 | }, 142 | errors: { 143 | transfer_sender: "ERC20: transfer from the zero address", 144 | transfer_recipient: "ERC20: transfer to the zero address", 145 | mint_account: "ERC20: mint to the zero address", 146 | burn_account: "ERC20: burn from the zero address", 147 | approve_owner: "ERC20: approve from the zero address", 148 | approve_spender: "ERC20: approve to the zero address", 149 | minter_is_minter: "MinterRole: caller does not have the Minter role", 150 | minter_has_role: "Roles: account already has role", 151 | minter_not_minter: "Roles: account does not have role", 152 | minter_is_role: "Roles: account is the zero address", 153 | cap_zero: "ERC20Capped: cap is 0", 154 | cap_exceeded: "ERC20Capped: cap exceeded", 155 | }, 156 | constructor_valid: { 157 | name: "Swarm Token", 158 | symbol: "BZZ", 159 | decimals: 18, 160 | cap: ethers.utils.parseUnits("1000000", 18), 161 | }, 162 | constructor_invalid: { 163 | cap: 0, 164 | zero_address: "0x0000000000000000000000000000000000000000", 165 | }, 166 | }; 167 | 168 | module.exports = { 169 | ethers, 170 | curve_abi, 171 | curve_test_abi, 172 | token_abi, 173 | i_token_abi, 174 | i_curve_abi, 175 | mock_dai_abi, 176 | eth_broker_abi, 177 | mock_router_abi, 178 | pre_mint_sequence, 179 | tokenSettings, 180 | test_settings, 181 | erc20, 182 | }; 183 | 184 | -------------------------------------------------------------------------------- /packages/chain/test/token.test.js: -------------------------------------------------------------------------------- 1 | require("@nomiclabs/hardhat-waffle"); 2 | const hre = require("hardhat"); 3 | const { expect, assert } = require("chai"); 4 | const { 5 | ethers, 6 | token_abi, 7 | erc20, 8 | tokenSettings 9 | } = require("./settings.test.js"); 10 | 11 | describe("🤑 Token Tests", () => { 12 | // Contract instance 13 | let tokenInstance; 14 | 15 | describe("Parent input validation checks", () => { 16 | it("(detailed) Correct deployment", async () => { 17 | const tokenArtifacts = await ethers.getContractFactory("Token"); 18 | tokenInstance = await tokenArtifacts.deploy( 19 | tokenSettings.bzz.name, 20 | tokenSettings.bzz.symbol, 21 | tokenSettings.bzz.decimals, 22 | tokenSettings.bzz.cap 23 | ); 24 | 25 | let name = await tokenInstance.name(); 26 | let symbol = await tokenInstance.symbol(); 27 | let decimals = await tokenInstance.decimals(); 28 | 29 | assert.equal( 30 | name, 31 | tokenSettings.bzz.name, 32 | "Name parameter incorrect" 33 | ); 34 | assert.equal( 35 | symbol, 36 | tokenSettings.bzz.symbol, 37 | "Symbol parameter incorrect" 38 | ); 39 | assert.equal( 40 | decimals, 41 | tokenSettings.bzz.decimals, 42 | "Decimals parameter incorrect" 43 | ); 44 | }); 45 | 46 | it("🚫 (cap) Can't deploy a 0 cap", async () => { 47 | const tokenArtifacts = await ethers.getContractFactory("Token"); 48 | await expect(tokenArtifacts.deploy( 49 | erc20.constructor_valid.name, 50 | erc20.constructor_valid.symbol, 51 | erc20.constructor_valid.decimals, 52 | erc20.constructor_invalid.cap 53 | )).to.be.revertedWith(erc20.errors.cap_zero); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /packages/chain/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "outDir": "dist" 8 | }, 9 | "include": ["./scripts", "./test", "./typings"], 10 | "files": ["./hardhat.config.ts"] 11 | } -------------------------------------------------------------------------------- /packages/chain/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": {}, 7 | "rules": {}, 8 | "rulesDirectory": [] 9 | } -------------------------------------------------------------------------------- /packages/chain/typings/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'dotenv'; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2018", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "outDir": "dist" 8 | }, 9 | "include": ["./scripts", "./test"], 10 | "files": ["./hardhat.config.ts"] 11 | } 12 | --------------------------------------------------------------------------------