├── .github ├── dependabot.yaml └── workflows │ └── ci.yaml ├── .gitignore ├── .nvmrc ├── .prettierrc ├── .solcover.js ├── LICENSE ├── README.md ├── archive ├── 2024-10-24-deploy-iotex-testnet-log.txt ├── 2024-12-13-deploy-iotex.txt ├── 2025-04-09-deploy-iotex.txt ├── DATAv2_flattened-2011-06-10.sol ├── DATAv2_flattened-2011-07-13.sol ├── DATAv2onPolygon_flattened-2011-10-28.sol ├── DATAv2onPolygon_flattened-2011-11-08.sol └── DataTokenMigrator_flattened-2011-07-13.sol ├── contracts ├── CrosschainERC677.sol ├── CrowdsaleToken.sol ├── DATAv2.sol ├── DATAv2onPolygon.sol ├── DataTokenMigrator.sol ├── IERC677.sol ├── IERC677Receiver.sol └── test │ ├── MockRecipient.sol │ ├── MockRecipientNotERC677Receiver.sol │ └── MockRecipientReturnBool.sol ├── eslint.config.js ├── flattened └── DATAv2.sol ├── hardhat.config.js ├── index.d.ts ├── index.js ├── index.ts ├── package-lock.json ├── package.json ├── scripts ├── 2024-10-24-deploy-iotex-testnet.sh ├── 2024-12-13-deploy-iotex.sh ├── 2025-04-09-deploy-iotex-custom-token.sh ├── deploy-1-datav2.js ├── deploy-2-migrator.js ├── deploy-3-grant-roles.js ├── deploy-iotex.js ├── deploy-without-migrator.js ├── flatten_datav2_sol.sh ├── upgrade-tokens.js └── waitForTx.js ├── test ├── CrosschainERC677-test.js ├── DATAv2-test.js ├── DATAv2onPolygon-test.js └── DataTokenMigrator-test.js ├── tsconfig.build.json └── tsconfig.json /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # 3 | # https://docs.github.com/en/free-pro-team@latest/github/administering-a-repository/configuration-options-for-dependency-updates 4 | # https://dependabot.com/docs/config-file/validator/ 5 | # 6 | version: 2 7 | updates: 8 | - package-ecosystem: github-actions 9 | directory: / 10 | open-pull-requests-limit: 5 11 | schedule: 12 | interval: daily 13 | time: "08:00" 14 | timezone: Europe/Helsinki 15 | commit-message: 16 | prefix: ci 17 | include: scope 18 | labels: 19 | - ci 20 | assignees: 21 | - DaisyDomergue 22 | - package-ecosystem: npm 23 | directory: / 24 | open-pull-requests-limit: 5 25 | schedule: 26 | interval: daily 27 | time: "08:00" 28 | timezone: Europe/Helsinki 29 | commit-message: 30 | prefix: build 31 | include: scope 32 | labels: 33 | - build 34 | assignees: 35 | - jtakalai 36 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | - release/* 8 | push: 9 | branches: 10 | - main 11 | - release/* 12 | 13 | jobs: 14 | build: 15 | name: Build 16 | timeout-minutes: 20 17 | runs-on: ${{ matrix.os }} 18 | continue-on-error: ${{ matrix.experimental }} 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | os: 23 | - ubuntu-latest 24 | experimental: [false] 25 | steps: 26 | - uses: actions/checkout@v3 27 | with: 28 | submodules: true 29 | - uses: actions/setup-node@v3 30 | with: 31 | node-version-file: '.nvmrc' 32 | - uses: actions/cache@v3 33 | with: 34 | path: ~/.npm 35 | key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 36 | restore-keys: | 37 | ${{ runner.os }}-node- 38 | - name: Install dependencies 39 | run: npm ci 40 | - name: Run lint and unit tests 41 | run: | 42 | npm run lint 43 | npm run build 44 | npm run test 45 | npm run coverage 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Solidity-coverage output 4 | coverage.json 5 | coverage 6 | 7 | # Hardhat compilation output 8 | cache 9 | artifacts 10 | typechain -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | v20.11.0 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "overrides": [ 3 | { 4 | "files": "*.sol", 5 | "options": { 6 | "printWidth": 80, 7 | "tabWidth": 4, 8 | "useTabs": true, 9 | "singleQuote": false, 10 | "bracketSpacing": true, 11 | "explicitTypes": "always" 12 | } 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: ["CrowdsaleToken.sol"] 3 | }; 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Streamr 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DATAv2 token contract 2 | [![Continuous Integration](https://github.com/streamr-dev/DATAv2/actions/workflows/ci.yaml/badge.svg)](https://github.com/streamr-dev/DATAv2/actions/workflows/ci.yaml) 3 | [![Audit by LimeChain](https://img.shields.io/badge/Audit-LimeChain-green)](https://streamr-public.s3.amazonaws.com/DATAv2_audit_LimeChain.pdf) 4 | [![Audit by Isentropy](https://img.shields.io/badge/Audit-Isentropy-green)](https://streamr-public.s3.amazonaws.com/DATAv2_audit_Isentropy.pdf) 5 | 6 | ## Deployments 7 | 8 | | Chain | Address | 9 | |-------|---------| 10 | | Ethereum Mainnet | [0x8f693ca8d21b157107184d29d398a8d082b38b76](https://etherscan.io/address/0x8f693ca8d21b157107184d29d398a8d082b38b76#readContract) | 11 | | Binance Smart Chain | [0x0864c156b3c5f69824564dec60c629ae6401bf2a](https://bscscan.com/address/0x0864c156b3c5f69824564dec60c629ae6401bf2a#readContract) | 12 | | Gnosis (formerly xDAI) | [0x256eb8a51f382650B2A1e946b8811953640ee47D](https://gnosis.blockscout.com/address/0x256eb8a51f382650B2A1e946b8811953640ee47D) | 13 | | Polygon | [0x3a9A81d576d83FF21f26f325066054540720fC34](https://polygonscan.com/address/0x3a9A81d576d83FF21f26f325066054540720fC34#code) | 14 | | Polygon Amoy testnet | [0xf5e28a2E7BbedbE97c3782b17b102410E10d90f1](https://amoy.polygonscan.com/address/0xf5e28a2E7BbedbE97c3782b17b102410E10d90f1#code) | 15 | | IoTeX testnet | [0x5ABD469031d2B5f939808565EAB8562d7Cbaa939](https://testnet.iotexscan.io/address/0x5ABD469031d2B5f939808565EAB8562d7Cbaa939) | 16 | 17 | # NPM package contents 18 | 19 | JS/TypeScript utilities to get a nicely typed DATAv2 instance. Here's a sample code for plain node.js: 20 | ```javascript 21 | const { JsonRpcProvider } = require("@ethersproject/providers") 22 | const { formatEther, parseEther } = require("@ethersproject/units") 23 | const { Wallet } = require("@ethersproject/wallet") 24 | const { deployToken } = require("@streamr/data-v2") 25 | 26 | const key = process.env.PRIVATE_KEY 27 | const provider = new JsonRpcProvider(process.env.ETHEREUM_URL) 28 | const wallet = new Wallet(key, provider) 29 | 30 | async function main() { 31 | const token = await deployToken(wallet) 32 | console.log("Before: %s", formatEther(await token.balanceOf(wallet.address))) 33 | await (await token.grantRole(await token.MINTER_ROLE(), wallet.address)).wait() 34 | await (await token.mint(wallet.address, parseEther("10"))).wait() 35 | console.log("After: %s", formatEther(await token.balanceOf(wallet.address))) 36 | } 37 | main().catch(console.error) 38 | ``` 39 | And here's another sample code for TypeScript, run with ts-node: 40 | ```typescript 41 | import { JsonRpcProvider } from "@ethersproject/providers" 42 | import { getTokenAt } from "@streamr/data-v2" 43 | import type { DATAv2 } from "@streamr/data-v2" 44 | 45 | const provider: JsonRpcProvider = new JsonRpcProvider(process.env.ETHEREUM_URL) 46 | const token: DATAv2 = await getTokenAt(process.env.TOKEN_ADDRESS, provider) 47 | console.log("Symbol:", await token.symbol()) 48 | ``` 49 | 50 | ## List of files 51 | 52 | | File | Description | Deployed | 53 | |--------------------------------|-------------|----------| 54 | |contracts/CrowdsaleToken.sol | The current (or "old") DATA token | ETH [0x0cf0...23](https://etherscan.io/address/0x0cf0ee63788a0849fe5297f3407f701e122cc023#readContract) | 55 | |contracts/DATAv2.sol | The new DATA token | ETH [0x8f6...b76](https://etherscan.io/address/0x8f693ca8d21b157107184d29d398a8d082b38b76#readContract) (see above) | 56 | |contracts/DataTokenMigrator.sol | Migrator contract that acts as UpgradeAgent for the old token | ETH [0xc7...c16c](https://etherscan.io/address/0xc7aaf6c62e86a36395d8108fe95d5f758794c16c#readContract) | 57 | |contracts/IERC677.sol | Interface of ERC677 as defined in [the LINK token](https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca#code) | 58 | |contracts/IERC677Receiver.sol | Interface of ERC677Receiver also defined in [the LINK token](https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca#code) | 59 | |contracts/MockRecipient.sol | IERC677Receiver implementation for the purpose of testing | 60 | |test/DATAv2-test.js | DATAv2.sol tests | 61 | |test/DataTokenMigrator-test.js | DataTokenMigrator.sol tests | 62 | |contracts/DATAv2onPolygon.sol `*` | DATAv2 deployed on MATIC/Polygon chain, extended slightly for bridging | MATIC [0x3a9...34](https://polygonscan.com/address/0x3a9A81d576d83FF21f26f325066054540720fC34#code) | 63 | 64 | `*` _added after the audit_ 65 | 66 | ## NPM package changelog 67 | 68 | 1.0.0 deployed to Polygon 69 | 1.0.1 export abi, types, and bytecode; helper functions `getTokenAt` and `deployToken` 70 | 1.0.2 ethers v5 -> v6 71 | 1.0.3 peaq and iotex 72 | 1.0.4 included .sol dependencies and flattened .sol file 73 | 74 | # DATAv2 features 75 | 76 | Some of the following features are inherited from [OpenZeppelin contracts](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v4.0.0/contracts): 77 | 78 | | Feature | Associated function | File where the function is | 79 | |-----------------------------------------------------------|-----------------------|-----------------------------------------------------------------------| 80 | | [ERC-677](https://github.com/ethereum/EIPs/issues/677) | transferAndCall | contracts/DATAv2.sol | 81 | | [EIP-2612](https://eips.ethereum.org/EIPS/eip-2612) | permit | @openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol | 82 | | Minting | grantRole, revokeRole | @openzeppelin/contracts/access/AccessControl.sol | 83 | | Minting | mint | contracts/DATAv2.sol | 84 | | Burning | burn, burnFrom | @openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol | 85 | 86 | ## Minting 87 | 88 | DATAv2 has an admin role (controlled by Streamr) that can add and remove minters, who can mint tokens tokens at will. The admin and minters will be contracts that are not controlled by a single wallet. 89 | 90 | The whole old DATA supply is minted as part of migration, and new tokens may be minted later as decided by the community process in the form of [Streamr Improvement Proposals](https://snapshot.org/#/streamr.eth), see e.g. [SIP-6](https://snapshot.org/#/streamr.eth/proposal/QmU383LMPAHdzMevcxY6UzyL5vkBaNHQhCcp2WbXw5kXS1). 91 | 92 | ## ERC677 functionality 93 | 94 | DATAv2 follows the convention of [LINK](https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca#code) and other tokens with regard to the callback function `onTokenTransfer` invoked by `transferAndCall`: DATAv2 does not check the bool return value of `onTokenTransfer`. Instead `onTokenTransfer` should revert if the transfer is to be rejected by the recipient contract. If the recipient is a contract, it must implement `onTokenTransfer` or else `transferAndCall` reverts. 95 | 96 | ## Migration process 97 | 98 | 1. We deploy the DATAv2 and DataTokenMigrator contracts 99 | 2. We call `grantRole(id('MINTER_ROLE'), minter.address)` on the DATAv2 contract 100 | 3. Minter calls `mint(migrator.address, tokenSupply)` on the DATAv2 contract to mint the tokens belonging to old DATA holders into the DataTokenMigrator contract 101 | 4. We call `setUpgradeAgent(migrator.address)` in the old DATA contract to start the upgrade 102 | 5. DATA token holders call `upgrade(amountWei)` in the old DATA contract 103 | 104 | Result: 105 | * Old DATA tokens are burned 106 | * DATAv2 tokens are transferred from the DataTokenMigrator to the holders 107 | 108 | At all times, DATAv2 tokens left in the DataTokenMigrator are the unmigrated DATA tokens, and they should be equal in number to the tokenSupply of the old DATA contract 109 | 110 | ## DATAv2 contract anatomy 111 | 112 | [OpenZeppelin contracts](https://github.com/OpenZeppelin/openzeppelin-contracts/tree/v4.0.0/contracts) were used where available to implement the features listed in the above table. 113 | 114 | Explanations of the origins and intents of all code found in DATAv2.sol: 115 | | Lines | Explanation | 116 | |---------|-------------------------------------------------------------| 117 | | 1...12 | OpenZeppelin and interface imports, DATAv2 token properties | 118 | | 16...46 | Adapted from @openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol to implement the minting feature. The AccessControl.sol is a bit of a can of worms when it comes to state-space: the "role admin" can set up any constellation of roles and grant those roles and their administration to anyone; its implementation is quite elegant though, so it didn't feel very significantly worse than a "brute" `mapping (address => bool) isMinter` type of implementation, especially since it's all library code. Added isMinter convenience function. | 119 | | 48...74 | Adapted from LINK token to implement the ERC-677 token transfer hook. Recipient smart contracts can now react to DATAv2 token transfer which makes approve+transferFrom two-transaction flow avoidable if not entirely redundant. Decided to go for the simply ERC-677 instead of the ERC-777 because of some misfeatures on ERC-777 (defaultOperators, higher plain transfer gas price) as well as uncertain adoption at this stage. | 120 | | 76...103 | Allow the admin to change token symbol and name just in case, e.g. to support token rebranding, and graceful end-of-life after the possible next migration | 121 | 122 | ## Dependencies 123 | 124 | * [`@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.0/contracts/token/ERC20/extensions/draft-ERC20Permit.sol) 125 | * Allows a smart contract to spend tokens using a signature from the token holder 126 | * For usage, see [`ERC20Permit.test.js`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/token/ERC20/extensions/ERC20Permit.test.js) in `openzeppelin-contracts` 127 | * [`@openzeppelin/contracts/access/AccessControl.sol`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.0/contracts/access/AccessControl.sol) 128 | * Role-based access control (RBAC) where each role has another role as role-admin that can grant it 129 | * DATAv2 has a _minter_ role as well as the _default_ role that manages it. 130 | * [Tests also in the openzeppelin-contracts repo](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/test/access/AccessControl.behavior.js) 131 | * [`@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.0/contracts/token/ERC20/extensions/ERC20Burnable.sol) 132 | * Unlocks the simple burning functionality in the OpenZeppelin ERC20.sol 133 | * Emits transfer to zero address: [`emit Transfer(account, address(0), amount);`](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.0/contracts/token/ERC20/ERC20.sol#L264) 134 | -------------------------------------------------------------------------------- /archive/2024-10-24-deploy-iotex-testnet-log.txt: -------------------------------------------------------------------------------- 1 | $ ./scripts/2024-10-24-deploy-iotex-testnet.sh [20:20:54] 2 | + '[' -z ']' 3 | + export PROVIDER=https://babel-api.testnet.iotex.io 4 | + PROVIDER=https://babel-api.testnet.iotex.io 5 | + export EXPLORER=https://testnet.iotexscan.io 6 | + EXPLORER=https://testnet.iotexscan.io 7 | + export OUTPUT_FILE=iotex-testnet-address.txt 8 | + OUTPUT_FILE=iotex-testnet-address.txt 9 | + ./scripts/deploy-without-migrator.js 10 | Deploying contracts from 0x41e36D4fFb5B443B20f55bcFf27c68fF086Fe06f 11 | Connected to network at https://babel-api.testnet.iotex.io: {"name":"unknown","chainId":"4690"} 12 | Follow deployment: https://testnet.iotexscan.io/tx/0x13c362b06e5a14c8826beb448c5d13527b17ec5d3517d3fa123b7cf164c2092c 13 | DATAv2 deployed to: 0x5ABD469031d2B5f939808565EAB8562d7Cbaa939 14 | Follow grant minter role: https://testnet.iotexscan.io/tx/0x9bcc8c2a79aad3ca9d94fc91e86caf6ab2c6f4abf1f66aa14513cc7d4cc4a747 15 | Follow grant admin role: https://testnet.iotexscan.io/tx/0xd204ed0533514ddcb07d42d2520728b6918296fcaf540cf2b485a44f36323588 16 | ++ cat iotex-testnet-address.txt 17 | + npx hardhat verify --network iotexTestnet 0x5ABD469031d2B5f939808565EAB8562d7Cbaa939 18 | [INFO] Sourcify Verification Skipped: Sourcify verification is currently disabled. To enable it, add the following entry to your Hardhat configuration: 19 | 20 | sourcify: { 21 | enabled: true 22 | } 23 | 24 | Or set 'enabled' to false to hide this message. 25 | 26 | For more information, visit https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#verifying-on-sourcify 27 | hardhat-verify found one or more errors during the verification process: 28 | 29 | Etherscan: 30 | The address 0x5ABD469031d2B5f939808565EAB8562d7Cbaa939 has no bytecode. Is the contract deployed to this network? 31 | The selected network is iotexTestnet. 32 | 33 | 34 | + echo 'Retrying in 10 seconds...' 35 | Retrying in 10 seconds... 36 | + sleep 10 37 | ++ cat iotex-testnet-address.txt 38 | + npx hardhat verify --network iotexTestnet 0x5ABD469031d2B5f939808565EAB8562d7Cbaa939 39 | [INFO] Sourcify Verification Skipped: Sourcify verification is currently disabled. To enable it, add the following entry to your Hardhat configuration: 40 | 41 | sourcify: { 42 | enabled: true 43 | } 44 | 45 | Or set 'enabled' to false to hide this message. 46 | 47 | For more information, visit https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#verifying-on-sourcify 48 | Successfully submitted source code for contract 49 | contracts/DATAv2.sol:DATAv2 at 0x5ABD469031d2B5f939808565EAB8562d7Cbaa939 50 | for verification on the block explorer. Waiting for verification result... 51 | 52 | Successfully verified contract DATAv2 on the block explorer. 53 | https://testnet.iotexscan.io/address/0x5ABD469031d2B5f939808565EAB8562d7Cbaa939#code 54 | -------------------------------------------------------------------------------- /archive/2024-12-13-deploy-iotex.txt: -------------------------------------------------------------------------------- 1 | + declare -p KEY 2 | + echo 'Using deployer private key from environment variable KEY' 3 | Using deployer private key from environment variable KEY 4 | + export PROVIDER=https://babel-api.mainnet.iotex.one 5 | + PROVIDER=https://babel-api.mainnet.iotex.one 6 | + export EXPLORER=https://iotexscan.io 7 | + EXPLORER=https://iotexscan.io 8 | + export OUTPUT_FILE=iotex-address.txt 9 | + OUTPUT_FILE=iotex-address.txt 10 | + ./scripts/deploy-without-migrator.js 11 | Deploying contracts from 0x72ea90e2Fe101090fc44156cae3141527AE9F429 12 | Connected to network at https://babel-api.mainnet.iotex.one: {"name":"unknown","chainId":"4689"} 13 | Follow deployment: https://iotexscan.io/tx/0x371e71b0713113ad9e6188a0a0912a31f442b567304e2c9b43e4068108b61df2 14 | DATAv2 deployed to: 0x871a20C72d636A8C987e762616Ab047e1B52653e 15 | Follow grant minter role: https://iotexscan.io/tx/0x10c9a18a44fd7d5994689723a0b20b5787904b3b222e237e75b2e26c6248102c 16 | Follow grant admin role: https://iotexscan.io/tx/0xa53fd8b82141e135c11c139175914d431d8fd315d18082ab330f9684d5ad90cf 17 | ++ cat iotex-address.txt 18 | + npx hardhat verify --network iotex 0x871a20C72d636A8C987e762616Ab047e1B52653e 19 | [INFO] Sourcify Verification Skipped: Sourcify verification is currently disabled. To enable it, add the following entry to your Hardhat configuration: 20 | 21 | sourcify: { 22 | enabled: true 23 | } 24 | 25 | Or set 'enabled' to false to hide this message. 26 | 27 | For more information, visit https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#verifying-on-sourcify 28 | hardhat-verify found one or more errors during the verification process: 29 | 30 | Etherscan: 31 | The address 0x871a20C72d636A8C987e762616Ab047e1B52653e has no bytecode. Is the contract deployed to this network? 32 | The selected network is iotex. 33 | 34 | 35 | + echo 'Retrying in 10 seconds...' 36 | Retrying in 10 seconds... 37 | + sleep 10 38 | ++ cat iotex-address.txt 39 | + npx hardhat verify --network iotex 0x871a20C72d636A8C987e762616Ab047e1B52653e 40 | [INFO] Sourcify Verification Skipped: Sourcify verification is currently disabled. To enable it, add the following entry to your Hardhat configuration: 41 | 42 | sourcify: { 43 | enabled: true 44 | } 45 | 46 | Or set 'enabled' to false to hide this message. 47 | 48 | For more information, visit https://hardhat.org/hardhat-runner/plugins/nomicfoundation-hardhat-verify#verifying-on-sourcify 49 | 50 | Successfully verified contract DATAv2 on the block explorer. 51 | https://iotexscan.io/address/0x871a20C72d636A8C987e762616Ab047e1B52653e#code 52 | -------------------------------------------------------------------------------- /archive/2025-04-09-deploy-iotex.txt: -------------------------------------------------------------------------------- 1 | $ ./scripts/2025-04-09-deploy-iotex-custom-token.sh [13:11:53] 2 | + declare -p KEY 3 | + echo 'Using deployer private key from environment variable KEY' 4 | Using deployer private key from environment variable KEY 5 | + export PROVIDER=https://babel-api.mainnet.iotex.one 6 | + PROVIDER=https://babel-api.mainnet.iotex.one 7 | + export EXPLORER=https://iotexscan.io 8 | + EXPLORER=https://iotexscan.io 9 | + export OUTPUT_FILE=iotex-address.txt 10 | + OUTPUT_FILE=iotex-address.txt 11 | + export MINTER_ADDRESS=0xAe1Ba4036610cF18A2Ca6ba0f43DB957ffA21024 12 | + MINTER_ADDRESS=0xAe1Ba4036610cF18A2Ca6ba0f43DB957ffA21024 13 | + ./scripts/deploy-iotex.js 14 | Deploying contracts from 0xAe1Ba4036610cF18A2Ca6ba0f43DB957ffA21024 15 | Connected to network at https://babel-api.mainnet.iotex.one: {"name":"unknown","chainId":"4689"} 16 | Follow deployment: https://iotexscan.io/tx/0x712d0c9d400cf4e2a1b5e85f4e0028f100405e2148f76e847b52bd4b0a0afb8f 17 | DATAv2 deployed to: 0xD94be6fd546d4cE502CB1E870A58330Cc8869e9B 18 | -------------------------------------------------------------------------------- /archive/DATAv2_flattened-2011-06-10.sol: -------------------------------------------------------------------------------- 1 | // Sources flattened with hardhat v2.1.2 https://hardhat.org 2 | 3 | // File contracts/DATAv2.sol 4 | 5 | // SPDX-License-Identifier: MIT 6 | pragma solidity >=0.8.0; 7 | 8 | interface IERC677Receiver { 9 | function onTokenTransfer( 10 | address _sender, 11 | uint256 _value, 12 | bytes calldata _data 13 | ) external; 14 | } 15 | 16 | contract DATAv2 { 17 | mapping (address => uint256) public balanceOf; 18 | mapping (address => mapping (address => uint256)) public allowance; 19 | mapping (address => bool) public isMinter; 20 | address public admin; 21 | address public newAdminCandidate; 22 | 23 | uint256 public totalSupply; 24 | 25 | string public name = "Streamr"; 26 | string public symbol = "DATA"; 27 | uint8 public decimals = 18; 28 | 29 | // solhint-disable-next-line var-name-mixedcase 30 | bytes32 private immutable _PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); 31 | bytes32 public DOMAIN_SEPARATOR; 32 | mapping (address => uint256) public nonces; 33 | 34 | /** 35 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 36 | * another (`to`). 37 | * 38 | * Note that `value` may be zero. 39 | */ 40 | event Transfer(address indexed from, address indexed to, uint256 value); 41 | 42 | /** 43 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 44 | * a call to {approve}. `value` is the new allowance. 45 | */ 46 | event Approval(address indexed owner, address indexed spender, uint256 value); 47 | 48 | // ERC-677 49 | event Transfer( 50 | address indexed from, 51 | address indexed to, 52 | uint value, 53 | bytes data 54 | ); 55 | 56 | event UpdatedTokenInformation(string newName, string newSymbol); 57 | event MinterAdded(address newMinter); 58 | event MinterRemoved(address oldMinter); 59 | event AdminOffered(address newAdminCandidate); 60 | event AdminChanged(address newAdmin); 61 | 62 | constructor() { 63 | admin = msg.sender; 64 | 65 | // EIP-712 / permit related stuff 66 | bytes32 hashedName = keccak256(bytes(name)); 67 | bytes32 hashedVersion = keccak256("1"); 68 | bytes32 typeHash = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); 69 | DOMAIN_SEPARATOR = keccak256( 70 | abi.encode( 71 | typeHash, 72 | hashedName, 73 | hashedVersion, 74 | block.chainid, 75 | address(this) 76 | ) 77 | ); 78 | } 79 | 80 | function setTokenInformation(string calldata newName, string calldata newSymbol) public { 81 | require(msg.sender == admin, "Transaction signer is not the admin"); 82 | name = newName; 83 | symbol = newSymbol; 84 | emit UpdatedTokenInformation(newName, newSymbol); 85 | } 86 | 87 | function addMinter(address newMinter) public { 88 | require(msg.sender == admin, "Transaction signer is not the admin"); 89 | isMinter[newMinter] = true; 90 | emit MinterAdded(newMinter); 91 | } 92 | 93 | function removeMinter(address minter) public { 94 | require(msg.sender == admin || msg.sender == minter, "Transaction signer is not the admin or the minter itself"); 95 | require(isMinter[minter], "Not a minter"); 96 | isMinter[minter] = false; 97 | emit MinterRemoved(minter); 98 | } 99 | 100 | function offerAdmin(address newAdmin) public { 101 | require(msg.sender == admin, "Transaction signer is not the admin"); 102 | newAdminCandidate = newAdmin; 103 | emit AdminOffered(newAdminCandidate); 104 | } 105 | 106 | function acceptAdmin() public { 107 | require(msg.sender == newAdminCandidate, "Transaction signer is not the new admin candidate"); 108 | admin = msg.sender; 109 | newAdminCandidate = address(0); 110 | emit AdminChanged(admin); 111 | } 112 | 113 | /** @dev Creates `amount` tokens and assigns them to `account`, increasing 114 | * the total supply. 115 | * 116 | * Emits a {Transfer} event with `from` set to the zero address. 117 | * 118 | * Requirements: 119 | * 120 | * - `to` cannot be the zero address. 121 | */ 122 | function mint(address account, uint256 amount) public { 123 | require(isMinter[msg.sender], "Transaction signer is not a minter"); 124 | require(account != address(0), "Mint to the zero address"); 125 | 126 | totalSupply += amount; 127 | balanceOf[account] += amount; 128 | emit Transfer(address(0), account, amount); 129 | } 130 | 131 | // ------------------------------------------------------------------------ 132 | // adapted from LINK token, see https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca#code 133 | // implements https://github.com/ethereum/EIPs/issues/677 134 | /** 135 | * @dev transfer token to a contract address with additional data if the recipient is a contact. 136 | * @param _to The address to transfer to. 137 | * @param _value The amount to be transferred. 138 | * @param _data The extra data to be passed to the receiving contract. 139 | */ 140 | function transferAndCall( 141 | address _to, 142 | uint256 _value, 143 | bytes calldata _data 144 | ) public returns (bool success) { 145 | transfer(_to, _value); 146 | emit Transfer(msg.sender, _to, _value, _data); 147 | 148 | uint256 recipientCodeSize; 149 | assembly { 150 | recipientCodeSize := extcodesize(_to) 151 | } 152 | if (recipientCodeSize > 0) { 153 | IERC677Receiver receiver = IERC677Receiver(_to); 154 | receiver.onTokenTransfer(msg.sender, _value, _data); 155 | } 156 | return true; 157 | } 158 | 159 | /** 160 | * @dev Returns an Ethereum Signed Typed Data, created from a 161 | * `domainSeparator` and a `structHash`. This produces hash corresponding 162 | * to the one signed with the 163 | * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] 164 | * JSON-RPC method as part of EIP-712. 165 | * 166 | * See {recover}. 167 | */ 168 | function ECDSA_toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { 169 | return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); 170 | } 171 | 172 | /** 173 | * @dev See {IERC20Permit-permit}. 174 | */ 175 | function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { 176 | // solhint-disable-next-line not-rely-on-time 177 | require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); 178 | 179 | bytes32 structHash = keccak256( 180 | abi.encode( 181 | _PERMIT_TYPEHASH, 182 | owner, 183 | spender, 184 | value, 185 | nonces[owner], 186 | deadline 187 | ) 188 | ); 189 | 190 | bytes32 hash = ECDSA_toTypedDataHash(DOMAIN_SEPARATOR, structHash); 191 | 192 | // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature 193 | // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines 194 | // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most 195 | // signatures from current libraries generate a unique signature with an s-value in the lower half order. 196 | // 197 | // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value 198 | // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or 199 | // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept 200 | // these malleable signatures as well. 201 | require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value"); 202 | require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value"); 203 | 204 | // If the signature is valid (and not malleable), return the signer address 205 | address signer = ecrecover(hash, v, r, s); 206 | require(signer == owner, "ERC20Permit: invalid signature"); 207 | 208 | nonces[owner] += 1; 209 | approve(owner, spender, value); 210 | } 211 | 212 | /** 213 | * @dev See {IERC20-transferFrom}. 214 | * 215 | * Emits an {Approval} event indicating the updated allowance. This is not 216 | * required by the EIP. See the note at the beginning of {ERC20}. 217 | * 218 | * Requirements: 219 | * 220 | * - `sender` and `recipient` cannot be the zero address. 221 | * - `sender` must have a balance of at least `amount`. 222 | * - the caller must have allowance for ``sender``'s tokens of at least 223 | * `amount`. 224 | */ 225 | function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { 226 | _transfer(sender, recipient, amount); 227 | 228 | uint256 currentAllowance = allowance[sender][msg.sender]; 229 | require(currentAllowance >= amount, "Transfer amount exceeds allowance"); 230 | approve(sender, msg.sender, currentAllowance - amount); 231 | 232 | return true; 233 | } 234 | 235 | /** 236 | * @dev Atomically increases the allowance granted to `spender` by the caller. 237 | * 238 | * This is an alternative to {approve} that can be used as a mitigation for 239 | * problems described in {IERC20-approve}. 240 | * 241 | * Emits an {Approval} event indicating the updated allowance. 242 | * 243 | * Requirements: 244 | * 245 | * - `spender` cannot be the zero address. 246 | */ 247 | function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { 248 | approve(msg.sender, spender, allowance[msg.sender][spender] + addedValue); 249 | return true; 250 | } 251 | 252 | /** 253 | * @dev Atomically decreases the allowance granted to `spender` by the caller. 254 | * 255 | * This is an alternative to {approve} that can be used as a mitigation for 256 | * problems described in {IERC20-approve}. 257 | * 258 | * Emits an {Approval} event indicating the updated allowance. 259 | * 260 | * Requirements: 261 | * 262 | * - `spender` cannot be the zero address. 263 | * - `spender` must have allowance for the caller of at least 264 | * `subtractedValue`. 265 | */ 266 | function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { 267 | uint256 currentAllowance = allowance[msg.sender][spender]; 268 | require(currentAllowance >= subtractedValue, "decreased allowance below zero"); 269 | approve(msg.sender, spender, currentAllowance - subtractedValue); 270 | 271 | return true; 272 | } 273 | 274 | /** 275 | * @dev See {IERC20-transfer}. 276 | * 277 | * Requirements: 278 | * 279 | * - `recipient` cannot be the zero address. 280 | * - the caller must have a balance of at least `amount`. 281 | */ 282 | function transfer(address recipient, uint256 amount) public returns (bool) { 283 | _transfer(msg.sender, recipient, amount); 284 | return true; 285 | } 286 | 287 | /** 288 | * @dev Moves tokens `amount` from `sender` to `recipient`. 289 | * 290 | * Emits a {Transfer} event. 291 | * 292 | * Requirements: 293 | * 294 | * - `sender` cannot be the zero address. 295 | * - `recipient` cannot be the zero address. 296 | * - `sender` must have a balance of at least `amount`. 297 | */ 298 | function _transfer(address sender, address recipient, uint256 amount) private { 299 | require(sender != address(0), "Transfer from the zero address"); 300 | require(recipient != address(0), "Transfer to the zero address"); 301 | 302 | uint256 senderBalance = balanceOf[sender]; 303 | require(senderBalance >= amount, "Transfer amount exceeds balance"); 304 | balanceOf[sender] = senderBalance - amount; 305 | balanceOf[recipient] += amount; 306 | 307 | emit Transfer(sender, recipient, amount); 308 | } 309 | 310 | /** 311 | * @dev Destroys `amount` tokens from `account`, reducing the total supply. 312 | * 313 | * Emits a {Transfer} event with `to` set to the zero address. 314 | * 315 | * Requirements: 316 | * 317 | * - `account` cannot be the zero address. 318 | * - `account` must have at least `amount` tokens. 319 | */ 320 | function burn(address account, uint256 amount) public { 321 | require(account != address(0), "burn from the zero address"); 322 | 323 | uint256 accountBalance = balanceOf[account]; 324 | require(accountBalance >= amount, "burn amount exceeds balance"); 325 | balanceOf[account] = accountBalance - amount; 326 | totalSupply -= amount; 327 | 328 | emit Transfer(account, address(0), amount); 329 | } 330 | 331 | /** 332 | * @dev Destroys `amount` tokens from `account`, deducting from the caller's allowance. 333 | * 334 | * See {ERC20-_burn} and {ERC20-allowance}. 335 | * 336 | * Requirements: 337 | * 338 | * - the caller must have allowance for ``accounts``'s tokens of at least 339 | * `amount`. 340 | */ 341 | function burnFrom(address account, uint256 amount) public { 342 | uint256 currentAllowance = allowance[account][msg.sender]; 343 | require(currentAllowance >= amount, "Burn amount exceeds allowance"); 344 | approve(account, msg.sender, currentAllowance - amount); 345 | burn(account, amount); 346 | } 347 | 348 | /** 349 | * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. 350 | * 351 | * This internal function is equivalent to `approve`, and can be used to 352 | * e.g. set automatic allowances for certain subsystems, etc. 353 | * 354 | * Emits an {Approval} event. 355 | * 356 | * Requirements: 357 | * 358 | * - `owner` cannot be the zero address. 359 | * - `spender` cannot be the zero address. 360 | */ 361 | function approve(address owner, address spender, uint256 amount) internal { 362 | require(owner != address(0), "Approve from the zero address"); 363 | require(spender != address(0), "Approve to the zero address"); 364 | 365 | allowance[owner][spender] = amount; 366 | emit Approval(owner, spender, amount); 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /archive/DATAv2_flattened-2011-07-13.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | // Sources flattened with hardhat v2.1.2 https://hardhat.org 5 | // NOTE: manually removed extraneous licence identifies and pragmas to accommodate Etherscan Verify & Publish 6 | 7 | // File @openzeppelin/contracts/utils/Context.sol@v4.0.0 8 | 9 | 10 | 11 | 12 | 13 | /* 14 | * @dev Provides information about the current execution context, including the 15 | * sender of the transaction and its data. While these are generally available 16 | * via msg.sender and msg.data, they should not be accessed in such a direct 17 | * manner, since when dealing with meta-transactions the account sending and 18 | * paying for execution may not be the actual sender (as far as an application 19 | * is concerned). 20 | * 21 | * This contract is only required for intermediate, library-like contracts. 22 | */ 23 | abstract contract Context { 24 | function _msgSender() internal view virtual returns (address) { 25 | return msg.sender; 26 | } 27 | 28 | function _msgData() internal view virtual returns (bytes calldata) { 29 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 30 | return msg.data; 31 | } 32 | } 33 | 34 | 35 | // File @openzeppelin/contracts/utils/introspection/IERC165.sol@v4.0.0 36 | 37 | 38 | 39 | 40 | 41 | /** 42 | * @dev Interface of the ERC165 standard, as defined in the 43 | * https://eips.ethereum.org/EIPS/eip-165[EIP]. 44 | * 45 | * Implementers can declare support of contract interfaces, which can then be 46 | * queried by others ({ERC165Checker}). 47 | * 48 | * For an implementation, see {ERC165}. 49 | */ 50 | interface IERC165 { 51 | /** 52 | * @dev Returns true if this contract implements the interface defined by 53 | * `interfaceId`. See the corresponding 54 | * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] 55 | * to learn more about how these ids are created. 56 | * 57 | * This function call must use less than 30 000 gas. 58 | */ 59 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 60 | } 61 | 62 | 63 | // File @openzeppelin/contracts/utils/introspection/ERC165.sol@v4.0.0 64 | 65 | 66 | 67 | 68 | 69 | /** 70 | * @dev Implementation of the {IERC165} interface. 71 | * 72 | * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check 73 | * for the additional interface id that will be supported. For example: 74 | * 75 | * ```solidity 76 | * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 77 | * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); 78 | * } 79 | * ``` 80 | * 81 | * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. 82 | */ 83 | abstract contract ERC165 is IERC165 { 84 | /** 85 | * @dev See {IERC165-supportsInterface}. 86 | */ 87 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 88 | return interfaceId == type(IERC165).interfaceId; 89 | } 90 | } 91 | 92 | 93 | // File @openzeppelin/contracts/access/AccessControl.sol@v4.0.0 94 | 95 | 96 | 97 | 98 | 99 | 100 | /** 101 | * @dev External interface of AccessControl declared to support ERC165 detection. 102 | */ 103 | interface IAccessControl { 104 | function hasRole(bytes32 role, address account) external view returns (bool); 105 | function getRoleAdmin(bytes32 role) external view returns (bytes32); 106 | function grantRole(bytes32 role, address account) external; 107 | function revokeRole(bytes32 role, address account) external; 108 | function renounceRole(bytes32 role, address account) external; 109 | } 110 | 111 | /** 112 | * @dev Contract module that allows children to implement role-based access 113 | * control mechanisms. This is a lightweight version that doesn't allow enumerating role 114 | * members except through off-chain means by accessing the contract event logs. Some 115 | * applications may benefit from on-chain enumerability, for those cases see 116 | * {AccessControlEnumerable}. 117 | * 118 | * Roles are referred to by their `bytes32` identifier. These should be exposed 119 | * in the external API and be unique. The best way to achieve this is by 120 | * using `public constant` hash digests: 121 | * 122 | * ``` 123 | * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); 124 | * ``` 125 | * 126 | * Roles can be used to represent a set of permissions. To restrict access to a 127 | * function call, use {hasRole}: 128 | * 129 | * ``` 130 | * function foo() public { 131 | * require(hasRole(MY_ROLE, msg.sender)); 132 | * ... 133 | * } 134 | * ``` 135 | * 136 | * Roles can be granted and revoked dynamically via the {grantRole} and 137 | * {revokeRole} functions. Each role has an associated admin role, and only 138 | * accounts that have a role's admin role can call {grantRole} and {revokeRole}. 139 | * 140 | * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means 141 | * that only accounts with this role will be able to grant or revoke other 142 | * roles. More complex role relationships can be created by using 143 | * {_setRoleAdmin}. 144 | * 145 | * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to 146 | * grant and revoke this role. Extra precautions should be taken to secure 147 | * accounts that have been granted it. 148 | */ 149 | abstract contract AccessControl is Context, IAccessControl, ERC165 { 150 | struct RoleData { 151 | mapping (address => bool) members; 152 | bytes32 adminRole; 153 | } 154 | 155 | mapping (bytes32 => RoleData) private _roles; 156 | 157 | bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; 158 | 159 | /** 160 | * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` 161 | * 162 | * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite 163 | * {RoleAdminChanged} not being emitted signaling this. 164 | * 165 | * _Available since v3.1._ 166 | */ 167 | event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); 168 | 169 | /** 170 | * @dev Emitted when `account` is granted `role`. 171 | * 172 | * `sender` is the account that originated the contract call, an admin role 173 | * bearer except when using {_setupRole}. 174 | */ 175 | event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); 176 | 177 | /** 178 | * @dev Emitted when `account` is revoked `role`. 179 | * 180 | * `sender` is the account that originated the contract call: 181 | * - if using `revokeRole`, it is the admin role bearer 182 | * - if using `renounceRole`, it is the role bearer (i.e. `account`) 183 | */ 184 | event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); 185 | 186 | /** 187 | * @dev See {IERC165-supportsInterface}. 188 | */ 189 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 190 | return interfaceId == type(IAccessControl).interfaceId 191 | || super.supportsInterface(interfaceId); 192 | } 193 | 194 | /** 195 | * @dev Returns `true` if `account` has been granted `role`. 196 | */ 197 | function hasRole(bytes32 role, address account) public view override returns (bool) { 198 | return _roles[role].members[account]; 199 | } 200 | 201 | /** 202 | * @dev Returns the admin role that controls `role`. See {grantRole} and 203 | * {revokeRole}. 204 | * 205 | * To change a role's admin, use {_setRoleAdmin}. 206 | */ 207 | function getRoleAdmin(bytes32 role) public view override returns (bytes32) { 208 | return _roles[role].adminRole; 209 | } 210 | 211 | /** 212 | * @dev Grants `role` to `account`. 213 | * 214 | * If `account` had not been already granted `role`, emits a {RoleGranted} 215 | * event. 216 | * 217 | * Requirements: 218 | * 219 | * - the caller must have ``role``'s admin role. 220 | */ 221 | function grantRole(bytes32 role, address account) public virtual override { 222 | require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to grant"); 223 | 224 | _grantRole(role, account); 225 | } 226 | 227 | /** 228 | * @dev Revokes `role` from `account`. 229 | * 230 | * If `account` had been granted `role`, emits a {RoleRevoked} event. 231 | * 232 | * Requirements: 233 | * 234 | * - the caller must have ``role``'s admin role. 235 | */ 236 | function revokeRole(bytes32 role, address account) public virtual override { 237 | require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to revoke"); 238 | 239 | _revokeRole(role, account); 240 | } 241 | 242 | /** 243 | * @dev Revokes `role` from the calling account. 244 | * 245 | * Roles are often managed via {grantRole} and {revokeRole}: this function's 246 | * purpose is to provide a mechanism for accounts to lose their privileges 247 | * if they are compromised (such as when a trusted device is misplaced). 248 | * 249 | * If the calling account had been granted `role`, emits a {RoleRevoked} 250 | * event. 251 | * 252 | * Requirements: 253 | * 254 | * - the caller must be `account`. 255 | */ 256 | function renounceRole(bytes32 role, address account) public virtual override { 257 | require(account == _msgSender(), "AccessControl: can only renounce roles for self"); 258 | 259 | _revokeRole(role, account); 260 | } 261 | 262 | /** 263 | * @dev Grants `role` to `account`. 264 | * 265 | * If `account` had not been already granted `role`, emits a {RoleGranted} 266 | * event. Note that unlike {grantRole}, this function doesn't perform any 267 | * checks on the calling account. 268 | * 269 | * [WARNING] 270 | * ==== 271 | * This function should only be called from the constructor when setting 272 | * up the initial roles for the system. 273 | * 274 | * Using this function in any other way is effectively circumventing the admin 275 | * system imposed by {AccessControl}. 276 | * ==== 277 | */ 278 | function _setupRole(bytes32 role, address account) internal virtual { 279 | _grantRole(role, account); 280 | } 281 | 282 | /** 283 | * @dev Sets `adminRole` as ``role``'s admin role. 284 | * 285 | * Emits a {RoleAdminChanged} event. 286 | */ 287 | function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { 288 | emit RoleAdminChanged(role, getRoleAdmin(role), adminRole); 289 | _roles[role].adminRole = adminRole; 290 | } 291 | 292 | function _grantRole(bytes32 role, address account) private { 293 | if (!hasRole(role, account)) { 294 | _roles[role].members[account] = true; 295 | emit RoleGranted(role, account, _msgSender()); 296 | } 297 | } 298 | 299 | function _revokeRole(bytes32 role, address account) private { 300 | if (hasRole(role, account)) { 301 | _roles[role].members[account] = false; 302 | emit RoleRevoked(role, account, _msgSender()); 303 | } 304 | } 305 | } 306 | 307 | 308 | // File @openzeppelin/contracts/token/ERC20/IERC20.sol@v4.0.0 309 | 310 | 311 | 312 | 313 | 314 | /** 315 | * @dev Interface of the ERC20 standard as defined in the EIP. 316 | */ 317 | interface IERC20 { 318 | /** 319 | * @dev Returns the amount of tokens in existence. 320 | */ 321 | function totalSupply() external view returns (uint256); 322 | 323 | /** 324 | * @dev Returns the amount of tokens owned by `account`. 325 | */ 326 | function balanceOf(address account) external view returns (uint256); 327 | 328 | /** 329 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 330 | * 331 | * Returns a boolean value indicating whether the operation succeeded. 332 | * 333 | * Emits a {Transfer} event. 334 | */ 335 | function transfer(address recipient, uint256 amount) external returns (bool); 336 | 337 | /** 338 | * @dev Returns the remaining number of tokens that `spender` will be 339 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 340 | * zero by default. 341 | * 342 | * This value changes when {approve} or {transferFrom} are called. 343 | */ 344 | function allowance(address owner, address spender) external view returns (uint256); 345 | 346 | /** 347 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 348 | * 349 | * Returns a boolean value indicating whether the operation succeeded. 350 | * 351 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 352 | * that someone may use both the old and the new allowance by unfortunate 353 | * transaction ordering. One possible solution to mitigate this race 354 | * condition is to first reduce the spender's allowance to 0 and set the 355 | * desired value afterwards: 356 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 357 | * 358 | * Emits an {Approval} event. 359 | */ 360 | function approve(address spender, uint256 amount) external returns (bool); 361 | 362 | /** 363 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 364 | * allowance mechanism. `amount` is then deducted from the caller's 365 | * allowance. 366 | * 367 | * Returns a boolean value indicating whether the operation succeeded. 368 | * 369 | * Emits a {Transfer} event. 370 | */ 371 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 372 | 373 | /** 374 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 375 | * another (`to`). 376 | * 377 | * Note that `value` may be zero. 378 | */ 379 | event Transfer(address indexed from, address indexed to, uint256 value); 380 | 381 | /** 382 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 383 | * a call to {approve}. `value` is the new allowance. 384 | */ 385 | event Approval(address indexed owner, address indexed spender, uint256 value); 386 | } 387 | 388 | 389 | // File @openzeppelin/contracts/token/ERC20/ERC20.sol@v4.0.0 390 | 391 | 392 | 393 | 394 | 395 | 396 | /** 397 | * @dev Implementation of the {IERC20} interface. 398 | * 399 | * This implementation is agnostic to the way tokens are created. This means 400 | * that a supply mechanism has to be added in a derived contract using {_mint}. 401 | * For a generic mechanism see {ERC20PresetMinterPauser}. 402 | * 403 | * TIP: For a detailed writeup see our guide 404 | * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How 405 | * to implement supply mechanisms]. 406 | * 407 | * We have followed general OpenZeppelin guidelines: functions revert instead 408 | * of returning `false` on failure. This behavior is nonetheless conventional 409 | * and does not conflict with the expectations of ERC20 applications. 410 | * 411 | * Additionally, an {Approval} event is emitted on calls to {transferFrom}. 412 | * This allows applications to reconstruct the allowance for all accounts just 413 | * by listening to said events. Other implementations of the EIP may not emit 414 | * these events, as it isn't required by the specification. 415 | * 416 | * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} 417 | * functions have been added to mitigate the well-known issues around setting 418 | * allowances. See {IERC20-approve}. 419 | */ 420 | contract ERC20 is Context, IERC20 { 421 | mapping (address => uint256) private _balances; 422 | 423 | mapping (address => mapping (address => uint256)) private _allowances; 424 | 425 | uint256 private _totalSupply; 426 | 427 | string private _name; 428 | string private _symbol; 429 | 430 | /** 431 | * @dev Sets the values for {name} and {symbol}. 432 | * 433 | * The defaut value of {decimals} is 18. To select a different value for 434 | * {decimals} you should overload it. 435 | * 436 | * All three of these values are immutable: they can only be set once during 437 | * construction. 438 | */ 439 | constructor (string memory name_, string memory symbol_) { 440 | _name = name_; 441 | _symbol = symbol_; 442 | } 443 | 444 | /** 445 | * @dev Returns the name of the token. 446 | */ 447 | function name() public view virtual returns (string memory) { 448 | return _name; 449 | } 450 | 451 | /** 452 | * @dev Returns the symbol of the token, usually a shorter version of the 453 | * name. 454 | */ 455 | function symbol() public view virtual returns (string memory) { 456 | return _symbol; 457 | } 458 | 459 | /** 460 | * @dev Returns the number of decimals used to get its user representation. 461 | * For example, if `decimals` equals `2`, a balance of `505` tokens should 462 | * be displayed to a user as `5,05` (`505 / 10 ** 2`). 463 | * 464 | * Tokens usually opt for a value of 18, imitating the relationship between 465 | * Ether and Wei. This is the value {ERC20} uses, unless this function is 466 | * overloaded; 467 | * 468 | * NOTE: This information is only used for _display_ purposes: it in 469 | * no way affects any of the arithmetic of the contract, including 470 | * {IERC20-balanceOf} and {IERC20-transfer}. 471 | */ 472 | function decimals() public view virtual returns (uint8) { 473 | return 18; 474 | } 475 | 476 | /** 477 | * @dev See {IERC20-totalSupply}. 478 | */ 479 | function totalSupply() public view virtual override returns (uint256) { 480 | return _totalSupply; 481 | } 482 | 483 | /** 484 | * @dev See {IERC20-balanceOf}. 485 | */ 486 | function balanceOf(address account) public view virtual override returns (uint256) { 487 | return _balances[account]; 488 | } 489 | 490 | /** 491 | * @dev See {IERC20-transfer}. 492 | * 493 | * Requirements: 494 | * 495 | * - `recipient` cannot be the zero address. 496 | * - the caller must have a balance of at least `amount`. 497 | */ 498 | function transfer(address recipient, uint256 amount) public virtual override returns (bool) { 499 | _transfer(_msgSender(), recipient, amount); 500 | return true; 501 | } 502 | 503 | /** 504 | * @dev See {IERC20-allowance}. 505 | */ 506 | function allowance(address owner, address spender) public view virtual override returns (uint256) { 507 | return _allowances[owner][spender]; 508 | } 509 | 510 | /** 511 | * @dev See {IERC20-approve}. 512 | * 513 | * Requirements: 514 | * 515 | * - `spender` cannot be the zero address. 516 | */ 517 | function approve(address spender, uint256 amount) public virtual override returns (bool) { 518 | _approve(_msgSender(), spender, amount); 519 | return true; 520 | } 521 | 522 | /** 523 | * @dev See {IERC20-transferFrom}. 524 | * 525 | * Emits an {Approval} event indicating the updated allowance. This is not 526 | * required by the EIP. See the note at the beginning of {ERC20}. 527 | * 528 | * Requirements: 529 | * 530 | * - `sender` and `recipient` cannot be the zero address. 531 | * - `sender` must have a balance of at least `amount`. 532 | * - the caller must have allowance for ``sender``'s tokens of at least 533 | * `amount`. 534 | */ 535 | function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { 536 | _transfer(sender, recipient, amount); 537 | 538 | uint256 currentAllowance = _allowances[sender][_msgSender()]; 539 | require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); 540 | _approve(sender, _msgSender(), currentAllowance - amount); 541 | 542 | return true; 543 | } 544 | 545 | /** 546 | * @dev Atomically increases the allowance granted to `spender` by the caller. 547 | * 548 | * This is an alternative to {approve} that can be used as a mitigation for 549 | * problems described in {IERC20-approve}. 550 | * 551 | * Emits an {Approval} event indicating the updated allowance. 552 | * 553 | * Requirements: 554 | * 555 | * - `spender` cannot be the zero address. 556 | */ 557 | function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { 558 | _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue); 559 | return true; 560 | } 561 | 562 | /** 563 | * @dev Atomically decreases the allowance granted to `spender` by the caller. 564 | * 565 | * This is an alternative to {approve} that can be used as a mitigation for 566 | * problems described in {IERC20-approve}. 567 | * 568 | * Emits an {Approval} event indicating the updated allowance. 569 | * 570 | * Requirements: 571 | * 572 | * - `spender` cannot be the zero address. 573 | * - `spender` must have allowance for the caller of at least 574 | * `subtractedValue`. 575 | */ 576 | function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { 577 | uint256 currentAllowance = _allowances[_msgSender()][spender]; 578 | require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); 579 | _approve(_msgSender(), spender, currentAllowance - subtractedValue); 580 | 581 | return true; 582 | } 583 | 584 | /** 585 | * @dev Moves tokens `amount` from `sender` to `recipient`. 586 | * 587 | * This is internal function is equivalent to {transfer}, and can be used to 588 | * e.g. implement automatic token fees, slashing mechanisms, etc. 589 | * 590 | * Emits a {Transfer} event. 591 | * 592 | * Requirements: 593 | * 594 | * - `sender` cannot be the zero address. 595 | * - `recipient` cannot be the zero address. 596 | * - `sender` must have a balance of at least `amount`. 597 | */ 598 | function _transfer(address sender, address recipient, uint256 amount) internal virtual { 599 | require(sender != address(0), "ERC20: transfer from the zero address"); 600 | require(recipient != address(0), "ERC20: transfer to the zero address"); 601 | 602 | _beforeTokenTransfer(sender, recipient, amount); 603 | 604 | uint256 senderBalance = _balances[sender]; 605 | require(senderBalance >= amount, "ERC20: transfer amount exceeds balance"); 606 | _balances[sender] = senderBalance - amount; 607 | _balances[recipient] += amount; 608 | 609 | emit Transfer(sender, recipient, amount); 610 | } 611 | 612 | /** @dev Creates `amount` tokens and assigns them to `account`, increasing 613 | * the total supply. 614 | * 615 | * Emits a {Transfer} event with `from` set to the zero address. 616 | * 617 | * Requirements: 618 | * 619 | * - `to` cannot be the zero address. 620 | */ 621 | function _mint(address account, uint256 amount) internal virtual { 622 | require(account != address(0), "ERC20: mint to the zero address"); 623 | 624 | _beforeTokenTransfer(address(0), account, amount); 625 | 626 | _totalSupply += amount; 627 | _balances[account] += amount; 628 | emit Transfer(address(0), account, amount); 629 | } 630 | 631 | /** 632 | * @dev Destroys `amount` tokens from `account`, reducing the 633 | * total supply. 634 | * 635 | * Emits a {Transfer} event with `to` set to the zero address. 636 | * 637 | * Requirements: 638 | * 639 | * - `account` cannot be the zero address. 640 | * - `account` must have at least `amount` tokens. 641 | */ 642 | function _burn(address account, uint256 amount) internal virtual { 643 | require(account != address(0), "ERC20: burn from the zero address"); 644 | 645 | _beforeTokenTransfer(account, address(0), amount); 646 | 647 | uint256 accountBalance = _balances[account]; 648 | require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); 649 | _balances[account] = accountBalance - amount; 650 | _totalSupply -= amount; 651 | 652 | emit Transfer(account, address(0), amount); 653 | } 654 | 655 | /** 656 | * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. 657 | * 658 | * This internal function is equivalent to `approve`, and can be used to 659 | * e.g. set automatic allowances for certain subsystems, etc. 660 | * 661 | * Emits an {Approval} event. 662 | * 663 | * Requirements: 664 | * 665 | * - `owner` cannot be the zero address. 666 | * - `spender` cannot be the zero address. 667 | */ 668 | function _approve(address owner, address spender, uint256 amount) internal virtual { 669 | require(owner != address(0), "ERC20: approve from the zero address"); 670 | require(spender != address(0), "ERC20: approve to the zero address"); 671 | 672 | _allowances[owner][spender] = amount; 673 | emit Approval(owner, spender, amount); 674 | } 675 | 676 | /** 677 | * @dev Hook that is called before any transfer of tokens. This includes 678 | * minting and burning. 679 | * 680 | * Calling conditions: 681 | * 682 | * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens 683 | * will be to transferred to `to`. 684 | * - when `from` is zero, `amount` tokens will be minted for `to`. 685 | * - when `to` is zero, `amount` of ``from``'s tokens will be burned. 686 | * - `from` and `to` are never both zero. 687 | * 688 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. 689 | */ 690 | function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } 691 | } 692 | 693 | 694 | // File @openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol@v4.0.0 695 | 696 | 697 | 698 | 699 | 700 | 701 | /** 702 | * @dev Extension of {ERC20} that allows token holders to destroy both their own 703 | * tokens and those that they have an allowance for, in a way that can be 704 | * recognized off-chain (via event analysis). 705 | */ 706 | abstract contract ERC20Burnable is Context, ERC20 { 707 | /** 708 | * @dev Destroys `amount` tokens from the caller. 709 | * 710 | * See {ERC20-_burn}. 711 | */ 712 | function burn(uint256 amount) public virtual { 713 | _burn(_msgSender(), amount); 714 | } 715 | 716 | /** 717 | * @dev Destroys `amount` tokens from `account`, deducting from the caller's 718 | * allowance. 719 | * 720 | * See {ERC20-_burn} and {ERC20-allowance}. 721 | * 722 | * Requirements: 723 | * 724 | * - the caller must have allowance for ``accounts``'s tokens of at least 725 | * `amount`. 726 | */ 727 | function burnFrom(address account, uint256 amount) public virtual { 728 | uint256 currentAllowance = allowance(account, _msgSender()); 729 | require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance"); 730 | _approve(account, _msgSender(), currentAllowance - amount); 731 | _burn(account, amount); 732 | } 733 | } 734 | 735 | 736 | // File @openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol@v4.0.0 737 | 738 | 739 | 740 | 741 | 742 | /** 743 | * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in 744 | * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. 745 | * 746 | * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by 747 | * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't 748 | * need to send a transaction, and thus is not required to hold Ether at all. 749 | */ 750 | interface IERC20Permit { 751 | /** 752 | * @dev Sets `value` as the allowance of `spender` over `owner`'s tokens, 753 | * given `owner`'s signed approval. 754 | * 755 | * IMPORTANT: The same issues {IERC20-approve} has related to transaction 756 | * ordering also apply here. 757 | * 758 | * Emits an {Approval} event. 759 | * 760 | * Requirements: 761 | * 762 | * - `spender` cannot be the zero address. 763 | * - `deadline` must be a timestamp in the future. 764 | * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` 765 | * over the EIP712-formatted function arguments. 766 | * - the signature must use ``owner``'s current nonce (see {nonces}). 767 | * 768 | * For more information on the signature format, see the 769 | * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP 770 | * section]. 771 | */ 772 | function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; 773 | 774 | /** 775 | * @dev Returns the current nonce for `owner`. This value must be 776 | * included whenever a signature is generated for {permit}. 777 | * 778 | * Every successful call to {permit} increases ``owner``'s nonce by one. This 779 | * prevents a signature from being used multiple times. 780 | */ 781 | function nonces(address owner) external view returns (uint256); 782 | 783 | /** 784 | * @dev Returns the domain separator used in the encoding of the signature for `permit`, as defined by {EIP712}. 785 | */ 786 | // solhint-disable-next-line func-name-mixedcase 787 | function DOMAIN_SEPARATOR() external view returns (bytes32); 788 | } 789 | 790 | 791 | // File @openzeppelin/contracts/utils/cryptography/ECDSA.sol@v4.0.0 792 | 793 | 794 | 795 | 796 | 797 | /** 798 | * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. 799 | * 800 | * These functions can be used to verify that a message was signed by the holder 801 | * of the private keys of a given address. 802 | */ 803 | library ECDSA { 804 | /** 805 | * @dev Returns the address that signed a hashed message (`hash`) with 806 | * `signature`. This address can then be used for verification purposes. 807 | * 808 | * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: 809 | * this function rejects them by requiring the `s` value to be in the lower 810 | * half order, and the `v` value to be either 27 or 28. 811 | * 812 | * IMPORTANT: `hash` _must_ be the result of a hash operation for the 813 | * verification to be secure: it is possible to craft signatures that 814 | * recover to arbitrary addresses for non-hashed data. A safe way to ensure 815 | * this is by receiving a hash of the original message (which may otherwise 816 | * be too long), and then calling {toEthSignedMessageHash} on it. 817 | */ 818 | function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { 819 | // Check the signature length 820 | if (signature.length != 65) { 821 | revert("ECDSA: invalid signature length"); 822 | } 823 | 824 | // Divide the signature in r, s and v variables 825 | bytes32 r; 826 | bytes32 s; 827 | uint8 v; 828 | 829 | // ecrecover takes the signature parameters, and the only way to get them 830 | // currently is to use assembly. 831 | // solhint-disable-next-line no-inline-assembly 832 | assembly { 833 | r := mload(add(signature, 0x20)) 834 | s := mload(add(signature, 0x40)) 835 | v := byte(0, mload(add(signature, 0x60))) 836 | } 837 | 838 | return recover(hash, v, r, s); 839 | } 840 | 841 | /** 842 | * @dev Overload of {ECDSA-recover} that receives the `v`, 843 | * `r` and `s` signature fields separately. 844 | */ 845 | function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { 846 | // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature 847 | // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines 848 | // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most 849 | // signatures from current libraries generate a unique signature with an s-value in the lower half order. 850 | // 851 | // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value 852 | // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or 853 | // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept 854 | // these malleable signatures as well. 855 | require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value"); 856 | require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value"); 857 | 858 | // If the signature is valid (and not malleable), return the signer address 859 | address signer = ecrecover(hash, v, r, s); 860 | require(signer != address(0), "ECDSA: invalid signature"); 861 | 862 | return signer; 863 | } 864 | 865 | /** 866 | * @dev Returns an Ethereum Signed Message, created from a `hash`. This 867 | * produces hash corresponding to the one signed with the 868 | * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] 869 | * JSON-RPC method as part of EIP-191. 870 | * 871 | * See {recover}. 872 | */ 873 | function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { 874 | // 32 is the length in bytes of hash, 875 | // enforced by the type signature above 876 | return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); 877 | } 878 | 879 | /** 880 | * @dev Returns an Ethereum Signed Typed Data, created from a 881 | * `domainSeparator` and a `structHash`. This produces hash corresponding 882 | * to the one signed with the 883 | * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] 884 | * JSON-RPC method as part of EIP-712. 885 | * 886 | * See {recover}. 887 | */ 888 | function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { 889 | return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); 890 | } 891 | } 892 | 893 | 894 | // File @openzeppelin/contracts/utils/cryptography/draft-EIP712.sol@v4.0.0 895 | 896 | 897 | 898 | 899 | 900 | /** 901 | * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. 902 | * 903 | * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, 904 | * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding 905 | * they need in their contracts using a combination of `abi.encode` and `keccak256`. 906 | * 907 | * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding 908 | * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA 909 | * ({_hashTypedDataV4}). 910 | * 911 | * The implementation of the domain separator was designed to be as efficient as possible while still properly updating 912 | * the chain id to protect against replay attacks on an eventual fork of the chain. 913 | * 914 | * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method 915 | * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. 916 | * 917 | * _Available since v3.4._ 918 | */ 919 | abstract contract EIP712 { 920 | /* solhint-disable var-name-mixedcase */ 921 | // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to 922 | // invalidate the cached domain separator if the chain id changes. 923 | bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; 924 | uint256 private immutable _CACHED_CHAIN_ID; 925 | 926 | bytes32 private immutable _HASHED_NAME; 927 | bytes32 private immutable _HASHED_VERSION; 928 | bytes32 private immutable _TYPE_HASH; 929 | /* solhint-enable var-name-mixedcase */ 930 | 931 | /** 932 | * @dev Initializes the domain separator and parameter caches. 933 | * 934 | * The meaning of `name` and `version` is specified in 935 | * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: 936 | * 937 | * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. 938 | * - `version`: the current major version of the signing domain. 939 | * 940 | * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart 941 | * contract upgrade]. 942 | */ 943 | constructor(string memory name, string memory version) { 944 | bytes32 hashedName = keccak256(bytes(name)); 945 | bytes32 hashedVersion = keccak256(bytes(version)); 946 | bytes32 typeHash = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); 947 | _HASHED_NAME = hashedName; 948 | _HASHED_VERSION = hashedVersion; 949 | _CACHED_CHAIN_ID = block.chainid; 950 | _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); 951 | _TYPE_HASH = typeHash; 952 | } 953 | 954 | /** 955 | * @dev Returns the domain separator for the current chain. 956 | */ 957 | function _domainSeparatorV4() internal view returns (bytes32) { 958 | if (block.chainid == _CACHED_CHAIN_ID) { 959 | return _CACHED_DOMAIN_SEPARATOR; 960 | } else { 961 | return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); 962 | } 963 | } 964 | 965 | function _buildDomainSeparator(bytes32 typeHash, bytes32 name, bytes32 version) private view returns (bytes32) { 966 | return keccak256( 967 | abi.encode( 968 | typeHash, 969 | name, 970 | version, 971 | block.chainid, 972 | address(this) 973 | ) 974 | ); 975 | } 976 | 977 | /** 978 | * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this 979 | * function returns the hash of the fully encoded EIP712 message for this domain. 980 | * 981 | * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: 982 | * 983 | * ```solidity 984 | * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( 985 | * keccak256("Mail(address to,string contents)"), 986 | * mailTo, 987 | * keccak256(bytes(mailContents)) 988 | * ))); 989 | * address signer = ECDSA.recover(digest, signature); 990 | * ``` 991 | */ 992 | function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { 993 | return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); 994 | } 995 | } 996 | 997 | 998 | // File @openzeppelin/contracts/utils/Counters.sol@v4.0.0 999 | 1000 | 1001 | 1002 | 1003 | 1004 | /** 1005 | * @title Counters 1006 | * @author Matt Condon (@shrugs) 1007 | * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number 1008 | * of elements in a mapping, issuing ERC721 ids, or counting request ids. 1009 | * 1010 | * Include with `using Counters for Counters.Counter;` 1011 | */ 1012 | library Counters { 1013 | struct Counter { 1014 | // This variable should never be directly accessed by users of the library: interactions must be restricted to 1015 | // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add 1016 | // this feature: see https://github.com/ethereum/solidity/issues/4637 1017 | uint256 _value; // default: 0 1018 | } 1019 | 1020 | function current(Counter storage counter) internal view returns (uint256) { 1021 | return counter._value; 1022 | } 1023 | 1024 | function increment(Counter storage counter) internal { 1025 | unchecked { 1026 | counter._value += 1; 1027 | } 1028 | } 1029 | 1030 | function decrement(Counter storage counter) internal { 1031 | uint256 value = counter._value; 1032 | require(value > 0, "Counter: decrement overflow"); 1033 | unchecked { 1034 | counter._value = value - 1; 1035 | } 1036 | } 1037 | } 1038 | 1039 | 1040 | // File @openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol@v4.0.0 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | /** 1051 | * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in 1052 | * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. 1053 | * 1054 | * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by 1055 | * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't 1056 | * need to send a transaction, and thus is not required to hold Ether at all. 1057 | * 1058 | * _Available since v3.4._ 1059 | */ 1060 | abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { 1061 | using Counters for Counters.Counter; 1062 | 1063 | mapping (address => Counters.Counter) private _nonces; 1064 | 1065 | // solhint-disable-next-line var-name-mixedcase 1066 | bytes32 private immutable _PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); 1067 | 1068 | /** 1069 | * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. 1070 | * 1071 | * It's a good idea to use the same `name` that is defined as the ERC20 token name. 1072 | */ 1073 | constructor(string memory name) EIP712(name, "1") { 1074 | } 1075 | 1076 | /** 1077 | * @dev See {IERC20Permit-permit}. 1078 | */ 1079 | function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual override { 1080 | // solhint-disable-next-line not-rely-on-time 1081 | require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); 1082 | 1083 | bytes32 structHash = keccak256( 1084 | abi.encode( 1085 | _PERMIT_TYPEHASH, 1086 | owner, 1087 | spender, 1088 | value, 1089 | _nonces[owner].current(), 1090 | deadline 1091 | ) 1092 | ); 1093 | 1094 | bytes32 hash = _hashTypedDataV4(structHash); 1095 | 1096 | address signer = ECDSA.recover(hash, v, r, s); 1097 | require(signer == owner, "ERC20Permit: invalid signature"); 1098 | 1099 | _nonces[owner].increment(); 1100 | _approve(owner, spender, value); 1101 | } 1102 | 1103 | /** 1104 | * @dev See {IERC20Permit-nonces}. 1105 | */ 1106 | function nonces(address owner) public view override returns (uint256) { 1107 | return _nonces[owner].current(); 1108 | } 1109 | 1110 | /** 1111 | * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. 1112 | */ 1113 | // solhint-disable-next-line func-name-mixedcase 1114 | function DOMAIN_SEPARATOR() external view override returns (bytes32) { 1115 | return _domainSeparatorV4(); 1116 | } 1117 | } 1118 | 1119 | 1120 | // File contracts/IERC677.sol 1121 | 1122 | 1123 | 1124 | 1125 | // adapted from LINK token https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca#code 1126 | // implements https://github.com/ethereum/EIPs/issues/677 1127 | interface IERC677 is IERC20 { 1128 | function transferAndCall( 1129 | address to, 1130 | uint value, 1131 | bytes calldata data 1132 | ) external returns (bool success); 1133 | 1134 | event Transfer( 1135 | address indexed from, 1136 | address indexed to, 1137 | uint value, 1138 | bytes data 1139 | ); 1140 | } 1141 | 1142 | 1143 | // File contracts/IERC677Receiver.sol 1144 | 1145 | 1146 | 1147 | 1148 | interface IERC677Receiver { 1149 | function onTokenTransfer( 1150 | address _sender, 1151 | uint256 _value, 1152 | bytes calldata _data 1153 | ) external; 1154 | } 1155 | 1156 | 1157 | // File contracts/DATAv2.sol 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | contract DATAv2 is ERC20Permit, ERC20Burnable, AccessControl, IERC677 { 1167 | string private _name = "Streamr"; 1168 | string private _symbol = "DATA"; 1169 | 1170 | event UpdatedTokenInformation(string newName, string newSymbol); 1171 | 1172 | // ------------------------------------------------------------------------ 1173 | // adapted from @openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol 1174 | bytes32 constant public MINTER_ROLE = keccak256("MINTER_ROLE"); 1175 | 1176 | constructor() ERC20("", "") ERC20Permit(_name) { 1177 | // make contract deployer the role admin that can later grant the minter role 1178 | _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); 1179 | } 1180 | 1181 | function isMinter( 1182 | address minter 1183 | ) public view returns (bool) { 1184 | return hasRole(MINTER_ROLE, minter); 1185 | } 1186 | 1187 | /** 1188 | * @dev Creates `amount` new tokens for `to`. 1189 | * 1190 | * See {ERC20-_mint}. 1191 | * 1192 | * Requirements: 1193 | * 1194 | * - the caller must have the `MINTER_ROLE`. 1195 | */ 1196 | function mint( 1197 | address to, 1198 | uint256 amount 1199 | ) public { 1200 | require(isMinter(_msgSender()), "Transaction signer is not a minter"); 1201 | _mint(to, amount); 1202 | } 1203 | 1204 | // ------------------------------------------------------------------------ 1205 | // adapted from LINK token, see https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca#code 1206 | // implements https://github.com/ethereum/EIPs/issues/677 1207 | /** 1208 | * @dev transfer token to a contract address with additional data if the recipient is a contact. 1209 | * @param _to The address to transfer to. 1210 | * @param _value The amount to be transferred. 1211 | * @param _data The extra data to be passed to the receiving contract. 1212 | */ 1213 | function transferAndCall( 1214 | address _to, 1215 | uint256 _value, 1216 | bytes calldata _data 1217 | ) public override returns (bool success) { 1218 | super.transfer(_to, _value); 1219 | emit Transfer(_msgSender(), _to, _value, _data); 1220 | 1221 | uint256 recipientCodeSize; 1222 | assembly { 1223 | recipientCodeSize := extcodesize(_to) 1224 | } 1225 | if (recipientCodeSize > 0) { 1226 | IERC677Receiver receiver = IERC677Receiver(_to); 1227 | receiver.onTokenTransfer(_msgSender(), _value, _data); 1228 | } 1229 | return true; 1230 | } 1231 | 1232 | // ------------------------------------------------------------------------ 1233 | // allow admin to change the token name and symbol 1234 | 1235 | function setTokenInformation(string calldata newName, string calldata newSymbol) public { 1236 | require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Transaction signer is not an admin"); 1237 | _name = newName; 1238 | _symbol = newSymbol; 1239 | emit UpdatedTokenInformation(_name, _symbol); 1240 | } 1241 | 1242 | /** 1243 | * @dev Returns the name of the token. 1244 | */ 1245 | function name() public view override returns (string memory) { 1246 | return _name; 1247 | } 1248 | 1249 | /** 1250 | * @dev Returns the symbol of the token, usually a shorter version of the name. 1251 | */ 1252 | function symbol() public view override returns (string memory) { 1253 | return _symbol; 1254 | } 1255 | } 1256 | -------------------------------------------------------------------------------- /archive/DataTokenMigrator_flattened-2011-07-13.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | // Sources flattened with hardhat v2.1.2 https://hardhat.org 5 | // NOTE: manually removed extraneous licence identifies and pragmas to accommodate Etherscan Verify & Publish 6 | 7 | // File @openzeppelin/contracts/token/ERC20/IERC20.sol@v4.0.0 8 | 9 | 10 | 11 | 12 | /** 13 | * @dev Interface of the ERC20 standard as defined in the EIP. 14 | */ 15 | interface IERC20 { 16 | /** 17 | * @dev Returns the amount of tokens in existence. 18 | */ 19 | function totalSupply() external view returns (uint256); 20 | 21 | /** 22 | * @dev Returns the amount of tokens owned by `account`. 23 | */ 24 | function balanceOf(address account) external view returns (uint256); 25 | 26 | /** 27 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 28 | * 29 | * Returns a boolean value indicating whether the operation succeeded. 30 | * 31 | * Emits a {Transfer} event. 32 | */ 33 | function transfer(address recipient, uint256 amount) external returns (bool); 34 | 35 | /** 36 | * @dev Returns the remaining number of tokens that `spender` will be 37 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 38 | * zero by default. 39 | * 40 | * This value changes when {approve} or {transferFrom} are called. 41 | */ 42 | function allowance(address owner, address spender) external view returns (uint256); 43 | 44 | /** 45 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 46 | * 47 | * Returns a boolean value indicating whether the operation succeeded. 48 | * 49 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 50 | * that someone may use both the old and the new allowance by unfortunate 51 | * transaction ordering. One possible solution to mitigate this race 52 | * condition is to first reduce the spender's allowance to 0 and set the 53 | * desired value afterwards: 54 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 55 | * 56 | * Emits an {Approval} event. 57 | */ 58 | function approve(address spender, uint256 amount) external returns (bool); 59 | 60 | /** 61 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 62 | * allowance mechanism. `amount` is then deducted from the caller's 63 | * allowance. 64 | * 65 | * Returns a boolean value indicating whether the operation succeeded. 66 | * 67 | * Emits a {Transfer} event. 68 | */ 69 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 70 | 71 | /** 72 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 73 | * another (`to`). 74 | * 75 | * Note that `value` may be zero. 76 | */ 77 | event Transfer(address indexed from, address indexed to, uint256 value); 78 | 79 | /** 80 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 81 | * a call to {approve}. `value` is the new allowance. 82 | */ 83 | event Approval(address indexed owner, address indexed spender, uint256 value); 84 | } 85 | 86 | 87 | // File contracts/DataTokenMigrator.sol 88 | 89 | 90 | contract DataTokenMigrator { // is UpgradeAgent, see Crowdsale.sol 91 | 92 | IERC20 public oldToken; 93 | IERC20 public newToken; 94 | 95 | constructor(IERC20 _oldToken, IERC20 _newToken) { 96 | oldToken = _oldToken; 97 | newToken = _newToken; 98 | } 99 | 100 | /** 101 | * Everything below this comment is the DATA token UpgradeAgent interface. 102 | * See https://etherscan.io/address/0x0cf0ee63788a0849fe5297f3407f701e122cc023#code 103 | */ 104 | 105 | // See totalSupply at https://etherscan.io/address/0x0cf0ee63788a0849fe5297f3407f701e122cc023#readContract 106 | uint256 public originalSupply = 987154514 ether; 107 | 108 | /** UpgradeAgent Interface marker */ 109 | function isUpgradeAgent() public pure returns (bool) { 110 | return true; 111 | } 112 | 113 | /** 114 | * @param _from token holder that called CrowdsaleToken.upgrade(_value) 115 | * @param _value amount of tokens to upgrade, checked by the CrowdsaleToken 116 | */ 117 | function upgradeFrom(address _from, uint256 _value) external { 118 | require( 119 | msg.sender == address(oldToken), 120 | "Call not permitted, UpgradableToken only" 121 | ); 122 | newToken.transfer(_from, _value); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /contracts/CrosschainERC677.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; 6 | 7 | import "./IERC677.sol"; 8 | import "./IERC677Receiver.sol"; 9 | 10 | /** 11 | * Version of DATAv2 adapted from IoTeX/iotube CrosschainERC20V2 12 | * https://iotexscan.io/address/0x1ae24d4928a86faaacd71cf414d2b3a499adb29b#code 13 | */ 14 | contract CrosschainERC677 is ERC20Burnable, IERC677 { 15 | using SafeERC20 for ERC20; 16 | 17 | event MinterSet(address indexed minter); 18 | 19 | modifier onlyMinter() { 20 | require(minter == msg.sender, "not the minter"); 21 | _; 22 | } 23 | 24 | ERC20Burnable public immutable legacyToken; 25 | ERC20 public coToken; 26 | address public minter; 27 | uint8 private decimals_; 28 | 29 | constructor( 30 | ERC20Burnable _legacyToken, 31 | ERC20 _coToken, 32 | address _minter, 33 | string memory _name, 34 | string memory _symbol, 35 | uint8 _decimals 36 | ) ERC20(_name, _symbol) { 37 | legacyToken = _legacyToken; 38 | coToken = _coToken; 39 | minter = _minter; 40 | decimals_ = _decimals; 41 | emit MinterSet(_minter); 42 | } 43 | 44 | function decimals() public view virtual override returns (uint8) { 45 | return decimals_; 46 | } 47 | 48 | function transferMintership(address _newMinter) public onlyMinter { 49 | minter = _newMinter; 50 | emit MinterSet(_newMinter); 51 | } 52 | 53 | function deposit(uint256 _amount) public { 54 | depositTo(msg.sender, _amount); 55 | } 56 | 57 | function depositTo(address _to, uint256 _amount) public { 58 | require(address(coToken) != address(0), "no co-token"); 59 | uint256 originBalance = coToken.balanceOf(address(this)); 60 | coToken.safeTransferFrom(msg.sender, address(this), _amount); 61 | uint256 newBalance = coToken.balanceOf(address(this)); 62 | require(newBalance > originBalance, "invalid balance"); 63 | _mint(_to, newBalance - originBalance); 64 | } 65 | 66 | function withdraw(uint256 _amount) public { 67 | withdrawTo(msg.sender, _amount); 68 | } 69 | 70 | function withdrawTo(address _to, uint256 _amount) public { 71 | require(address(coToken) != address(0), "no co-token"); 72 | require(_amount != 0, "amount is 0"); 73 | _burn(msg.sender, _amount); 74 | coToken.safeTransfer(_to, _amount); 75 | } 76 | 77 | function exchange(uint256 _amount) public { 78 | legacyToken.burnFrom(msg.sender, _amount); 79 | _mint(msg.sender, _amount); 80 | } 81 | 82 | function mint(address _to, uint256 _amount) public onlyMinter returns (bool) { 83 | require(_amount != 0, "amount is 0"); 84 | _mint(_to, _amount); 85 | return true; 86 | } 87 | 88 | // ------------------------------------------------------------------------ 89 | // adapted from LINK token, see https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca#code 90 | // implements https://github.com/ethereum/EIPs/issues/677 91 | /** 92 | * @dev transfer token to a contract address with additional data if the recipient is a contact. 93 | * @param _to The address to transfer to. 94 | * @param _value The amount to be transferred. 95 | * @param _data The extra data to be passed to the receiving contract. 96 | */ 97 | function transferAndCall( 98 | address _to, 99 | uint256 _value, 100 | bytes calldata _data 101 | ) public override returns (bool success) { 102 | super.transfer(_to, _value); 103 | emit Transfer(_msgSender(), _to, _value, _data); 104 | 105 | uint256 recipientCodeSize; 106 | assembly { 107 | recipientCodeSize := extcodesize(_to) 108 | } 109 | if (recipientCodeSize > 0) { 110 | IERC677Receiver receiver = IERC677Receiver(_to); 111 | receiver.onTokenTransfer(_msgSender(), _value, _data); 112 | } 113 | return true; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /contracts/CrowdsaleToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.4.0; 3 | 4 | /** 5 | * DATA token (flattened) 6 | * copied from https://etherscan.io/address/0x0cf0ee63788a0849fe5297f3407f701e122cc023#code 7 | */ 8 | 9 | /* 10 | * ERC20 interface 11 | * see https://github.com/ethereum/EIPs/issues/20 12 | */ 13 | contract ERC20 { 14 | uint public totalSupply; 15 | function balanceOf(address who) constant returns (uint); 16 | function allowance(address owner, address spender) constant returns (uint); 17 | 18 | function transfer(address to, uint value) returns (bool ok); 19 | function transferFrom(address from, address to, uint value) returns (bool ok); 20 | function approve(address spender, uint value) returns (bool ok); 21 | event Transfer(address indexed from, address indexed to, uint value); 22 | event Approval(address indexed owner, address indexed spender, uint value); 23 | } 24 | 25 | 26 | 27 | /** 28 | * Math operations with safety checks 29 | */ 30 | contract SafeMath { 31 | function safeMul(uint a, uint b) internal returns (uint) { 32 | uint c = a * b; 33 | assert(a == 0 || c / a == b); 34 | return c; 35 | } 36 | 37 | function safeDiv(uint a, uint b) internal returns (uint) { 38 | assert(b > 0); 39 | uint c = a / b; 40 | assert(a == b * c + a % b); 41 | return c; 42 | } 43 | 44 | function safeSub(uint a, uint b) internal returns (uint) { 45 | assert(b <= a); 46 | return a - b; 47 | } 48 | 49 | function safeAdd(uint a, uint b) internal returns (uint) { 50 | uint c = a + b; 51 | assert(c>=a && c>=b); 52 | return c; 53 | } 54 | 55 | function max64(uint64 a, uint64 b) internal constant returns (uint64) { 56 | return a >= b ? a : b; 57 | } 58 | 59 | function min64(uint64 a, uint64 b) internal constant returns (uint64) { 60 | return a < b ? a : b; 61 | } 62 | 63 | function max256(uint256 a, uint256 b) internal constant returns (uint256) { 64 | return a >= b ? a : b; 65 | } 66 | 67 | function min256(uint256 a, uint256 b) internal constant returns (uint256) { 68 | return a < b ? a : b; 69 | } 70 | 71 | function assert(bool assertion) internal { 72 | if (!assertion) { 73 | throw; 74 | } 75 | } 76 | } 77 | 78 | 79 | 80 | /** 81 | * Standard ERC20 token with Short Hand Attack and approve() race condition mitigation. 82 | * 83 | * Based on code by FirstBlood: 84 | * https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol 85 | */ 86 | contract StandardToken is ERC20, SafeMath { 87 | 88 | /* Token supply got increased and a new owner received these tokens */ 89 | event Minted(address receiver, uint amount); 90 | 91 | /* Actual balances of token holders */ 92 | mapping(address => uint) balances; 93 | 94 | /* approve() allowances */ 95 | mapping (address => mapping (address => uint)) allowed; 96 | 97 | /* Interface declaration */ 98 | function isToken() public constant returns (bool weAre) { 99 | return true; 100 | } 101 | 102 | function transfer(address _to, uint _value) returns (bool success) { 103 | balances[msg.sender] = safeSub(balances[msg.sender], _value); 104 | balances[_to] = safeAdd(balances[_to], _value); 105 | Transfer(msg.sender, _to, _value); 106 | return true; 107 | } 108 | 109 | function transferFrom(address _from, address _to, uint _value) returns (bool success) { 110 | uint _allowance = allowed[_from][msg.sender]; 111 | 112 | balances[_to] = safeAdd(balances[_to], _value); 113 | balances[_from] = safeSub(balances[_from], _value); 114 | allowed[_from][msg.sender] = safeSub(_allowance, _value); 115 | Transfer(_from, _to, _value); 116 | return true; 117 | } 118 | 119 | function balanceOf(address _owner) constant returns (uint balance) { 120 | return balances[_owner]; 121 | } 122 | 123 | function approve(address _spender, uint _value) returns (bool success) { 124 | 125 | // To change the approve amount you first have to reduce the addresses` 126 | // allowance to zero by calling `approve(_spender, 0)` if it is not 127 | // already 0 to mitigate the race condition described here: 128 | // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 129 | if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw; 130 | 131 | allowed[msg.sender][_spender] = _value; 132 | Approval(msg.sender, _spender, _value); 133 | return true; 134 | } 135 | 136 | function allowance(address _owner, address _spender) constant returns (uint remaining) { 137 | return allowed[_owner][_spender]; 138 | } 139 | 140 | } 141 | 142 | 143 | 144 | 145 | 146 | /** 147 | * Upgrade agent interface inspired by Lunyr. 148 | * 149 | * Upgrade agent transfers tokens to a new contract. 150 | * Upgrade agent itself can be the token contract, or just a middle man contract doing the heavy lifting. 151 | */ 152 | contract UpgradeAgent { 153 | 154 | uint public originalSupply; 155 | 156 | /** Interface marker */ 157 | function isUpgradeAgent() public constant returns (bool) { 158 | return true; 159 | } 160 | 161 | function upgradeFrom(address _from, uint256 _value) public; 162 | 163 | } 164 | 165 | 166 | /** 167 | * A token upgrade mechanism where users can opt-in amount of tokens to the next smart contract revision. 168 | * 169 | * First envisioned by Golem and Lunyr projects. 170 | */ 171 | contract UpgradeableToken is StandardToken { 172 | 173 | /** Contract / person who can set the upgrade path. This can be the same as team multisig wallet, as what it is with its default value. */ 174 | address public upgradeMaster; 175 | 176 | /** The next contract where the tokens will be migrated. */ 177 | UpgradeAgent public upgradeAgent; 178 | 179 | /** How many tokens we have upgraded by now. */ 180 | uint256 public totalUpgraded; 181 | 182 | /** 183 | * Upgrade states. 184 | * 185 | * - NotAllowed: The child contract has not reached a condition where the upgrade can bgun 186 | * - WaitingForAgent: Token allows upgrade, but we don't have a new agent yet 187 | * - ReadyToUpgrade: The agent is set, but not a single token has been upgraded yet 188 | * - Upgrading: Upgrade agent is set and the balance holders can upgrade their tokens 189 | * 190 | */ 191 | enum UpgradeState {Unknown, NotAllowed, WaitingForAgent, ReadyToUpgrade, Upgrading} 192 | 193 | /** 194 | * Somebody has upgraded some of his tokens. 195 | */ 196 | event Upgrade(address indexed _from, address indexed _to, uint256 _value); 197 | 198 | /** 199 | * New upgrade agent available. 200 | */ 201 | event UpgradeAgentSet(address agent); 202 | 203 | /** 204 | * Do not allow construction without upgrade master set. 205 | */ 206 | function UpgradeableToken(address _upgradeMaster) { 207 | upgradeMaster = _upgradeMaster; 208 | } 209 | 210 | /** 211 | * Allow the token holder to upgrade some of their tokens to a new contract. 212 | */ 213 | function upgrade(uint256 value) public { 214 | 215 | UpgradeState state = getUpgradeState(); 216 | if(!(state == UpgradeState.ReadyToUpgrade || state == UpgradeState.Upgrading)) { 217 | // Called in a bad state 218 | throw; 219 | } 220 | 221 | // Validate input value. 222 | if (value == 0) throw; 223 | 224 | balances[msg.sender] = safeSub(balances[msg.sender], value); 225 | 226 | // Take tokens out from circulation 227 | totalSupply = safeSub(totalSupply, value); 228 | totalUpgraded = safeAdd(totalUpgraded, value); 229 | 230 | // Upgrade agent reissues the tokens 231 | upgradeAgent.upgradeFrom(msg.sender, value); 232 | Upgrade(msg.sender, upgradeAgent, value); 233 | } 234 | 235 | /** 236 | * Set an upgrade agent that handles 237 | */ 238 | function setUpgradeAgent(address agent) external { 239 | 240 | if(!canUpgrade()) { 241 | // The token is not yet in a state that we could think upgrading 242 | throw; 243 | } 244 | 245 | if (agent == 0x0) throw; 246 | // Only a master can designate the next agent 247 | if (msg.sender != upgradeMaster) throw; 248 | // Upgrade has already begun for an agent 249 | if (getUpgradeState() == UpgradeState.Upgrading) throw; 250 | 251 | upgradeAgent = UpgradeAgent(agent); 252 | 253 | // Bad interface 254 | if(!upgradeAgent.isUpgradeAgent()) throw; 255 | // Make sure that token supplies match in source and target 256 | if (upgradeAgent.originalSupply() != totalSupply) throw; 257 | 258 | UpgradeAgentSet(upgradeAgent); 259 | } 260 | 261 | /** 262 | * Get the state of the token upgrade. 263 | */ 264 | function getUpgradeState() public constant returns(UpgradeState) { 265 | if(!canUpgrade()) return UpgradeState.NotAllowed; 266 | else if(address(upgradeAgent) == 0x00) return UpgradeState.WaitingForAgent; 267 | else if(totalUpgraded == 0) return UpgradeState.ReadyToUpgrade; 268 | else return UpgradeState.Upgrading; 269 | } 270 | 271 | /** 272 | * Change the upgrade master. 273 | * 274 | * This allows us to set a new owner for the upgrade mechanism. 275 | */ 276 | function setUpgradeMaster(address master) public { 277 | if (master == 0x0) throw; 278 | if (msg.sender != upgradeMaster) throw; 279 | upgradeMaster = master; 280 | } 281 | 282 | /** 283 | * Child contract can enable to provide the condition when the upgrade can begun. 284 | */ 285 | function canUpgrade() public constant returns(bool) { 286 | return true; 287 | } 288 | 289 | } 290 | 291 | 292 | 293 | 294 | /* 295 | * Ownable 296 | * 297 | * Base contract with an owner. 298 | * Provides onlyOwner modifier, which prevents function from running if it is called by anyone other than the owner. 299 | */ 300 | contract Ownable { 301 | address public owner; 302 | 303 | function Ownable() { 304 | owner = msg.sender; 305 | } 306 | 307 | modifier onlyOwner() { 308 | if (msg.sender != owner) { 309 | throw; 310 | } 311 | _; 312 | } 313 | 314 | function transferOwnership(address newOwner) onlyOwner { 315 | if (newOwner != address(0)) { 316 | owner = newOwner; 317 | } 318 | } 319 | 320 | } 321 | 322 | 323 | 324 | 325 | /** 326 | * Define interface for releasing the token transfer after a successful crowdsale. 327 | */ 328 | contract ReleasableToken is ERC20, Ownable { 329 | 330 | /* The finalizer contract that allows unlift the transfer limits on this token */ 331 | address public releaseAgent; 332 | 333 | /** A crowdsale contract can release us to the wild if ICO success. If false we are are in transfer lock up period.*/ 334 | bool public released = false; 335 | 336 | /** Map of agents that are allowed to transfer tokens regardless of the lock down period. These are crowdsale contracts and possible the team multisig itself. */ 337 | mapping (address => bool) public transferAgents; 338 | 339 | /** 340 | * Limit token transfer until the crowdsale is over. 341 | * 342 | */ 343 | modifier canTransfer(address _sender) { 344 | 345 | if(!released) { 346 | if(!transferAgents[_sender]) { 347 | throw; 348 | } 349 | } 350 | 351 | _; 352 | } 353 | 354 | /** 355 | * Set the contract that can call release and make the token transferable. 356 | * 357 | * Design choice. Allow reset the release agent to fix fat finger mistakes. 358 | */ 359 | function setReleaseAgent(address addr) onlyOwner inReleaseState(false) public { 360 | 361 | // We don't do interface check here as we might want to a normal wallet address to act as a release agent 362 | releaseAgent = addr; 363 | } 364 | 365 | /** 366 | * Owner can allow a particular address (a crowdsale contract) to transfer tokens despite the lock up period. 367 | */ 368 | function setTransferAgent(address addr, bool state) onlyOwner inReleaseState(false) public { 369 | transferAgents[addr] = state; 370 | } 371 | 372 | /** 373 | * One way function to release the tokens to the wild. 374 | * 375 | * Can be called only from the release agent that is the final ICO contract. It is only called if the crowdsale has been success (first milestone reached). 376 | */ 377 | function releaseTokenTransfer() public onlyReleaseAgent { 378 | released = true; 379 | } 380 | 381 | /** The function can be called only before or after the tokens have been releasesd */ 382 | modifier inReleaseState(bool releaseState) { 383 | if(releaseState != released) { 384 | throw; 385 | } 386 | _; 387 | } 388 | 389 | /** The function can be called only by a whitelisted release agent. */ 390 | modifier onlyReleaseAgent() { 391 | if(msg.sender != releaseAgent) { 392 | throw; 393 | } 394 | _; 395 | } 396 | 397 | function transfer(address _to, uint _value) canTransfer(msg.sender) returns (bool success) { 398 | // Call StandardToken.transfer() 399 | return super.transfer(_to, _value); 400 | } 401 | 402 | function transferFrom(address _from, address _to, uint _value) canTransfer(_from) returns (bool success) { 403 | // Call StandardToken.transferForm() 404 | return super.transferFrom(_from, _to, _value); 405 | } 406 | 407 | } 408 | 409 | 410 | 411 | 412 | 413 | /** 414 | * Safe unsigned safe math. 415 | * 416 | * https://blog.aragon.one/library-driven-development-in-solidity-2bebcaf88736#.750gwtwli 417 | * 418 | * Originally from https://raw.githubusercontent.com/AragonOne/zeppelin-solidity/master/contracts/SafeMathLib.sol 419 | * 420 | * Maintained here until merged to mainline zeppelin-solidity. 421 | * 422 | 423 | library SafeMathLib { 424 | 425 | function times(uint a, uint b) returns (uint) { 426 | uint c = a * b; 427 | assert(a == 0 || c / a == b); 428 | return c; 429 | } 430 | 431 | function minus(uint a, uint b) returns (uint) { 432 | assert(b <= a); 433 | return a - b; 434 | } 435 | 436 | function plus(uint a, uint b) returns (uint) { 437 | uint c = a + b; 438 | assert(c>=a); 439 | return c; 440 | } 441 | 442 | function assert(bool assertion) private { 443 | if (!assertion) throw; 444 | } 445 | } 446 | */ 447 | 448 | // above SafeMathLib was used in the below MintableToken. Removed it and replaced plus with safeAdd. 449 | // this avoided "library" 450 | /** 451 | * A token that can increase its supply by another contract. 452 | * 453 | * This allows uncapped crowdsale by dynamically increasing the supply when money pours in. 454 | * Only mint agents, contracts whitelisted by owner, can mint new tokens. 455 | * 456 | */ 457 | contract MintableToken is StandardToken, Ownable { 458 | 459 | bool public mintingFinished = false; 460 | 461 | /** List of agents that are allowed to create new tokens */ 462 | mapping (address => bool) public mintAgents; 463 | 464 | event MintingAgentChanged(address addr, bool state ); 465 | 466 | /** 467 | * Create new tokens and allocate them to an address.. 468 | * 469 | * Only callably by a crowdsale contract (mint agent). 470 | */ 471 | function mint(address receiver, uint amount) onlyMintAgent canMint public { 472 | totalSupply = safeAdd(totalSupply, amount); 473 | balances[receiver] = safeAdd(balances[receiver], amount); 474 | 475 | // This will make the mint transaction apper in EtherScan.io 476 | // We can remove this after there is a standardized minting event 477 | Transfer(0, receiver, amount); 478 | } 479 | 480 | /** 481 | * Owner can allow a crowdsale contract to mint new tokens. 482 | */ 483 | function setMintAgent(address addr, bool state) onlyOwner canMint public { 484 | mintAgents[addr] = state; 485 | MintingAgentChanged(addr, state); 486 | } 487 | 488 | modifier onlyMintAgent() { 489 | // Only crowdsale contracts are allowed to mint new tokens 490 | if(!mintAgents[msg.sender]) { 491 | throw; 492 | } 493 | _; 494 | } 495 | 496 | /** Make sure we are not done yet. */ 497 | modifier canMint() { 498 | if(mintingFinished) throw; 499 | _; 500 | } 501 | } 502 | 503 | 504 | 505 | /** 506 | * A crowdsaled token. 507 | * 508 | * An ERC-20 token designed specifically for crowdsales with investor protection and further development path. 509 | * 510 | * - The token transfer() is disabled until the crowdsale is over 511 | * - The token contract gives an opt-in upgrade path to a new contract 512 | * - The same token can be part of several crowdsales through approve() mechanism 513 | * - The token can be capped (supply set in the constructor) or uncapped (crowdsale contract can mint new tokens) 514 | * 515 | */ 516 | contract CrowdsaleToken is ReleasableToken, MintableToken, UpgradeableToken { 517 | 518 | /** Name and symbol were updated. */ 519 | event UpdatedTokenInformation(string newName, string newSymbol); 520 | 521 | string public name; 522 | 523 | string public symbol; 524 | 525 | uint public decimals; 526 | 527 | /** 528 | * Construct the token. 529 | * 530 | * This token must be created through a team multisig wallet, so that it is owned by that wallet. 531 | * 532 | * @param _name Token name 533 | * @param _symbol Token symbol - should be all caps 534 | * @param _initialSupply How many tokens we start with 535 | * @param _decimals Number of decimal places 536 | * @param _mintable Are new tokens created over the crowdsale or do we distribute only the initial supply? Note that when the token becomes transferable the minting always ends. 537 | */ 538 | function CrowdsaleToken(string _name, string _symbol, uint _initialSupply, uint _decimals, bool _mintable) 539 | UpgradeableToken(msg.sender) { 540 | 541 | // Create any address, can be transferred 542 | // to team multisig via changeOwner(), 543 | // also remember to call setUpgradeMaster() 544 | owner = msg.sender; 545 | 546 | name = _name; 547 | symbol = _symbol; 548 | 549 | totalSupply = _initialSupply; 550 | 551 | decimals = _decimals; 552 | 553 | // Create initially all balance on the team multisig 554 | balances[owner] = totalSupply; 555 | 556 | if(totalSupply > 0) { 557 | Minted(owner, totalSupply); 558 | } 559 | 560 | // No more new supply allowed after the token creation 561 | if(!_mintable) { 562 | mintingFinished = true; 563 | if(totalSupply == 0) { 564 | throw; // Cannot create a token without supply and no minting 565 | } 566 | } 567 | } 568 | 569 | /** 570 | * When token is released to be transferable, enforce no new tokens can be created. 571 | */ 572 | function releaseTokenTransfer() public onlyReleaseAgent { 573 | mintingFinished = true; 574 | super.releaseTokenTransfer(); 575 | } 576 | 577 | /** 578 | * Allow upgrade agent functionality kick in only if the crowdsale was success. 579 | */ 580 | function canUpgrade() public constant returns(bool) { 581 | return released && super.canUpgrade(); 582 | } 583 | 584 | /** 585 | * Owner can update token information here. 586 | * 587 | * It is often useful to conceal the actual token association, until 588 | * the token operations, like central issuance or reissuance have been completed. 589 | * 590 | * This function allows the token owner to rename the token after the operations 591 | * have been completed and then point the audience to use the token contract. 592 | */ 593 | function setTokenInformation(string _name, string _symbol) onlyOwner { 594 | name = _name; 595 | symbol = _symbol; 596 | 597 | UpdatedTokenInformation(name, symbol); 598 | } 599 | 600 | } -------------------------------------------------------------------------------- /contracts/DATAv2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | import "@openzeppelin/contracts/access/AccessControl.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol"; 7 | import "./IERC677.sol"; 8 | import "./IERC677Receiver.sol"; 9 | 10 | contract DATAv2 is ERC20Permit, ERC20Burnable, AccessControl, IERC677 { 11 | string private _name = "Streamr"; 12 | string private _symbol = "DATA"; 13 | 14 | event UpdatedTokenInformation(string newName, string newSymbol); 15 | 16 | // ------------------------------------------------------------------------ 17 | // adapted from @openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol 18 | bytes32 constant public MINTER_ROLE = keccak256("MINTER_ROLE"); 19 | 20 | constructor() ERC20("", "") ERC20Permit(_name) { 21 | // make contract deployer the role admin that can later grant the minter role 22 | _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); 23 | } 24 | 25 | function isMinter( 26 | address minter 27 | ) public view returns (bool) { 28 | return hasRole(MINTER_ROLE, minter); 29 | } 30 | 31 | /** 32 | * @dev Creates `amount` new tokens for `to`. 33 | * 34 | * See {ERC20-_mint}. 35 | * 36 | * Requirements: 37 | * 38 | * - the caller must have the `MINTER_ROLE`. 39 | */ 40 | function mint( 41 | address to, 42 | uint256 amount 43 | ) public { 44 | require(isMinter(_msgSender()), "Transaction signer is not a minter"); 45 | _mint(to, amount); 46 | } 47 | 48 | // ------------------------------------------------------------------------ 49 | // adapted from LINK token, see https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca#code 50 | // implements https://github.com/ethereum/EIPs/issues/677 51 | /** 52 | * @dev transfer token to a contract address with additional data if the recipient is a contact. 53 | * @param _to The address to transfer to. 54 | * @param _value The amount to be transferred. 55 | * @param _data The extra data to be passed to the receiving contract. 56 | */ 57 | function transferAndCall( 58 | address _to, 59 | uint256 _value, 60 | bytes calldata _data 61 | ) public override returns (bool success) { 62 | super.transfer(_to, _value); 63 | emit Transfer(_msgSender(), _to, _value, _data); 64 | 65 | uint256 recipientCodeSize; 66 | assembly { 67 | recipientCodeSize := extcodesize(_to) 68 | } 69 | if (recipientCodeSize > 0) { 70 | IERC677Receiver receiver = IERC677Receiver(_to); 71 | receiver.onTokenTransfer(_msgSender(), _value, _data); 72 | } 73 | return true; 74 | } 75 | 76 | // ------------------------------------------------------------------------ 77 | // allow admin to change the token name and symbol 78 | 79 | modifier onlyAdmin { 80 | require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Transaction signer is not an admin"); 81 | _; 82 | } 83 | 84 | function setTokenInformation(string calldata newName, string calldata newSymbol) public onlyAdmin { 85 | _name = newName; 86 | _symbol = newSymbol; 87 | emit UpdatedTokenInformation(_name, _symbol); 88 | } 89 | 90 | /** 91 | * @dev Returns the name of the token. 92 | */ 93 | function name() public view override returns (string memory) { 94 | return _name; 95 | } 96 | 97 | /** 98 | * @dev Returns the symbol of the token, usually a shorter version of the name. 99 | */ 100 | function symbol() public view override returns (string memory) { 101 | return _symbol; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /contracts/DATAv2onPolygon.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | import "./DATAv2.sol"; 5 | 6 | /** 7 | * Version of DATAv2 that fulfills the requirements in https://docs.polygon.technology/docs/develop/ethereum-polygon/pos/mapping-assets/ 8 | */ 9 | contract DATAv2onPolygon is DATAv2 { 10 | address public bridgeAddress; 11 | 12 | constructor(address initialBridgeAddress) DATAv2() { 13 | setBridgeAddress(initialBridgeAddress); 14 | } 15 | 16 | ///@dev docs say: being proxified smart contract, most probably bridge addreess is not going to change ever, but... just in case 17 | function setBridgeAddress(address newBridgeAddress) public onlyAdmin { 18 | bridgeAddress = newBridgeAddress; 19 | } 20 | 21 | /** 22 | * When tokens are bridged from mainnet, perform a "mind-and-call" to activate 23 | * the receiving contract's ERC677 onTokenTransfer callback 24 | * Equal amount of tokens got locked in RootChainManager on the mainnet side 25 | */ 26 | function deposit(address user, bytes calldata depositData) external { 27 | require(_msgSender() == bridgeAddress, "error_onlyBridge"); 28 | uint256 amount = abi.decode(depositData, (uint256)); 29 | 30 | // emits two Transfer events: 0x0 -> bridgeAddress -> user 31 | _mint(bridgeAddress, amount); 32 | transferAndCall(user, amount, depositData); 33 | } 34 | 35 | /** 36 | * When returning to mainnet, it's enough to simply burn the tokens on the Polygon side 37 | */ 38 | function withdraw(uint256 amount) external { 39 | _burn(_msgSender(), amount); 40 | } 41 | } -------------------------------------------------------------------------------- /contracts/DataTokenMigrator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | contract DataTokenMigrator { // is UpgradeAgent, see Crowdsale.sol 7 | 8 | IERC20 public oldToken; 9 | IERC20 public newToken; 10 | 11 | constructor(IERC20 _oldToken, IERC20 _newToken) { 12 | oldToken = _oldToken; 13 | newToken = _newToken; 14 | } 15 | 16 | /** 17 | * Everything below this comment is the DATA token UpgradeAgent interface. 18 | * See https://etherscan.io/address/0x0cf0ee63788a0849fe5297f3407f701e122cc023#code 19 | */ 20 | 21 | // See totalSupply at https://etherscan.io/address/0x0cf0ee63788a0849fe5297f3407f701e122cc023#readContract 22 | uint256 public originalSupply = 987154514 ether; 23 | 24 | /** UpgradeAgent Interface marker */ 25 | function isUpgradeAgent() public pure returns (bool) { 26 | return true; 27 | } 28 | 29 | /** 30 | * @param _from token holder that called CrowdsaleToken.upgrade(_value) 31 | * @param _value amount of tokens to upgrade, checked by the CrowdsaleToken 32 | */ 33 | function upgradeFrom(address _from, uint256 _value) external { 34 | require( 35 | msg.sender == address(oldToken), 36 | "Call not permitted, UpgradableToken only" 37 | ); 38 | newToken.transfer(_from, _value); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /contracts/IERC677.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | // adapted from LINK token https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca#code 7 | // implements https://github.com/ethereum/EIPs/issues/677 8 | interface IERC677 is IERC20 { 9 | function transferAndCall( 10 | address to, 11 | uint value, 12 | bytes calldata data 13 | ) external returns (bool success); 14 | 15 | event Transfer( 16 | address indexed from, 17 | address indexed to, 18 | uint value, 19 | bytes data 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /contracts/IERC677Receiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | interface IERC677Receiver { 5 | function onTokenTransfer( 6 | address _sender, 7 | uint256 _value, 8 | bytes calldata _data 9 | ) external; 10 | } 11 | -------------------------------------------------------------------------------- /contracts/test/MockRecipient.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | // import "hardhat/console.sol"; 5 | import "../IERC677Receiver.sol"; 6 | 7 | contract MockRecipient is IERC677Receiver { 8 | uint public txCount; 9 | 10 | function onTokenTransfer( 11 | address, // _sender, 12 | uint256, // _value, 13 | bytes calldata _data 14 | ) external override { 15 | // console.log("Received from", _sender); 16 | // console.log("Amount", _value); 17 | // console.log("With data", string(_data)); 18 | txCount += 1; 19 | require(keccak256(_data) != keccak256("err")); // for testing: revert if passed "err" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /contracts/test/MockRecipientNotERC677Receiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | /** Just a blank contract that doesnt implement onTokensTransferred */ 5 | contract MockRecipientNotERC677Receiver { 6 | uint public x = 1; 7 | } 8 | -------------------------------------------------------------------------------- /contracts/test/MockRecipientReturnBool.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.6; 3 | 4 | // import "hardhat/console.sol"; 5 | 6 | /** 7 | * ERC677 recipient that returns false for error instead of reverting 8 | * The return value gets ignored, and transfer proceeds succesfully. This is by design. 9 | */ 10 | contract MockRecipientReturnBool { 11 | uint public txCount; 12 | 13 | function onTokenTransfer( 14 | address, // _sender, 15 | uint256, // _value, 16 | bytes calldata _data 17 | ) external returns (bool) { 18 | // console.log("Received from", _sender); 19 | // console.log("Amount", _value); 20 | // console.log("With data", string(_data)); 21 | txCount += 1; 22 | bool retval = keccak256(_data) != keccak256("err"); // for testing: return false if passed "err" 23 | // console.log("retval", retval); 24 | return retval; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = [{ 2 | ignores: ["dist/**/*.js", "typechain/**/*.js", "index.js", "coverage/**/*"], // generated files 3 | languageOptions: { 4 | sourceType: "commonjs", 5 | }, 6 | "rules": { 7 | ...require("@eslint/js").configs.recommended.rules, 8 | "indent": [ 9 | "error", 10 | 4, 11 | { 12 | "SwitchCase": 1, 13 | "flatTernaryExpressions": true 14 | }, 15 | ], 16 | "linebreak-style": [ 17 | "error", 18 | "unix" 19 | ], 20 | "quotes": [ 21 | "error", 22 | "double" 23 | ], 24 | "semi": [ 25 | "warn", 26 | "never" 27 | ], 28 | "no-console": "error", 29 | "keyword-spacing": "error", 30 | "func-call-spacing": "error", 31 | "space-infix-ops": "error", 32 | "space-before-blocks": "error", 33 | "no-unexpected-multiline": "error" 34 | } 35 | }, { 36 | files: ["test/**/*.js"], 37 | languageOptions: { 38 | globals: { // mocha 39 | describe: "readonly", 40 | it: "readonly", 41 | before: "readonly", 42 | beforeEach: "readonly", 43 | after: "readonly", 44 | afterEach: "readonly", 45 | }, 46 | }, 47 | rules: { 48 | "no-console": "warn", 49 | }, 50 | }, { 51 | files: ["scripts/**/*.js", "hardhat.config.js"], 52 | languageOptions: { 53 | globals: { 54 | process: "readonly", 55 | console: "readonly", 56 | } 57 | }, 58 | rules: { 59 | "no-console": "off", 60 | }, 61 | }, { 62 | files: ["*.js"], 63 | rules: { 64 | "semi": "off", // autogenerated files 65 | }, 66 | }] 67 | -------------------------------------------------------------------------------- /flattened/DATAv2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma experimental ABIEncoderV2; 3 | pragma solidity 0.8.6; 4 | // Sources flattened with hardhat v2.22.4 https://hardhat.org 5 | 6 | 7 | // File @openzeppelin/contracts/utils/Context.sol@v4.0.0 8 | 9 | // Original license: SPDX_License_Identifier: MIT 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 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 | abstract contract Context { 23 | function _msgSender() internal view virtual returns (address) { 24 | return msg.sender; 25 | } 26 | 27 | function _msgData() internal view virtual returns (bytes calldata) { 28 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 29 | return msg.data; 30 | } 31 | } 32 | 33 | 34 | // File @openzeppelin/contracts/utils/introspection/IERC165.sol@v4.0.0 35 | 36 | // Original license: SPDX_License_Identifier: MIT 37 | 38 | 39 | /** 40 | * @dev Interface of the ERC165 standard, as defined in the 41 | * https://eips.ethereum.org/EIPS/eip-165[EIP]. 42 | * 43 | * Implementers can declare support of contract interfaces, which can then be 44 | * queried by others ({ERC165Checker}). 45 | * 46 | * For an implementation, see {ERC165}. 47 | */ 48 | interface IERC165 { 49 | /** 50 | * @dev Returns true if this contract implements the interface defined by 51 | * `interfaceId`. See the corresponding 52 | * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] 53 | * to learn more about how these ids are created. 54 | * 55 | * This function call must use less than 30 000 gas. 56 | */ 57 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 58 | } 59 | 60 | 61 | // File @openzeppelin/contracts/utils/introspection/ERC165.sol@v4.0.0 62 | 63 | // Original license: SPDX_License_Identifier: MIT 64 | 65 | 66 | /** 67 | * @dev Implementation of the {IERC165} interface. 68 | * 69 | * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check 70 | * for the additional interface id that will be supported. For example: 71 | * 72 | * ```solidity 73 | * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 74 | * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); 75 | * } 76 | * ``` 77 | * 78 | * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. 79 | */ 80 | abstract contract ERC165 is IERC165 { 81 | /** 82 | * @dev See {IERC165-supportsInterface}. 83 | */ 84 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 85 | return interfaceId == type(IERC165).interfaceId; 86 | } 87 | } 88 | 89 | 90 | // File @openzeppelin/contracts/access/AccessControl.sol@v4.0.0 91 | 92 | // Original license: SPDX_License_Identifier: MIT 93 | 94 | 95 | 96 | /** 97 | * @dev External interface of AccessControl declared to support ERC165 detection. 98 | */ 99 | interface IAccessControl { 100 | function hasRole(bytes32 role, address account) external view returns (bool); 101 | function getRoleAdmin(bytes32 role) external view returns (bytes32); 102 | function grantRole(bytes32 role, address account) external; 103 | function revokeRole(bytes32 role, address account) external; 104 | function renounceRole(bytes32 role, address account) external; 105 | } 106 | 107 | /** 108 | * @dev Contract module that allows children to implement role-based access 109 | * control mechanisms. This is a lightweight version that doesn't allow enumerating role 110 | * members except through off-chain means by accessing the contract event logs. Some 111 | * applications may benefit from on-chain enumerability, for those cases see 112 | * {AccessControlEnumerable}. 113 | * 114 | * Roles are referred to by their `bytes32` identifier. These should be exposed 115 | * in the external API and be unique. The best way to achieve this is by 116 | * using `public constant` hash digests: 117 | * 118 | * ``` 119 | * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); 120 | * ``` 121 | * 122 | * Roles can be used to represent a set of permissions. To restrict access to a 123 | * function call, use {hasRole}: 124 | * 125 | * ``` 126 | * function foo() public { 127 | * require(hasRole(MY_ROLE, msg.sender)); 128 | * ... 129 | * } 130 | * ``` 131 | * 132 | * Roles can be granted and revoked dynamically via the {grantRole} and 133 | * {revokeRole} functions. Each role has an associated admin role, and only 134 | * accounts that have a role's admin role can call {grantRole} and {revokeRole}. 135 | * 136 | * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means 137 | * that only accounts with this role will be able to grant or revoke other 138 | * roles. More complex role relationships can be created by using 139 | * {_setRoleAdmin}. 140 | * 141 | * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to 142 | * grant and revoke this role. Extra precautions should be taken to secure 143 | * accounts that have been granted it. 144 | */ 145 | abstract contract AccessControl is Context, IAccessControl, ERC165 { 146 | struct RoleData { 147 | mapping (address => bool) members; 148 | bytes32 adminRole; 149 | } 150 | 151 | mapping (bytes32 => RoleData) private _roles; 152 | 153 | bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; 154 | 155 | /** 156 | * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` 157 | * 158 | * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite 159 | * {RoleAdminChanged} not being emitted signaling this. 160 | * 161 | * _Available since v3.1._ 162 | */ 163 | event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); 164 | 165 | /** 166 | * @dev Emitted when `account` is granted `role`. 167 | * 168 | * `sender` is the account that originated the contract call, an admin role 169 | * bearer except when using {_setupRole}. 170 | */ 171 | event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); 172 | 173 | /** 174 | * @dev Emitted when `account` is revoked `role`. 175 | * 176 | * `sender` is the account that originated the contract call: 177 | * - if using `revokeRole`, it is the admin role bearer 178 | * - if using `renounceRole`, it is the role bearer (i.e. `account`) 179 | */ 180 | event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); 181 | 182 | /** 183 | * @dev See {IERC165-supportsInterface}. 184 | */ 185 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 186 | return interfaceId == type(IAccessControl).interfaceId 187 | || super.supportsInterface(interfaceId); 188 | } 189 | 190 | /** 191 | * @dev Returns `true` if `account` has been granted `role`. 192 | */ 193 | function hasRole(bytes32 role, address account) public view override returns (bool) { 194 | return _roles[role].members[account]; 195 | } 196 | 197 | /** 198 | * @dev Returns the admin role that controls `role`. See {grantRole} and 199 | * {revokeRole}. 200 | * 201 | * To change a role's admin, use {_setRoleAdmin}. 202 | */ 203 | function getRoleAdmin(bytes32 role) public view override returns (bytes32) { 204 | return _roles[role].adminRole; 205 | } 206 | 207 | /** 208 | * @dev Grants `role` to `account`. 209 | * 210 | * If `account` had not been already granted `role`, emits a {RoleGranted} 211 | * event. 212 | * 213 | * Requirements: 214 | * 215 | * - the caller must have ``role``'s admin role. 216 | */ 217 | function grantRole(bytes32 role, address account) public virtual override { 218 | require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to grant"); 219 | 220 | _grantRole(role, account); 221 | } 222 | 223 | /** 224 | * @dev Revokes `role` from `account`. 225 | * 226 | * If `account` had been granted `role`, emits a {RoleRevoked} event. 227 | * 228 | * Requirements: 229 | * 230 | * - the caller must have ``role``'s admin role. 231 | */ 232 | function revokeRole(bytes32 role, address account) public virtual override { 233 | require(hasRole(getRoleAdmin(role), _msgSender()), "AccessControl: sender must be an admin to revoke"); 234 | 235 | _revokeRole(role, account); 236 | } 237 | 238 | /** 239 | * @dev Revokes `role` from the calling account. 240 | * 241 | * Roles are often managed via {grantRole} and {revokeRole}: this function's 242 | * purpose is to provide a mechanism for accounts to lose their privileges 243 | * if they are compromised (such as when a trusted device is misplaced). 244 | * 245 | * If the calling account had been granted `role`, emits a {RoleRevoked} 246 | * event. 247 | * 248 | * Requirements: 249 | * 250 | * - the caller must be `account`. 251 | */ 252 | function renounceRole(bytes32 role, address account) public virtual override { 253 | require(account == _msgSender(), "AccessControl: can only renounce roles for self"); 254 | 255 | _revokeRole(role, account); 256 | } 257 | 258 | /** 259 | * @dev Grants `role` to `account`. 260 | * 261 | * If `account` had not been already granted `role`, emits a {RoleGranted} 262 | * event. Note that unlike {grantRole}, this function doesn't perform any 263 | * checks on the calling account. 264 | * 265 | * [WARNING] 266 | * ==== 267 | * This function should only be called from the constructor when setting 268 | * up the initial roles for the system. 269 | * 270 | * Using this function in any other way is effectively circumventing the admin 271 | * system imposed by {AccessControl}. 272 | * ==== 273 | */ 274 | function _setupRole(bytes32 role, address account) internal virtual { 275 | _grantRole(role, account); 276 | } 277 | 278 | /** 279 | * @dev Sets `adminRole` as ``role``'s admin role. 280 | * 281 | * Emits a {RoleAdminChanged} event. 282 | */ 283 | function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { 284 | emit RoleAdminChanged(role, getRoleAdmin(role), adminRole); 285 | _roles[role].adminRole = adminRole; 286 | } 287 | 288 | function _grantRole(bytes32 role, address account) private { 289 | if (!hasRole(role, account)) { 290 | _roles[role].members[account] = true; 291 | emit RoleGranted(role, account, _msgSender()); 292 | } 293 | } 294 | 295 | function _revokeRole(bytes32 role, address account) private { 296 | if (hasRole(role, account)) { 297 | _roles[role].members[account] = false; 298 | emit RoleRevoked(role, account, _msgSender()); 299 | } 300 | } 301 | } 302 | 303 | 304 | // File @openzeppelin/contracts/token/ERC20/IERC20.sol@v4.0.0 305 | 306 | // Original license: SPDX_License_Identifier: MIT 307 | 308 | 309 | /** 310 | * @dev Interface of the ERC20 standard as defined in the EIP. 311 | */ 312 | interface IERC20 { 313 | /** 314 | * @dev Returns the amount of tokens in existence. 315 | */ 316 | function totalSupply() external view returns (uint256); 317 | 318 | /** 319 | * @dev Returns the amount of tokens owned by `account`. 320 | */ 321 | function balanceOf(address account) external view returns (uint256); 322 | 323 | /** 324 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 325 | * 326 | * Returns a boolean value indicating whether the operation succeeded. 327 | * 328 | * Emits a {Transfer} event. 329 | */ 330 | function transfer(address recipient, uint256 amount) external returns (bool); 331 | 332 | /** 333 | * @dev Returns the remaining number of tokens that `spender` will be 334 | * allowed to spend on behalf of `owner` through {transferFrom}. This is 335 | * zero by default. 336 | * 337 | * This value changes when {approve} or {transferFrom} are called. 338 | */ 339 | function allowance(address owner, address spender) external view returns (uint256); 340 | 341 | /** 342 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 343 | * 344 | * Returns a boolean value indicating whether the operation succeeded. 345 | * 346 | * IMPORTANT: Beware that changing an allowance with this method brings the risk 347 | * that someone may use both the old and the new allowance by unfortunate 348 | * transaction ordering. One possible solution to mitigate this race 349 | * condition is to first reduce the spender's allowance to 0 and set the 350 | * desired value afterwards: 351 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 352 | * 353 | * Emits an {Approval} event. 354 | */ 355 | function approve(address spender, uint256 amount) external returns (bool); 356 | 357 | /** 358 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 359 | * allowance mechanism. `amount` is then deducted from the caller's 360 | * allowance. 361 | * 362 | * Returns a boolean value indicating whether the operation succeeded. 363 | * 364 | * Emits a {Transfer} event. 365 | */ 366 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 367 | 368 | /** 369 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 370 | * another (`to`). 371 | * 372 | * Note that `value` may be zero. 373 | */ 374 | event Transfer(address indexed from, address indexed to, uint256 value); 375 | 376 | /** 377 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 378 | * a call to {approve}. `value` is the new allowance. 379 | */ 380 | event Approval(address indexed owner, address indexed spender, uint256 value); 381 | } 382 | 383 | 384 | // File @openzeppelin/contracts/token/ERC20/ERC20.sol@v4.0.0 385 | 386 | // Original license: SPDX_License_Identifier: MIT 387 | 388 | 389 | 390 | /** 391 | * @dev Implementation of the {IERC20} interface. 392 | * 393 | * This implementation is agnostic to the way tokens are created. This means 394 | * that a supply mechanism has to be added in a derived contract using {_mint}. 395 | * For a generic mechanism see {ERC20PresetMinterPauser}. 396 | * 397 | * TIP: For a detailed writeup see our guide 398 | * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How 399 | * to implement supply mechanisms]. 400 | * 401 | * We have followed general OpenZeppelin guidelines: functions revert instead 402 | * of returning `false` on failure. This behavior is nonetheless conventional 403 | * and does not conflict with the expectations of ERC20 applications. 404 | * 405 | * Additionally, an {Approval} event is emitted on calls to {transferFrom}. 406 | * This allows applications to reconstruct the allowance for all accounts just 407 | * by listening to said events. Other implementations of the EIP may not emit 408 | * these events, as it isn't required by the specification. 409 | * 410 | * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} 411 | * functions have been added to mitigate the well-known issues around setting 412 | * allowances. See {IERC20-approve}. 413 | */ 414 | contract ERC20 is Context, IERC20 { 415 | mapping (address => uint256) private _balances; 416 | 417 | mapping (address => mapping (address => uint256)) private _allowances; 418 | 419 | uint256 private _totalSupply; 420 | 421 | string private _name; 422 | string private _symbol; 423 | 424 | /** 425 | * @dev Sets the values for {name} and {symbol}. 426 | * 427 | * The defaut value of {decimals} is 18. To select a different value for 428 | * {decimals} you should overload it. 429 | * 430 | * All three of these values are immutable: they can only be set once during 431 | * construction. 432 | */ 433 | constructor (string memory name_, string memory symbol_) { 434 | _name = name_; 435 | _symbol = symbol_; 436 | } 437 | 438 | /** 439 | * @dev Returns the name of the token. 440 | */ 441 | function name() public view virtual returns (string memory) { 442 | return _name; 443 | } 444 | 445 | /** 446 | * @dev Returns the symbol of the token, usually a shorter version of the 447 | * name. 448 | */ 449 | function symbol() public view virtual returns (string memory) { 450 | return _symbol; 451 | } 452 | 453 | /** 454 | * @dev Returns the number of decimals used to get its user representation. 455 | * For example, if `decimals` equals `2`, a balance of `505` tokens should 456 | * be displayed to a user as `5,05` (`505 / 10 ** 2`). 457 | * 458 | * Tokens usually opt for a value of 18, imitating the relationship between 459 | * Ether and Wei. This is the value {ERC20} uses, unless this function is 460 | * overloaded; 461 | * 462 | * NOTE: This information is only used for _display_ purposes: it in 463 | * no way affects any of the arithmetic of the contract, including 464 | * {IERC20-balanceOf} and {IERC20-transfer}. 465 | */ 466 | function decimals() public view virtual returns (uint8) { 467 | return 18; 468 | } 469 | 470 | /** 471 | * @dev See {IERC20-totalSupply}. 472 | */ 473 | function totalSupply() public view virtual override returns (uint256) { 474 | return _totalSupply; 475 | } 476 | 477 | /** 478 | * @dev See {IERC20-balanceOf}. 479 | */ 480 | function balanceOf(address account) public view virtual override returns (uint256) { 481 | return _balances[account]; 482 | } 483 | 484 | /** 485 | * @dev See {IERC20-transfer}. 486 | * 487 | * Requirements: 488 | * 489 | * - `recipient` cannot be the zero address. 490 | * - the caller must have a balance of at least `amount`. 491 | */ 492 | function transfer(address recipient, uint256 amount) public virtual override returns (bool) { 493 | _transfer(_msgSender(), recipient, amount); 494 | return true; 495 | } 496 | 497 | /** 498 | * @dev See {IERC20-allowance}. 499 | */ 500 | function allowance(address owner, address spender) public view virtual override returns (uint256) { 501 | return _allowances[owner][spender]; 502 | } 503 | 504 | /** 505 | * @dev See {IERC20-approve}. 506 | * 507 | * Requirements: 508 | * 509 | * - `spender` cannot be the zero address. 510 | */ 511 | function approve(address spender, uint256 amount) public virtual override returns (bool) { 512 | _approve(_msgSender(), spender, amount); 513 | return true; 514 | } 515 | 516 | /** 517 | * @dev See {IERC20-transferFrom}. 518 | * 519 | * Emits an {Approval} event indicating the updated allowance. This is not 520 | * required by the EIP. See the note at the beginning of {ERC20}. 521 | * 522 | * Requirements: 523 | * 524 | * - `sender` and `recipient` cannot be the zero address. 525 | * - `sender` must have a balance of at least `amount`. 526 | * - the caller must have allowance for ``sender``'s tokens of at least 527 | * `amount`. 528 | */ 529 | function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { 530 | _transfer(sender, recipient, amount); 531 | 532 | uint256 currentAllowance = _allowances[sender][_msgSender()]; 533 | require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance"); 534 | _approve(sender, _msgSender(), currentAllowance - amount); 535 | 536 | return true; 537 | } 538 | 539 | /** 540 | * @dev Atomically increases the allowance granted to `spender` by the caller. 541 | * 542 | * This is an alternative to {approve} that can be used as a mitigation for 543 | * problems described in {IERC20-approve}. 544 | * 545 | * Emits an {Approval} event indicating the updated allowance. 546 | * 547 | * Requirements: 548 | * 549 | * - `spender` cannot be the zero address. 550 | */ 551 | function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { 552 | _approve(_msgSender(), spender, _allowances[_msgSender()][spender] + addedValue); 553 | return true; 554 | } 555 | 556 | /** 557 | * @dev Atomically decreases the allowance granted to `spender` by the caller. 558 | * 559 | * This is an alternative to {approve} that can be used as a mitigation for 560 | * problems described in {IERC20-approve}. 561 | * 562 | * Emits an {Approval} event indicating the updated allowance. 563 | * 564 | * Requirements: 565 | * 566 | * - `spender` cannot be the zero address. 567 | * - `spender` must have allowance for the caller of at least 568 | * `subtractedValue`. 569 | */ 570 | function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { 571 | uint256 currentAllowance = _allowances[_msgSender()][spender]; 572 | require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); 573 | _approve(_msgSender(), spender, currentAllowance - subtractedValue); 574 | 575 | return true; 576 | } 577 | 578 | /** 579 | * @dev Moves tokens `amount` from `sender` to `recipient`. 580 | * 581 | * This is internal function is equivalent to {transfer}, and can be used to 582 | * e.g. implement automatic token fees, slashing mechanisms, etc. 583 | * 584 | * Emits a {Transfer} event. 585 | * 586 | * Requirements: 587 | * 588 | * - `sender` cannot be the zero address. 589 | * - `recipient` cannot be the zero address. 590 | * - `sender` must have a balance of at least `amount`. 591 | */ 592 | function _transfer(address sender, address recipient, uint256 amount) internal virtual { 593 | require(sender != address(0), "ERC20: transfer from the zero address"); 594 | require(recipient != address(0), "ERC20: transfer to the zero address"); 595 | 596 | _beforeTokenTransfer(sender, recipient, amount); 597 | 598 | uint256 senderBalance = _balances[sender]; 599 | require(senderBalance >= amount, "ERC20: transfer amount exceeds balance"); 600 | _balances[sender] = senderBalance - amount; 601 | _balances[recipient] += amount; 602 | 603 | emit Transfer(sender, recipient, amount); 604 | } 605 | 606 | /** @dev Creates `amount` tokens and assigns them to `account`, increasing 607 | * the total supply. 608 | * 609 | * Emits a {Transfer} event with `from` set to the zero address. 610 | * 611 | * Requirements: 612 | * 613 | * - `to` cannot be the zero address. 614 | */ 615 | function _mint(address account, uint256 amount) internal virtual { 616 | require(account != address(0), "ERC20: mint to the zero address"); 617 | 618 | _beforeTokenTransfer(address(0), account, amount); 619 | 620 | _totalSupply += amount; 621 | _balances[account] += amount; 622 | emit Transfer(address(0), account, amount); 623 | } 624 | 625 | /** 626 | * @dev Destroys `amount` tokens from `account`, reducing the 627 | * total supply. 628 | * 629 | * Emits a {Transfer} event with `to` set to the zero address. 630 | * 631 | * Requirements: 632 | * 633 | * - `account` cannot be the zero address. 634 | * - `account` must have at least `amount` tokens. 635 | */ 636 | function _burn(address account, uint256 amount) internal virtual { 637 | require(account != address(0), "ERC20: burn from the zero address"); 638 | 639 | _beforeTokenTransfer(account, address(0), amount); 640 | 641 | uint256 accountBalance = _balances[account]; 642 | require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); 643 | _balances[account] = accountBalance - amount; 644 | _totalSupply -= amount; 645 | 646 | emit Transfer(account, address(0), amount); 647 | } 648 | 649 | /** 650 | * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. 651 | * 652 | * This internal function is equivalent to `approve`, and can be used to 653 | * e.g. set automatic allowances for certain subsystems, etc. 654 | * 655 | * Emits an {Approval} event. 656 | * 657 | * Requirements: 658 | * 659 | * - `owner` cannot be the zero address. 660 | * - `spender` cannot be the zero address. 661 | */ 662 | function _approve(address owner, address spender, uint256 amount) internal virtual { 663 | require(owner != address(0), "ERC20: approve from the zero address"); 664 | require(spender != address(0), "ERC20: approve to the zero address"); 665 | 666 | _allowances[owner][spender] = amount; 667 | emit Approval(owner, spender, amount); 668 | } 669 | 670 | /** 671 | * @dev Hook that is called before any transfer of tokens. This includes 672 | * minting and burning. 673 | * 674 | * Calling conditions: 675 | * 676 | * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens 677 | * will be to transferred to `to`. 678 | * - when `from` is zero, `amount` tokens will be minted for `to`. 679 | * - when `to` is zero, `amount` of ``from``'s tokens will be burned. 680 | * - `from` and `to` are never both zero. 681 | * 682 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. 683 | */ 684 | function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } 685 | } 686 | 687 | 688 | // File @openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol@v4.0.0 689 | 690 | // Original license: SPDX_License_Identifier: MIT 691 | 692 | 693 | /** 694 | * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in 695 | * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. 696 | * 697 | * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by 698 | * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't 699 | * need to send a transaction, and thus is not required to hold Ether at all. 700 | */ 701 | interface IERC20Permit { 702 | /** 703 | * @dev Sets `value` as the allowance of `spender` over `owner`'s tokens, 704 | * given `owner`'s signed approval. 705 | * 706 | * IMPORTANT: The same issues {IERC20-approve} has related to transaction 707 | * ordering also apply here. 708 | * 709 | * Emits an {Approval} event. 710 | * 711 | * Requirements: 712 | * 713 | * - `spender` cannot be the zero address. 714 | * - `deadline` must be a timestamp in the future. 715 | * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` 716 | * over the EIP712-formatted function arguments. 717 | * - the signature must use ``owner``'s current nonce (see {nonces}). 718 | * 719 | * For more information on the signature format, see the 720 | * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP 721 | * section]. 722 | */ 723 | function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; 724 | 725 | /** 726 | * @dev Returns the current nonce for `owner`. This value must be 727 | * included whenever a signature is generated for {permit}. 728 | * 729 | * Every successful call to {permit} increases ``owner``'s nonce by one. This 730 | * prevents a signature from being used multiple times. 731 | */ 732 | function nonces(address owner) external view returns (uint256); 733 | 734 | /** 735 | * @dev Returns the domain separator used in the encoding of the signature for `permit`, as defined by {EIP712}. 736 | */ 737 | // solhint-disable-next-line func-name-mixedcase 738 | function DOMAIN_SEPARATOR() external view returns (bytes32); 739 | } 740 | 741 | 742 | // File @openzeppelin/contracts/utils/Counters.sol@v4.0.0 743 | 744 | // Original license: SPDX_License_Identifier: MIT 745 | 746 | 747 | /** 748 | * @title Counters 749 | * @author Matt Condon (@shrugs) 750 | * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number 751 | * of elements in a mapping, issuing ERC721 ids, or counting request ids. 752 | * 753 | * Include with `using Counters for Counters.Counter;` 754 | */ 755 | library Counters { 756 | struct Counter { 757 | // This variable should never be directly accessed by users of the library: interactions must be restricted to 758 | // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add 759 | // this feature: see https://github.com/ethereum/solidity/issues/4637 760 | uint256 _value; // default: 0 761 | } 762 | 763 | function current(Counter storage counter) internal view returns (uint256) { 764 | return counter._value; 765 | } 766 | 767 | function increment(Counter storage counter) internal { 768 | unchecked { 769 | counter._value += 1; 770 | } 771 | } 772 | 773 | function decrement(Counter storage counter) internal { 774 | uint256 value = counter._value; 775 | require(value > 0, "Counter: decrement overflow"); 776 | unchecked { 777 | counter._value = value - 1; 778 | } 779 | } 780 | } 781 | 782 | 783 | // File @openzeppelin/contracts/utils/cryptography/ECDSA.sol@v4.0.0 784 | 785 | // Original license: SPDX_License_Identifier: MIT 786 | 787 | 788 | /** 789 | * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. 790 | * 791 | * These functions can be used to verify that a message was signed by the holder 792 | * of the private keys of a given address. 793 | */ 794 | library ECDSA { 795 | /** 796 | * @dev Returns the address that signed a hashed message (`hash`) with 797 | * `signature`. This address can then be used for verification purposes. 798 | * 799 | * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: 800 | * this function rejects them by requiring the `s` value to be in the lower 801 | * half order, and the `v` value to be either 27 or 28. 802 | * 803 | * IMPORTANT: `hash` _must_ be the result of a hash operation for the 804 | * verification to be secure: it is possible to craft signatures that 805 | * recover to arbitrary addresses for non-hashed data. A safe way to ensure 806 | * this is by receiving a hash of the original message (which may otherwise 807 | * be too long), and then calling {toEthSignedMessageHash} on it. 808 | */ 809 | function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { 810 | // Check the signature length 811 | if (signature.length != 65) { 812 | revert("ECDSA: invalid signature length"); 813 | } 814 | 815 | // Divide the signature in r, s and v variables 816 | bytes32 r; 817 | bytes32 s; 818 | uint8 v; 819 | 820 | // ecrecover takes the signature parameters, and the only way to get them 821 | // currently is to use assembly. 822 | // solhint-disable-next-line no-inline-assembly 823 | assembly { 824 | r := mload(add(signature, 0x20)) 825 | s := mload(add(signature, 0x40)) 826 | v := byte(0, mload(add(signature, 0x60))) 827 | } 828 | 829 | return recover(hash, v, r, s); 830 | } 831 | 832 | /** 833 | * @dev Overload of {ECDSA-recover} that receives the `v`, 834 | * `r` and `s` signature fields separately. 835 | */ 836 | function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { 837 | // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature 838 | // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines 839 | // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most 840 | // signatures from current libraries generate a unique signature with an s-value in the lower half order. 841 | // 842 | // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value 843 | // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or 844 | // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept 845 | // these malleable signatures as well. 846 | require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value"); 847 | require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value"); 848 | 849 | // If the signature is valid (and not malleable), return the signer address 850 | address signer = ecrecover(hash, v, r, s); 851 | require(signer != address(0), "ECDSA: invalid signature"); 852 | 853 | return signer; 854 | } 855 | 856 | /** 857 | * @dev Returns an Ethereum Signed Message, created from a `hash`. This 858 | * produces hash corresponding to the one signed with the 859 | * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] 860 | * JSON-RPC method as part of EIP-191. 861 | * 862 | * See {recover}. 863 | */ 864 | function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { 865 | // 32 is the length in bytes of hash, 866 | // enforced by the type signature above 867 | return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); 868 | } 869 | 870 | /** 871 | * @dev Returns an Ethereum Signed Typed Data, created from a 872 | * `domainSeparator` and a `structHash`. This produces hash corresponding 873 | * to the one signed with the 874 | * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] 875 | * JSON-RPC method as part of EIP-712. 876 | * 877 | * See {recover}. 878 | */ 879 | function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { 880 | return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); 881 | } 882 | } 883 | 884 | 885 | // File @openzeppelin/contracts/utils/cryptography/draft-EIP712.sol@v4.0.0 886 | 887 | // Original license: SPDX_License_Identifier: MIT 888 | 889 | 890 | /** 891 | * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. 892 | * 893 | * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, 894 | * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding 895 | * they need in their contracts using a combination of `abi.encode` and `keccak256`. 896 | * 897 | * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding 898 | * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA 899 | * ({_hashTypedDataV4}). 900 | * 901 | * The implementation of the domain separator was designed to be as efficient as possible while still properly updating 902 | * the chain id to protect against replay attacks on an eventual fork of the chain. 903 | * 904 | * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method 905 | * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. 906 | * 907 | * _Available since v3.4._ 908 | */ 909 | abstract contract EIP712 { 910 | /* solhint-disable var-name-mixedcase */ 911 | // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to 912 | // invalidate the cached domain separator if the chain id changes. 913 | bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; 914 | uint256 private immutable _CACHED_CHAIN_ID; 915 | 916 | bytes32 private immutable _HASHED_NAME; 917 | bytes32 private immutable _HASHED_VERSION; 918 | bytes32 private immutable _TYPE_HASH; 919 | /* solhint-enable var-name-mixedcase */ 920 | 921 | /** 922 | * @dev Initializes the domain separator and parameter caches. 923 | * 924 | * The meaning of `name` and `version` is specified in 925 | * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: 926 | * 927 | * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. 928 | * - `version`: the current major version of the signing domain. 929 | * 930 | * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart 931 | * contract upgrade]. 932 | */ 933 | constructor(string memory name, string memory version) { 934 | bytes32 hashedName = keccak256(bytes(name)); 935 | bytes32 hashedVersion = keccak256(bytes(version)); 936 | bytes32 typeHash = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); 937 | _HASHED_NAME = hashedName; 938 | _HASHED_VERSION = hashedVersion; 939 | _CACHED_CHAIN_ID = block.chainid; 940 | _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); 941 | _TYPE_HASH = typeHash; 942 | } 943 | 944 | /** 945 | * @dev Returns the domain separator for the current chain. 946 | */ 947 | function _domainSeparatorV4() internal view returns (bytes32) { 948 | if (block.chainid == _CACHED_CHAIN_ID) { 949 | return _CACHED_DOMAIN_SEPARATOR; 950 | } else { 951 | return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); 952 | } 953 | } 954 | 955 | function _buildDomainSeparator(bytes32 typeHash, bytes32 name, bytes32 version) private view returns (bytes32) { 956 | return keccak256( 957 | abi.encode( 958 | typeHash, 959 | name, 960 | version, 961 | block.chainid, 962 | address(this) 963 | ) 964 | ); 965 | } 966 | 967 | /** 968 | * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this 969 | * function returns the hash of the fully encoded EIP712 message for this domain. 970 | * 971 | * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: 972 | * 973 | * ```solidity 974 | * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( 975 | * keccak256("Mail(address to,string contents)"), 976 | * mailTo, 977 | * keccak256(bytes(mailContents)) 978 | * ))); 979 | * address signer = ECDSA.recover(digest, signature); 980 | * ``` 981 | */ 982 | function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { 983 | return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); 984 | } 985 | } 986 | 987 | 988 | // File @openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol@v4.0.0 989 | 990 | // Original license: SPDX_License_Identifier: MIT 991 | 992 | 993 | 994 | 995 | 996 | 997 | /** 998 | * @dev Implementation of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in 999 | * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. 1000 | * 1001 | * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by 1002 | * presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't 1003 | * need to send a transaction, and thus is not required to hold Ether at all. 1004 | * 1005 | * _Available since v3.4._ 1006 | */ 1007 | abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712 { 1008 | using Counters for Counters.Counter; 1009 | 1010 | mapping (address => Counters.Counter) private _nonces; 1011 | 1012 | // solhint-disable-next-line var-name-mixedcase 1013 | bytes32 private immutable _PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); 1014 | 1015 | /** 1016 | * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. 1017 | * 1018 | * It's a good idea to use the same `name` that is defined as the ERC20 token name. 1019 | */ 1020 | constructor(string memory name) EIP712(name, "1") { 1021 | } 1022 | 1023 | /** 1024 | * @dev See {IERC20Permit-permit}. 1025 | */ 1026 | function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual override { 1027 | // solhint-disable-next-line not-rely-on-time 1028 | require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); 1029 | 1030 | bytes32 structHash = keccak256( 1031 | abi.encode( 1032 | _PERMIT_TYPEHASH, 1033 | owner, 1034 | spender, 1035 | value, 1036 | _nonces[owner].current(), 1037 | deadline 1038 | ) 1039 | ); 1040 | 1041 | bytes32 hash = _hashTypedDataV4(structHash); 1042 | 1043 | address signer = ECDSA.recover(hash, v, r, s); 1044 | require(signer == owner, "ERC20Permit: invalid signature"); 1045 | 1046 | _nonces[owner].increment(); 1047 | _approve(owner, spender, value); 1048 | } 1049 | 1050 | /** 1051 | * @dev See {IERC20Permit-nonces}. 1052 | */ 1053 | function nonces(address owner) public view override returns (uint256) { 1054 | return _nonces[owner].current(); 1055 | } 1056 | 1057 | /** 1058 | * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. 1059 | */ 1060 | // solhint-disable-next-line func-name-mixedcase 1061 | function DOMAIN_SEPARATOR() external view override returns (bytes32) { 1062 | return _domainSeparatorV4(); 1063 | } 1064 | } 1065 | 1066 | 1067 | // File @openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol@v4.0.0 1068 | 1069 | // Original license: SPDX_License_Identifier: MIT 1070 | 1071 | 1072 | 1073 | /** 1074 | * @dev Extension of {ERC20} that allows token holders to destroy both their own 1075 | * tokens and those that they have an allowance for, in a way that can be 1076 | * recognized off-chain (via event analysis). 1077 | */ 1078 | abstract contract ERC20Burnable is Context, ERC20 { 1079 | /** 1080 | * @dev Destroys `amount` tokens from the caller. 1081 | * 1082 | * See {ERC20-_burn}. 1083 | */ 1084 | function burn(uint256 amount) public virtual { 1085 | _burn(_msgSender(), amount); 1086 | } 1087 | 1088 | /** 1089 | * @dev Destroys `amount` tokens from `account`, deducting from the caller's 1090 | * allowance. 1091 | * 1092 | * See {ERC20-_burn} and {ERC20-allowance}. 1093 | * 1094 | * Requirements: 1095 | * 1096 | * - the caller must have allowance for ``accounts``'s tokens of at least 1097 | * `amount`. 1098 | */ 1099 | function burnFrom(address account, uint256 amount) public virtual { 1100 | uint256 currentAllowance = allowance(account, _msgSender()); 1101 | require(currentAllowance >= amount, "ERC20: burn amount exceeds allowance"); 1102 | _approve(account, _msgSender(), currentAllowance - amount); 1103 | _burn(account, amount); 1104 | } 1105 | } 1106 | 1107 | 1108 | // File contracts/IERC677.sol 1109 | 1110 | // Original license: SPDX_License_Identifier: MIT 1111 | 1112 | // adapted from LINK token https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca#code 1113 | // implements https://github.com/ethereum/EIPs/issues/677 1114 | interface IERC677 is IERC20 { 1115 | function transferAndCall( 1116 | address to, 1117 | uint value, 1118 | bytes calldata data 1119 | ) external returns (bool success); 1120 | 1121 | event Transfer( 1122 | address indexed from, 1123 | address indexed to, 1124 | uint value, 1125 | bytes data 1126 | ); 1127 | } 1128 | 1129 | 1130 | // File contracts/IERC677Receiver.sol 1131 | 1132 | // Original license: SPDX_License_Identifier: MIT 1133 | 1134 | interface IERC677Receiver { 1135 | function onTokenTransfer( 1136 | address _sender, 1137 | uint256 _value, 1138 | bytes calldata _data 1139 | ) external; 1140 | } 1141 | 1142 | 1143 | // File contracts/DATAv2.sol 1144 | 1145 | // Original license: SPDX_License_Identifier: MIT 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | contract DATAv2 is ERC20Permit, ERC20Burnable, AccessControl, IERC677 { 1152 | string private _name = "Streamr"; 1153 | string private _symbol = "DATA"; 1154 | 1155 | event UpdatedTokenInformation(string newName, string newSymbol); 1156 | 1157 | // ------------------------------------------------------------------------ 1158 | // adapted from @openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol 1159 | bytes32 constant public MINTER_ROLE = keccak256("MINTER_ROLE"); 1160 | 1161 | constructor() ERC20("", "") ERC20Permit(_name) { 1162 | // make contract deployer the role admin that can later grant the minter role 1163 | _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); 1164 | } 1165 | 1166 | function isMinter( 1167 | address minter 1168 | ) public view returns (bool) { 1169 | return hasRole(MINTER_ROLE, minter); 1170 | } 1171 | 1172 | /** 1173 | * @dev Creates `amount` new tokens for `to`. 1174 | * 1175 | * See {ERC20-_mint}. 1176 | * 1177 | * Requirements: 1178 | * 1179 | * - the caller must have the `MINTER_ROLE`. 1180 | */ 1181 | function mint( 1182 | address to, 1183 | uint256 amount 1184 | ) public { 1185 | require(isMinter(_msgSender()), "Transaction signer is not a minter"); 1186 | _mint(to, amount); 1187 | } 1188 | 1189 | // ------------------------------------------------------------------------ 1190 | // adapted from LINK token, see https://etherscan.io/address/0x514910771af9ca656af840dff83e8264ecf986ca#code 1191 | // implements https://github.com/ethereum/EIPs/issues/677 1192 | /** 1193 | * @dev transfer token to a contract address with additional data if the recipient is a contact. 1194 | * @param _to The address to transfer to. 1195 | * @param _value The amount to be transferred. 1196 | * @param _data The extra data to be passed to the receiving contract. 1197 | */ 1198 | function transferAndCall( 1199 | address _to, 1200 | uint256 _value, 1201 | bytes calldata _data 1202 | ) public override returns (bool success) { 1203 | super.transfer(_to, _value); 1204 | emit Transfer(_msgSender(), _to, _value, _data); 1205 | 1206 | uint256 recipientCodeSize; 1207 | assembly { 1208 | recipientCodeSize := extcodesize(_to) 1209 | } 1210 | if (recipientCodeSize > 0) { 1211 | IERC677Receiver receiver = IERC677Receiver(_to); 1212 | receiver.onTokenTransfer(_msgSender(), _value, _data); 1213 | } 1214 | return true; 1215 | } 1216 | 1217 | // ------------------------------------------------------------------------ 1218 | // allow admin to change the token name and symbol 1219 | 1220 | modifier onlyAdmin { 1221 | require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Transaction signer is not an admin"); 1222 | _; 1223 | } 1224 | 1225 | function setTokenInformation(string calldata newName, string calldata newSymbol) public onlyAdmin { 1226 | _name = newName; 1227 | _symbol = newSymbol; 1228 | emit UpdatedTokenInformation(_name, _symbol); 1229 | } 1230 | 1231 | /** 1232 | * @dev Returns the name of the token. 1233 | */ 1234 | function name() public view override returns (string memory) { 1235 | return _name; 1236 | } 1237 | 1238 | /** 1239 | * @dev Returns the symbol of the token, usually a shorter version of the name. 1240 | */ 1241 | function symbol() public view override returns (string memory) { 1242 | return _symbol; 1243 | } 1244 | } 1245 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomicfoundation/hardhat-chai-matchers") 2 | require("@nomicfoundation/hardhat-ethers") 3 | require("@typechain/hardhat") 4 | require("hardhat-gas-reporter") 5 | require("solidity-coverage") 6 | require("@nomicfoundation/hardhat-verify") 7 | 8 | // You need to export an object to set up your config 9 | // Go to https://hardhat.org/config/ to learn more 10 | 11 | /** 12 | * @type import('hardhat/config').HardhatUserConfig 13 | */ 14 | module.exports = { 15 | solidity: { 16 | compilers: [ 17 | { 18 | version: "0.8.6", 19 | settings: { 20 | optimizer: { 21 | enabled: true, 22 | runs: 1000, 23 | }, 24 | }, 25 | }, 26 | { 27 | version: "0.4.11" 28 | } 29 | ] 30 | }, 31 | networks: { 32 | hardhat: {}, 33 | polygon: { 34 | chainId: 137, 35 | url: "https://polygon-rpc.com", 36 | // gasPrice: 80000000000, 37 | }, 38 | polygonAmoy: { 39 | chainId: 80002, 40 | url: process.env.ETHEREUM_RPC || "https://rpc-amoy.polygon.technology", 41 | }, 42 | ethereum: { 43 | chainId: 1, 44 | url: "https://mainnet.infura.io/v3/" + process.env.INFURA_KEY || "", 45 | }, 46 | peaq: { 47 | chainId: 3338, 48 | url: "https://peaq.api.onfinality.io/public", 49 | }, 50 | iotex: { 51 | url: "https://babel-api.mainnet.IoTeX.io", 52 | chainId: 4689, 53 | gas: 8500000, 54 | gasPrice: 1000000000000 55 | }, 56 | iotexTestnet: { 57 | url: "https://babel-api.testnet.IoTeX.io", 58 | chainId: 4690, 59 | gas: 8500000, 60 | gasPrice: 1000000000000 61 | }, 62 | }, 63 | typechain: { 64 | outDir: "typechain", 65 | target: "ethers-v6" 66 | }, 67 | etherscan: { 68 | apiKey: { 69 | polygon: process.env.VERIFY_API_KEY || "", 70 | polygonAmoy: process.env.VERIFY_API_KEY || "", 71 | peaq: process.env.VERIFY_API_KEY || "", 72 | iotexTestnet: "no key needed!", 73 | iotex: "no key needed!", 74 | }, 75 | customChains: [{ 76 | network: "polygonAmoy", 77 | chainId: 80002, 78 | urls: { 79 | apiURL: "https://api-amoy.polygonscan.com/api", 80 | browserURL: "https://amoy.polygonscan.com" 81 | }, 82 | }, { 83 | network: "peaq", 84 | chainId: 3338, 85 | urls: { 86 | apiURL: "https://peaq-testnet.api.subscan.io", 87 | browserURL: "https://peaq.subscan.io/" 88 | }, 89 | }, { 90 | network: "iotex", 91 | chainId: 4689, 92 | urls: { 93 | apiURL: "https://iotexscout.io/api", 94 | browserURL: "https://iotexscan.io" 95 | }, 96 | }, { 97 | network: "iotexTestnet", 98 | chainId: 4690, 99 | urls: { 100 | apiURL: "https://testnet.iotexscout.io/api", 101 | browserURL: "https://testnet.iotexscan.io" 102 | }, 103 | }] 104 | }, 105 | } 106 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | import { Provider, Signer } from "ethers"; 2 | import type { DATAv2 } from "./typechain/contracts/DATAv2"; 3 | export declare const abi: ({ 4 | inputs: never[]; 5 | stateMutability: string; 6 | type: string; 7 | anonymous?: undefined; 8 | name?: undefined; 9 | outputs?: undefined; 10 | } | { 11 | anonymous: boolean; 12 | inputs: { 13 | indexed: boolean; 14 | internalType: string; 15 | name: string; 16 | type: string; 17 | }[]; 18 | name: string; 19 | type: string; 20 | stateMutability?: undefined; 21 | outputs?: undefined; 22 | } | { 23 | inputs: { 24 | internalType: string; 25 | name: string; 26 | type: string; 27 | }[]; 28 | name: string; 29 | outputs: { 30 | internalType: string; 31 | name: string; 32 | type: string; 33 | }[]; 34 | stateMutability: string; 35 | type: string; 36 | anonymous?: undefined; 37 | })[], bytecode: string; 38 | export type { DATAv2 }; 39 | export declare function getTokenAt(address: string, signerOrProvider: Provider | Signer): DATAv2; 40 | export declare function deployToken(signer: Signer): Promise; 41 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { 14 | Object.defineProperty(o, "default", { enumerable: true, value: v }); 15 | }) : function(o, v) { 16 | o["default"] = v; 17 | }); 18 | var __importStar = (this && this.__importStar) || function (mod) { 19 | if (mod && mod.__esModule) return mod; 20 | var result = {}; 21 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); 22 | __setModuleDefault(result, mod); 23 | return result; 24 | }; 25 | Object.defineProperty(exports, "__esModule", { value: true }); 26 | exports.deployToken = exports.getTokenAt = exports.bytecode = exports.abi = void 0; 27 | const ethers_1 = require("ethers"); 28 | const dataV2json = __importStar(require("./artifacts/contracts/DATAv2.sol/DATAv2.json")); 29 | exports.abi = dataV2json.abi, exports.bytecode = dataV2json.bytecode; 30 | function getTokenAt(address, signerOrProvider) { 31 | return new ethers_1.Contract(address, exports.abi, signerOrProvider); 32 | } 33 | exports.getTokenAt = getTokenAt; 34 | async function deployToken(signer) { 35 | const factory = new ethers_1.ContractFactory(exports.abi, exports.bytecode, signer); 36 | const contract = await factory.deploy(); 37 | await contract.waitForDeployment(); 38 | return contract; 39 | } 40 | exports.deployToken = deployToken; 41 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Contract, 3 | ContractFactory, 4 | Provider, 5 | Signer, 6 | } from "ethers" 7 | 8 | import * as dataV2json from "./artifacts/contracts/DATAv2.sol/DATAv2.json" 9 | import type { DATAv2 } from "./typechain/contracts/DATAv2" 10 | 11 | export const { abi, bytecode } = dataV2json 12 | export type { DATAv2 } 13 | 14 | export function getTokenAt(address: string, signerOrProvider: Provider | Signer): DATAv2 { 15 | return new Contract(address, abi, signerOrProvider) as unknown as DATAv2 16 | } 17 | 18 | export async function deployToken(signer: Signer): Promise { 19 | const factory = new ContractFactory(abi, bytecode, signer) 20 | const contract = await factory.deploy() as unknown as DATAv2 21 | await contract.waitForDeployment() 22 | return contract 23 | } 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@streamr/data-v2", 3 | "version": "1.0.4", 4 | "description": "The second iteration of the DATA token", 5 | "files": [ 6 | "flattened/*.sol", 7 | "contracts/*.sol", 8 | "artifacts/contracts/DATAv2.sol/DATAv2.json", 9 | "typechain/contracts/DATAv2.d.ts", 10 | "index.*" 11 | ], 12 | "scripts": { 13 | "lint": "eslint", 14 | "clean": "rm -rf cache artifacts coverage typechain index.js index.d.ts", 15 | "test": "hardhat test", 16 | "flatten": "hardhat flatten contracts/DATAv2.sol > DATAv2", 17 | "build": "hardhat compile && ./scripts/flatten_datav2_sol.sh && tsc -p tsconfig.build.json", 18 | "coverage": "hardhat coverage" 19 | }, 20 | "author": "Streamr Network AG", 21 | "license": "MIT", 22 | "dependencies": { 23 | "@openzeppelin/contracts": "4.0.0", 24 | "ethers": "^6.1.0", 25 | "typescript": "^5.4.5" 26 | }, 27 | "devDependencies": { 28 | "@eslint/js": "^9.13.0", 29 | "@nomicfoundation/hardhat-toolbox": "^5.0.0", 30 | "chai": "^4.3.4", 31 | "eslint": "^9.13.0", 32 | "hardhat": "^2.22.4", 33 | "hardhat-gas-reporter": "^1.0.4", 34 | "mocha": "^8.3.2", 35 | "prettier": "^2.2.1", 36 | "prettier-plugin-solidity": "^1.4.1", 37 | "typechain": "^8.3.2" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /scripts/2024-10-24-deploy-iotex-testnet.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | if [ -z "$KEY" ]; then 5 | read -p "Enter deployer private key: " KEY 6 | export KEY="$KEY" 7 | fi 8 | 9 | export PROVIDER=https://babel-api.testnet.iotex.io 10 | export EXPLORER=https://testnet.iotexscan.io 11 | export OUTPUT_FILE=iotex-testnet-address.txt 12 | ./scripts/deploy-without-migrator.js 13 | 14 | # might take a while for the iotexscan indexers to notice the contract... 15 | until npx hardhat verify --network iotexTestnet $(cat iotex-testnet-address.txt); do 16 | echo "Retrying in 10 seconds..." 17 | sleep 10 18 | done 19 | -------------------------------------------------------------------------------- /scripts/2024-12-13-deploy-iotex.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | if declare -p KEY >/dev/null 2>&1; then 5 | echo "Using deployer private key from environment variable KEY" 6 | else 7 | read -p "Enter deployer private key: " KEY 8 | export KEY="$KEY" 9 | fi 10 | 11 | export PROVIDER=https://babel-api.mainnet.iotex.one 12 | export EXPLORER=https://iotexscan.io 13 | export OUTPUT_FILE=iotex-address.txt 14 | ./scripts/deploy-without-migrator.js 15 | 16 | # might take a while for the iotexscan indexers to notice the contract... 17 | until npx hardhat verify --network iotex $(cat iotex-address.txt); do 18 | echo "Retrying in 10 seconds..." 19 | sleep 10 20 | done 21 | -------------------------------------------------------------------------------- /scripts/2025-04-09-deploy-iotex-custom-token.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euxo pipefail 3 | 4 | if declare -p KEY >/dev/null 2>&1; then 5 | echo "Using deployer private key from environment variable KEY" 6 | else 7 | read -p "Enter deployer private key: " KEY 8 | export KEY="$KEY" 9 | fi 10 | 11 | export PROVIDER=https://babel-api.mainnet.iotex.one 12 | export EXPLORER=https://iotexscan.io 13 | export OUTPUT_FILE=iotex-address.txt 14 | export MINTER_ADDRESS=0xAe1Ba4036610cF18A2Ca6ba0f43DB957ffA21024 15 | ./scripts/deploy-iotex.js 16 | 17 | # might take a while for the iotexscan indexers to notice the contract... 18 | until npx hardhat verify --network iotex $(cat iotex-address.txt); do 19 | echo "Retrying in 10 seconds..." 20 | sleep 10 21 | done 22 | -------------------------------------------------------------------------------- /scripts/deploy-1-datav2.js: -------------------------------------------------------------------------------- 1 | const { ContractFactory, Wallet, JsonRpcProvider, getDefaultProvider } = require("ethers") 2 | 3 | const DATAv2Json = require("../artifacts/contracts/DATAv2.sol/DATAv2.json") 4 | 5 | const { KEY, ETHEREUM_RPC_URL } = process.env 6 | 7 | if (!KEY) { throw new Error("Please provide env variable KEY") } 8 | 9 | const provider = ETHEREUM_RPC_URL ? new JsonRpcProvider(ETHEREUM_RPC_URL) : getDefaultProvider() 10 | const explorerUrl = "https://etherscan.io/tx" 11 | const deployer = new Wallet(KEY, provider) 12 | console.log("Deploying contracts from %s", deployer.address) 13 | 14 | async function main() { 15 | 16 | const DATAv2 = new ContractFactory(DATAv2Json.abi, DATAv2Json.bytecode, deployer) 17 | const token = await DATAv2.deploy() 18 | console.log("Follow deployment: %s/%s", explorerUrl, token.deploymentTransaction().hash) 19 | 20 | await token.waitForDeployment() 21 | const tokenAddress = await token.getAddress() 22 | 23 | console.log("DATAv2 deployed to:", tokenAddress) 24 | } 25 | 26 | // We recommend this pattern to be able to use async/await everywhere 27 | // and properly handle errors. 28 | main() 29 | .then(() => process.exit(0)) 30 | .catch(error => { 31 | console.error(error) 32 | process.exit(1) 33 | }) 34 | -------------------------------------------------------------------------------- /scripts/deploy-2-migrator.js: -------------------------------------------------------------------------------- 1 | const { ContractFactory, Wallet, getDefaultProvider } = require("ethers") 2 | 3 | const DataTokenMigratorJson = require("../artifacts/contracts/DataTokenMigrator.sol/DataTokenMigrator.json") 4 | 5 | const { KEY } = process.env 6 | 7 | if (!KEY) { throw new Error("Please provide env variable KEY") } 8 | 9 | const provider = getDefaultProvider() 10 | const deployer = new Wallet(KEY, provider) 11 | console.log("Deploying contracts from %s", deployer.address) 12 | 13 | const oldTokenAddress = "0x0cf0ee63788a0849fe5297f3407f701e122cc023" 14 | const newTokenAddress = "0x8f693ca8D21b157107184d29D398A8D082b38b76" 15 | 16 | async function main() { 17 | 18 | const DataTokenMigrator = new ContractFactory(DataTokenMigratorJson.abi, DataTokenMigratorJson.bytecode, deployer) 19 | const migrator = await DataTokenMigrator.deploy(oldTokenAddress, newTokenAddress) 20 | console.log("Follow deployment: https://etherscan.io/tx/%s", migrator.deploymentTransaction().hash) 21 | 22 | await migrator.waitForDeployment() 23 | 24 | console.log("DataTokenMigrator deployed to:", await migrator.getAddress()) 25 | } 26 | 27 | // We recommend this pattern to be able to use async/await everywhere 28 | // and properly handle errors. 29 | main() 30 | .then(() => process.exit(0)) 31 | .catch(error => { 32 | console.error(error) 33 | process.exit(1) 34 | }) 35 | -------------------------------------------------------------------------------- /scripts/deploy-3-grant-roles.js: -------------------------------------------------------------------------------- 1 | const { Contract, Wallet, getDefaultProvider, utils: { id } } = require("ethers") 2 | 3 | const DATAv2Json = require("../artifacts/contracts/DATAv2.sol/DATAv2.json") 4 | 5 | const { KEY } = process.env 6 | 7 | if (!KEY) { throw new Error("Please provide env variable KEY") } 8 | 9 | const provider = getDefaultProvider() 10 | const deployer = new Wallet(KEY, provider) 11 | console.log("Sending tx from %s", deployer.address) 12 | 13 | const newTokenAddress = "0x8f693ca8D21b157107184d29D398A8D082b38b76" 14 | const adminAddress = "0xc726F659Fc9B3BC5286a584C8976F915BCad2401" 15 | 16 | async function main() { 17 | const token = new Contract(newTokenAddress, DATAv2Json.abi, deployer) 18 | console.log("Using %s token deployed at: %s", await token.symbol(), token.address) 19 | 20 | const tx1 = await token.grantRole(id("MINTER_ROLE"), adminAddress) 21 | console.log("Follow grant minter tx: https://etherscan.io/tx/%s", tx1.hash) 22 | const tr1 = await tx1.wait() 23 | console.log("Transaction receipt: ", tr1) 24 | 25 | const tx2 = await token.grantRole("0x0000000000000000000000000000000000000000000000000000000000000000", adminAddress) 26 | console.log("Follow grant admin tx: https://etherscan.io/tx/%s", tx2.hash) 27 | const tr2 = await tx2.wait() 28 | console.log("Transaction receipt: ", tr2) 29 | } 30 | 31 | main() 32 | .then(() => process.exit(0)) 33 | .catch(error => { 34 | console.error(error) 35 | process.exit(1) 36 | }) 37 | -------------------------------------------------------------------------------- /scripts/deploy-iotex.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const fs = require("fs") 4 | const { ContractFactory, Wallet, JsonRpcProvider, ZeroAddress } = require("ethers") 5 | 6 | const DATAv2Json = require("../artifacts/contracts/CrosschainERC677.sol/CrosschainERC677.json") 7 | 8 | const { waitForTx } = require("./waitForTx") 9 | 10 | const { 11 | KEY, 12 | PROVIDER, 13 | OUTPUT_FILE, 14 | MINTER_ADDRESS, 15 | PREVIOUS_TOKEN_ADDRESS = "0x1ae24d4928a86faaacd71cf414d2b3a499adb29b", 16 | } = process.env 17 | if (!KEY) { throw new Error("Please provide env variable KEY") } 18 | if (!PROVIDER) { throw new Error("Please provide env variable PROVIDER") } 19 | if (!MINTER_ADDRESS) { throw new Error("Please provide env variable MINTER_ADDRESS") } 20 | 21 | const { log } = console 22 | 23 | const provider = new JsonRpcProvider(PROVIDER) 24 | const deployer = new Wallet(KEY, provider) 25 | log("Deploying contracts from %s", deployer.address) 26 | 27 | async function main() { 28 | log("Connected to network at %s: %s", PROVIDER, JSON.stringify(await provider.getNetwork())) 29 | 30 | const DATAv2 = new ContractFactory(DATAv2Json.abi, DATAv2Json.bytecode, deployer) 31 | const token = await DATAv2.deploy( 32 | PREVIOUS_TOKEN_ADDRESS, 33 | ZeroAddress, 34 | MINTER_ADDRESS, 35 | "Streamr", 36 | "DATA", 37 | 18, 38 | ) 39 | waitForTx("deployment", token.deploymentTransaction()) 40 | 41 | const tokenAddress = await token.getAddress() 42 | log("DATAv2 deployed to:", tokenAddress) 43 | if (OUTPUT_FILE) { 44 | fs.writeFileSync(OUTPUT_FILE, tokenAddress) 45 | } 46 | } 47 | 48 | main() 49 | .then(() => process.exit(0)) 50 | .catch(error => { 51 | console.error(error) 52 | process.exit(1) 53 | }) 54 | -------------------------------------------------------------------------------- /scripts/deploy-without-migrator.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // PEAQ chain 4 | // const providerUrl = "https://peaq.api.onfinality.io/public" 5 | // const explorerUrl = "https://peaq.subscan.io" 6 | 7 | // Binance Smart Chain 8 | // const providerUrl = "https://bsc-dataseed.binance.org/" 9 | // const explorerUrl = "https://bscscan.com" 10 | 11 | // Matic's Polygon 12 | // const providerUrl = "https://polygon-rpc.com" 13 | // const explorerUrl = "https://polygonscan.com" 14 | 15 | // Plain token, same as mainnet 16 | const DATAv2Json = require("../artifacts/contracts/DATAv2.sol/DATAv2.json") 17 | 18 | // Polygon wants a special token for the bridge 19 | // const DATAv2Json = require("../artifacts/contracts/DATAv2onPolygon.sol/DATAv2onPolygon.json") 20 | 21 | const fs = require("fs") 22 | const { ContractFactory, Wallet, JsonRpcProvider, id } = require("ethers") 23 | 24 | const { waitForTx } = require("./waitForTx") 25 | 26 | const { 27 | KEY, 28 | PROVIDER, 29 | OUTPUT_FILE, 30 | } = process.env 31 | if (!KEY) { throw new Error("Please provide env variable KEY") } 32 | if (!PROVIDER) { throw new Error("Please provide env variable PROVIDER") } 33 | 34 | const { log } = console 35 | 36 | const provider = new JsonRpcProvider(PROVIDER) 37 | const deployer = new Wallet(KEY, provider) 38 | log("Deploying contracts from %s", deployer.address) 39 | 40 | const adminAddress = "0x42355e7dc0A872C465bE9DE4AcAAAcB5709Ce813" 41 | 42 | async function main() { 43 | log("Connected to network at %s: %s", PROVIDER, JSON.stringify(await provider.getNetwork())) 44 | 45 | const DATAv2 = new ContractFactory(DATAv2Json.abi, DATAv2Json.bytecode, deployer) 46 | const token = await DATAv2.deploy() // plain token 47 | // const token = await DATAv2.deploy("0xA6FA4fB5f76172d178d61B04b0ecd319C5d1C0aa") // Polygon version of the token 48 | waitForTx("deployment", token.deploymentTransaction()) 49 | 50 | const tokenAddress = await token.getAddress() 51 | log("DATAv2 deployed to:", tokenAddress) 52 | if (OUTPUT_FILE) { 53 | fs.writeFileSync(OUTPUT_FILE, tokenAddress) 54 | } 55 | 56 | const tx1 = await token.grantRole(id("MINTER_ROLE"), adminAddress) 57 | waitForTx("grant minter role", tx1) 58 | 59 | const tx2 = await token.grantRole("0x0000000000000000000000000000000000000000000000000000000000000000", adminAddress) 60 | waitForTx("grant admin role", tx2) 61 | } 62 | 63 | main() 64 | .then(() => process.exit(0)) 65 | .catch(error => { 66 | console.error(error) 67 | process.exit(1) 68 | }) 69 | -------------------------------------------------------------------------------- /scripts/flatten_datav2_sol.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | rm -rf flattened 4 | mkdir -p flattened 5 | npx hardhat flatten contracts/DATAv2.sol > temp.sol 6 | grep -v SPDX-License-Identifier temp.sol > temp2.sol 7 | grep -v "pragma" temp2.sol > temp.sol 8 | echo "// SPDX-License-Identifier: MIT" > temp2.sol 9 | echo "pragma experimental ABIEncoderV2;" >> temp2.sol 10 | echo "pragma solidity 0.8.6;" >> temp2.sol 11 | cat temp.sol >> temp2.sol 12 | mv temp2.sol flattened/DATAv2.sol 13 | rm temp.sol 14 | -------------------------------------------------------------------------------- /scripts/upgrade-tokens.js: -------------------------------------------------------------------------------- 1 | const { Contract, Wallet, getDefaultProvider, utils: { formatEther } } = require("ethers") 2 | 3 | const OldTokenJson = require("../artifacts/contracts/CrowdsaleToken.sol/CrowdsaleToken.json") 4 | const DATAv2Json = require("../artifacts/contracts/DATAv2.sol/DATAv2.json") 5 | 6 | const { KEY } = process.env 7 | 8 | if (!KEY) { throw new Error("Please provide env variable KEY") } 9 | 10 | const provider = getDefaultProvider() 11 | const hodler = new Wallet(KEY, provider) 12 | console.log("Sending tx from %s", hodler.address) 13 | 14 | const oldTokenAddress = "0x0cf0ee63788a0849fe5297f3407f701e122cc023" 15 | const newTokenAddress = "0x8f693ca8D21b157107184d29D398A8D082b38b76" 16 | 17 | async function main() { 18 | const oldToken = new Contract(oldTokenAddress, OldTokenJson.abi, hodler) 19 | console.log("Using old %s token deployed at: %s", await oldToken.symbol(), oldToken.address) 20 | 21 | const newToken = new Contract(newTokenAddress, DATAv2Json.abi, hodler) 22 | console.log("Using new %s token deployed at: %s", await newToken.symbol(), newToken.address) 23 | 24 | const oldTokenBalance = await oldToken.balanceOf(hodler.address) 25 | console.log("Old token balance: %s", formatEther(oldTokenBalance)) 26 | console.log("Old token totalSupply: %s", formatEther(await oldToken.totalSupply())) 27 | console.log("New token balance: %s", formatEther(await newToken.balanceOf(hodler.address))) 28 | 29 | const tx = await oldToken.upgrade(oldTokenBalance) 30 | console.log("Follow upgrade tx: https://etherscan.io/tx/%s", tx.hash) 31 | const tr = await tx.wait() 32 | console.log("Transaction receipt: ", tr) 33 | 34 | console.log("Old token balance: %s", formatEther(await oldToken.balanceOf(hodler.address))) 35 | console.log("Old token totalSupply: %s", formatEther(await oldToken.totalSupply())) 36 | console.log("New token balance: %s", formatEther(await newToken.balanceOf(hodler.address))) 37 | } 38 | 39 | // We recommend this pattern to be able to use async/await everywhere 40 | // and properly handle errors. 41 | main() 42 | .then(() => process.exit(0)) 43 | .catch(error => { 44 | console.error(error) 45 | process.exit(1) 46 | }) 47 | -------------------------------------------------------------------------------- /scripts/waitForTx.js: -------------------------------------------------------------------------------- 1 | const { 2 | EXPLORER, 3 | } = process.env 4 | 5 | const { log } = console 6 | 7 | /** 8 | * Print pretty logging for transactions 9 | * @param {string} txDescription 10 | * @param {import("ethers").ContractTransaction} tx 11 | */ 12 | 13 | async function waitForTx(txDescription, tx) { 14 | if (EXPLORER) { 15 | log("Follow %s: %s/tx/%s", txDescription, EXPLORER, tx.hash) 16 | } else { 17 | log("Waiting for %s, hash: %s", txDescription, tx.hash) 18 | } 19 | const tr = await tx.wait() 20 | log("Transaction receipt: ", tr) 21 | } 22 | exports.waitForTx = waitForTx 23 | -------------------------------------------------------------------------------- /test/CrosschainERC677-test.js: -------------------------------------------------------------------------------- 1 | const { parseEther, id, ZeroAddress } = require("ethers") 2 | const { expect } = require("chai") 3 | const { ethers } = require("hardhat") 4 | 5 | // "err" as bytes, induces a simulated error in MockRecipient.sol and MockRecipientReturnBool.sol 6 | const errData = "0x657272" 7 | 8 | describe("CrosschainERC677", () => { 9 | it("transferAndCall triggers ERC677 callback", async () => { 10 | const [signer, minter] = await ethers.getSigners() 11 | 12 | const MockRecipient = await ethers.getContractFactory("MockRecipient") 13 | const recipient = await MockRecipient.deploy() 14 | await recipient.waitForDeployment() 15 | const recipientAddress = await recipient.getAddress() 16 | 17 | const MockRecipientNotERC677Receiver = await ethers.getContractFactory("MockRecipientNotERC677Receiver") 18 | const nonReceiverRecipient = await MockRecipientNotERC677Receiver.deploy() 19 | await nonReceiverRecipient.waitForDeployment() 20 | const nonReceiverRecipientAddress = await nonReceiverRecipient.getAddress() 21 | 22 | const MockRecipientReturnBool = await ethers.getContractFactory("MockRecipientReturnBool") 23 | const returnBoolRecipient = await MockRecipientReturnBool.deploy() 24 | await returnBoolRecipient.waitForDeployment() 25 | const returnBoolRecipientAddress = await returnBoolRecipient.getAddress() 26 | 27 | // (ERC20 _coToken, address _minter, string memory _name, string memory _symbol, uint8 _decimals) 28 | const CrosschainERC677 = await ethers.getContractFactory("CrosschainERC677") 29 | const token = await CrosschainERC677.deploy(ZeroAddress, ZeroAddress, minter.address, "TestToken", "TEST", 18) 30 | await token.waitForDeployment() 31 | 32 | await expect(token.connect(minter).mint(signer.address, parseEther("10"))).to.emit(token, "Transfer(address,address,uint256)") 33 | 34 | // revert in callback => should revert transferAndCall 35 | await expect(token.transferAndCall(recipientAddress, parseEther("1"), errData)).to.be.reverted 36 | 37 | // no callback => should revert transferAndCall 38 | await expect(token.transferAndCall(nonReceiverRecipientAddress, parseEther("1"), "0x")).to.be.reverted 39 | 40 | // contract that implements ERC677Receiver executes the callback 41 | const txsBefore = await recipient.txCount() 42 | await token.transferAndCall(recipientAddress, parseEther("1"), "0x6c6f6c") 43 | const txsAfter = await recipient.txCount() 44 | 45 | // callback returns true or false but doesn't revert => should NOT revert 46 | const txsBeforeBool = await returnBoolRecipient.txCount() 47 | await token.transferAndCall(returnBoolRecipientAddress, parseEther("1"), errData) 48 | await token.transferAndCall(returnBoolRecipientAddress, parseEther("1"), "0x") 49 | const txsAfterBool = await returnBoolRecipient.txCount() 50 | 51 | expect(txsAfter).to.equal(txsBefore + 1n) 52 | expect(txsAfterBool).to.equal(txsBeforeBool + 2n) 53 | }) 54 | 55 | it("transferAndCall just does normal transfer for non-contract accounts", async () => { 56 | const [signer, minter] = await ethers.getSigners() 57 | const targetAddress = "0x0000000000000000000000000000000000000001" 58 | 59 | const DATAv2 = await ethers.getContractFactory("DATAv2") 60 | const token = await DATAv2.deploy() 61 | await token.waitForDeployment() 62 | 63 | await expect(token.grantRole(id("MINTER_ROLE"), minter.address)).to.emit(token, "RoleGranted") 64 | await expect(token.connect(minter).mint(signer.address, parseEther("1"))).to.emit(token, "Transfer(address,address,uint256)") 65 | 66 | const balanceBefore = await token.balanceOf(targetAddress) 67 | await token.transferAndCall(targetAddress, parseEther("1"), "0x6c6f6c") 68 | const balanceAfter = await token.balanceOf(targetAddress) 69 | 70 | expect(balanceAfter - balanceBefore).to.equal(parseEther("1")) 71 | }) 72 | }) 73 | -------------------------------------------------------------------------------- /test/DATAv2-test.js: -------------------------------------------------------------------------------- 1 | const { parseEther, id } = require("ethers") 2 | const { expect } = require("chai") 3 | const { ethers } = require("hardhat") 4 | 5 | // "err" as bytes, induces a simulated error in MockRecipient.sol and MockRecipientReturnBool.sol 6 | const errData = "0x657272" 7 | 8 | describe("DATAv2", () => { 9 | it("transferAndCall triggers ERC677 callback", async () => { 10 | const [signer, minter] = await ethers.getSigners() 11 | 12 | const MockRecipient = await ethers.getContractFactory("MockRecipient") 13 | const recipient = await MockRecipient.deploy() 14 | await recipient.waitForDeployment() 15 | const recipientAddress = await recipient.getAddress() 16 | 17 | const MockRecipientNotERC677Receiver = await ethers.getContractFactory("MockRecipientNotERC677Receiver") 18 | const nonReceiverRecipient = await MockRecipientNotERC677Receiver.deploy() 19 | await nonReceiverRecipient.waitForDeployment() 20 | const nonReceiverRecipientAddress = await nonReceiverRecipient.getAddress() 21 | 22 | const MockRecipientReturnBool = await ethers.getContractFactory("MockRecipientReturnBool") 23 | const returnBoolRecipient = await MockRecipientReturnBool.deploy() 24 | await returnBoolRecipient.waitForDeployment() 25 | const returnBoolRecipientAddress = await returnBoolRecipient.getAddress() 26 | 27 | const DATAv2 = await ethers.getContractFactory("DATAv2") 28 | const token = await DATAv2.deploy() 29 | await token.waitForDeployment() 30 | 31 | await expect(token.grantRole(id("MINTER_ROLE"), minter.address)).to.emit(token, "RoleGranted") 32 | await expect(token.connect(minter).mint(signer.address, parseEther("10"))).to.emit(token, "Transfer(address,address,uint256)") 33 | 34 | // revert in callback => should revert transferAndCall 35 | await expect(token.transferAndCall(recipientAddress, parseEther("1"), errData)).to.be.reverted 36 | 37 | // no callback => should revert transferAndCall 38 | await expect(token.transferAndCall(nonReceiverRecipientAddress, parseEther("1"), "0x")).to.be.reverted 39 | 40 | // contract that implements ERC677Receiver executes the callback 41 | const txsBefore = await recipient.txCount() 42 | await token.transferAndCall(recipientAddress, parseEther("1"), "0x6c6f6c") 43 | const txsAfter = await recipient.txCount() 44 | 45 | // callback returns true or false but doesn't revert => should NOT revert 46 | const txsBeforeBool = await returnBoolRecipient.txCount() 47 | await token.transferAndCall(returnBoolRecipientAddress, parseEther("1"), errData) 48 | await token.transferAndCall(returnBoolRecipientAddress, parseEther("1"), "0x") 49 | const txsAfterBool = await returnBoolRecipient.txCount() 50 | 51 | expect(txsAfter).to.equal(txsBefore + 1n) 52 | expect(txsAfterBool).to.equal(txsBeforeBool + 2n) 53 | }) 54 | 55 | it("transferAndCall just does normal transfer for non-contract accounts", async () => { 56 | const [signer, minter] = await ethers.getSigners() 57 | const targetAddress = "0x0000000000000000000000000000000000000001" 58 | 59 | const DATAv2 = await ethers.getContractFactory("DATAv2") 60 | const token = await DATAv2.deploy() 61 | await token.waitForDeployment() 62 | 63 | await expect(token.grantRole(id("MINTER_ROLE"), minter.address)).to.emit(token, "RoleGranted") 64 | await expect(token.connect(minter).mint(signer.address, parseEther("1"))).to.emit(token, "Transfer(address,address,uint256)") 65 | 66 | const balanceBefore = await token.balanceOf(targetAddress) 67 | await token.transferAndCall(targetAddress, parseEther("1"), "0x6c6f6c") 68 | const balanceAfter = await token.balanceOf(targetAddress) 69 | 70 | expect(balanceAfter - balanceBefore).to.equal(parseEther("1")) 71 | }) 72 | 73 | it("minting uses MINTER_ROLE", async () => { 74 | const [signer] = await ethers.getSigners() 75 | const targetAddress = "0x0000000000000000000000000000000000000002" 76 | 77 | const DATAv2 = await ethers.getContractFactory("DATAv2") 78 | const token = await DATAv2.deploy() 79 | await token.waitForDeployment() 80 | 81 | await expect(token.mint(targetAddress, "1000")).to.be.revertedWith("Transaction signer is not a minter") 82 | await expect(token.grantRole(id("MINTER_ROLE"), signer.address)).to.emit(token, "RoleGranted") 83 | 84 | const balanceBefore = await token.balanceOf(targetAddress) 85 | await expect(token.mint(targetAddress, "1000")).to.emit(token, "Transfer(address,address,uint256)") 86 | const balanceAfter = await token.balanceOf(targetAddress) 87 | 88 | await expect(token.revokeRole(id("MINTER_ROLE"), signer.address)).to.emit(token, "RoleRevoked") 89 | await expect(token.mint(targetAddress, "1000")).to.be.revertedWith("Transaction signer is not a minter") 90 | 91 | expect(balanceAfter - balanceBefore).to.equal("1000") 92 | }) 93 | 94 | it("name and symbol can be changed by admin", async () => { 95 | const [, notAdmin] = await ethers.getSigners() 96 | 97 | const DATAv2 = await ethers.getContractFactory("DATAv2") 98 | const token = await DATAv2.deploy() 99 | await token.waitForDeployment() 100 | 101 | await expect(token.connect(notAdmin).setTokenInformation("Test token", "TEST")).to.be.revertedWith("Transaction signer is not an admin") 102 | await expect(token.setTokenInformation("Test token", "TEST")).to.emit(token, "UpdatedTokenInformation") 103 | 104 | expect(await token.name()).to.equal("Test token") 105 | expect(await token.symbol()).to.equal("TEST") 106 | }) 107 | }) -------------------------------------------------------------------------------- /test/DATAv2onPolygon-test.js: -------------------------------------------------------------------------------- 1 | const { zeroPadValue, parseEther } = require("ethers") 2 | const { expect } = require("chai") 3 | const { ethers: hardhatEthers } = require("hardhat") 4 | 5 | describe("DATAv2onPolygon", () => { 6 | it("deposit mints tokens as expected", async () => { 7 | const [bridge] = await hardhatEthers.getSigners() 8 | const targetUser = "0x1234567890123456789012345678901234567890" 9 | const DATAv2onPolygon = await hardhatEthers.getContractFactory("DATAv2onPolygon") 10 | const token = await DATAv2onPolygon.deploy(bridge.address) 11 | await token.waitForDeployment() 12 | 13 | expect((await token.balanceOf(targetUser)).toString()).to.equal("0") 14 | 15 | const amountBytes = zeroPadValue("0x" + parseEther("10").toString(16), 32) 16 | await expect(token.connect(bridge).deposit(targetUser, amountBytes)).to.emit(token, "Transfer(address,address,uint256)") 17 | 18 | expect((await token.balanceOf(targetUser)).toString()).to.equal(parseEther("10").toString()) 19 | }) 20 | 21 | it("only bridge is allowed to deposit", async () => { 22 | const [bridge, another] = await hardhatEthers.getSigners() 23 | const DATAv2onPolygon = await hardhatEthers.getContractFactory("DATAv2onPolygon") 24 | const token = await DATAv2onPolygon.deploy(bridge.address) 25 | await token.waitForDeployment() 26 | const amountBytes = zeroPadValue("0x" + parseEther("10").toString(16), 32) 27 | await expect(token.connect(another).deposit(another.address, amountBytes)).to.be.revertedWith("error_onlyBridge") 28 | }) 29 | 30 | it("withdraw burns tokens", async () => { 31 | const [bridge, user] = await hardhatEthers.getSigners() 32 | const DATAv2onPolygon = await hardhatEthers.getContractFactory("DATAv2onPolygon") 33 | const token = await DATAv2onPolygon.deploy(bridge.address) 34 | await token.waitForDeployment() 35 | 36 | expect((await token.balanceOf(user.address)).toString()).to.equal("0") 37 | const amountWei = parseEther("10") 38 | const amountBytes = zeroPadValue("0x" + amountWei.toString(16), 32) 39 | await expect(token.connect(bridge).deposit(user.address, amountBytes)).to.emit(token, "Transfer(address,address,uint256,bytes)") 40 | expect((await token.balanceOf(user.address)).toString()).to.equal(amountWei.toString()) 41 | 42 | await expect(token.connect(user).withdraw(amountWei)).to.emit(token, "Transfer(address,address,uint256)") 43 | .withArgs(user.address, "0x0000000000000000000000000000000000000000", amountWei) 44 | 45 | expect((await token.balanceOf(user.address)).toString()).to.equal("0") 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /test/DataTokenMigrator-test.js: -------------------------------------------------------------------------------- 1 | const { parseEther, id } = require("ethers") 2 | const { expect } = require("chai") 3 | const { ethers } = require("hardhat") 4 | 5 | describe("DataTokenMigrator", () => { 6 | it("hands out the new tokens in the upgrade", async function() { 7 | this.timeout(100000) 8 | const [signer, hodler, minter] = await ethers.getSigners() 9 | 10 | // Current DATA token supply 11 | // See totalSupply at https://etherscan.io/address/0x0cf0ee63788a0849fe5297f3407f701e122cc023#readContract 12 | const oldSupply = parseEther("987154514") 13 | 14 | // Set up the old token into a valid upgrade state by simulating the crowdsale: hodler gets all, then token is released 15 | const CrowdsaleToken = await ethers.getContractFactory("CrowdsaleToken") 16 | const oldToken = await CrowdsaleToken.deploy("Streamr DATAcoin", "DATA", 0, 18, true) 17 | await oldToken.waitForDeployment() 18 | const oldTokenAddress = await oldToken.getAddress() 19 | await oldToken.setReleaseAgent(signer.address) 20 | await oldToken.setMintAgent(signer.address, true) 21 | await oldToken.mint(hodler.address, oldSupply) 22 | expect(await oldToken.getUpgradeState()).to.equal(1n) // NotAllowed 23 | await oldToken.releaseTokenTransfer() 24 | expect(await oldToken.getUpgradeState()).to.equal(2n) // WaitingForAgent 25 | 26 | // WaitingForAgent is also the current pre-migration state of the DATA token in mainnet, 27 | // verify that getUpgradeState=2 at https://etherscan.io/address/0x0cf0ee63788a0849fe5297f3407f701e122cc023#readContract 28 | 29 | // Migration process step 1: Deploy the contracts 30 | const DATAv2 = await ethers.getContractFactory("DATAv2") 31 | const newToken = await DATAv2.deploy() 32 | await newToken.waitForDeployment() 33 | const newTokenAddress = await newToken.getAddress() 34 | const DataTokenMigrator = await ethers.getContractFactory("DataTokenMigrator") 35 | const migrator = await DataTokenMigrator.deploy(oldTokenAddress, newTokenAddress) 36 | await migrator.waitForDeployment() 37 | const migratorAddress = await migrator.getAddress() 38 | 39 | // Step 2: Create/enable the minter 40 | await expect(newToken.grantRole(id("MINTER_ROLE"), minter.address)).to.emit(newToken, "RoleGranted") 41 | 42 | // Step 3: Mint the tokens belonging to old DATA holders into the DataTokenMigrator contract 43 | await expect(newToken.connect(minter).mint(migratorAddress, oldSupply)).to.emit(newToken, "Transfer(address,address,uint256)") 44 | 45 | // Step 4: Set the migrator as the upgrade agent in the DATA token contract to start the upgrade 46 | await expect(oldToken.setUpgradeAgent(migratorAddress)).to.emit(oldToken, "UpgradeAgentSet") 47 | expect(await oldToken.getUpgradeState()).to.equal(3) // ReadyToUpgrade 48 | 49 | // Step 5: Hodler upgrades some tokens 50 | const oldTokensBefore = await oldToken.balanceOf(hodler.address) 51 | const newTokensBefore = await newToken.balanceOf(hodler.address) 52 | await expect(oldToken.connect(hodler).upgrade(parseEther("1"))).to.emit(oldToken, "Upgrade") 53 | await expect(oldToken.connect(hodler).upgrade(parseEther("20"))).to.emit(oldToken, "Upgrade") 54 | await expect(oldToken.connect(hodler).upgrade(parseEther("300"))).to.emit(oldToken, "Upgrade") 55 | const oldTokensAfter = await oldToken.balanceOf(hodler.address) 56 | const newTokensAfter = await newToken.balanceOf(hodler.address) 57 | 58 | // Old token is in "Upgrading" state and new tokens were dispensed correctly 59 | expect(await oldToken.getUpgradeState()).to.equal(4) // Upgrading 60 | expect(newTokensAfter - newTokensBefore).to.equal(parseEther("321")) 61 | expect(oldTokensBefore - oldTokensAfter).to.equal(parseEther("321")) 62 | 63 | // this invariant should hold at all times 64 | expect(await oldToken.totalSupply()).to.equal(await newToken.balanceOf(migratorAddress)) 65 | }) 66 | 67 | it("can't be called by the others than the old token contract", async () => { 68 | const [signer] = await ethers.getSigners() 69 | 70 | const DATAv2 = await ethers.getContractFactory("DATAv2") 71 | const newToken = await DATAv2.deploy() 72 | await newToken.waitForDeployment() 73 | const newTokenAddress = await newToken.getAddress() 74 | const DataTokenMigrator = await ethers.getContractFactory("DataTokenMigrator") 75 | const migrator = await DataTokenMigrator.deploy(newTokenAddress, newTokenAddress) 76 | await migrator.waitForDeployment() 77 | 78 | await expect(migrator.upgradeFrom(signer.address, parseEther("1"))).to.be.revertedWith("Call not permitted, UpgradableToken only") 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Node 12", 4 | "compilerOptions": { 5 | "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], 6 | "module": "CommonJS", 7 | "target": "es2019", 8 | "preserveSymlinks": true, 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "resolveJsonModule": true, 14 | "declaration": true 15 | }, 16 | "include": ["./index.ts"] 17 | } 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Node 12", 4 | "compilerOptions": { 5 | "lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"], 6 | "module": "CommonJS", 7 | "target": "es2019", 8 | "preserveSymlinks": true, 9 | "strict": true, 10 | "esModuleInterop": true, 11 | "skipLibCheck": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "resolveJsonModule": true, 14 | "outDir": "dist" 15 | }, 16 | "include": ["./scripts", "./test", "./typechain", "./src"], 17 | "files": [ 18 | "./hardhat.config.ts" 19 | ] 20 | } --------------------------------------------------------------------------------