├── .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 |
--------------------------------------------------------------------------------