├── .github └── CODEOWNERS ├── .gitignore ├── .openzeppelin ├── dev-1598294931254.json ├── dev-5777.json ├── project.json └── ropsten.json ├── LICENSE ├── README.md ├── build ├── abi │ ├── AccessControl.json │ ├── Address.json │ ├── ContextAware.json │ ├── ERC20.json │ ├── EnumerableSet.json │ ├── ExternallyTransferable.json │ ├── Migrations.json │ ├── Ownable.json │ ├── Pausable.json │ ├── SafeMath.json │ └── StableCoin.json └── contracts │ └── Migrations.json ├── contracts ├── AccessControl.sol ├── Context.sol ├── ERC20.sol ├── ExternallyTransferable.sol ├── Migrations.sol ├── Ownable.sol ├── Pausable.sol └── StableCoin.sol ├── migrations ├── 1_initial_migration.js └── 2_deploy.js ├── package.json ├── secrets.json.sample ├── test ├── .gitkeep └── StableCoin.spec.js ├── truffle-config.js ├── yarn-error.log └── yarn.lock /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Default code owners for entire repository 2 | * @hashgraph/developer-advocates 3 | ######################### 4 | ##### Core Files ###### 5 | ######################### 6 | 7 | # NOTE: Must be placed last to ensure enforcement over all other rules 8 | 9 | # Protection Rules for Github Configuration Files and Actions Workflows 10 | /.github/ @hashgraph/platform-ci @hashgraph/release-engineering-managers 11 | 12 | # Codacy Tool Configurations 13 | /config/ @hashgraph/platform-ci @hashgraph/release-engineering-managers @hashgraph/developer-advocates @hashgraph/platform-ci-committers 14 | .remarkrc @hashgraph/platform-ci @hashgraph/release-engineering-managers @hashgraph/developer-advocates @hashgraph/platform-ci-committers 15 | 16 | # Self-protection for root CODEOWNERS files (this file should not exist and should definitely require approval) 17 | /CODEOWNERS @hashgraph/release-engineering-managers 18 | 19 | # Protect the repository root files 20 | /README.md @hashgraph/platform-ci @hashgraph/release-engineering-managers @hashgraph/platform-ci-committers @hashgraph/developer-advocates 21 | **/LICENSE @hashgraph/release-engineering-managers 22 | 23 | # CodeCov configuration 24 | **/codecov.yml @hashgraph/platform-ci @hashgraph/release-engineering-managers 25 | 26 | # Git Ignore definitions 27 | **/.gitignore @hashgraph/platform-ci @hashgraph/release-engineering-managers @hashgraph/platform-ci-committers 28 | **/.gitignore.* @hashgraph/platform-ci @hashgraph/release-engineering-managers @hashgraph/platform-ci-committers 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | node_modules/ 3 | 4 | # Backup files 5 | *.backup 6 | 7 | # Environment and secret files 8 | .env 9 | secrets.json 10 | 11 | # Build artifacts 12 | build/ 13 | 14 | # IDE and system files 15 | .DS_Store 16 | .idea/ 17 | .vscode/ 18 | 19 | # Truffle build directory 20 | .truffle/ 21 | 22 | # Yarn/npm debug logs 23 | yarn-debug.log* 24 | yarn-error.log* 25 | npm-debug.log* 26 | 27 | # Local development files 28 | *.local 29 | -------------------------------------------------------------------------------- /.openzeppelin/dev-1598294931254.json: -------------------------------------------------------------------------------- 1 | { 2 | "contracts": {}, 3 | "solidityLibs": {}, 4 | "proxies": { 5 | "StableCoin/StableCoin": [ 6 | { 7 | "address": "0xDeA75B0233e3aC9B72C8f909548B059Ab1344fb9", 8 | "kind": "NonProxy", 9 | "bytecodeHash": "af4f9511f80f8951838c510f3256d0038fcc63432112a882e94c232f68b27503" 10 | } 11 | ] 12 | }, 13 | "manifestVersion": "2.2" 14 | } 15 | -------------------------------------------------------------------------------- /.openzeppelin/dev-5777.json: -------------------------------------------------------------------------------- 1 | { 2 | "contracts": { 3 | "StableCoin": { 4 | "address": "0xB16D9f61d62377F7Dba0973352765FE6b4F9eB09", 5 | "constructorCode": "608060405234801561001057600080fd5b506140dc806100206000396000f3fe", 6 | "bodyBytecodeHash": "702039475efec5e0d77a922447ecddb7322b4b0c21648d3e4b5e4a9e048ae9af", 7 | "localBytecodeHash": "b61380c39a9cf09cc4f94eb7f97f04eeff90c9669f30de9530d8d59724644f02", 8 | "deployedBytecodeHash": "b61380c39a9cf09cc4f94eb7f97f04eeff90c9669f30de9530d8d59724644f02", 9 | "types": { 10 | "t_bool": { 11 | "id": "t_bool", 12 | "kind": "elementary", 13 | "label": "bool" 14 | }, 15 | "t_uint256": { 16 | "id": "t_uint256", 17 | "kind": "elementary", 18 | "label": "uint256" 19 | }, 20 | "t_array:50": { 21 | "id": "t_array:50", 22 | "valueType": "t_uint256", 23 | "length": "50", 24 | "kind": "array", 25 | "label": "uint256[50]" 26 | }, 27 | "t_array:49": { 28 | "id": "t_array:49", 29 | "valueType": "t_uint256", 30 | "length": "49", 31 | "kind": "array", 32 | "label": "uint256[49]" 33 | }, 34 | "t_address": { 35 | "id": "t_address", 36 | "kind": "elementary", 37 | "label": "address" 38 | }, 39 | "t_struct": { 40 | "id": "t_struct", 41 | "kind": "struct", 42 | "label": "AccessControlUpgradeSafe.RoleData", 43 | "members": [ 44 | { 45 | "label": "members", 46 | "astId": 154, 47 | "type": "t_struct", 48 | "src": "1655:32:2" 49 | }, 50 | { 51 | "label": "adminRole", 52 | "astId": 156, 53 | "type": "t_bytes32", 54 | "src": "1697:17:2" 55 | } 56 | ] 57 | }, 58 | "t_struct": { 59 | "id": "t_struct", 60 | "kind": "struct", 61 | "label": "EnumerableSet.AddressSet", 62 | "members": [ 63 | { 64 | "label": "_inner", 65 | "astId": 1469, 66 | "type": "t_struct", 67 | "src": "4644:10:7" 68 | } 69 | ] 70 | }, 71 | "t_struct": { 72 | "id": "t_struct", 73 | "kind": "struct", 74 | "label": "EnumerableSet.Set", 75 | "members": [ 76 | { 77 | "label": "_values", 78 | "astId": 1285, 79 | "type": "t_array:dyn", 80 | "src": "1213:17:7" 81 | }, 82 | { 83 | "label": "_indexes", 84 | "astId": 1289, 85 | "type": "t_mapping", 86 | "src": "1364:37:7" 87 | } 88 | ] 89 | }, 90 | "t_bytes32": { 91 | "id": "t_bytes32", 92 | "kind": "elementary", 93 | "label": "bytes32" 94 | }, 95 | "t_array:dyn": { 96 | "id": "t_array:dyn", 97 | "valueType": "t_bytes32", 98 | "length": "dyn", 99 | "kind": "array", 100 | "label": "bytes32[]" 101 | }, 102 | "t_mapping": { 103 | "id": "t_mapping", 104 | "valueType": "t_uint256", 105 | "label": "mapping(key => uint256)", 106 | "kind": "mapping" 107 | }, 108 | "t_mapping>": { 109 | "id": "t_mapping>", 110 | "valueType": "t_struct", 111 | "label": "mapping(key => AccessControlUpgradeSafe.RoleData)", 112 | "kind": "mapping" 113 | }, 114 | "t_string": { 115 | "id": "t_string", 116 | "kind": "elementary", 117 | "label": "string" 118 | }, 119 | "t_uint8": { 120 | "id": "t_uint8", 121 | "kind": "elementary", 122 | "label": "uint8" 123 | }, 124 | "t_array:44": { 125 | "id": "t_array:44", 126 | "valueType": "t_uint256", 127 | "length": "44", 128 | "kind": "array", 129 | "label": "uint256[44]" 130 | } 131 | }, 132 | "storage": [ 133 | { 134 | "contract": "Initializable", 135 | "path": "@openzeppelin/contracts-ethereum-package/contracts/Initializable.sol", 136 | "label": "initialized", 137 | "astId": 49, 138 | "type": "t_bool", 139 | "src": "757:24:1" 140 | }, 141 | { 142 | "contract": "Initializable", 143 | "path": "@openzeppelin/contracts-ethereum-package/contracts/Initializable.sol", 144 | "label": "initializing", 145 | "astId": 51, 146 | "type": "t_bool", 147 | "src": "876:25:1" 148 | }, 149 | { 150 | "contract": "Initializable", 151 | "path": "@openzeppelin/contracts-ethereum-package/contracts/Initializable.sol", 152 | "label": "______gap", 153 | "astId": 116, 154 | "type": "t_array:50", 155 | "src": "1982:29:1" 156 | }, 157 | { 158 | "contract": "ContextUpgradeSafe", 159 | "path": "@openzeppelin/contracts-ethereum-package/contracts/GSN/Context.sol", 160 | "label": "__gap", 161 | "astId": 43, 162 | "type": "t_array:50", 163 | "src": "1277:25:0" 164 | }, 165 | { 166 | "contract": "PausableUpgradeSafe", 167 | "path": "@openzeppelin/contracts-ethereum-package/contracts/utils/Pausable.sol", 168 | "label": "_paused", 169 | "astId": 1695, 170 | "type": "t_bool", 171 | "src": "825:20:8" 172 | }, 173 | { 174 | "contract": "PausableUpgradeSafe", 175 | "path": "@openzeppelin/contracts-ethereum-package/contracts/utils/Pausable.sol", 176 | "label": "__gap", 177 | "astId": 1784, 178 | "type": "t_array:49", 179 | "src": "2073:25:8" 180 | }, 181 | { 182 | "contract": "OwnableUpgradeSafe", 183 | "path": "contracts/Ownable.sol", 184 | "label": "_owner", 185 | "astId": 1832, 186 | "type": "t_address", 187 | "src": "398:22:10" 188 | }, 189 | { 190 | "contract": "OwnableUpgradeSafe", 191 | "path": "contracts/Ownable.sol", 192 | "label": "_proposedOwner", 193 | "astId": 1834, 194 | "type": "t_address", 195 | "src": "426:30:10" 196 | }, 197 | { 198 | "contract": "OwnableUpgradeSafe", 199 | "path": "contracts/Ownable.sol", 200 | "label": "__gap", 201 | "astId": 1977, 202 | "type": "t_array:49", 203 | "src": "1912:25:10" 204 | }, 205 | { 206 | "contract": "AccessControlUpgradeSafe", 207 | "path": "@openzeppelin/contracts-ethereum-package/contracts/access/AccessControl.sol", 208 | "label": "_roles", 209 | "astId": 161, 210 | "type": "t_mapping>", 211 | "src": "1727:44:2" 212 | }, 213 | { 214 | "contract": "AccessControlUpgradeSafe", 215 | "path": "@openzeppelin/contracts-ethereum-package/contracts/access/AccessControl.sol", 216 | "label": "__gap", 217 | "astId": 406, 218 | "type": "t_array:49", 219 | "src": "6927:25:2" 220 | }, 221 | { 222 | "contract": "ERC20UpgradeSafe", 223 | "path": "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol", 224 | "label": "_balances", 225 | "astId": 627, 226 | "type": "t_mapping", 227 | "src": "1481:46:4" 228 | }, 229 | { 230 | "contract": "ERC20UpgradeSafe", 231 | "path": "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol", 232 | "label": "_allowances", 233 | "astId": 633, 234 | "type": "t_mapping", 235 | "src": "1534:69:4" 236 | }, 237 | { 238 | "contract": "ERC20UpgradeSafe", 239 | "path": "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol", 240 | "label": "_totalSupply", 241 | "astId": 635, 242 | "type": "t_uint256", 243 | "src": "1610:28:4" 244 | }, 245 | { 246 | "contract": "ERC20UpgradeSafe", 247 | "path": "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol", 248 | "label": "_name", 249 | "astId": 637, 250 | "type": "t_string", 251 | "src": "1645:20:4" 252 | }, 253 | { 254 | "contract": "ERC20UpgradeSafe", 255 | "path": "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol", 256 | "label": "_symbol", 257 | "astId": 639, 258 | "type": "t_string", 259 | "src": "1671:22:4" 260 | }, 261 | { 262 | "contract": "ERC20UpgradeSafe", 263 | "path": "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol", 264 | "label": "_decimals", 265 | "astId": 641, 266 | "type": "t_uint8", 267 | "src": "1699:23:4" 268 | }, 269 | { 270 | "contract": "ERC20UpgradeSafe", 271 | "path": "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/ERC20.sol", 272 | "label": "__gap", 273 | "astId": 1136, 274 | "type": "t_array:44", 275 | "src": "11045:25:4" 276 | }, 277 | { 278 | "contract": "StableCoin", 279 | "path": "contracts/StableCoin.sol", 280 | "label": "_supplyManager", 281 | "astId": 2012, 282 | "type": "t_address", 283 | "src": "750:30:11" 284 | }, 285 | { 286 | "contract": "StableCoin", 287 | "path": "contracts/StableCoin.sol", 288 | "label": "_assetProtectionManager", 289 | "astId": 2014, 290 | "type": "t_address", 291 | "src": "786:39:11" 292 | }, 293 | { 294 | "contract": "StableCoin", 295 | "path": "contracts/StableCoin.sol", 296 | "label": "__gap", 297 | "astId": 3147, 298 | "type": "t_array:50", 299 | "src": "13838:25:11" 300 | } 301 | ], 302 | "warnings": { 303 | "hasConstructor": false, 304 | "hasSelfDestruct": false, 305 | "hasDelegateCall": false, 306 | "hasInitialValuesInDeclarations": false, 307 | "uninitializedBaseContracts": [] 308 | } 309 | } 310 | }, 311 | "solidityLibs": {}, 312 | "proxies": { 313 | "StableCoin/StableCoin": [ 314 | { 315 | "address": "0x4655bde52de89B8233aFca0FcBf0666a1deAaadC", 316 | "version": "1.0.0", 317 | "implementation": "0xB16D9f61d62377F7Dba0973352765FE6b4F9eB09", 318 | "admin": "0x8e9205b663829655B34986e55025eE2d7215D1AD", 319 | "kind": "Upgradeable" 320 | } 321 | ] 322 | }, 323 | "manifestVersion": "2.2", 324 | "version": "1.0.0", 325 | "proxyAdmin": { 326 | "address": "0x8e9205b663829655B34986e55025eE2d7215D1AD" 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /.openzeppelin/project.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifestVersion": "2.2", 3 | "name": "StableCoin", 4 | "version": "1.0.0", 5 | "compiler": { 6 | "solcVersion": "0.6.8", 7 | "manager": "openzeppelin", 8 | "artifactsDir": "build/contracts", 9 | "contractsDir": "contracts", 10 | "compilerSettings": { 11 | "optimizer": { 12 | "enabled": true, 13 | "runs": "2000" 14 | } 15 | }, 16 | "typechain": { 17 | "enabled": false 18 | } 19 | }, 20 | "contracts": { 21 | "StableCoin": "StableCoin" 22 | }, 23 | "dependencies": {}, 24 | "telemetryOptIn": false 25 | } 26 | -------------------------------------------------------------------------------- /.openzeppelin/ropsten.json: -------------------------------------------------------------------------------- 1 | { 2 | "contracts": {}, 3 | "solidityLibs": {}, 4 | "proxies": { 5 | "StableCoin/StableCoin": [ 6 | { 7 | "address": "0x42E9d6514D613D63c8c32cE385aad3dE9917C681", 8 | "kind": "NonProxy", 9 | "bytecodeHash": "af4f9511f80f8951838c510f3256d0038fcc63432112a882e94c232f68b27503" 10 | } 11 | ] 12 | }, 13 | "manifestVersion": "2.2" 14 | } 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Christopher Campbell 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 | ### StableCoin Smart Contract 2 | 3 | ERC20 Externally Minted Token with Access Control 4 | 5 | Developed using OpenZeppelin CLI and the Truffle Suite 6 | 7 | ##### Setup 8 | 9 | You'll need Yarn, the Open Zeppelin CLI and the Truffle Suite to set up the development environment. I also recommend using NVM for managing the Node Version you are currently using. 10 | 11 | - [NVM](https://github.com/nvm-sh/nvm) 12 | 13 | - [Yarn](https://classic.yarnpkg.com/en/docs/) 14 | 15 | - [OpenZeppelin CLI](https://docs.openzeppelin.com/cli/2.8/) 16 | 17 | - [Truffle Suite](https://www.trufflesuite.com/) 18 | 19 | - [Using OZ CLI w/ Truffle](https://docs.openzeppelin.com/cli/2.8/truffle) 20 | 21 | ##### Commands 22 | 23 | - `yarn` installs dependencies 24 | 25 | - `yarn oz compile` uses the openzeppelin cli (**and oz sdk**) to compile the solidity contracts. 26 | 27 | - `yarn truffle compile` uses truffle to compile the solidity contracts 28 | 29 | - `yarn test` runs mocha/chai/oz tests inside of a managed test environment 30 | 31 | - `yarn network:truffle` sets up the local development network via truffle 32 | 33 | - `yarn network:ganache` sets up the local development network via ganache cli 34 | 35 | - `yarn deploy` runs the openzeppelin deployment in interactive mode 36 | 37 | - `yarn deploy:truffle` deploys the compiled smart contract to the local truffle development network 38 | 39 | - `yarn deploy:ganache` deploys the compiled smart contract to the local ganache development network 40 | 41 | Network information is defined in truffle-config.js, which is read by openzeppelin CLI automatically. 42 | 43 | SOLC 0.6.8 used as compiler for both the oz sdk and truffle 44 | 45 | ##### Contract 46 | 47 | - ERC20, Ownable, Claimable, Pausable 48 | 49 | - RBAC for Supply, Asset Protection, KYC, Frozen 50 | -------------------------------------------------------------------------------- /build/abi/AccessControl.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "stateMutability": "nonpayable", 5 | "type": "constructor" 6 | }, 7 | { 8 | "inputs": [], 9 | "name": "DEFAULT_ADMIN_ROLE", 10 | "outputs": [ 11 | { 12 | "internalType": "bytes32", 13 | "name": "", 14 | "type": "bytes32" 15 | } 16 | ], 17 | "stateMutability": "view", 18 | "type": "function" 19 | } 20 | ] -------------------------------------------------------------------------------- /build/abi/Address.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /build/abi/ContextAware.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /build/abi/ERC20.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "address", 6 | "name": "owner", 7 | "type": "address" 8 | }, 9 | { 10 | "internalType": "address", 11 | "name": "spender", 12 | "type": "address" 13 | } 14 | ], 15 | "name": "allowance", 16 | "outputs": [ 17 | { 18 | "internalType": "uint256", 19 | "name": "", 20 | "type": "uint256" 21 | } 22 | ], 23 | "stateMutability": "view", 24 | "type": "function" 25 | }, 26 | { 27 | "inputs": [ 28 | { 29 | "internalType": "address", 30 | "name": "spender", 31 | "type": "address" 32 | }, 33 | { 34 | "internalType": "uint256", 35 | "name": "amount", 36 | "type": "uint256" 37 | } 38 | ], 39 | "name": "approveAllowance", 40 | "outputs": [], 41 | "stateMutability": "nonpayable", 42 | "type": "function" 43 | }, 44 | { 45 | "inputs": [ 46 | { 47 | "internalType": "address", 48 | "name": "account", 49 | "type": "address" 50 | } 51 | ], 52 | "name": "balanceOf", 53 | "outputs": [ 54 | { 55 | "internalType": "uint256", 56 | "name": "", 57 | "type": "uint256" 58 | } 59 | ], 60 | "stateMutability": "view", 61 | "type": "function" 62 | }, 63 | { 64 | "inputs": [], 65 | "name": "decimals", 66 | "outputs": [ 67 | { 68 | "internalType": "uint8", 69 | "name": "", 70 | "type": "uint8" 71 | } 72 | ], 73 | "stateMutability": "view", 74 | "type": "function" 75 | }, 76 | { 77 | "inputs": [ 78 | { 79 | "internalType": "address", 80 | "name": "spender", 81 | "type": "address" 82 | }, 83 | { 84 | "internalType": "uint256", 85 | "name": "subtractedValue", 86 | "type": "uint256" 87 | } 88 | ], 89 | "name": "decreaseAllowance", 90 | "outputs": [], 91 | "stateMutability": "nonpayable", 92 | "type": "function" 93 | }, 94 | { 95 | "inputs": [ 96 | { 97 | "internalType": "address", 98 | "name": "spender", 99 | "type": "address" 100 | }, 101 | { 102 | "internalType": "uint256", 103 | "name": "addedValue", 104 | "type": "uint256" 105 | } 106 | ], 107 | "name": "increaseAllowance", 108 | "outputs": [], 109 | "stateMutability": "nonpayable", 110 | "type": "function" 111 | }, 112 | { 113 | "inputs": [], 114 | "name": "name", 115 | "outputs": [ 116 | { 117 | "internalType": "string", 118 | "name": "", 119 | "type": "string" 120 | } 121 | ], 122 | "stateMutability": "view", 123 | "type": "function" 124 | }, 125 | { 126 | "inputs": [], 127 | "name": "symbol", 128 | "outputs": [ 129 | { 130 | "internalType": "string", 131 | "name": "", 132 | "type": "string" 133 | } 134 | ], 135 | "stateMutability": "view", 136 | "type": "function" 137 | }, 138 | { 139 | "inputs": [], 140 | "name": "totalSupply", 141 | "outputs": [ 142 | { 143 | "internalType": "uint256", 144 | "name": "", 145 | "type": "uint256" 146 | } 147 | ], 148 | "stateMutability": "view", 149 | "type": "function" 150 | }, 151 | { 152 | "inputs": [ 153 | { 154 | "internalType": "address", 155 | "name": "recipient", 156 | "type": "address" 157 | }, 158 | { 159 | "internalType": "uint256", 160 | "name": "amount", 161 | "type": "uint256" 162 | } 163 | ], 164 | "name": "transfer", 165 | "outputs": [], 166 | "stateMutability": "nonpayable", 167 | "type": "function" 168 | }, 169 | { 170 | "inputs": [ 171 | { 172 | "internalType": "address", 173 | "name": "sender", 174 | "type": "address" 175 | }, 176 | { 177 | "internalType": "address", 178 | "name": "recipient", 179 | "type": "address" 180 | }, 181 | { 182 | "internalType": "uint256", 183 | "name": "amount", 184 | "type": "uint256" 185 | } 186 | ], 187 | "name": "transferFrom", 188 | "outputs": [], 189 | "stateMutability": "nonpayable", 190 | "type": "function" 191 | } 192 | ] -------------------------------------------------------------------------------- /build/abi/EnumerableSet.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /build/abi/ExternallyTransferable.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "string", 6 | "name": "networkURI", 7 | "type": "string" 8 | }, 9 | { 10 | "internalType": "bytes", 11 | "name": "externalAddress", 12 | "type": "bytes" 13 | }, 14 | { 15 | "internalType": "uint256", 16 | "name": "amount", 17 | "type": "uint256" 18 | } 19 | ], 20 | "name": "approveExternalTransfer", 21 | "outputs": [], 22 | "stateMutability": "nonpayable", 23 | "type": "function" 24 | }, 25 | { 26 | "inputs": [ 27 | { 28 | "internalType": "address", 29 | "name": "owner", 30 | "type": "address" 31 | }, 32 | { 33 | "internalType": "string", 34 | "name": "networkURI", 35 | "type": "string" 36 | }, 37 | { 38 | "internalType": "bytes", 39 | "name": "externalAddress", 40 | "type": "bytes" 41 | } 42 | ], 43 | "name": "externalAllowanceOf", 44 | "outputs": [ 45 | { 46 | "internalType": "uint256", 47 | "name": "", 48 | "type": "uint256" 49 | } 50 | ], 51 | "stateMutability": "view", 52 | "type": "function" 53 | }, 54 | { 55 | "inputs": [ 56 | { 57 | "internalType": "address", 58 | "name": "from", 59 | "type": "address" 60 | }, 61 | { 62 | "internalType": "string", 63 | "name": "networkURI", 64 | "type": "string" 65 | }, 66 | { 67 | "internalType": "bytes", 68 | "name": "to", 69 | "type": "bytes" 70 | }, 71 | { 72 | "internalType": "uint256", 73 | "name": "amount", 74 | "type": "uint256" 75 | } 76 | ], 77 | "name": "externalTransfer", 78 | "outputs": [], 79 | "stateMutability": "nonpayable", 80 | "type": "function" 81 | }, 82 | { 83 | "inputs": [ 84 | { 85 | "internalType": "bytes", 86 | "name": "from", 87 | "type": "bytes" 88 | }, 89 | { 90 | "internalType": "string", 91 | "name": "networkURI", 92 | "type": "string" 93 | }, 94 | { 95 | "internalType": "address", 96 | "name": "to", 97 | "type": "address" 98 | }, 99 | { 100 | "internalType": "uint256", 101 | "name": "amount", 102 | "type": "uint256" 103 | } 104 | ], 105 | "name": "externalTransferFrom", 106 | "outputs": [], 107 | "stateMutability": "nonpayable", 108 | "type": "function" 109 | } 110 | ] -------------------------------------------------------------------------------- /build/abi/Migrations.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "stateMutability": "nonpayable", 5 | "type": "constructor" 6 | }, 7 | { 8 | "inputs": [], 9 | "name": "last_completed_migration", 10 | "outputs": [ 11 | { 12 | "internalType": "uint256", 13 | "name": "", 14 | "type": "uint256" 15 | } 16 | ], 17 | "stateMutability": "view", 18 | "type": "function" 19 | }, 20 | { 21 | "inputs": [], 22 | "name": "owner", 23 | "outputs": [ 24 | { 25 | "internalType": "address", 26 | "name": "", 27 | "type": "address" 28 | } 29 | ], 30 | "stateMutability": "view", 31 | "type": "function" 32 | }, 33 | { 34 | "inputs": [ 35 | { 36 | "internalType": "uint256", 37 | "name": "completed", 38 | "type": "uint256" 39 | } 40 | ], 41 | "name": "setCompleted", 42 | "outputs": [], 43 | "stateMutability": "nonpayable", 44 | "type": "function" 45 | } 46 | ] -------------------------------------------------------------------------------- /build/abi/Ownable.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "stateMutability": "nonpayable", 5 | "type": "constructor" 6 | }, 7 | { 8 | "anonymous": false, 9 | "inputs": [ 10 | { 11 | "indexed": false, 12 | "internalType": "address", 13 | "name": "newOwner", 14 | "type": "address" 15 | } 16 | ], 17 | "name": "ClaimOwnership", 18 | "type": "event" 19 | }, 20 | { 21 | "anonymous": false, 22 | "inputs": [ 23 | { 24 | "indexed": true, 25 | "internalType": "address", 26 | "name": "proposedOwner", 27 | "type": "address" 28 | } 29 | ], 30 | "name": "ProposeOwner", 31 | "type": "event" 32 | }, 33 | { 34 | "inputs": [], 35 | "name": "claimOwnership", 36 | "outputs": [], 37 | "stateMutability": "nonpayable", 38 | "type": "function" 39 | }, 40 | { 41 | "inputs": [], 42 | "name": "owner", 43 | "outputs": [ 44 | { 45 | "internalType": "address", 46 | "name": "", 47 | "type": "address" 48 | } 49 | ], 50 | "stateMutability": "view", 51 | "type": "function" 52 | }, 53 | { 54 | "inputs": [ 55 | { 56 | "internalType": "address", 57 | "name": "newOwner", 58 | "type": "address" 59 | } 60 | ], 61 | "name": "proposeOwner", 62 | "outputs": [], 63 | "stateMutability": "nonpayable", 64 | "type": "function" 65 | }, 66 | { 67 | "inputs": [], 68 | "name": "proposedOwner", 69 | "outputs": [ 70 | { 71 | "internalType": "address", 72 | "name": "", 73 | "type": "address" 74 | } 75 | ], 76 | "stateMutability": "view", 77 | "type": "function" 78 | } 79 | ] -------------------------------------------------------------------------------- /build/abi/Pausable.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "stateMutability": "nonpayable", 5 | "type": "constructor" 6 | }, 7 | { 8 | "inputs": [], 9 | "name": "paused", 10 | "outputs": [ 11 | { 12 | "internalType": "bool", 13 | "name": "", 14 | "type": "bool" 15 | } 16 | ], 17 | "stateMutability": "view", 18 | "type": "function" 19 | } 20 | ] -------------------------------------------------------------------------------- /build/abi/SafeMath.json: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /build/abi/StableCoin.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [ 4 | { 5 | "internalType": "string", 6 | "name": "tokenName", 7 | "type": "string" 8 | }, 9 | { 10 | "internalType": "string", 11 | "name": "tokenSymbol", 12 | "type": "string" 13 | }, 14 | { 15 | "internalType": "uint8", 16 | "name": "tokenDecimal", 17 | "type": "uint8" 18 | }, 19 | { 20 | "internalType": "uint256", 21 | "name": "totalSupply", 22 | "type": "uint256" 23 | }, 24 | { 25 | "internalType": "address", 26 | "name": "supplyManager", 27 | "type": "address" 28 | }, 29 | { 30 | "internalType": "address", 31 | "name": "complianceManager", 32 | "type": "address" 33 | }, 34 | { 35 | "internalType": "address", 36 | "name": "enforcementManager", 37 | "type": "address" 38 | } 39 | ], 40 | "stateMutability": "nonpayable", 41 | "type": "constructor" 42 | }, 43 | { 44 | "anonymous": false, 45 | "inputs": [ 46 | { 47 | "indexed": false, 48 | "internalType": "address", 49 | "name": "sender", 50 | "type": "address" 51 | }, 52 | { 53 | "indexed": false, 54 | "internalType": "address", 55 | "name": "spender", 56 | "type": "address" 57 | }, 58 | { 59 | "indexed": false, 60 | "internalType": "uint256", 61 | "name": "amount", 62 | "type": "uint256" 63 | } 64 | ], 65 | "name": "Approve", 66 | "type": "event" 67 | }, 68 | { 69 | "anonymous": false, 70 | "inputs": [ 71 | { 72 | "indexed": false, 73 | "internalType": "address", 74 | "name": "from", 75 | "type": "address" 76 | }, 77 | { 78 | "indexed": false, 79 | "internalType": "string", 80 | "name": "networkURI", 81 | "type": "string" 82 | }, 83 | { 84 | "indexed": false, 85 | "internalType": "bytes", 86 | "name": "to", 87 | "type": "bytes" 88 | }, 89 | { 90 | "indexed": false, 91 | "internalType": "uint256", 92 | "name": "amount", 93 | "type": "uint256" 94 | } 95 | ], 96 | "name": "ApproveExternalTransfer", 97 | "type": "event" 98 | }, 99 | { 100 | "anonymous": false, 101 | "inputs": [ 102 | { 103 | "indexed": false, 104 | "internalType": "address", 105 | "name": "account", 106 | "type": "address" 107 | }, 108 | { 109 | "indexed": false, 110 | "internalType": "uint256", 111 | "name": "amount", 112 | "type": "uint256" 113 | } 114 | ], 115 | "name": "Burn", 116 | "type": "event" 117 | }, 118 | { 119 | "anonymous": false, 120 | "inputs": [ 121 | { 122 | "indexed": false, 123 | "internalType": "address", 124 | "name": "newComplianceManager", 125 | "type": "address" 126 | } 127 | ], 128 | "name": "ChangeComplianceManager", 129 | "type": "event" 130 | }, 131 | { 132 | "anonymous": false, 133 | "inputs": [ 134 | { 135 | "indexed": false, 136 | "internalType": "address", 137 | "name": "newEnforcementManager", 138 | "type": "address" 139 | } 140 | ], 141 | "name": "ChangeEnforcementManager", 142 | "type": "event" 143 | }, 144 | { 145 | "anonymous": false, 146 | "inputs": [ 147 | { 148 | "indexed": false, 149 | "internalType": "address", 150 | "name": "newSupplyManager", 151 | "type": "address" 152 | } 153 | ], 154 | "name": "ChangeSupplyManager", 155 | "type": "event" 156 | }, 157 | { 158 | "anonymous": false, 159 | "inputs": [ 160 | { 161 | "indexed": false, 162 | "internalType": "address", 163 | "name": "newOwner", 164 | "type": "address" 165 | } 166 | ], 167 | "name": "ClaimOwnership", 168 | "type": "event" 169 | }, 170 | { 171 | "anonymous": false, 172 | "inputs": [ 173 | { 174 | "indexed": false, 175 | "internalType": "string", 176 | "name": "tokenName", 177 | "type": "string" 178 | }, 179 | { 180 | "indexed": false, 181 | "internalType": "string", 182 | "name": "tokenSymbol", 183 | "type": "string" 184 | }, 185 | { 186 | "indexed": false, 187 | "internalType": "uint8", 188 | "name": "tokenDecimal", 189 | "type": "uint8" 190 | }, 191 | { 192 | "indexed": false, 193 | "internalType": "uint256", 194 | "name": "totalSupply", 195 | "type": "uint256" 196 | }, 197 | { 198 | "indexed": false, 199 | "internalType": "address", 200 | "name": "supplyManager", 201 | "type": "address" 202 | }, 203 | { 204 | "indexed": false, 205 | "internalType": "address", 206 | "name": "complianceManager", 207 | "type": "address" 208 | }, 209 | { 210 | "indexed": false, 211 | "internalType": "address", 212 | "name": "enforcementManager", 213 | "type": "address" 214 | } 215 | ], 216 | "name": "Constructed", 217 | "type": "event" 218 | }, 219 | { 220 | "anonymous": false, 221 | "inputs": [ 222 | { 223 | "indexed": false, 224 | "internalType": "address", 225 | "name": "sender", 226 | "type": "address" 227 | }, 228 | { 229 | "indexed": false, 230 | "internalType": "address", 231 | "name": "spender", 232 | "type": "address" 233 | }, 234 | { 235 | "indexed": false, 236 | "internalType": "uint256", 237 | "name": "amount", 238 | "type": "uint256" 239 | } 240 | ], 241 | "name": "DecreaseAllowance", 242 | "type": "event" 243 | }, 244 | { 245 | "anonymous": false, 246 | "inputs": [ 247 | { 248 | "indexed": false, 249 | "internalType": "address", 250 | "name": "from", 251 | "type": "address" 252 | }, 253 | { 254 | "indexed": false, 255 | "internalType": "string", 256 | "name": "networkURI", 257 | "type": "string" 258 | }, 259 | { 260 | "indexed": false, 261 | "internalType": "bytes", 262 | "name": "to", 263 | "type": "bytes" 264 | }, 265 | { 266 | "indexed": false, 267 | "internalType": "uint256", 268 | "name": "amount", 269 | "type": "uint256" 270 | } 271 | ], 272 | "name": "ExternalTransfer", 273 | "type": "event" 274 | }, 275 | { 276 | "anonymous": false, 277 | "inputs": [ 278 | { 279 | "indexed": false, 280 | "internalType": "bytes", 281 | "name": "from", 282 | "type": "bytes" 283 | }, 284 | { 285 | "indexed": false, 286 | "internalType": "string", 287 | "name": "networkURI", 288 | "type": "string" 289 | }, 290 | { 291 | "indexed": false, 292 | "internalType": "address", 293 | "name": "to", 294 | "type": "address" 295 | }, 296 | { 297 | "indexed": false, 298 | "internalType": "uint256", 299 | "name": "amount", 300 | "type": "uint256" 301 | } 302 | ], 303 | "name": "ExternalTransferFrom", 304 | "type": "event" 305 | }, 306 | { 307 | "anonymous": false, 308 | "inputs": [ 309 | { 310 | "indexed": false, 311 | "internalType": "address", 312 | "name": "account", 313 | "type": "address" 314 | } 315 | ], 316 | "name": "Freeze", 317 | "type": "event" 318 | }, 319 | { 320 | "anonymous": false, 321 | "inputs": [ 322 | { 323 | "indexed": false, 324 | "internalType": "address", 325 | "name": "sender", 326 | "type": "address" 327 | }, 328 | { 329 | "indexed": false, 330 | "internalType": "address", 331 | "name": "spender", 332 | "type": "address" 333 | }, 334 | { 335 | "indexed": false, 336 | "internalType": "uint256", 337 | "name": "amount", 338 | "type": "uint256" 339 | } 340 | ], 341 | "name": "IncreaseAllowance", 342 | "type": "event" 343 | }, 344 | { 345 | "anonymous": false, 346 | "inputs": [ 347 | { 348 | "indexed": false, 349 | "internalType": "address", 350 | "name": "account", 351 | "type": "address" 352 | }, 353 | { 354 | "indexed": false, 355 | "internalType": "uint256", 356 | "name": "amount", 357 | "type": "uint256" 358 | } 359 | ], 360 | "name": "Mint", 361 | "type": "event" 362 | }, 363 | { 364 | "anonymous": false, 365 | "inputs": [ 366 | { 367 | "indexed": false, 368 | "internalType": "address", 369 | "name": "sender", 370 | "type": "address" 371 | } 372 | ], 373 | "name": "Pause", 374 | "type": "event" 375 | }, 376 | { 377 | "anonymous": false, 378 | "inputs": [ 379 | { 380 | "indexed": true, 381 | "internalType": "address", 382 | "name": "proposedOwner", 383 | "type": "address" 384 | } 385 | ], 386 | "name": "ProposeOwner", 387 | "type": "event" 388 | }, 389 | { 390 | "anonymous": false, 391 | "inputs": [ 392 | { 393 | "indexed": false, 394 | "internalType": "address", 395 | "name": "account", 396 | "type": "address" 397 | } 398 | ], 399 | "name": "SetKycPassed", 400 | "type": "event" 401 | }, 402 | { 403 | "anonymous": false, 404 | "inputs": [ 405 | { 406 | "indexed": false, 407 | "internalType": "address", 408 | "name": "sender", 409 | "type": "address" 410 | }, 411 | { 412 | "indexed": false, 413 | "internalType": "address", 414 | "name": "recipient", 415 | "type": "address" 416 | }, 417 | { 418 | "indexed": false, 419 | "internalType": "uint256", 420 | "name": "amount", 421 | "type": "uint256" 422 | } 423 | ], 424 | "name": "Transfer", 425 | "type": "event" 426 | }, 427 | { 428 | "anonymous": false, 429 | "inputs": [ 430 | { 431 | "indexed": false, 432 | "internalType": "address", 433 | "name": "account", 434 | "type": "address" 435 | } 436 | ], 437 | "name": "Unfreeze", 438 | "type": "event" 439 | }, 440 | { 441 | "anonymous": false, 442 | "inputs": [ 443 | { 444 | "indexed": false, 445 | "internalType": "address", 446 | "name": "sender", 447 | "type": "address" 448 | } 449 | ], 450 | "name": "Unpause", 451 | "type": "event" 452 | }, 453 | { 454 | "anonymous": false, 455 | "inputs": [ 456 | { 457 | "indexed": false, 458 | "internalType": "address", 459 | "name": "account", 460 | "type": "address" 461 | } 462 | ], 463 | "name": "UnsetKycPassed", 464 | "type": "event" 465 | }, 466 | { 467 | "anonymous": false, 468 | "inputs": [ 469 | { 470 | "indexed": false, 471 | "internalType": "address", 472 | "name": "account", 473 | "type": "address" 474 | }, 475 | { 476 | "indexed": false, 477 | "internalType": "uint256", 478 | "name": "amount", 479 | "type": "uint256" 480 | } 481 | ], 482 | "name": "Wipe", 483 | "type": "event" 484 | }, 485 | { 486 | "inputs": [], 487 | "name": "DEFAULT_ADMIN_ROLE", 488 | "outputs": [ 489 | { 490 | "internalType": "bytes32", 491 | "name": "", 492 | "type": "bytes32" 493 | } 494 | ], 495 | "stateMutability": "view", 496 | "type": "function" 497 | }, 498 | { 499 | "inputs": [ 500 | { 501 | "internalType": "address", 502 | "name": "owner", 503 | "type": "address" 504 | }, 505 | { 506 | "internalType": "address", 507 | "name": "spender", 508 | "type": "address" 509 | } 510 | ], 511 | "name": "allowance", 512 | "outputs": [ 513 | { 514 | "internalType": "uint256", 515 | "name": "", 516 | "type": "uint256" 517 | } 518 | ], 519 | "stateMutability": "view", 520 | "type": "function" 521 | }, 522 | { 523 | "inputs": [ 524 | { 525 | "internalType": "address", 526 | "name": "spender", 527 | "type": "address" 528 | }, 529 | { 530 | "internalType": "uint256", 531 | "name": "amount", 532 | "type": "uint256" 533 | } 534 | ], 535 | "name": "approveAllowance", 536 | "outputs": [], 537 | "stateMutability": "nonpayable", 538 | "type": "function" 539 | }, 540 | { 541 | "inputs": [ 542 | { 543 | "internalType": "string", 544 | "name": "networkURI", 545 | "type": "string" 546 | }, 547 | { 548 | "internalType": "bytes", 549 | "name": "externalAddress", 550 | "type": "bytes" 551 | }, 552 | { 553 | "internalType": "uint256", 554 | "name": "amount", 555 | "type": "uint256" 556 | } 557 | ], 558 | "name": "approveExternalTransfer", 559 | "outputs": [], 560 | "stateMutability": "nonpayable", 561 | "type": "function" 562 | }, 563 | { 564 | "inputs": [ 565 | { 566 | "internalType": "address", 567 | "name": "account", 568 | "type": "address" 569 | } 570 | ], 571 | "name": "balanceOf", 572 | "outputs": [ 573 | { 574 | "internalType": "uint256", 575 | "name": "", 576 | "type": "uint256" 577 | } 578 | ], 579 | "stateMutability": "view", 580 | "type": "function" 581 | }, 582 | { 583 | "inputs": [ 584 | { 585 | "internalType": "uint256", 586 | "name": "amount", 587 | "type": "uint256" 588 | } 589 | ], 590 | "name": "burn", 591 | "outputs": [], 592 | "stateMutability": "nonpayable", 593 | "type": "function" 594 | }, 595 | { 596 | "inputs": [ 597 | { 598 | "internalType": "address", 599 | "name": "newComplianceManager", 600 | "type": "address" 601 | } 602 | ], 603 | "name": "changeComplianceManager", 604 | "outputs": [], 605 | "stateMutability": "nonpayable", 606 | "type": "function" 607 | }, 608 | { 609 | "inputs": [ 610 | { 611 | "internalType": "address", 612 | "name": "newEnforcementManager", 613 | "type": "address" 614 | } 615 | ], 616 | "name": "changeEnforcementManager", 617 | "outputs": [], 618 | "stateMutability": "nonpayable", 619 | "type": "function" 620 | }, 621 | { 622 | "inputs": [ 623 | { 624 | "internalType": "address", 625 | "name": "newSupplyManager", 626 | "type": "address" 627 | } 628 | ], 629 | "name": "changeSupplyManager", 630 | "outputs": [], 631 | "stateMutability": "nonpayable", 632 | "type": "function" 633 | }, 634 | { 635 | "inputs": [ 636 | { 637 | "internalType": "address", 638 | "name": "account", 639 | "type": "address" 640 | } 641 | ], 642 | "name": "checkTransferAllowed", 643 | "outputs": [ 644 | { 645 | "internalType": "bool", 646 | "name": "", 647 | "type": "bool" 648 | } 649 | ], 650 | "stateMutability": "view", 651 | "type": "function" 652 | }, 653 | { 654 | "inputs": [], 655 | "name": "claimOwnership", 656 | "outputs": [], 657 | "stateMutability": "nonpayable", 658 | "type": "function" 659 | }, 660 | { 661 | "inputs": [], 662 | "name": "complianceManager", 663 | "outputs": [ 664 | { 665 | "internalType": "address", 666 | "name": "", 667 | "type": "address" 668 | } 669 | ], 670 | "stateMutability": "view", 671 | "type": "function" 672 | }, 673 | { 674 | "inputs": [], 675 | "name": "decimals", 676 | "outputs": [ 677 | { 678 | "internalType": "uint8", 679 | "name": "", 680 | "type": "uint8" 681 | } 682 | ], 683 | "stateMutability": "view", 684 | "type": "function" 685 | }, 686 | { 687 | "inputs": [ 688 | { 689 | "internalType": "address", 690 | "name": "spender", 691 | "type": "address" 692 | }, 693 | { 694 | "internalType": "uint256", 695 | "name": "amount", 696 | "type": "uint256" 697 | } 698 | ], 699 | "name": "decreaseAllowance", 700 | "outputs": [], 701 | "stateMutability": "nonpayable", 702 | "type": "function" 703 | }, 704 | { 705 | "inputs": [], 706 | "name": "enforcementManager", 707 | "outputs": [ 708 | { 709 | "internalType": "address", 710 | "name": "", 711 | "type": "address" 712 | } 713 | ], 714 | "stateMutability": "view", 715 | "type": "function" 716 | }, 717 | { 718 | "inputs": [ 719 | { 720 | "internalType": "address", 721 | "name": "owner", 722 | "type": "address" 723 | }, 724 | { 725 | "internalType": "string", 726 | "name": "networkURI", 727 | "type": "string" 728 | }, 729 | { 730 | "internalType": "bytes", 731 | "name": "externalAddress", 732 | "type": "bytes" 733 | } 734 | ], 735 | "name": "externalAllowanceOf", 736 | "outputs": [ 737 | { 738 | "internalType": "uint256", 739 | "name": "", 740 | "type": "uint256" 741 | } 742 | ], 743 | "stateMutability": "view", 744 | "type": "function" 745 | }, 746 | { 747 | "inputs": [ 748 | { 749 | "internalType": "address", 750 | "name": "from", 751 | "type": "address" 752 | }, 753 | { 754 | "internalType": "string", 755 | "name": "networkURI", 756 | "type": "string" 757 | }, 758 | { 759 | "internalType": "bytes", 760 | "name": "to", 761 | "type": "bytes" 762 | }, 763 | { 764 | "internalType": "uint256", 765 | "name": "amount", 766 | "type": "uint256" 767 | } 768 | ], 769 | "name": "externalTransfer", 770 | "outputs": [], 771 | "stateMutability": "nonpayable", 772 | "type": "function" 773 | }, 774 | { 775 | "inputs": [ 776 | { 777 | "internalType": "bytes", 778 | "name": "from", 779 | "type": "bytes" 780 | }, 781 | { 782 | "internalType": "string", 783 | "name": "networkURI", 784 | "type": "string" 785 | }, 786 | { 787 | "internalType": "address", 788 | "name": "to", 789 | "type": "address" 790 | }, 791 | { 792 | "internalType": "uint256", 793 | "name": "amount", 794 | "type": "uint256" 795 | } 796 | ], 797 | "name": "externalTransferFrom", 798 | "outputs": [], 799 | "stateMutability": "nonpayable", 800 | "type": "function" 801 | }, 802 | { 803 | "inputs": [ 804 | { 805 | "internalType": "address", 806 | "name": "account", 807 | "type": "address" 808 | } 809 | ], 810 | "name": "freeze", 811 | "outputs": [], 812 | "stateMutability": "nonpayable", 813 | "type": "function" 814 | }, 815 | { 816 | "inputs": [ 817 | { 818 | "internalType": "address", 819 | "name": "spender", 820 | "type": "address" 821 | }, 822 | { 823 | "internalType": "uint256", 824 | "name": "amount", 825 | "type": "uint256" 826 | } 827 | ], 828 | "name": "increaseAllowance", 829 | "outputs": [], 830 | "stateMutability": "nonpayable", 831 | "type": "function" 832 | }, 833 | { 834 | "inputs": [ 835 | { 836 | "internalType": "address", 837 | "name": "account", 838 | "type": "address" 839 | } 840 | ], 841 | "name": "isFrozen", 842 | "outputs": [ 843 | { 844 | "internalType": "bool", 845 | "name": "", 846 | "type": "bool" 847 | } 848 | ], 849 | "stateMutability": "view", 850 | "type": "function" 851 | }, 852 | { 853 | "inputs": [ 854 | { 855 | "internalType": "address", 856 | "name": "account", 857 | "type": "address" 858 | } 859 | ], 860 | "name": "isKycPassed", 861 | "outputs": [ 862 | { 863 | "internalType": "bool", 864 | "name": "", 865 | "type": "bool" 866 | } 867 | ], 868 | "stateMutability": "view", 869 | "type": "function" 870 | }, 871 | { 872 | "inputs": [ 873 | { 874 | "internalType": "address", 875 | "name": "account", 876 | "type": "address" 877 | } 878 | ], 879 | "name": "isPrivilegedRole", 880 | "outputs": [ 881 | { 882 | "internalType": "bool", 883 | "name": "", 884 | "type": "bool" 885 | } 886 | ], 887 | "stateMutability": "view", 888 | "type": "function" 889 | }, 890 | { 891 | "inputs": [ 892 | { 893 | "internalType": "uint256", 894 | "name": "amount", 895 | "type": "uint256" 896 | } 897 | ], 898 | "name": "mint", 899 | "outputs": [], 900 | "stateMutability": "nonpayable", 901 | "type": "function" 902 | }, 903 | { 904 | "inputs": [], 905 | "name": "name", 906 | "outputs": [ 907 | { 908 | "internalType": "string", 909 | "name": "", 910 | "type": "string" 911 | } 912 | ], 913 | "stateMutability": "view", 914 | "type": "function" 915 | }, 916 | { 917 | "inputs": [], 918 | "name": "owner", 919 | "outputs": [ 920 | { 921 | "internalType": "address", 922 | "name": "", 923 | "type": "address" 924 | } 925 | ], 926 | "stateMutability": "view", 927 | "type": "function" 928 | }, 929 | { 930 | "inputs": [], 931 | "name": "pause", 932 | "outputs": [], 933 | "stateMutability": "nonpayable", 934 | "type": "function" 935 | }, 936 | { 937 | "inputs": [], 938 | "name": "paused", 939 | "outputs": [ 940 | { 941 | "internalType": "bool", 942 | "name": "", 943 | "type": "bool" 944 | } 945 | ], 946 | "stateMutability": "view", 947 | "type": "function" 948 | }, 949 | { 950 | "inputs": [ 951 | { 952 | "internalType": "address", 953 | "name": "newOwner", 954 | "type": "address" 955 | } 956 | ], 957 | "name": "proposeOwner", 958 | "outputs": [], 959 | "stateMutability": "nonpayable", 960 | "type": "function" 961 | }, 962 | { 963 | "inputs": [], 964 | "name": "proposedOwner", 965 | "outputs": [ 966 | { 967 | "internalType": "address", 968 | "name": "", 969 | "type": "address" 970 | } 971 | ], 972 | "stateMutability": "view", 973 | "type": "function" 974 | }, 975 | { 976 | "inputs": [ 977 | { 978 | "internalType": "address", 979 | "name": "account", 980 | "type": "address" 981 | } 982 | ], 983 | "name": "setKycPassed", 984 | "outputs": [], 985 | "stateMutability": "nonpayable", 986 | "type": "function" 987 | }, 988 | { 989 | "inputs": [], 990 | "name": "supplyManager", 991 | "outputs": [ 992 | { 993 | "internalType": "address", 994 | "name": "", 995 | "type": "address" 996 | } 997 | ], 998 | "stateMutability": "view", 999 | "type": "function" 1000 | }, 1001 | { 1002 | "inputs": [], 1003 | "name": "symbol", 1004 | "outputs": [ 1005 | { 1006 | "internalType": "string", 1007 | "name": "", 1008 | "type": "string" 1009 | } 1010 | ], 1011 | "stateMutability": "view", 1012 | "type": "function" 1013 | }, 1014 | { 1015 | "inputs": [], 1016 | "name": "totalSupply", 1017 | "outputs": [ 1018 | { 1019 | "internalType": "uint256", 1020 | "name": "", 1021 | "type": "uint256" 1022 | } 1023 | ], 1024 | "stateMutability": "view", 1025 | "type": "function" 1026 | }, 1027 | { 1028 | "inputs": [ 1029 | { 1030 | "internalType": "address", 1031 | "name": "to", 1032 | "type": "address" 1033 | }, 1034 | { 1035 | "internalType": "uint256", 1036 | "name": "amount", 1037 | "type": "uint256" 1038 | } 1039 | ], 1040 | "name": "transfer", 1041 | "outputs": [], 1042 | "stateMutability": "nonpayable", 1043 | "type": "function" 1044 | }, 1045 | { 1046 | "inputs": [ 1047 | { 1048 | "internalType": "address", 1049 | "name": "from", 1050 | "type": "address" 1051 | }, 1052 | { 1053 | "internalType": "address", 1054 | "name": "to", 1055 | "type": "address" 1056 | }, 1057 | { 1058 | "internalType": "uint256", 1059 | "name": "amount", 1060 | "type": "uint256" 1061 | } 1062 | ], 1063 | "name": "transferFrom", 1064 | "outputs": [], 1065 | "stateMutability": "nonpayable", 1066 | "type": "function" 1067 | }, 1068 | { 1069 | "inputs": [ 1070 | { 1071 | "internalType": "address", 1072 | "name": "account", 1073 | "type": "address" 1074 | } 1075 | ], 1076 | "name": "unfreeze", 1077 | "outputs": [], 1078 | "stateMutability": "nonpayable", 1079 | "type": "function" 1080 | }, 1081 | { 1082 | "inputs": [], 1083 | "name": "unpause", 1084 | "outputs": [], 1085 | "stateMutability": "nonpayable", 1086 | "type": "function" 1087 | }, 1088 | { 1089 | "inputs": [ 1090 | { 1091 | "internalType": "address", 1092 | "name": "account", 1093 | "type": "address" 1094 | } 1095 | ], 1096 | "name": "unsetKycPassed", 1097 | "outputs": [], 1098 | "stateMutability": "nonpayable", 1099 | "type": "function" 1100 | }, 1101 | { 1102 | "inputs": [ 1103 | { 1104 | "internalType": "address", 1105 | "name": "account", 1106 | "type": "address" 1107 | }, 1108 | { 1109 | "internalType": "uint256", 1110 | "name": "amount", 1111 | "type": "uint256" 1112 | } 1113 | ], 1114 | "name": "wipe", 1115 | "outputs": [], 1116 | "stateMutability": "nonpayable", 1117 | "type": "function" 1118 | } 1119 | ] -------------------------------------------------------------------------------- /build/contracts/Migrations.json: -------------------------------------------------------------------------------- 1 | { 2 | "fileName": "Migrations.sol", 3 | "contractName": "Migrations", 4 | "source": "// SPDX-License-Identifier: MIT\npragma solidity >=0.4.21 <0.7.0;\n\ncontract Migrations {\n address public owner;\n uint public last_completed_migration;\n\n constructor() public {\n owner = msg.sender;\n }\n\n modifier restricted() {\n if (msg.sender == owner) _;\n }\n\n function setCompleted(uint completed) public restricted {\n last_completed_migration = completed;\n }\n}\n", 5 | "sourcePath": "contracts/Migrations.sol", 6 | "sourceMap": "66:311:7:-:0;;;155:50;5:9:-1;2:2;;;27:1;24;17:12;2:2;-1:-1;182:5:7;:18;;-1:-1:-1;;;;;;182:18:7;190:10;182:18;;;66:311;;;;;;", 7 | "deployedSourceMap": "66:311:7:-:0;;;;5:9:-1;2:2;;;27:1;24;17:12;2:2;66:311:7;;;;;;;;;;;;;;;;;;;;;;;;;;12:1:-1;9;2:12;114:36:7;;;:::i;:::-;;;;;;;;;;;;;;;;90:20;;;:::i;:::-;;;;;;;;;;;;;;;;;;;272:103;;;;;;15:2:-1;10:3;7:11;4:2;;;31:1;28;21:12;4:2;-1:-1;272:103:7;;:::i;:::-;;114:36;;;;:::o;90:20::-;;;;;;:::o;272:103::-;255:5;;;;241:10;:19;237:26;;;334:24:::1;:36:::0;;;237:26;272:103;:::o", 8 | "abi": [ 9 | { 10 | "inputs": [], 11 | "stateMutability": "nonpayable", 12 | "type": "constructor" 13 | }, 14 | { 15 | "inputs": [], 16 | "name": "last_completed_migration", 17 | "outputs": [ 18 | { 19 | "internalType": "uint256", 20 | "name": "", 21 | "type": "uint256" 22 | } 23 | ], 24 | "stateMutability": "view", 25 | "type": "function" 26 | }, 27 | { 28 | "inputs": [], 29 | "name": "owner", 30 | "outputs": [ 31 | { 32 | "internalType": "address", 33 | "name": "", 34 | "type": "address" 35 | } 36 | ], 37 | "stateMutability": "view", 38 | "type": "function" 39 | }, 40 | { 41 | "inputs": [ 42 | { 43 | "internalType": "uint256", 44 | "name": "completed", 45 | "type": "uint256" 46 | } 47 | ], 48 | "name": "setCompleted", 49 | "outputs": [], 50 | "stateMutability": "nonpayable", 51 | "type": "function" 52 | } 53 | ], 54 | "ast": { 55 | "absolutePath": "contracts/Migrations.sol", 56 | "exportedSymbols": { 57 | "Migrations": [ 58 | 1433 59 | ] 60 | }, 61 | "id": 1434, 62 | "license": "MIT", 63 | "nodeType": "SourceUnit", 64 | "nodes": [ 65 | { 66 | "id": 1398, 67 | "literals": [ 68 | "solidity", 69 | ">=", 70 | "0.4", 71 | ".21", 72 | "<", 73 | "0.7", 74 | ".0" 75 | ], 76 | "nodeType": "PragmaDirective", 77 | "src": "32:32:7" 78 | }, 79 | { 80 | "abstract": false, 81 | "baseContracts": [], 82 | "contractDependencies": [], 83 | "contractKind": "contract", 84 | "documentation": null, 85 | "fullyImplemented": true, 86 | "id": 1433, 87 | "linearizedBaseContracts": [ 88 | 1433 89 | ], 90 | "name": "Migrations", 91 | "nodeType": "ContractDefinition", 92 | "nodes": [ 93 | { 94 | "constant": false, 95 | "functionSelector": "8da5cb5b", 96 | "id": 1400, 97 | "mutability": "mutable", 98 | "name": "owner", 99 | "nodeType": "VariableDeclaration", 100 | "overrides": null, 101 | "scope": 1433, 102 | "src": "90:20:7", 103 | "stateVariable": true, 104 | "storageLocation": "default", 105 | "typeDescriptions": { 106 | "typeIdentifier": "t_address", 107 | "typeString": "address" 108 | }, 109 | "typeName": { 110 | "id": 1399, 111 | "name": "address", 112 | "nodeType": "ElementaryTypeName", 113 | "src": "90:7:7", 114 | "stateMutability": "nonpayable", 115 | "typeDescriptions": { 116 | "typeIdentifier": "t_address", 117 | "typeString": "address" 118 | } 119 | }, 120 | "value": null, 121 | "visibility": "public" 122 | }, 123 | { 124 | "constant": false, 125 | "functionSelector": "445df0ac", 126 | "id": 1402, 127 | "mutability": "mutable", 128 | "name": "last_completed_migration", 129 | "nodeType": "VariableDeclaration", 130 | "overrides": null, 131 | "scope": 1433, 132 | "src": "114:36:7", 133 | "stateVariable": true, 134 | "storageLocation": "default", 135 | "typeDescriptions": { 136 | "typeIdentifier": "t_uint256", 137 | "typeString": "uint256" 138 | }, 139 | "typeName": { 140 | "id": 1401, 141 | "name": "uint", 142 | "nodeType": "ElementaryTypeName", 143 | "src": "114:4:7", 144 | "typeDescriptions": { 145 | "typeIdentifier": "t_uint256", 146 | "typeString": "uint256" 147 | } 148 | }, 149 | "value": null, 150 | "visibility": "public" 151 | }, 152 | { 153 | "body": { 154 | "id": 1410, 155 | "nodeType": "Block", 156 | "src": "176:29:7", 157 | "statements": [ 158 | { 159 | "expression": { 160 | "argumentTypes": null, 161 | "id": 1408, 162 | "isConstant": false, 163 | "isLValue": false, 164 | "isPure": false, 165 | "lValueRequested": false, 166 | "leftHandSide": { 167 | "argumentTypes": null, 168 | "id": 1405, 169 | "name": "owner", 170 | "nodeType": "Identifier", 171 | "overloadedDeclarations": [], 172 | "referencedDeclaration": 1400, 173 | "src": "182:5:7", 174 | "typeDescriptions": { 175 | "typeIdentifier": "t_address", 176 | "typeString": "address" 177 | } 178 | }, 179 | "nodeType": "Assignment", 180 | "operator": "=", 181 | "rightHandSide": { 182 | "argumentTypes": null, 183 | "expression": { 184 | "argumentTypes": null, 185 | "id": 1406, 186 | "name": "msg", 187 | "nodeType": "Identifier", 188 | "overloadedDeclarations": [], 189 | "referencedDeclaration": -15, 190 | "src": "190:3:7", 191 | "typeDescriptions": { 192 | "typeIdentifier": "t_magic_message", 193 | "typeString": "msg" 194 | } 195 | }, 196 | "id": 1407, 197 | "isConstant": false, 198 | "isLValue": false, 199 | "isPure": false, 200 | "lValueRequested": false, 201 | "memberName": "sender", 202 | "nodeType": "MemberAccess", 203 | "referencedDeclaration": null, 204 | "src": "190:10:7", 205 | "typeDescriptions": { 206 | "typeIdentifier": "t_address_payable", 207 | "typeString": "address payable" 208 | } 209 | }, 210 | "src": "182:18:7", 211 | "typeDescriptions": { 212 | "typeIdentifier": "t_address", 213 | "typeString": "address" 214 | } 215 | }, 216 | "id": 1409, 217 | "nodeType": "ExpressionStatement", 218 | "src": "182:18:7" 219 | } 220 | ] 221 | }, 222 | "documentation": null, 223 | "id": 1411, 224 | "implemented": true, 225 | "kind": "constructor", 226 | "modifiers": [], 227 | "name": "", 228 | "nodeType": "FunctionDefinition", 229 | "overrides": null, 230 | "parameters": { 231 | "id": 1403, 232 | "nodeType": "ParameterList", 233 | "parameters": [], 234 | "src": "166:2:7" 235 | }, 236 | "returnParameters": { 237 | "id": 1404, 238 | "nodeType": "ParameterList", 239 | "parameters": [], 240 | "src": "176:0:7" 241 | }, 242 | "scope": 1433, 243 | "src": "155:50:7", 244 | "stateMutability": "nonpayable", 245 | "virtual": false, 246 | "visibility": "public" 247 | }, 248 | { 249 | "body": { 250 | "id": 1419, 251 | "nodeType": "Block", 252 | "src": "231:37:7", 253 | "statements": [ 254 | { 255 | "condition": { 256 | "argumentTypes": null, 257 | "commonType": { 258 | "typeIdentifier": "t_address", 259 | "typeString": "address" 260 | }, 261 | "id": 1416, 262 | "isConstant": false, 263 | "isLValue": false, 264 | "isPure": false, 265 | "lValueRequested": false, 266 | "leftExpression": { 267 | "argumentTypes": null, 268 | "expression": { 269 | "argumentTypes": null, 270 | "id": 1413, 271 | "name": "msg", 272 | "nodeType": "Identifier", 273 | "overloadedDeclarations": [], 274 | "referencedDeclaration": -15, 275 | "src": "241:3:7", 276 | "typeDescriptions": { 277 | "typeIdentifier": "t_magic_message", 278 | "typeString": "msg" 279 | } 280 | }, 281 | "id": 1414, 282 | "isConstant": false, 283 | "isLValue": false, 284 | "isPure": false, 285 | "lValueRequested": false, 286 | "memberName": "sender", 287 | "nodeType": "MemberAccess", 288 | "referencedDeclaration": null, 289 | "src": "241:10:7", 290 | "typeDescriptions": { 291 | "typeIdentifier": "t_address_payable", 292 | "typeString": "address payable" 293 | } 294 | }, 295 | "nodeType": "BinaryOperation", 296 | "operator": "==", 297 | "rightExpression": { 298 | "argumentTypes": null, 299 | "id": 1415, 300 | "name": "owner", 301 | "nodeType": "Identifier", 302 | "overloadedDeclarations": [], 303 | "referencedDeclaration": 1400, 304 | "src": "255:5:7", 305 | "typeDescriptions": { 306 | "typeIdentifier": "t_address", 307 | "typeString": "address" 308 | } 309 | }, 310 | "src": "241:19:7", 311 | "typeDescriptions": { 312 | "typeIdentifier": "t_bool", 313 | "typeString": "bool" 314 | } 315 | }, 316 | "falseBody": null, 317 | "id": 1418, 318 | "nodeType": "IfStatement", 319 | "src": "237:26:7", 320 | "trueBody": { 321 | "id": 1417, 322 | "nodeType": "PlaceholderStatement", 323 | "src": "262:1:7" 324 | } 325 | } 326 | ] 327 | }, 328 | "documentation": null, 329 | "id": 1420, 330 | "name": "restricted", 331 | "nodeType": "ModifierDefinition", 332 | "overrides": null, 333 | "parameters": { 334 | "id": 1412, 335 | "nodeType": "ParameterList", 336 | "parameters": [], 337 | "src": "228:2:7" 338 | }, 339 | "src": "209:59:7", 340 | "virtual": false, 341 | "visibility": "internal" 342 | }, 343 | { 344 | "body": { 345 | "id": 1431, 346 | "nodeType": "Block", 347 | "src": "328:47:7", 348 | "statements": [ 349 | { 350 | "expression": { 351 | "argumentTypes": null, 352 | "id": 1429, 353 | "isConstant": false, 354 | "isLValue": false, 355 | "isPure": false, 356 | "lValueRequested": false, 357 | "leftHandSide": { 358 | "argumentTypes": null, 359 | "id": 1427, 360 | "name": "last_completed_migration", 361 | "nodeType": "Identifier", 362 | "overloadedDeclarations": [], 363 | "referencedDeclaration": 1402, 364 | "src": "334:24:7", 365 | "typeDescriptions": { 366 | "typeIdentifier": "t_uint256", 367 | "typeString": "uint256" 368 | } 369 | }, 370 | "nodeType": "Assignment", 371 | "operator": "=", 372 | "rightHandSide": { 373 | "argumentTypes": null, 374 | "id": 1428, 375 | "name": "completed", 376 | "nodeType": "Identifier", 377 | "overloadedDeclarations": [], 378 | "referencedDeclaration": 1422, 379 | "src": "361:9:7", 380 | "typeDescriptions": { 381 | "typeIdentifier": "t_uint256", 382 | "typeString": "uint256" 383 | } 384 | }, 385 | "src": "334:36:7", 386 | "typeDescriptions": { 387 | "typeIdentifier": "t_uint256", 388 | "typeString": "uint256" 389 | } 390 | }, 391 | "id": 1430, 392 | "nodeType": "ExpressionStatement", 393 | "src": "334:36:7" 394 | } 395 | ] 396 | }, 397 | "documentation": null, 398 | "functionSelector": "fdacd576", 399 | "id": 1432, 400 | "implemented": true, 401 | "kind": "function", 402 | "modifiers": [ 403 | { 404 | "arguments": null, 405 | "id": 1425, 406 | "modifierName": { 407 | "argumentTypes": null, 408 | "id": 1424, 409 | "name": "restricted", 410 | "nodeType": "Identifier", 411 | "overloadedDeclarations": [], 412 | "referencedDeclaration": 1420, 413 | "src": "317:10:7", 414 | "typeDescriptions": { 415 | "typeIdentifier": "t_modifier$__$", 416 | "typeString": "modifier ()" 417 | } 418 | }, 419 | "nodeType": "ModifierInvocation", 420 | "src": "317:10:7" 421 | } 422 | ], 423 | "name": "setCompleted", 424 | "nodeType": "FunctionDefinition", 425 | "overrides": null, 426 | "parameters": { 427 | "id": 1423, 428 | "nodeType": "ParameterList", 429 | "parameters": [ 430 | { 431 | "constant": false, 432 | "id": 1422, 433 | "mutability": "mutable", 434 | "name": "completed", 435 | "nodeType": "VariableDeclaration", 436 | "overrides": null, 437 | "scope": 1432, 438 | "src": "294:14:7", 439 | "stateVariable": false, 440 | "storageLocation": "default", 441 | "typeDescriptions": { 442 | "typeIdentifier": "t_uint256", 443 | "typeString": "uint256" 444 | }, 445 | "typeName": { 446 | "id": 1421, 447 | "name": "uint", 448 | "nodeType": "ElementaryTypeName", 449 | "src": "294:4:7", 450 | "typeDescriptions": { 451 | "typeIdentifier": "t_uint256", 452 | "typeString": "uint256" 453 | } 454 | }, 455 | "value": null, 456 | "visibility": "internal" 457 | } 458 | ], 459 | "src": "293:16:7" 460 | }, 461 | "returnParameters": { 462 | "id": 1426, 463 | "nodeType": "ParameterList", 464 | "parameters": [], 465 | "src": "328:0:7" 466 | }, 467 | "scope": 1433, 468 | "src": "272:103:7", 469 | "stateMutability": "nonpayable", 470 | "virtual": false, 471 | "visibility": "public" 472 | } 473 | ], 474 | "scope": 1434, 475 | "src": "66:311:7" 476 | } 477 | ], 478 | "src": "32:346:7" 479 | }, 480 | "bytecode": "0x608060405234801561001057600080fd5b50600080546001600160a01b03191633179055610124806100326000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c8063445df0ac1460415780638da5cb5b146059578063fdacd576146088575b600080fd5b604760a4565b60408051918252519081900360200190f35b605f60aa565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b60a260048036036020811015609c57600080fd5b503560c6565b005b60015481565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b60005473ffffffffffffffffffffffffffffffffffffffff1633141560eb5760018190555b5056fea2646970667358221220e716f8dcb759ca3c9044547ce0b9abea04d657dfdebcb1898b125d676fa4e18964736f6c63430006080033", 481 | "deployedBytecode": "0x6080604052348015600f57600080fd5b5060043610603c5760003560e01c8063445df0ac1460415780638da5cb5b146059578063fdacd576146088575b600080fd5b604760a4565b60408051918252519081900360200190f35b605f60aa565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b60a260048036036020811015609c57600080fd5b503560c6565b005b60015481565b60005473ffffffffffffffffffffffffffffffffffffffff1681565b60005473ffffffffffffffffffffffffffffffffffffffff1633141560eb5760018190555b5056fea2646970667358221220e716f8dcb759ca3c9044547ce0b9abea04d657dfdebcb1898b125d676fa4e18964736f6c63430006080033", 482 | "compiler": { 483 | "name": "solc", 484 | "version": "0.6.8+commit.0bbfe453.Emscripten.clang", 485 | "optimizer": { 486 | "enabled": true, 487 | "runs": 2000 488 | }, 489 | "evmVersion": "petersburg" 490 | } 491 | } 492 | -------------------------------------------------------------------------------- /contracts/AccessControl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.6.0; 3 | 4 | import "@openzeppelin/contracts-upgradeable/utils/EnumerableSetUpgradeable.sol"; 5 | import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; 6 | 7 | import "./Context.sol"; 8 | 9 | contract AccessControl is ContextAware { 10 | using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; 11 | using AddressUpgradeable for address; 12 | 13 | struct RoleData { 14 | EnumerableSetUpgradeable.AddressSet members; 15 | bytes32 adminRole; 16 | } 17 | 18 | constructor() public {} 19 | 20 | mapping (bytes32 => RoleData) private _roles; 21 | 22 | bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; 23 | 24 | function hasRole(bytes32 role, address account) internal view returns (bool) { 25 | return _roles[role].members.contains(account); 26 | } 27 | 28 | function grantRole(bytes32 role, address account) internal virtual { 29 | require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant"); 30 | _grantRole(role, account); 31 | } 32 | 33 | function revokeRole(bytes32 role, address account) internal virtual { 34 | require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke"); 35 | _revokeRole(role, account); 36 | } 37 | 38 | function _setupRole(bytes32 role, address account) internal virtual { 39 | _grantRole(role, account); 40 | } 41 | 42 | function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { 43 | _roles[role].adminRole = adminRole; 44 | } 45 | 46 | function _grantRole(bytes32 role, address account) private { 47 | _roles[role].members.add(account); 48 | } 49 | 50 | function _revokeRole(bytes32 role, address account) private { 51 | _roles[role].members.remove(account); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /contracts/Context.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.21; 3 | 4 | contract ContextAware { 5 | function _msgSender() internal view virtual returns (address payable) { 6 | return msg.sender; 7 | } 8 | 9 | function _msgData() internal view virtual returns (bytes memory) { 10 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 11 | return msg.data; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /contracts/ERC20.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.21; 3 | 4 | import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; 5 | import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol"; 6 | 7 | import "./Context.sol"; 8 | 9 | /* 10 | * NOTE: This is a stripped down version of the OZ ERC20 Contract that does not 11 | * emit events and that renames approve --> approveAllowance 12 | */ 13 | abstract contract ERC20 is ContextAware { 14 | using SafeMathUpgradeable for uint256; 15 | using AddressUpgradeable for address; 16 | 17 | mapping (address => uint256) private _balances; 18 | 19 | mapping (address => mapping (address => uint256)) private _allowances; 20 | 21 | uint256 private _totalSupply; 22 | 23 | string private _name; 24 | string private _symbol; 25 | uint8 private _decimals; 26 | 27 | constructor(string memory name, string memory symbol, uint8 decimals) internal { 28 | _name = name; 29 | _symbol = symbol; 30 | _decimals = decimals; 31 | } 32 | 33 | function name() public view returns (string memory) { 34 | return _name; 35 | } 36 | 37 | function symbol() public view returns (string memory) { 38 | return _symbol; 39 | } 40 | 41 | function decimals() public view returns (uint8) { 42 | return _decimals; 43 | } 44 | 45 | function totalSupply() public view returns (uint256) { 46 | return _totalSupply; 47 | } 48 | 49 | function balanceOf(address account) public view returns (uint256) { 50 | return _balances[account]; 51 | } 52 | 53 | function transfer(address recipient, uint256 amount) public virtual { 54 | _transfer(_msgSender(), recipient, amount); 55 | } 56 | 57 | function allowance(address owner, address spender) public view virtual returns (uint256) { 58 | return _allowances[owner][spender]; 59 | } 60 | 61 | function approveAllowance(address spender, uint256 amount) public virtual { 62 | _approve(_msgSender(), spender, amount); 63 | } 64 | 65 | function transferFrom(address sender, address recipient, uint256 amount) public virtual { 66 | _transfer(sender, recipient, amount); 67 | _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); 68 | } 69 | 70 | function increaseAllowance(address spender, uint256 addedValue) public virtual { 71 | _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); 72 | } 73 | 74 | function decreaseAllowance(address spender, uint256 subtractedValue) public virtual { 75 | _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); 76 | } 77 | 78 | function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } 79 | 80 | function _transfer(address sender, address recipient, uint256 amount) internal virtual { 81 | require(sender != address(0), "ERC20: transfer from the zero address"); 82 | require(recipient != address(0), "ERC20: transfer to the zero address"); 83 | 84 | _beforeTokenTransfer(sender, recipient, amount); 85 | 86 | _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); 87 | _balances[recipient] = _balances[recipient].add(amount); 88 | } 89 | 90 | function _mint(address account, uint256 amount) internal virtual { 91 | require(account != address(0), "ERC20: mint to the zero address"); 92 | 93 | _beforeTokenTransfer(address(0), account, amount); 94 | 95 | _totalSupply = _totalSupply.add(amount); 96 | _balances[account] = _balances[account].add(amount); 97 | } 98 | 99 | function _burn(address account, uint256 amount) internal virtual { 100 | require(account != address(0), "ERC20: burn from the zero address"); 101 | 102 | _beforeTokenTransfer(account, address(0), amount); 103 | 104 | _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); 105 | _totalSupply = _totalSupply.sub(amount); 106 | } 107 | 108 | function _beforeTokenAllowance(address owner, address spender, uint256 amount) internal virtual { } 109 | 110 | function _approve(address owner, address spender, uint256 amount) internal virtual { 111 | require(owner != address(0), "ERC20: approve from the zero address"); 112 | require(spender != address(0), "ERC20: approve to the zero address"); 113 | 114 | _beforeTokenAllowance(owner, spender, amount); 115 | 116 | _allowances[owner][spender] = amount; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /contracts/ExternallyTransferable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.21; 3 | 4 | import "@openzeppelin/contracts-upgradeable/math/SafeMathUpgradeable.sol"; 5 | 6 | import "./Context.sol"; 7 | 8 | abstract contract ExternallyTransferable is ContextAware { 9 | using SafeMathUpgradeable for uint256; 10 | 11 | // address => string (network URI) => bytes (external address) => amount 12 | mapping(address => mapping(string => mapping(bytes => uint256))) 13 | private _externalAllowances; 14 | 15 | function externalAllowanceOf( 16 | address owner, 17 | string memory networkURI, 18 | bytes memory externalAddress 19 | ) public view returns (uint256) { 20 | return _externalAllowances[owner][networkURI][externalAddress]; 21 | } 22 | 23 | // User calls to allocate external transfer 24 | function approveExternalTransfer( 25 | string memory networkURI, 26 | bytes memory externalAddress, 27 | uint256 amount 28 | ) public virtual { 29 | _approveExternalAllowance(_msgSender(), networkURI, externalAddress, amount); 30 | } 31 | 32 | // Bridge calls after externalTransfer 33 | function _approveExternalAllowance( 34 | address from, 35 | string memory networkURI, 36 | bytes memory to, 37 | uint256 amount 38 | ) internal virtual { 39 | require(_msgSender() != address(0), "Approve from the zero address"); 40 | require(from != address(0), "Approve for the zero address"); 41 | _externalAllowances[from][networkURI][to] = amount; 42 | } 43 | 44 | // Bridge calls to burn coins on this network (sending external transfer) 45 | function externalTransfer( 46 | address from, 47 | string memory networkURI, 48 | bytes memory to, // external address 49 | uint256 amount 50 | ) public virtual {} 51 | 52 | // Bridge calls to mint coins on this network (receiving external transfer) 53 | function externalTransferFrom( 54 | bytes memory from, // external address 55 | string memory networkURI, 56 | address to, 57 | uint256 amount 58 | ) public virtual {} 59 | } 60 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.21 <0.7.0; 3 | 4 | contract Migrations { 5 | address public owner; 6 | uint public last_completed_migration; 7 | 8 | constructor() public { 9 | owner = msg.sender; 10 | } 11 | 12 | modifier restricted() { 13 | if (msg.sender == owner) _; 14 | } 15 | 16 | function setCompleted(uint completed) public restricted { 17 | last_completed_migration = completed; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /contracts/Ownable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.21; 3 | 4 | import "./Context.sol"; 5 | 6 | contract Ownable is ContextAware { 7 | address private _owner; 8 | address private _proposedOwner; 9 | 10 | event ProposeOwner(address indexed proposedOwner); 11 | event ClaimOwnership(address newOwner); 12 | 13 | constructor() public { 14 | address msgSender = _msgSender(); 15 | _owner = msgSender; 16 | emit ClaimOwnership(msgSender); 17 | } 18 | 19 | function owner() public view returns (address) { 20 | return _owner; 21 | } 22 | 23 | function proposedOwner() public view returns (address) { 24 | return _proposedOwner; 25 | } 26 | 27 | modifier onlyOwner() { 28 | require( 29 | _owner == _msgSender(), 30 | "Only the owner can call this function." 31 | ); 32 | _; 33 | } 34 | 35 | modifier onlyProposedOwner() { 36 | require( 37 | _msgSender() == _proposedOwner, 38 | "Only the proposed owner can call this function." 39 | ); 40 | _; 41 | } 42 | 43 | function disregardProposedOwner() private onlyOwner { 44 | _proposedOwner = address(0); 45 | } 46 | 47 | function proposeOwner(address newOwner) public onlyOwner { 48 | require(newOwner != address(0), "Cannot propose 0x0 as new owner."); 49 | _proposedOwner = newOwner; 50 | emit ProposeOwner(_proposedOwner); 51 | } 52 | 53 | function claimOwnership() public virtual onlyProposedOwner { 54 | _owner = _proposedOwner; 55 | _proposedOwner = address(0); 56 | emit ClaimOwnership(_owner); 57 | } 58 | 59 | uint256[49] private __gap; 60 | } 61 | -------------------------------------------------------------------------------- /contracts/Pausable.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.21; 3 | 4 | import "./Context.sol"; 5 | 6 | contract Pausable is ContextAware { 7 | bool private _paused; 8 | 9 | constructor() public { 10 | _paused = false; 11 | } 12 | 13 | function paused() public view returns (bool) { 14 | return _paused; 15 | } 16 | 17 | modifier whenNotPaused() { 18 | require(!_paused, "Pausable: paused"); 19 | _; 20 | } 21 | 22 | modifier whenPaused() { 23 | require(_paused, "Pausable: not paused"); 24 | _; 25 | } 26 | 27 | function _pause() internal virtual whenNotPaused { 28 | _paused = true; 29 | } 30 | 31 | function _unpause() internal virtual whenPaused { 32 | _paused = false; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /contracts/StableCoin.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.21 <0.7.1; 3 | 4 | import "./Context.sol"; 5 | import "./Pausable.sol"; 6 | import "./Ownable.sol"; 7 | import "./AccessControl.sol"; 8 | import "./ERC20.sol"; 9 | import "./ExternallyTransferable.sol"; 10 | 11 | contract StableCoin is 12 | ContextAware, // provides _msgSender(), _msgData() 13 | Pausable, // provides _pause(), _unpause() 14 | Ownable, // Ownable, Claimable 15 | AccessControl, // RBAC for KYC, Frozen 16 | ERC20, // ERC20 Functions (transfer, balance, allowance, mint, burn) 17 | ExternallyTransferable // Supports External Transfers 18 | { 19 | // Defined Roles 20 | bytes32 private constant KYC_PASSED = keccak256("KYC_PASSED"); 21 | bytes32 private constant FROZEN = keccak256("FROZEN"); 22 | 23 | // Special People 24 | address private _supplyManager; 25 | address private _complianceManager; 26 | address private _enforcementManager; 27 | 28 | // Events Emitted 29 | event Constructed( 30 | string tokenName, 31 | string tokenSymbol, 32 | uint8 tokenDecimal, 33 | uint256 totalSupply, 34 | address supplyManager, 35 | address complianceManager, 36 | address enforcementManager 37 | ); 38 | 39 | // Privileged Roles 40 | event ChangeSupplyManager(address newSupplyManager); 41 | event ChangeComplianceManager(address newComplianceManager); 42 | event ChangeEnforcementManager(address newEnforcementManager); 43 | 44 | // ERC20+ 45 | event Wipe(address account, uint256 amount); 46 | event Mint(address account, uint256 amount); 47 | event Burn(address account, uint256 amount); 48 | event Transfer(address sender, address recipient, uint256 amount); 49 | event Approve(address sender, address spender, uint256 amount); 50 | event IncreaseAllowance(address sender, address spender, uint256 amount); 51 | event DecreaseAllowance(address sender, address spender, uint256 amount); 52 | 53 | // KYC 54 | event Freeze(address account); // Freeze: Freeze this account 55 | event Unfreeze(address account); 56 | event SetKycPassed(address account); 57 | event UnsetKycPassed(address account); 58 | 59 | // Halt 60 | event Pause(address sender); // Pause: Pause entire contract 61 | event Unpause(address sender); 62 | 63 | // "External Transfer" 64 | // Signify to the coin bridge to perform external transfer 65 | event ApproveExternalTransfer( 66 | address from, 67 | string networkURI, 68 | bytes to, 69 | uint256 amount 70 | ); 71 | event ExternalTransfer( 72 | address from, 73 | string networkURI, 74 | bytes to, 75 | uint256 amount 76 | ); 77 | event ExternalTransferFrom( 78 | bytes from, 79 | string networkURI, 80 | address to, 81 | uint256 amount 82 | ); 83 | 84 | constructor( 85 | string memory tokenName, 86 | string memory tokenSymbol, 87 | uint8 tokenDecimal, 88 | uint256 totalSupply, 89 | address supplyManager, 90 | address complianceManager, 91 | address enforcementManager 92 | ) public ERC20(tokenName, tokenSymbol, tokenDecimal) { 93 | _supplyManager = supplyManager; 94 | _complianceManager = complianceManager; 95 | _enforcementManager = enforcementManager; 96 | 97 | // Owner has Admin Privileges on all roles 98 | _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); // sudo role 99 | 100 | // Give CM ability to grant/revoke roles (but not admin of this role) 101 | grantRole(DEFAULT_ADMIN_ROLE, complianceManager); 102 | 103 | // KYC accounts 104 | grantRole(KYC_PASSED, _msgSender()); 105 | grantRole(KYC_PASSED, supplyManager); 106 | grantRole(KYC_PASSED, complianceManager); 107 | grantRole(KYC_PASSED, enforcementManager); 108 | 109 | // Give supply manager all tokens 110 | mint(totalSupply); // Emits Mint 111 | 112 | // Did it 113 | emit Constructed( 114 | tokenName, 115 | tokenSymbol, 116 | tokenDecimal, 117 | totalSupply, 118 | supplyManager, 119 | complianceManager, 120 | enforcementManager 121 | ); 122 | } 123 | 124 | /* 125 | * RBAC 126 | */ 127 | 128 | function supplyManager() public view returns (address) { 129 | return _supplyManager; 130 | } 131 | 132 | modifier onlySupplyManager() { 133 | require( 134 | _msgSender() == supplyManager() || _msgSender() == owner(), 135 | "Only the supply manager can call this function." 136 | ); 137 | _; 138 | } 139 | 140 | function changeSupplyManager(address newSupplyManager) public onlyOwner { 141 | require( 142 | newSupplyManager != address(0), 143 | "Cannot change supply manager to 0x0." 144 | ); 145 | revokeRole(KYC_PASSED, _supplyManager); 146 | _supplyManager = newSupplyManager; 147 | grantRole(KYC_PASSED, _supplyManager); 148 | revokeRole(FROZEN, _supplyManager); 149 | emit ChangeSupplyManager(newSupplyManager); 150 | } 151 | 152 | function complianceManager() public view returns (address) { 153 | return _complianceManager; 154 | } 155 | 156 | modifier onlyComplianceManager() { 157 | require( 158 | _msgSender() == complianceManager() || _msgSender() == owner(), 159 | "Only the Compliance Manager can call this function." 160 | ); 161 | _; 162 | } 163 | 164 | function changeComplianceManager(address newComplianceManager) 165 | public 166 | onlyOwner 167 | { 168 | require( 169 | newComplianceManager != address(0), 170 | "Cannot change compliance manager to 0x0." 171 | ); 172 | revokeRole(KYC_PASSED, _complianceManager); 173 | _complianceManager = newComplianceManager; 174 | grantRole(KYC_PASSED, _complianceManager); 175 | revokeRole(FROZEN, _complianceManager); 176 | emit ChangeComplianceManager(newComplianceManager); 177 | } 178 | 179 | function enforcementManager() public view returns (address) { 180 | return _enforcementManager; 181 | } 182 | 183 | modifier onlyEnforcementManager() { 184 | require( 185 | _msgSender() == enforcementManager() || _msgSender() == owner(), 186 | "Only the Enforcement Manager can call this function." 187 | ); 188 | _; 189 | } 190 | 191 | function changeEnforcementManager(address newEnforcementManager) 192 | public 193 | onlyOwner 194 | { 195 | require( 196 | newEnforcementManager != address(0), 197 | "Cannot change enforcement manager to 0x0" 198 | ); 199 | revokeRole(KYC_PASSED, _enforcementManager); 200 | _enforcementManager = newEnforcementManager; 201 | grantRole(KYC_PASSED, _enforcementManager); 202 | revokeRole(FROZEN, _enforcementManager); 203 | emit ChangeEnforcementManager(newEnforcementManager); 204 | } 205 | 206 | function isPrivilegedRole(address account) public view returns (bool) { 207 | return 208 | account == supplyManager() || 209 | account == complianceManager() || 210 | account == enforcementManager() || 211 | account == owner(); 212 | } 213 | 214 | modifier requiresKYC() { 215 | require( 216 | hasRole(KYC_PASSED, _msgSender()), 217 | "Calling this function requires KYC approval." 218 | ); 219 | _; 220 | } 221 | 222 | function isKycPassed(address account) public view returns (bool) { 223 | return hasRole(KYC_PASSED, account); 224 | } 225 | 226 | // KYC: only CM 227 | function setKycPassed(address account) public onlyComplianceManager { 228 | grantRole(KYC_PASSED, account); 229 | emit SetKycPassed(account); 230 | } 231 | 232 | // Un-KYC: only CM, only non-privileged accounts 233 | function unsetKycPassed(address account) public onlyComplianceManager { 234 | require( 235 | !isPrivilegedRole(account), 236 | "Cannot unset KYC for administrator account." 237 | ); 238 | require(account != address(0), "Cannot unset KYC for address 0x0."); 239 | revokeRole(KYC_PASSED, account); 240 | emit UnsetKycPassed(account); 241 | } 242 | 243 | modifier requiresNotFrozen() { 244 | require( 245 | !hasRole(FROZEN, _msgSender()), 246 | "Your account has been frozen, cannot call function." 247 | ); 248 | _; 249 | } 250 | 251 | function isFrozen(address account) public view returns (bool) { 252 | return hasRole(FROZEN, account); 253 | } 254 | 255 | // Freeze an account: only CM, only non-privileged accounts 256 | function freeze(address account) public onlyComplianceManager { 257 | require( 258 | !isPrivilegedRole(account), 259 | "Cannot freeze administrator account." 260 | ); 261 | require(account != address(0), "Cannot freeze address 0x0."); 262 | grantRole(FROZEN, account); 263 | emit Freeze(account); 264 | } 265 | 266 | // Unfreeze an account: only CM 267 | function unfreeze(address account) public onlyComplianceManager { 268 | revokeRole(FROZEN, account); 269 | emit Unfreeze(account); 270 | } 271 | 272 | // Check Transfer Allowed (user facing) 273 | function checkTransferAllowed(address account) public view returns (bool) { 274 | return isKycPassed(account) && !isFrozen(account); 275 | } 276 | 277 | // Pause: Only CM 278 | function pause() public onlyComplianceManager { 279 | _pause(); 280 | emit Pause(_msgSender()); 281 | } 282 | 283 | // Unpause: Only CM 284 | function unpause() public onlyComplianceManager { 285 | _unpause(); 286 | emit Unpause(_msgSender()); 287 | } 288 | 289 | // Claim Ownership 290 | function claimOwnership() public override(Ownable) onlyProposedOwner { 291 | address prevOwner = owner(); 292 | super.claimOwnership(); // emits ClaimOwnership 293 | _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); 294 | grantRole(KYC_PASSED, _msgSender()); 295 | revokeRole(FROZEN, _msgSender()); 296 | revokeRole(KYC_PASSED, prevOwner); 297 | } 298 | 299 | // Wipe 300 | function wipe(address account, uint256 amount) public onlyEnforcementManager { 301 | uint256 balance = balanceOf(account); 302 | require( 303 | amount <= balance, 304 | "Amount cannot be greater than balance" 305 | ); 306 | super._transfer(account, _supplyManager, amount); 307 | _burn(_supplyManager, amount); 308 | emit Wipe(account, amount); 309 | } 310 | 311 | /* 312 | * Transfers 313 | */ 314 | 315 | // Mint 316 | function mint(uint256 amount) public onlySupplyManager { 317 | _mint(supplyManager(), amount); 318 | emit Mint(_msgSender(), amount); 319 | } 320 | 321 | // Burn 322 | function burn(uint256 amount) public onlySupplyManager { 323 | _burn(_supplyManager, amount); 324 | emit Burn(_msgSender(), amount); 325 | } 326 | 327 | // Check Transfer Allowed (internal) 328 | function _beforeTokenTransfer( 329 | address from, 330 | address to, 331 | uint256 amount 332 | ) internal override(ERC20) requiresKYC requiresNotFrozen whenNotPaused { 333 | if (from == supplyManager() && to == address(0)) { 334 | // allowed (burn) 335 | super._beforeTokenTransfer(from, to, amount); 336 | return; 337 | } 338 | 339 | if (to == supplyManager() && from == address(0)) { 340 | // allowed (mint) 341 | super._beforeTokenTransfer(from, to, amount); 342 | return; 343 | } 344 | 345 | if ( 346 | to == supplyManager() && 347 | hasRole(FROZEN, from) && 348 | amount == balanceOf(from) 349 | ) { 350 | // allowed (wipe account) 351 | super._beforeTokenTransfer(from, to, amount); 352 | return; 353 | } 354 | 355 | // All other transfers 356 | require(isKycPassed(from), "Sender account requires KYC to continue."); 357 | require(isKycPassed(to), "Receiver account requires KYC to continue."); 358 | require(!isFrozen(from), "Sender account is frozen."); 359 | require(!isFrozen(to), "Receiver account is frozen."); 360 | super._beforeTokenTransfer(from, to, amount); // callbacks from above (if any) 361 | } 362 | 363 | function transfer(address to, uint256 amount) public override(ERC20) { 364 | super._transfer(_msgSender(), to, amount); 365 | emit Transfer(_msgSender(), to, amount); 366 | } 367 | 368 | /* 369 | * External Transfers 370 | */ 371 | 372 | // approve an allowance for transfer to an external network 373 | function approveExternalTransfer( 374 | string memory networkURI, 375 | bytes memory externalAddress, 376 | uint256 amount 377 | ) 378 | public 379 | override(ExternallyTransferable) 380 | requiresKYC 381 | requiresNotFrozen 382 | whenNotPaused 383 | { 384 | require( 385 | amount <= balanceOf(_msgSender()), 386 | "Cannot approve more than balance." 387 | ); 388 | super.approveExternalTransfer(networkURI, externalAddress, amount); 389 | emit ApproveExternalTransfer( 390 | _msgSender(), 391 | networkURI, 392 | externalAddress, 393 | amount 394 | ); 395 | } 396 | 397 | function externalTransfer( 398 | address from, 399 | string memory networkURI, 400 | bytes memory to, 401 | uint256 amount 402 | ) public override(ExternallyTransferable) onlySupplyManager whenNotPaused { 403 | require(isKycPassed(from), "Spender account requires KYC to continue."); 404 | require(!isFrozen(from), "Spender account is frozen."); 405 | uint256 exAllowance = externalAllowanceOf(from, networkURI, to); 406 | require(amount <= exAllowance, "Amount greater than allowance."); 407 | super._transfer(from, _supplyManager, amount); 408 | _burn(_supplyManager, amount); 409 | _approveExternalAllowance( 410 | from, 411 | networkURI, 412 | to, 413 | exAllowance.sub(amount) 414 | ); 415 | emit ExternalTransfer(from, networkURI, to, amount); 416 | } 417 | 418 | function externalTransferFrom( 419 | bytes memory from, 420 | string memory networkURI, 421 | address to, 422 | uint256 amount 423 | ) public override(ExternallyTransferable) onlySupplyManager whenNotPaused { 424 | require(isKycPassed(to), "Recipient account requires KYC to continue."); 425 | require(!isFrozen(to), "Recipient account is frozen."); 426 | _mint(_supplyManager, amount); 427 | super._transfer(_supplyManager, to, amount); 428 | emit ExternalTransferFrom(from, networkURI, to, amount); 429 | } 430 | 431 | /* 432 | * Allowances 433 | */ 434 | 435 | // Check Allowance Allowed (internal) 436 | function _beforeTokenAllowance( 437 | address sender, 438 | address spender, 439 | uint256 amount 440 | ) internal override(ERC20) requiresKYC requiresNotFrozen whenNotPaused { 441 | require(isKycPassed(spender), "Spender account requires KYC to continue."); 442 | require(isKycPassed(sender), "Sender account requires KYC to continue."); 443 | require(!isFrozen(spender), "Spender account is frozen."); 444 | require(!isFrozen(sender), "Sender account is frozen."); 445 | require(amount >= 0, "Allowance must be greater than 0."); 446 | } 447 | 448 | // Transfer From (allowance --> user) 449 | function transferFrom( 450 | address from, 451 | address to, 452 | uint256 amount 453 | ) public override(ERC20) requiresKYC requiresNotFrozen { 454 | super.transferFrom(from, to, amount); 455 | emit Transfer(from, to, amount); 456 | emit Approve(from, _msgSender(), allowance(from, _msgSender())); 457 | } 458 | 459 | // Approve Allowance 460 | function approveAllowance(address spender, uint256 amount) 461 | public 462 | override(ERC20) 463 | requiresKYC 464 | requiresNotFrozen 465 | { 466 | super._approve(_msgSender(), spender, amount); 467 | emit Approve(_msgSender(), spender, amount); 468 | } 469 | 470 | // Increase Allowance 471 | function increaseAllowance(address spender, uint256 amount) 472 | public 473 | override(ERC20) 474 | requiresKYC 475 | requiresNotFrozen 476 | { 477 | uint256 newAllowance = allowance(_msgSender(), spender).add(amount); 478 | _approve(_msgSender(), spender, newAllowance); 479 | emit IncreaseAllowance(_msgSender(), spender, newAllowance); 480 | } 481 | 482 | // Decrease Allowance 483 | function decreaseAllowance(address spender, uint256 amount) 484 | public 485 | override(ERC20) 486 | requiresKYC 487 | requiresNotFrozen 488 | { 489 | uint256 newAllowance = allowance(_msgSender(), spender).sub( 490 | amount, 491 | "Amount greater than allowance." 492 | ); 493 | _approve(_msgSender(), spender, newAllowance); 494 | emit DecreaseAllowance(_msgSender(), spender, newAllowance); 495 | } 496 | } 497 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function (deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /migrations/2_deploy.js: -------------------------------------------------------------------------------- 1 | const { scripts, ConfigManager } = require("@openzeppelin/cli"); 2 | const { add, push, create } = scripts; 3 | 4 | async function deploy(options) { 5 | add({ contractsData: [{ name: "StableCoin", alias: "StableCoin" }]}); 6 | await push(options); 7 | await create(Object.assign({ contractAlias: "StableCoin" }, options)); 8 | } 9 | 10 | module.exports = function(deployer, networkName, accounts) { 11 | deployer.then(async() => { 12 | const { network, txParams } = await ConfigManager.initNetworkConfiguration({ 13 | network: networkName, 14 | from: accounts[0] 15 | }); 16 | await deploy({ network, txParams }); 17 | }) 18 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hashgraph/stable_coin", 3 | "license": "MIT", 4 | "dependencies": { 5 | "@openzeppelin/contracts-upgradeable": "3.4.2" 6 | }, 7 | "devDependencies": { 8 | "@openzeppelin/test-environment": "^0.1.9", 9 | "@openzeppelin/test-helpers": "^0.5.16", 10 | "@truffle/hdwallet-provider": "^2.1.15", 11 | "chai": "^4.3.10", 12 | "dotenv": "^16.3.1", 13 | "ganache": "^7.9.1", 14 | "mnemonics": "^1.1.3", 15 | "mocha": "^10.2.0", 16 | "solc": "0.6.9", 17 | "solidity-coverage": "^0.8.14", 18 | "truffle": "^5.11.5", 19 | "web3": "^1.10.0" 20 | }, 21 | "scripts": { 22 | "test": "mocha --exit --recursive test", 23 | "network:truffle": "truffle develop --log", 24 | "network:ganache": "ganache --port 8545", 25 | "deploy": "truffle deploy", 26 | "deploy:truffle": "truffle deploy --network truffle", 27 | "deploy:ganache": "truffle deploy --network ganache", 28 | "deploy:ropsten": "truffle deploy --network ropsten" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /secrets.json.sample: -------------------------------------------------------------------------------- 1 | { 2 | "API_KEY": "", 3 | "MNEMONIC": "" 4 | } -------------------------------------------------------------------------------- /test/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hashgraph/hedera-stable-coin-solidity-ethereum/1ee2f4d3292ba431353bf38c045fd814688fb625/test/.gitkeep -------------------------------------------------------------------------------- /test/StableCoin.spec.js: -------------------------------------------------------------------------------- 1 | const { accounts, contract } = require("@openzeppelin/test-environment"); 2 | const { expect } = require("chai"); 3 | const web3 = require("web3"); 4 | 5 | const { 6 | constants, // Common constants, like the zero address and largest integers 7 | expectEvent, // Assertions for emitted events 8 | expectRevert, // Assertions for transactions that should fail 9 | } = require("@openzeppelin/test-helpers"); 10 | 11 | const StableCoin = contract.fromArtifact("StableCoin"); 12 | 13 | describe("StableCoin", () => { 14 | this.contract = null; 15 | 16 | const [ 17 | squidward, 18 | skynet, 19 | rand_paul, 20 | nimdok, 21 | ultron, 22 | erasmus, 23 | walle, 24 | ] = accounts; // get accounts from test utils 25 | 26 | // Contract Information 27 | const name = "Bikini Bottom Bux"; 28 | const symbol = "~*~"; 29 | const decimals = 18; // same as ether <-> wei for convenience 30 | const totalSupply = web3.utils.toWei("300", "ether"); // 300 BBB in circulation 31 | const owner = skynet; 32 | const supplyManager = squidward; 33 | const complianceManager = rand_paul; 34 | const enforcementManager = walle; 35 | 36 | beforeEach(async () => { 37 | this.contract = await StableCoin.new( 38 | name, 39 | symbol, 40 | decimals, 41 | totalSupply, 42 | supplyManager, 43 | complianceManager, 44 | enforcementManager, 45 | { from: owner } 46 | ); 47 | }); 48 | 49 | it("initializes with expected state", async () => { 50 | expect((await this.contract.owner()) == owner); 51 | expect((await this.contract.supplyManager()) == supplyManager); 52 | expect((await this.contract.complianceManager()) == complianceManager); 53 | expect((await this.contract.enforcementManager()) == enforcementManager); 54 | expect( 55 | (await this.contract.balanceOf(supplyManager)) == 56 | (await this.contract.totalSupply()) 57 | ); 58 | expect((await this.contract.name()) == name); 59 | expect((await this.contract.symbol()) == symbol); 60 | expect((await this.contract.decimals()) == decimals); 61 | expect((await this.contract.totalSupply()) == totalSupply); 62 | expect(await this.contract.isKycPassed(owner)); 63 | expect(await this.contract.isKycPassed(supplyManager)); 64 | expect(await this.contract.isKycPassed(complianceManager)); 65 | expect(await this.contract.isKycPassed(enforcementManager)); 66 | expect((await this.contract.proposedOwner()) == constants.ZERO_ADDRESS); 67 | }); 68 | 69 | it("can change owner", async () => { 70 | const proposedOwner = nimdok; // oh no 71 | 72 | // Only owner can propose owner 73 | expectRevert( 74 | this.contract.proposeOwner(nimdok, { from: nimdok }), 75 | "Only the owner can call this function." 76 | ); 77 | 78 | // Can't claim ownership if not proposed 79 | expectRevert( 80 | this.contract.claimOwnership({ from: nimdok }), 81 | "Only the proposed owner can call this function." 82 | ); 83 | 84 | // Owner can propose ownership 85 | const proposeReceipt = await this.contract.proposeOwner(proposedOwner, { 86 | from: owner, 87 | }); 88 | 89 | // emits ProposeOwner 90 | expectEvent(proposeReceipt, "ProposeOwner", { proposedOwner: nimdok }); 91 | 92 | // Proposed owner can claim contract 93 | const claimReceipt = await this.contract.claimOwnership({ from: nimdok }); 94 | 95 | // emits ClaimOwnership 96 | expectEvent(claimReceipt, "ClaimOwnership", { newOwner: nimdok }); 97 | 98 | // new owner is proposed owner, has KYC passed, not frozen 99 | expect(this.contract.owner() == nimdok); 100 | expect(this.contract.isKycPassed(nimdok)); 101 | expect(this.contract.isFrozen(nimdok) == false); 102 | }); 103 | 104 | it("can change supply manager", async () => { 105 | expectRevert( 106 | this.contract.changeSupplyManager(nimdok, { from: nimdok }), 107 | "Only the owner can call this function" 108 | ); 109 | 110 | const changeReceipt = await this.contract.changeSupplyManager(nimdok, { 111 | from: owner, 112 | }); 113 | 114 | expectEvent(changeReceipt, "ChangeSupplyManager", { 115 | newSupplyManager: nimdok, 116 | }); 117 | expect(this.contract.supplyManager() == nimdok); 118 | }); 119 | 120 | it("can change compliance manager", async () => { 121 | expectRevert( 122 | this.contract.changeComplianceManager(nimdok, { from: nimdok }), 123 | "Only the owner can call this function." 124 | ); 125 | 126 | const changeReceipt = await this.contract.changeComplianceManager(nimdok, { 127 | from: owner, 128 | }); 129 | 130 | expectEvent(changeReceipt, "ChangeComplianceManager", { 131 | newComplianceManager: nimdok, 132 | }); 133 | expect(this.contract.complianceManager() == nimdok); 134 | }); 135 | 136 | it("can change enforcement manager", async () => { 137 | expectRevert( 138 | this.contract.changeEnforcementManager(nimdok, { from: nimdok }), 139 | "Only the owner can call this function." 140 | ); 141 | 142 | const changeReceipt = await this.contract.changeEnforcementManager(nimdok, { 143 | from: owner, 144 | }); 145 | 146 | expectEvent(changeReceipt, "ChangeEnforcementManager", { 147 | newEnforcementManager: nimdok, 148 | }); 149 | expect(this.contract.enforcementManager() == nimdok); 150 | }); 151 | 152 | it("can set and unset KYC for accounts", async () => { 153 | const kycReceipt = await this.contract.setKycPassed(nimdok, { 154 | from: complianceManager, 155 | }); 156 | expectEvent(kycReceipt, "SetKycPassed", { account: nimdok }); 157 | expect(await this.contract.isKycPassed(nimdok, { from: owner })); 158 | 159 | const unkycReceipt = await this.contract.unsetKycPassed(nimdok, { 160 | from: complianceManager, 161 | }); 162 | expectEvent(unkycReceipt, "UnsetKycPassed", { account: nimdok }); 163 | expect(!(await this.contract.isKycPassed(nimdok, { from: owner }))); 164 | expectRevert( 165 | this.contract.transfer(owner, web3.utils.toWei("5", "ether"), { 166 | from: nimdok, 167 | }), 168 | "Calling this function requires KYC approval." 169 | ); 170 | }); 171 | 172 | it("can freeze and unfreeze accounts", async () => { 173 | await this.contract.setKycPassed(nimdok, { from: complianceManager }); 174 | await this.contract.transfer(nimdok, web3.utils.toWei("10", "ether"), { 175 | from: supplyManager, 176 | }); 177 | 178 | const freezeReceipt = await this.contract.freeze(nimdok, { 179 | from: complianceManager, 180 | }); 181 | expectEvent(freezeReceipt, "Freeze", { account: nimdok }); 182 | 183 | expectRevert( 184 | this.contract.transfer(owner, web3.utils.toWei("5", "ether"), { 185 | from: nimdok, 186 | }), 187 | "Your account has been frozen, cannot call function." 188 | ); 189 | 190 | const unfreezeReceipt = await this.contract.unfreeze(nimdok, { 191 | from: complianceManager, 192 | }); 193 | expectEvent(unfreezeReceipt, "Unfreeze", { account: nimdok }); 194 | 195 | const transferReceipt = await this.contract.transfer( 196 | owner, 197 | web3.utils.toWei("1", "ether"), 198 | { from: nimdok } 199 | ); 200 | expectEvent(transferReceipt, "Transfer", { 201 | sender: nimdok, 202 | recipient: owner, 203 | amount: web3.utils.toWei("1", "ether"), 204 | }); 205 | }); 206 | 207 | it("is mintable", async () => { 208 | expectRevert( 209 | this.contract.mint(web3.utils.toWei("10", "ether"), { from: nimdok }), 210 | "Only the supply manager can call this function." 211 | ); 212 | await this.contract.mint(web3.utils.toWei("100", "ether"), { from: owner }); 213 | await this.contract.mint(web3.utils.toWei("10", "ether"), { 214 | from: supplyManager, 215 | }); 216 | expect( 217 | (await this.contract.totalSupply()) == web3.utils.toWei("410", "ether") 218 | ); 219 | expect( 220 | (await this.contract.totalSupply()) == 221 | (await this.contract.balanceOf(supplyManager)) 222 | ); 223 | }); 224 | 225 | it("is burnable", async () => { 226 | expectRevert( 227 | this.contract.burn(web3.utils.toWei("10", "ether"), { from: nimdok }), 228 | "Only the supply manager can call this function." 229 | ); 230 | await this.contract.burn(web3.utils.toWei("100", "ether"), { 231 | from: supplyManager, 232 | }); 233 | await this.contract.burn(web3.utils.toWei("10", "ether"), { from: owner }); 234 | expect( 235 | (await this.contract.totalSupply()) == web3.utils.toWei("190", "ether") 236 | ); 237 | expect( 238 | (await this.contract.totalSupply()) == 239 | (await this.contract.balanceOf(supplyManager)) 240 | ); 241 | }); 242 | 243 | it("is transferrable", async () => { 244 | await this.contract.setKycPassed(nimdok, { from: complianceManager }); 245 | await this.contract.setKycPassed(ultron, { from: complianceManager }); 246 | await this.contract.transfer(ultron, web3.utils.toWei("10", "ether"), { 247 | from: supplyManager, 248 | }); 249 | 250 | const transferReceipt = await this.contract.transfer( 251 | nimdok, 252 | web3.utils.toWei("1", "ether"), 253 | { from: ultron } 254 | ); 255 | 256 | expectEvent(transferReceipt, "Transfer", { 257 | sender: ultron, 258 | recipient: nimdok, 259 | amount: web3.utils.toWei("1", "ether"), 260 | }); 261 | expect( 262 | // Note: No Gas 263 | (await this.contract.balanceOf(ultron)) == web3.utils.toWei("9", "ether") 264 | ); 265 | expect( 266 | (await this.contract.balanceOf(nimdok)) == web3.utils.toWei("1", "ether") 267 | ); 268 | }); 269 | 270 | it("is pausable", async () => { 271 | await this.contract.setKycPassed(nimdok, { from: complianceManager }); 272 | await this.contract.setKycPassed(ultron, { from: complianceManager }); 273 | await this.contract.setKycPassed(erasmus, { from: complianceManager }); 274 | await this.contract.transfer(ultron, web3.utils.toWei("10", "ether"), { 275 | from: supplyManager, 276 | }); 277 | await this.contract.transfer(nimdok, web3.utils.toWei("5", "ether"), { 278 | from: ultron, 279 | }); 280 | await this.contract.transfer(erasmus, web3.utils.toWei("1", "ether"), { 281 | from: nimdok, 282 | }); 283 | 284 | // CEASE 285 | const pauseReceipt = await this.contract.pause({ 286 | from: complianceManager, 287 | }); 288 | expectEvent(pauseReceipt, "Pause", { sender: complianceManager }); 289 | 290 | const transfer1st = this.contract.transfer( 291 | nimdok, 292 | web3.utils.toWei("1", "ether"), 293 | { 294 | from: ultron, 295 | } 296 | ); 297 | const transfer2nd = this.contract.transfer( 298 | ultron, 299 | web3.utils.toWei("1", "ether"), 300 | { 301 | from: erasmus, 302 | } 303 | ); 304 | expectRevert(transfer1st, "Pausable: paused"); 305 | expectRevert(transfer2nd, "Pausable: paused"); 306 | 307 | // mk 308 | const unpauseReceipt = await this.contract.unpause({ 309 | from: complianceManager, 310 | }); 311 | expectEvent(unpauseReceipt, "Unpause", { sender: complianceManager }); 312 | 313 | const transfer3rdReceipt = await this.contract.transfer( 314 | nimdok, 315 | web3.utils.toWei("1", "ether"), 316 | { 317 | from: ultron, 318 | } 319 | ); 320 | const transfer4thReceipt = await this.contract.transfer( 321 | ultron, 322 | web3.utils.toWei("1", "ether"), 323 | { 324 | from: erasmus, 325 | } 326 | ); 327 | expectEvent(transfer3rdReceipt, "Transfer", { 328 | sender: ultron, 329 | recipient: nimdok, 330 | amount: web3.utils.toWei("1", "ether"), 331 | }); 332 | expectEvent(transfer4thReceipt, "Transfer", { 333 | sender: erasmus, 334 | recipient: ultron, 335 | amount: web3.utils.toWei("1", "ether"), 336 | }); 337 | }); 338 | 339 | it("is delegable", async () => { 340 | await this.contract.setKycPassed(nimdok, { from: complianceManager }); 341 | await this.contract.setKycPassed(ultron, { from: complianceManager }); 342 | await this.contract.transfer(ultron, web3.utils.toWei("10", "ether"), { 343 | from: supplyManager, 344 | }); 345 | 346 | const delegate1stReceipt = await this.contract.approveAllowance( 347 | nimdok, 348 | web3.utils.toWei("1", "ether"), 349 | { from: ultron } 350 | ); 351 | expectEvent(delegate1stReceipt, "Approve", { 352 | sender: ultron, 353 | spender: nimdok, 354 | amount: web3.utils.toWei("1", "ether"), 355 | }); 356 | 357 | expectRevert( 358 | this.contract.approveAllowance(erasmus, web3.utils.toWei("2", "ether"), { 359 | from: ultron, 360 | }), 361 | "Spender account requires KYC to continue." 362 | ); 363 | expectRevert( 364 | this.contract.approveAllowance( 365 | rand_paul, 366 | web3.utils.toWei("1", "ether"), 367 | { 368 | from: erasmus, 369 | } 370 | ), 371 | "Calling this function requires KYC approval." 372 | ); 373 | 374 | await this.contract.setKycPassed(erasmus, { from: complianceManager }); 375 | const delegate2ndReceipt = await this.contract.approveAllowance( 376 | erasmus, 377 | web3.utils.toWei("20", "ether"), 378 | { 379 | from: ultron, 380 | } 381 | ); 382 | expectEvent(delegate2ndReceipt, "Approve", { 383 | sender: ultron, 384 | spender: erasmus, 385 | amount: web3.utils.toWei("20", "ether"), 386 | }); 387 | 388 | // Increase Allowance 389 | const increaseReceipt = await this.contract.increaseAllowance( 390 | erasmus, 391 | web3.utils.toWei("10", "ether"), 392 | { from: ultron } 393 | ); 394 | expectEvent(increaseReceipt, "IncreaseAllowance", { 395 | sender: ultron, 396 | spender: erasmus, 397 | amount: web3.utils.toWei("30", "ether"), 398 | }); 399 | 400 | // Decrease Allowance 401 | const decreaseReceipt = await this.contract.decreaseAllowance( 402 | erasmus, 403 | web3.utils.toWei("1", "ether"), 404 | { from: ultron } 405 | ); 406 | expectEvent(decreaseReceipt, "DecreaseAllowance", { 407 | sender: ultron, 408 | spender: erasmus, 409 | amount: web3.utils.toWei("29", "ether"), 410 | }); 411 | expect( 412 | (await this.contract.allowance(ultron, erasmus)) == 413 | web3.utils.toWei("29", "ether") 414 | ); 415 | 416 | // erasmus tries to spend within allowance but more than ultron's balance 417 | expectRevert( 418 | this.contract.transferFrom( 419 | ultron, 420 | nimdok, 421 | web3.utils.toWei("15", "ether"), 422 | { 423 | from: erasmus, 424 | } 425 | ), 426 | "ERC20: transfer amount exceeds balance" 427 | ); 428 | 429 | // erasmus can spend within allowance, less than ultron's balance 430 | const transferFromReceipt = await this.contract.transferFrom( 431 | ultron, 432 | nimdok, 433 | web3.utils.toWei("1", "ether"), 434 | { from: erasmus } 435 | ); 436 | expectEvent(transferFromReceipt, "Transfer", { 437 | sender: ultron, 438 | recipient: nimdok, 439 | amount: web3.utils.toWei("1", "ether"), 440 | }); 441 | expectEvent(transferFromReceipt, "Approve", { 442 | sender: ultron, 443 | spender: erasmus, 444 | amount: web3.utils.toWei("28", "ether"), 445 | }); 446 | }); 447 | 448 | it("can wipe accounts", async () => { 449 | await this.contract.setKycPassed(nimdok, { from: complianceManager }); 450 | await this.contract.setKycPassed(ultron, { from: complianceManager }); 451 | await this.contract.transfer(ultron, web3.utils.toWei("10", "ether"), { 452 | from: supplyManager, 453 | }); 454 | 455 | await this.contract.freeze(ultron, { from: complianceManager }); 456 | const balance = await this.contract.balanceOf(ultron, { 457 | from: enforcementManager, 458 | }); 459 | const wipeReceipt = await this.contract.wipe(ultron, balance, { 460 | from: enforcementManager, 461 | }); 462 | expectEvent(wipeReceipt, "Wipe", { 463 | account: ultron, 464 | amount: balance, 465 | }); 466 | await this.contract.unfreeze(ultron, { from: complianceManager }); 467 | expect((await this.contract.balanceOf(ultron, { from: ultron })) == 0); 468 | expect( 469 | (await this.contract.totalSupply()) == web3.utils.toWei("290", "ether") 470 | ); 471 | }); 472 | 473 | it("can request external transfer", async () => { 474 | await this.contract.setKycPassed(nimdok, { from: complianceManager }); 475 | await this.contract.setKycPassed(ultron, { from: complianceManager }); 476 | await this.contract.transfer(ultron, web3.utils.toWei("10", "ether"), { 477 | from: supplyManager, 478 | }); 479 | 480 | const network = "hsc://0.0.999999"; 481 | const amount = web3.utils.toWei("1", "ether"); 482 | const externalAddress = web3.utils.fromAscii( 483 | "480474335c38c27bfde1f0c2010d3db95eeb74a1f8ac65212f7824ce1ab84eca" 484 | ); 485 | const requestReceipt = await this.contract.approveExternalTransfer( 486 | network, 487 | externalAddress, 488 | amount, 489 | { from: ultron } 490 | ); 491 | expectEvent(requestReceipt, "ApproveExternalTransfer", { 492 | from: ultron, 493 | networkURI: network, 494 | to: externalAddress, 495 | amount: amount, 496 | }); 497 | expect( 498 | (await this.contract.externalAllowanceOf( 499 | ultron, 500 | network, 501 | externalAddress 502 | )) == amount 503 | ); 504 | }); 505 | 506 | it("can send external transfer", async () => { 507 | await this.contract.setKycPassed(nimdok, { from: complianceManager }); 508 | await this.contract.setKycPassed(ultron, { from: complianceManager }); 509 | await this.contract.transfer(ultron, web3.utils.toWei("10", "ether"), { 510 | from: supplyManager, 511 | }); 512 | 513 | const network = "hsc://0.0.999999"; 514 | const amount = web3.utils.toWei("1", "ether"); 515 | const externalAddress = web3.utils.fromAscii( 516 | "480474335c38c27bfde1f0c2010d3db95eeb74a1f8ac65212f7824ce1ab84eca" 517 | ); 518 | 519 | expectRevert( 520 | this.contract.externalTransfer( 521 | ultron, 522 | network, 523 | externalAddress, 524 | amount, 525 | { from: supplyManager } 526 | ), 527 | "Amount greater than allowance." 528 | ); 529 | 530 | await this.contract.unsetKycPassed(ultron, { from: complianceManager }); 531 | expectRevert( 532 | this.contract.externalTransfer( 533 | ultron, 534 | network, 535 | externalAddress, 536 | amount, 537 | { from: supplyManager } 538 | ), 539 | "Spender account requires KYC to continue." 540 | ); 541 | 542 | await this.contract.setKycPassed(ultron, { from: complianceManager }); 543 | await this.contract.approveExternalTransfer( 544 | network, 545 | externalAddress, 546 | amount, 547 | { from: ultron } 548 | ); 549 | 550 | const externalTransferReceipt = await this.contract.externalTransfer( 551 | ultron, 552 | network, 553 | externalAddress, 554 | amount, 555 | { from: supplyManager } 556 | ); 557 | expectEvent(externalTransferReceipt, "ExternalTransfer", { 558 | from: ultron, 559 | networkURI: network, 560 | to: externalAddress, 561 | amount: amount 562 | }); 563 | 564 | expect((await this.contract.totalSupply()) == web3.utils.toWei("299", "ether")); 565 | expect((await this.contract.balanceOf(ultron)) == web3.utils.toWei("9", "ether")); 566 | }); 567 | 568 | it("can receive external transfer", async () => { 569 | const network = "hsc://0.0.999999"; 570 | const amount = web3.utils.toWei("1", "ether"); 571 | const externalAddress = web3.utils.fromAscii( 572 | "480474335c38c27bfde1f0c2010d3db95eeb74a1f8ac65212f7824ce1ab84eca" 573 | ); 574 | 575 | expectRevert( 576 | this.contract.externalTransferFrom( 577 | externalAddress, 578 | network, 579 | ultron, 580 | amount, 581 | { from: supplyManager } 582 | ), 583 | "Recipient account requires KYC to continue." 584 | ); 585 | 586 | await this.contract.setKycPassed(ultron, { from: complianceManager }) 587 | const externalTransferReceipt = await this.contract.externalTransferFrom( 588 | externalAddress, 589 | network, 590 | ultron, 591 | amount, 592 | { from: supplyManager } 593 | ); 594 | expectEvent(externalTransferReceipt, "ExternalTransferFrom", { 595 | from: externalAddress, 596 | networkURI: network, 597 | to: ultron, 598 | amount: amount 599 | }); 600 | 601 | expect((await this.contract.totalSupply()) == web3.utils.toWei("301", "ether")); 602 | expect((await this.contract.balanceOf(ultron)) == web3.utils.toWei("1", "ether")); 603 | }); 604 | 605 | afterEach(() => { 606 | this.contract = null; 607 | }); 608 | }); 609 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | const HDWalletProvider = require("@truffle/hdwallet-provider"); 2 | const { API_KEY, MNEMONIC } = require("./secrets.json"); 3 | 4 | // Deployed Contract (Ropsten) 5 | // 0x42E9d6514D613D63c8c32cE385aad3dE9917C681 6 | 7 | module.exports = { 8 | networks: { 9 | ganache: { 10 | host: "127.0.0.1", 11 | port: 8545, 12 | network_id: "*", 13 | }, 14 | truffle: { 15 | host: "127.0.0.1", 16 | port: 9545, 17 | network_id: "*", 18 | }, 19 | ropsten: { 20 | provider: function() { 21 | return new HDWalletProvider( 22 | MNEMONIC, 23 | `https://ropsten.infura.io/v3/${API_KEY}` 24 | ); 25 | }, 26 | network_id: "3", 27 | gas: 4000000, // Max Allowed 28 | skipDryRun: true 29 | }, 30 | }, 31 | 32 | compilers: { 33 | solc: { 34 | version: "0.6.8", 35 | docker: false, 36 | settings: { 37 | optimizer: { 38 | enabled: true, 39 | runs: 2000, 40 | }, 41 | evmVersion: "constantinople", 42 | }, 43 | }, 44 | }, 45 | }; 46 | --------------------------------------------------------------------------------