├── .DS_Store ├── .babelrc ├── .flowconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── contracts ├── AltBn128.sol ├── Heiswap.sol ├── LSAG.sol └── Migrations.sol ├── migrations ├── 1_initial_migration.js └── 2_contract_deployments.js ├── package.json ├── public ├── favicon.ico ├── index.html └── manifest.json ├── src ├── .DS_Store ├── App.css ├── App.js ├── App.test.js ├── assets │ └── key.png ├── components │ ├── .DS_Store │ ├── DepositPage.js │ ├── StatusPage.js │ ├── Web3StatusModal.js │ └── WithdrawPage.js ├── contracts │ ├── AltBn128.json │ ├── Heiswap.json │ ├── LSAG.json │ └── Migrations.json ├── index.css ├── index.js ├── serviceWorker.js ├── types │ └── DappGateway.js └── utils │ ├── AltBn128.js │ ├── getWeb3.js │ └── helper.js ├── test ├── AltBn128.js └── LSAG.js ├── timeline.md ├── truffle-config.js └── yarn.lock /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kendricktan/heiswap-dapp/61901f057e4b54241eade3545a4e3526514e6cd7/.DS_Store -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["flow"] 3 | } -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | [include] 4 | 5 | [libs] 6 | 7 | [lints] 8 | 9 | [options] 10 | 11 | [strict] 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | .vscode/ 64 | 65 | lib 66 | 67 | build 68 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | only: 3 | - master 4 | 5 | language: node_js 6 | node_js: 7 | - "10" 8 | env: 9 | - CI=true 10 | 11 | install: 12 | - yarn install 13 | 14 | script: 15 | - yarn test 16 | 17 | before_deploy: 18 | - yarn global add netlify-cli@2.11.23 19 | - yarn build 20 | 21 | deploy: 22 | provider: script 23 | script: netlify deploy -p --dir=./build 24 | skip_cleanup: true 25 | on: 26 | branch: master -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Kendrick Tan 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 | # Heiswap Dapp 2 | 3 | --- 4 | 5 | | Branch | Status | URL | 6 | | --- | --- | --- | 7 | | master | [![Build Status](https://travis-ci.org/kendricktan/heiswap-dapp.svg?branch=master)](https://travis-ci.org/kendricktan/heiswap-dapp) | heiswap.exchange | 8 | 9 | --- 10 | 11 | Heiswap (黑 swap) is an Ethereum transaction mixer that ultilizes parts of [CryptoNote](https://cryptonote.org) to enable zero-knowledge transactions. 12 | 13 | It ulitilizes Ring Signatures and pseudo-stealth addresses to achieve its zero-knowledge properties. The [deployed smart contract](https://ropsten.etherscan.io/address/0x8AAbE42EeCA45E040fab330fD24eA6746b832Ad2) handles the signature verification, while the client is responsible for generating the pseudo-stealth address. 14 | 15 | Ring signatures was only possible on the EVM (gas-wise) due to the recent addition of [EIP198](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-198.md) and [EIP1895](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1895.md). 16 | 17 | [You can play with the Ropsten version right now](https://heiswap.exchange/). 18 | 19 | # Development 20 | Project is a standard `create-react-app` project, using `truffle` to compile, migrate and deploy contracts. 21 | 22 | Solidity files are located in `contracts`, and compiled to `src/contracts` 23 | 24 | Run `yarn start` to run the project. 25 | 26 | The [proof-of-concept repository is located here](https://github.com/kendricktan/heiswap-poc). 27 | 28 | ## Verifying Contract on Etherscan 29 | 30 | 1. Deploy to etherscan. (`truffle migrate --network ropsten`) 31 | - Remember to export ENV vars `ETH_SK` and `INFURA_KEY` 32 | 2. Install [truffle-flattener](https://www.npmjs.com/package/truffle-flattener) 33 | 3. Flatten source files: `truffle-flattener contracts/AltBn128.sol contracts/Heiswap.sol contracts/LSAG.sol > /tmp/etherscan.sol` 34 | 4. Upload single large file (`/tmp/etherscan.sol`) on to etherscan to verify, remember to add the library addresses from `src/contracts/Heiswap.json` -> `links`. 35 | 36 | 37 | # Special Thanks 38 | 39 | This project would not have been possible without the existence of other open sourced projects, most notably 40 | 41 | - [HarryR](https://github.com/HarryR/solcrypto) 42 | - [CryptoNote](https://eprint.iacr.org/2004/027.pdf) 43 | - [Piper Merriam](https://github.com/ethereum/py_ecc/blob/master/py_ecc/bn128/bn128_curve.py) -------------------------------------------------------------------------------- /contracts/AltBn128.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0 <0.6.0; 2 | 3 | /** 4 | * Heavily referenced from https://github.com/ethereum/py_ecc/blob/master/py_ecc/bn128/bn128_curve.py 5 | */ 6 | 7 | library AltBn128 { 8 | uint256 constant public G1x = uint256(0x01); 9 | uint256 constant public G1y = uint256(0x02); 10 | 11 | // Number of elements in the field (often called `q`) 12 | // n = n(u) = 36u^4 + 36u^3 + 18u^2 + 6u + 1 13 | uint256 constant public N = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001; 14 | 15 | // p = p(u) = 36u^4 + 36u^3 + 24u^2 + 6u + 1 16 | // Field Order 17 | uint256 constant public P = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47; 18 | 19 | // (p+1) / 4 20 | uint256 constant public A = 0xc19139cb84c680a6e14116da060561765e05aa45a1c72a34f082305b61f3f52; 21 | 22 | /* ECC Functions */ 23 | function ecAdd(uint256[2] memory p0, uint256[2] memory p1) public view 24 | returns (uint256[2] memory retP) 25 | { 26 | uint256[4] memory i = [p0[0], p0[1], p1[0], p1[1]]; 27 | 28 | assembly { 29 | // call ecadd precompile 30 | // inputs are: x1, y1, x2, y2 31 | if iszero(staticcall(not(0), 0x06, i, 0x80, retP, 0x40)) { 32 | revert(0, 0) 33 | } 34 | } 35 | } 36 | 37 | function ecMul(uint256[2] memory p, uint256 s) public view 38 | returns (uint256[2] memory retP) 39 | { 40 | // With a public key (x, y), this computes p = scalar * (x, y). 41 | uint256[3] memory i = [p[0], p[1], s]; 42 | 43 | assembly { 44 | // call ecmul precompile 45 | // inputs are: x, y, scalar 46 | if iszero(staticcall(not(0), 0x07, i, 0x60, retP, 0x40)) { 47 | revert(0, 0) 48 | } 49 | } 50 | } 51 | 52 | function ecMulG(uint256 s) public view 53 | returns (uint256[2] memory retP) 54 | { 55 | return ecMul([G1x, G1y], s); 56 | } 57 | 58 | function powmod(uint256 base, uint256 e, uint256 m) public view 59 | returns (uint256 o) 60 | { 61 | // returns pow(base, e) % m 62 | assembly { 63 | // define pointer 64 | let p := mload(0x40) 65 | 66 | // Store data assembly-favouring ways 67 | mstore(p, 0x20) // Length of Base 68 | mstore(add(p, 0x20), 0x20) // Length of Exponent 69 | mstore(add(p, 0x40), 0x20) // Length of Modulus 70 | mstore(add(p, 0x60), base) // Base 71 | mstore(add(p, 0x80), e) // Exponent 72 | mstore(add(p, 0xa0), m) // Modulus 73 | 74 | // call modexp precompile! -- old school gas handling 75 | let success := staticcall(sub(gas, 2000), 0x05, p, 0xc0, p, 0x20) 76 | 77 | // gas fiddling 78 | switch success case 0 { 79 | revert(0, 0) 80 | } 81 | 82 | // data 83 | o := mload(p) 84 | } 85 | } 86 | 87 | // Keep everything contained within this lib 88 | function addmodn(uint256 x, uint256 n) public pure 89 | returns (uint256) 90 | { 91 | return addmod(x, n, N); 92 | } 93 | 94 | function modn(uint256 x) public pure 95 | returns (uint256) 96 | { 97 | return x % N; 98 | } 99 | 100 | /* 101 | Checks if the points x, y exists on alt_bn_128 curve 102 | */ 103 | function onCurve(uint256 x, uint256 y) public pure 104 | returns(bool) 105 | { 106 | uint256 beta = mulmod(x, x, P); 107 | beta = mulmod(beta, x, P); 108 | beta = addmod(beta, 3, P); 109 | 110 | return onCurveBeta(beta, y); 111 | } 112 | 113 | function onCurveBeta(uint256 beta, uint256 y) public pure 114 | returns(bool) 115 | { 116 | return beta == mulmod(y, y, P); 117 | } 118 | 119 | /* 120 | * Calculates point y value given x 121 | */ 122 | function evalCurve(uint256 x) public view 123 | returns (uint256, uint256) 124 | { 125 | uint256 beta = mulmod(x, x, P); 126 | beta = mulmod(beta, x, P); 127 | beta = addmod(beta, 3, P); 128 | 129 | uint256 y = powmod(beta, A, P); 130 | 131 | // require(beta == mulmod(y, y, P), "Invalid x for evalCurve"); 132 | return (beta, y); 133 | } 134 | } -------------------------------------------------------------------------------- /contracts/Heiswap.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0 <0.6.0; 2 | 3 | import "./AltBn128.sol"; 4 | import "./LSAG.sol"; 5 | 6 | contract Heiswap { 7 | // Events 8 | event Deposited(address, uint256 etherAmount, uint256 idx); 9 | 10 | // Default Relayer Address 11 | address payable public relayerAddress = 0x20a4b066fc4F70b0245B43e2F5a781C6d1030748; 12 | 13 | // Maximum number of participants in a ring 14 | uint256 constant ringMaxParticipants = 6; 15 | 16 | struct Ring { 17 | // Ring created on block number X 18 | uint256 createdBlockNumber; 19 | 20 | // Ring hash will be available once 21 | // there is 5 participants in the ring 22 | // TODO: Manually call the function "closeRing" 23 | bytes32 ringHash; 24 | 25 | // In a ring, everyone deposits 26 | // the same amount of ETH. Otherwise 27 | // the sender and receiver can be identified 28 | // which defeats the whole purpose of this 29 | // application 30 | uint256 amountDeposited; 31 | 32 | // Number of participants who've deposited 33 | uint8 dParticipantsNo; 34 | 35 | // The Public Key (stealth addresses) 36 | mapping (uint256 => uint256[2]) publicKeys; 37 | 38 | // Number of participants who've withdrawn 39 | uint8 wParticipantsNo; 40 | 41 | // Key Images of participants who have withdrawn 42 | // Used to determine if a participant is trying to 43 | // double withdraw 44 | mapping (uint256 => uint256[2]) keyImages; 45 | } 46 | 47 | // Fixed amounts allowed to be inserted into the rings 48 | uint256[10] allowedAmounts = [ 1 ether, 2 ether, 4 ether, 8 ether, 16 ether, 32 ether, 64 ether ]; 49 | 50 | // Mimics dynamic 'lists' 51 | // allowedAmount => numberOfRings (in the current amount) 52 | mapping(uint256 => uint256) ringsNo; 53 | 54 | // allowedAmount => ringIndex => Ring 55 | mapping (uint256 => mapping(uint256 => Ring)) rings; 56 | 57 | function deposit(uint256[2] memory publicKey) public payable 58 | { 59 | // Get amount sent 60 | uint256 receivedEther = floorEtherAndCheck(msg.value); 61 | 62 | // Returns non-exact value ETH 63 | // Gets the value of the first decimal place 64 | // in ETH deposited 65 | // i.e. 2.1 will give 1, 2.6 will give 6 66 | // if it's greater than 1, then refund the 67 | // amounts (we'll count 0.1 ETH as a donation to our relayer ;)) 68 | uint256 etherDecimalVal = (msg.value / (1 ether / 10)) % 10; 69 | if (etherDecimalVal > 1) { 70 | uint256 refundEtherDecimalVal = (etherDecimalVal - 1) * (1 ether / 10); 71 | relayerAddress.transfer(1 ether / 10); 72 | msg.sender.transfer(refundEtherDecimalVal); 73 | } 74 | 75 | // Gets the current ring for the amounts 76 | uint256 curIndex = ringsNo[receivedEther]; 77 | Ring storage ring = rings[receivedEther][curIndex]; 78 | 79 | if (!AltBn128.onCurve(uint256(publicKey[0]), uint256(publicKey[1]))) { 80 | revert("Public Key no on Curve"); 81 | } 82 | 83 | // Make sure that public key (stealth address) 84 | // isn't already in there 85 | for (uint8 i = 0; i < ring.dParticipantsNo; i++) { 86 | if (ring.publicKeys[i][0] == publicKey[0] && 87 | ring.publicKeys[i][1] == publicKey[1]) { 88 | revert("Address already in current Ring"); 89 | } 90 | } 91 | 92 | // If its a new ring 93 | // set createdBlockNum size 94 | if (ring.dParticipantsNo == 0) { 95 | ring.createdBlockNumber = block.number - 1; 96 | } 97 | 98 | // Update ring params 99 | ring.publicKeys[ring.dParticipantsNo] = publicKey; 100 | ring.dParticipantsNo++; 101 | ring.amountDeposited += receivedEther; 102 | 103 | // Create new ring if current ring has exceeded number of participants 104 | if (ring.dParticipantsNo >= ringMaxParticipants) { 105 | // Set ringHash 106 | ring.ringHash = createRingHash(receivedEther / (1 ether), curIndex); 107 | 108 | // Add new Ring pool 109 | ringsNo[receivedEther] += 1; 110 | } 111 | 112 | // Broadcast Event 113 | emit Deposited(msg.sender, receivedEther, curIndex); 114 | } 115 | 116 | // User can only withdraw if the ring is closed 117 | // NOTE: Convert to ether 118 | // i.e. there is a ringHash 119 | function withdraw( 120 | address payable receiver, uint256 amountEther, uint256 index, 121 | uint256 c0, uint256[2] memory keyImage, uint256[] memory s 122 | ) public 123 | { 124 | uint i; 125 | uint256 startGas = gasleft(); 126 | 127 | // Get amount sent in whole number 128 | uint256 withdrawEther = floorEtherAndCheck(amountEther * 1 ether); 129 | 130 | // Gets the current ring, given the amount and idx 131 | Ring storage ring = rings[withdrawEther][index]; 132 | 133 | if (receiver == 0x0000000000000000000000000000000000000000) { 134 | revert("No zero address receiver"); 135 | } 136 | 137 | // If everyone has withdrawn 138 | if (ring.wParticipantsNo >= ringMaxParticipants) { 139 | revert("All funds from current Ring has been withdrawn"); 140 | } 141 | 142 | // Ring needs to be closed first 143 | if (ring.ringHash == bytes32(0x00)) { 144 | revert("Ring isn't closed"); 145 | } 146 | 147 | // Convert public key to dynamic array 148 | // Based on number of people who have 149 | // deposited 150 | uint256[2][] memory publicKeys = new uint256[2][](ring.dParticipantsNo); 151 | 152 | for (i = 0; i < ring.dParticipantsNo; i++) { 153 | publicKeys[i] = [ 154 | uint256(ring.publicKeys[uint8(i)][0]), 155 | uint256(ring.publicKeys[uint8(i)][1]) 156 | ]; 157 | } 158 | 159 | // Attempts to verify ring signature 160 | bool signatureVerified = LSAG.verify( 161 | abi.encodePacked(ring.ringHash, receiver), // Convert to bytes 162 | c0, 163 | keyImage, 164 | s, 165 | publicKeys 166 | ); 167 | 168 | if (!signatureVerified) { 169 | revert("Invalid signature"); 170 | } 171 | 172 | // Checks if Key Image has been used 173 | // AKA No double withdraw 174 | for (i = 0; i < ring.wParticipantsNo; i++) { 175 | if (ring.keyImages[uint8(i)][0] == keyImage[0] && 176 | ring.keyImages[uint8(i)][1] == keyImage[1]) { 177 | revert("Signature has been used!"); 178 | } 179 | } 180 | 181 | // Otherwise adds key image to the current key image 182 | // And adjusts params accordingly 183 | ring.keyImages[ring.wParticipantsNo] = keyImage; 184 | ring.wParticipantsNo += 1; 185 | 186 | // Send ETH to receiver 187 | // Calculate gasUsage fees 188 | uint256 gasUsed = (startGas - gasleft()) * tx.gasprice; 189 | 190 | // Calculate relayer fees (1.33%) 191 | uint256 relayerFees = (withdrawEther / 75); 192 | 193 | // Total fees 194 | uint256 fees = gasUsed + relayerFees; 195 | 196 | // Relayer gets compensated 197 | msg.sender.transfer(fees); 198 | 199 | // Reciever then gets the remaining ETH 200 | receiver.transfer(withdrawEther - fees); 201 | } 202 | 203 | /* Helper functions */ 204 | // TODO: Use safemath library 205 | 206 | // Creates ring hash (used for signing) 207 | function createRingHash(uint256 amountEther, uint256 index) internal view 208 | returns (bytes32) 209 | { 210 | uint256[2][ringMaxParticipants] memory publicKeys; 211 | uint256 receivedEther = floorEtherAndCheck(amountEther * 1 ether); 212 | 213 | Ring storage r = rings[receivedEther][index]; 214 | 215 | for (uint8 i = 0; i < ringMaxParticipants; i++) { 216 | publicKeys[i] = r.publicKeys[i]; 217 | } 218 | 219 | bytes memory b = abi.encodePacked( 220 | blockhash(block.number - 1), 221 | r.createdBlockNumber, 222 | r.amountDeposited, 223 | r.dParticipantsNo, 224 | publicKeys 225 | ); 226 | 227 | return keccak256(b); 228 | } 229 | 230 | // Gets ring hash needed to generate signature 231 | function getRingHash(uint256 amountEther, uint256 index) public view 232 | returns (bytes memory) 233 | { 234 | uint256 receivedEther = floorEtherAndCheck(amountEther * 1 ether); 235 | Ring memory r = rings[receivedEther][index]; 236 | 237 | // If the ringhash hasn't been closed 238 | // return the hash needed to close the 239 | // ring 240 | if (r.ringHash == bytes32(0x00)) { 241 | return abi.encodePacked("closeRing", receivedEther, index); 242 | } 243 | 244 | return abi.encodePacked(r.ringHash); 245 | } 246 | 247 | // Gets all addresses in a Ring 248 | // Converting to Bytes32 cause web3.js has a bug that doesn't convert 249 | // BigNum correctly.... 250 | function getPublicKeys(uint256 amountEther, uint256 index) public view 251 | returns (bytes32[2][ringMaxParticipants] memory) 252 | { 253 | uint256 receivedEther = floorEtherAndCheck(amountEther * 1 ether); 254 | 255 | bytes32[2][ringMaxParticipants] memory publicKeys; 256 | 257 | for (uint i = 0; i < ringMaxParticipants; i++) { 258 | publicKeys[i][0] = bytes32(rings[receivedEther][index].publicKeys[i][0]); 259 | publicKeys[i][1] = bytes32(rings[receivedEther][index].publicKeys[i][1]); 260 | } 261 | 262 | return publicKeys; 263 | } 264 | 265 | // Gets number of participants who 266 | // have deposited and withdrawn 267 | // ret: (dParticipants, wParticipants) 268 | function getParticipants(uint256 amountEther, uint256 index) public view 269 | returns (uint8, uint8) 270 | { 271 | uint256 receivedEther = floorEtherAndCheck(amountEther * 1 ether); 272 | Ring memory r = rings[receivedEther][index]; 273 | 274 | return (r.dParticipantsNo, r.wParticipantsNo); 275 | } 276 | 277 | // Gets the max nunmber of ring participants 278 | function getRingMaxParticipants() public pure 279 | returns (uint256) 280 | { 281 | return ringMaxParticipants; 282 | } 283 | 284 | // Gets the current ring index 285 | // for the given amount of ether 286 | // Used to estimate the current idx for better UX 287 | function getCurrentRingIdx(uint256 amountEther) public view 288 | returns (uint256) 289 | { 290 | uint256 receivedEther = floorEtherAndCheck(amountEther * 1 ether); 291 | return ringsNo[receivedEther]; 292 | } 293 | 294 | // Floors the current ether values 295 | // Makes sure the values needs to in `allowedAmounts` 296 | function floorEtherAndCheck(uint256 receivedAmount) internal view 297 | returns (uint256) 298 | { 299 | uint256 i; 300 | bool allowed = false; 301 | 302 | // Floors received ether 303 | uint256 receivedEther = (receivedAmount / 1 ether) * 1 ether; 304 | 305 | for (i = 0; i < 10; i ++) { 306 | if (allowedAmounts[i] == receivedEther) { 307 | allowed = true; 308 | } 309 | if (allowed) { 310 | break; 311 | } 312 | } 313 | 314 | // Revert if ETH sent isn't in the allowed fixed amounts 315 | require(allowed, "Only ETH values of 1, 2, 4, 6, 8 ... 32 are allowed"); 316 | 317 | return receivedEther; 318 | } 319 | } -------------------------------------------------------------------------------- /contracts/LSAG.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.0 <0.6.0; 2 | 3 | import "./AltBn128.sol"; 4 | 5 | /* 6 | Linkable Spontaneous Anonymous Groups 7 | 8 | https://eprint.iacr.org/2004/027.pdf 9 | */ 10 | 11 | library LSAG { 12 | // abi.encodePacked is the "concat" or "serialization" 13 | // of all supplied arguments into one long bytes value 14 | // i.e. abi.encodePacked :: [a] -> bytes 15 | 16 | /** 17 | * Converts an integer to an elliptic curve point 18 | */ 19 | function intToPoint(uint256 _x) public view 20 | returns (uint256[2] memory) 21 | { 22 | uint256 x = _x; 23 | uint256 y; 24 | uint256 beta; 25 | 26 | while (true) { 27 | (beta, y) = AltBn128.evalCurve(x); 28 | 29 | if (AltBn128.onCurveBeta(beta, y)) { 30 | return [x, y]; 31 | } 32 | 33 | x = AltBn128.addmodn(x, 1); 34 | } 35 | } 36 | 37 | /** 38 | * Returns an integer representation of the hash 39 | * of the input 40 | */ 41 | function H1(bytes memory b) public pure 42 | returns (uint256) 43 | { 44 | return AltBn128.modn(uint256(keccak256(b))); 45 | } 46 | 47 | /** 48 | * Returns elliptic curve point of the integer representation 49 | * of the hash of the input 50 | */ 51 | function H2(bytes memory b) public view 52 | returns (uint256[2] memory) 53 | { 54 | return intToPoint(H1(b)); 55 | } 56 | 57 | /** 58 | * Helper function to calculate Z1 59 | * Avoids stack too deep problem 60 | */ 61 | function ringCalcZ1( 62 | uint256[2] memory pubKey, 63 | uint256 c, 64 | uint256 s 65 | ) public view 66 | returns (uint256[2] memory) 67 | { 68 | return AltBn128.ecAdd( 69 | AltBn128.ecMulG(s), 70 | AltBn128.ecMul(pubKey, c) 71 | ); 72 | } 73 | 74 | /** 75 | * Helper function to calculate Z2 76 | * Avoids stack too deep problem 77 | */ 78 | function ringCalcZ2( 79 | uint256[2] memory keyImage, 80 | uint256[2] memory h, 81 | uint256 s, 82 | uint256 c 83 | ) public view 84 | returns (uint256[2] memory) 85 | { 86 | return AltBn128.ecAdd( 87 | AltBn128.ecMul(h, s), 88 | AltBn128.ecMul(keyImage, c) 89 | ); 90 | } 91 | 92 | 93 | /** 94 | * Verifies the ring signature 95 | * Section 4.2 of the paper https://eprint.iacr.org/2004/027.pdf 96 | */ 97 | function verify( 98 | bytes memory message, 99 | uint256 c0, 100 | uint256[2] memory keyImage, 101 | uint256[] memory s, 102 | uint256[2][] memory publicKeys 103 | ) public view 104 | returns (bool) 105 | { 106 | require(publicKeys.length >= 2, "Signature size too small"); 107 | require(publicKeys.length == s.length, "Signature sizes do not match!"); 108 | 109 | uint256 c = c0; 110 | uint256 i = 0; 111 | 112 | // Step 1 113 | // Extract out public key bytes 114 | bytes memory hBytes = ""; 115 | 116 | for (i = 0; i < publicKeys.length; i++) { 117 | hBytes = abi.encodePacked( 118 | hBytes, 119 | publicKeys[i] 120 | ); 121 | } 122 | 123 | uint256[2] memory h = H2(hBytes); 124 | 125 | // Step 2 126 | uint256[2] memory z_1; 127 | uint256[2] memory z_2; 128 | 129 | 130 | for (i = 0; i < publicKeys.length; i++) { 131 | z_1 = ringCalcZ1(publicKeys[i], c, s[i]); 132 | z_2 = ringCalcZ2(keyImage, h, s[i], c); 133 | 134 | if (i != publicKeys.length - 1) { 135 | c = H1( 136 | abi.encodePacked( 137 | hBytes, 138 | keyImage, 139 | message, 140 | z_1, 141 | z_2 142 | ) 143 | ); 144 | } 145 | } 146 | 147 | return c0 == H1( 148 | abi.encodePacked( 149 | hBytes, 150 | keyImage, 151 | message, 152 | z_1, 153 | z_2 154 | ) 155 | ); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.0; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /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_contract_deployments.js: -------------------------------------------------------------------------------- 1 | const AltBn128 = artifacts.require("AltBn128"); 2 | const LSAG = artifacts.require("LSAG"); 3 | const Heiswap = artifacts.require("Heiswap"); 4 | 5 | module.exports = function(deployer) { 6 | deployer.deploy(AltBn128); 7 | deployer.link(AltBn128, LSAG); 8 | deployer.deploy(LSAG); 9 | deployer.link(LSAG, Heiswap); 10 | deployer.link(AltBn128, Heiswap); 11 | deployer.deploy(Heiswap); 12 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "heiswap", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@drizzle-utils/core": "^0.3.1-alpha.0", 7 | "@drizzle-utils/get-web3": "^0.2.2-alpha.0", 8 | "axios": "^0.19.0", 9 | "bn.js": "^4.11.8", 10 | "crypto": "^1.0.1", 11 | "elliptic": "^6.5.0", 12 | "react": "^16.8.6", 13 | "react-dom": "^16.8.6", 14 | "react-router-dom": "^5.0.1", 15 | "react-scripts": "3.0.1", 16 | "rimble-ui": "^0.9.4", 17 | "styled-components": "^4.3.2", 18 | "web3": "1.0.0-beta.37", 19 | "web3-utils": "1.0.0-beta.37" 20 | }, 21 | "scripts": { 22 | "start": "react-scripts start", 23 | "build": "react-scripts build", 24 | "test": "(ganache-cli -k=petersburg --quiet &) && truffle test && react-scripts test", 25 | "eject": "react-scripts eject" 26 | }, 27 | "eslintConfig": { 28 | "extends": [ 29 | "standard", 30 | "standard-react", 31 | "plugin:flowtype/recommended" 32 | ], 33 | "parser": "babel-eslint", 34 | "plugins": [ 35 | "flowtype" 36 | ], 37 | "settings": { 38 | "flowtype": { 39 | "onlyFilesWithFlowAnnotation": false 40 | } 41 | } 42 | }, 43 | "browserslist": { 44 | "production": [ 45 | ">0.2%", 46 | "not dead", 47 | "not op_mini all" 48 | ], 49 | "development": [ 50 | "last 1 chrome version", 51 | "last 1 firefox version", 52 | "last 1 safari version" 53 | ] 54 | }, 55 | "devDependencies": { 56 | "babel-cli": "^6.26.0", 57 | "babel-preset-flow": "^6.23.0", 58 | "eslint-config-standard": "^12.0.0", 59 | "eslint-config-standard-react": "^7.0.2", 60 | "eslint-plugin-flowtype": "^3.11.1", 61 | "eslint-plugin-import": "^2.18.0", 62 | "eslint-plugin-node": "^9.1.0", 63 | "eslint-plugin-promise": "^4.2.1", 64 | "eslint-plugin-react": "^7.14.2", 65 | "eslint-plugin-standard": "^4.0.0", 66 | "flow-bin": "^0.102.0", 67 | "flow-remove-types": "^2.103.0", 68 | "ganache-cli": "^6.4.4", 69 | "truffle": "^5.0.26", 70 | "truffle-hdwallet-provider": "^1.0.12" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kendricktan/heiswap-dapp/61901f057e4b54241eade3545a4e3526514e6cd7/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | Heiswap Exchange 23 | 24 | 25 | 26 |
27 | 37 | 38 | 39 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kendricktan/heiswap-dapp/61901f057e4b54241eade3545a4e3526514e6cd7/src/.DS_Store -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kendricktan/heiswap-dapp/61901f057e4b54241eade3545a4e3526514e6cd7/src/App.css -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | /* eslint-disable jsx-ally/accessible-emoji */ 4 | 5 | import React, { useState, useEffect } from 'react' 6 | import getWeb3 from './utils/getWeb3' 7 | import createDrizzleUtils from '@drizzle-utils/core' 8 | import Web3StatusModal from './components/Web3StatusModal' 9 | import Logo from './assets/key.png' 10 | import DepositPage from './components/DepositPage' 11 | import WithdrawPage from './components/WithdrawPage' 12 | import StatusPage from './components/StatusPage' 13 | 14 | import { 15 | Heading, 16 | Text, 17 | Flex, 18 | Box, 19 | Button, 20 | Blockie, 21 | QR, 22 | Flash, 23 | ThemeProvider, 24 | Pill, 25 | PublicAddress, 26 | EthAddress, 27 | theme 28 | } from 'rimble-ui' 29 | 30 | import { DappGateway } from './types/DappGateway' 31 | import heiswapArtifact from './contracts/Heiswap.json' 32 | 33 | type TabState = { 34 | index: number 35 | } 36 | 37 | const HeiSwapApp = () => { 38 | // Dapp gateway state 39 | const [dappGateway: DappGateway, setDappGateway] = useState({ 40 | web3: null, 41 | drizzleUtils: null, 42 | ethAddress: null, 43 | attempted: false, 44 | heiswapInstance: null, 45 | heiswapEvent$: null 46 | }) 47 | 48 | // Status Modal 49 | const [web3StatusModal: Boolean, setweb3StatusModal] = useState(false) 50 | 51 | // "Tabs" (made with buttons) 52 | const [curTab: TabState, setCurTab] = useState({ 53 | index: 0 54 | }) 55 | 56 | // Helper function to initialize web3, drizzleUtils, and the ETH accounts 57 | const initDappGateway = async (): Boolean => { 58 | // Already initialized 59 | if (dappGateway.web3 !== null && dappGateway.drizzleUtils !== null && dappGateway.ethAddress !== null) { 60 | return true 61 | } 62 | 63 | try { 64 | const web3 = await getWeb3() 65 | const drizzleUtils = await createDrizzleUtils({ web3 }) 66 | const accounts = await drizzleUtils.getAccounts() 67 | 68 | let heiswapInstance = null; let heiswapEvent$ = null 69 | 70 | try { 71 | heiswapInstance = await drizzleUtils.getContractInstance({ artifact: heiswapArtifact }) 72 | heiswapEvent$ = await drizzleUtils.createEvent$({ artifact: heiswapArtifact }) 73 | } catch (err) { 74 | heiswapInstance = null 75 | heiswapEvent$ = null 76 | } 77 | 78 | setDappGateway({ 79 | web3, 80 | drizzleUtils, 81 | ethAddress: accounts[0], 82 | heiswapInstance, 83 | heiswapEvent$, 84 | attempted: true 85 | }) 86 | 87 | // Setup Account Stream 88 | drizzleUtils.currentAccount$.subscribe(a => { 89 | if (a !== dappGateway.ethAddress) { 90 | setDappGateway(Object.assign({}, dappGateway, { ethAddress: a })) 91 | } 92 | }) 93 | 94 | return true 95 | } catch (err) { 96 | setDappGateway(Object.assign({}, dappGateway, { attempted: true })) 97 | 98 | return false 99 | } 100 | } 101 | 102 | // On page load, grab web3 and drizzle utils and contract 103 | // definitions, as props, and inject them into the browser 104 | useEffect(() => { 105 | if ( 106 | (dappGateway.web3 === null || dappGateway.drizzleUtils === null) && 107 | !dappGateway.attempted 108 | ) { 109 | (async () => { 110 | await initDappGateway() 111 | })() 112 | } 113 | }) 114 | 115 | // Display warning if no web3 found 116 | const noWeb3: boolean = ( 117 | dappGateway.web3 === null && 118 | dappGateway.drizzleUtils === null && 119 | dappGateway.attempted 120 | ) 121 | 122 | const noContractInstance: boolean = ( 123 | dappGateway.heiswapInstance === null && 124 | dappGateway.web3 !== null 125 | ) 126 | 127 | return ( 128 | 129 |
130 |
131 | 132 | 133 | 134 | logo 135 | Heiswap 136 | 137 | 138 | 139 | 140 | {dappGateway.ethAddress === null ? ( 141 | 154 | ) : ( 155 | setweb3StatusModal(true)} 157 | > 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | )} 168 | 169 | 170 | 171 | 172 | 173 | 174 | Move ETH privately 🌚 175 | Hide your transfers from internet strangers. 176 | 177 | 178 | 184 | { curTab.index === 0 185 | ? Send 186 | : setCurTab({ index: 0 })}>Send 187 | } 188 | { curTab.index === 1 189 | ? Get 190 | : setCurTab({ index: 1 })}>Get 191 | } 192 | {/* { curTab.index === 2 193 | ? Status 194 | : setCurTab({ index: 2 })}>Status 195 | } */} 196 | 197 | 198 | { 199 | noWeb3 200 | ? 205 | 206 | Connect your Ethereum account to continue. 207 | 208 | 209 | : dappGateway.heiswapInstance === null && dappGateway.web3 !== null 210 | ? 215 | 216 | Switch to the Ropsten network to use Heiswap. 217 | 218 | 219 | : null 220 | } 221 | 222 | 227 | { 228 | (curTab.index === 0) ? 229 | : (curTab.index === 1) ? 230 | : (curTab.index === 2) ? 231 | :
Invalid Page
232 | } 233 |
234 |
235 |
236 | 237 | 238 | { 239 | dappGateway.ethAddress === null 240 | ? ( 241 |
242 | No Ethereum account found 243 |
244 | 245 | Please visit this page in a Web3 enabled browser.{' '} 246 | 247 | Learn more 248 | 249 | 250 |
251 | ) 252 | : ( 253 |
254 | Your connected Ethereum account 255 | Scan this QR code to send funds to your connected account. 256 | 257 | 258 | 259 | 260 | 266 | 267 |
268 | ) 269 | } 270 |
271 |
272 |
273 | 274 | Built by Kendrick Tan | Source code |  275 | Help & FAQ 276 | 277 | 278 |
279 |
280 |
281 | ) 282 | } 283 | 284 | const App = () => { 285 | return ( 286 | 287 | ) 288 | } 289 | 290 | export default App 291 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /src/assets/key.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kendricktan/heiswap-dapp/61901f057e4b54241eade3545a4e3526514e6cd7/src/assets/key.png -------------------------------------------------------------------------------- /src/components/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kendricktan/heiswap-dapp/61901f057e4b54241eade3545a4e3526514e6cd7/src/components/.DS_Store -------------------------------------------------------------------------------- /src/components/DepositPage.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | import crypto from 'crypto' 3 | import React, { useState } from 'react' 4 | import { Loader, Card, Form, Icon, Box, Flash, Modal, Select, Text, Button, Checkbox, PublicAddress, Heading, Flex } from 'rimble-ui' 5 | import { serialize, h1, bn128 } from '../utils/AltBn128' 6 | import { DappGateway } from '../types/DappGateway' 7 | 8 | type DepositForumParams = { 9 | targetEthAmount: Number, 10 | targetEthAddress: String, 11 | validEthAddress: Boolean 12 | } 13 | 14 | type ModalParams = { 15 | isOpen: Boolean, 16 | heiToken: String, 17 | acknowledgeClose: Boolean 18 | } 19 | 20 | const DepositPage = (props: { dappGateway: DappGateway, noWeb3: Boolean, noContractInstance: Boolean }) => { 21 | const { dappGateway } = props 22 | 23 | // Form validation 24 | const [depForumParams: DepositForumParams, setDepForumParams] = useState({ 25 | targetEthAmount: 2, 26 | targetEthAddress: '', 27 | validEthAddress: false 28 | }) 29 | 30 | // Modal to preview progress 31 | const [modalParams: ModalParams, setModalParams] = useState({ 32 | isOpen: false, 33 | acknowledgeClose: false, 34 | txHash: null, // transaction hash 35 | heiTokenEst: null, // Estimate what the hei-token will be 36 | heiTokenFinal: null // The real hei-token generated from the contract's ret value firing 37 | }) 38 | 39 | // Disable buttons etc if web3 isn't injected 40 | const { noWeb3, noContractInstance } = props 41 | 42 | return ( 43 | 44 |
{ 46 | (async () => { 47 | // No refresh 48 | e.preventDefault() 49 | 50 | const { targetEthAmount, targetEthAddress } = depForumParams 51 | const { ethAddress, heiswapInstance, web3 } = dappGateway 52 | 53 | // Generaete a burner secret key 54 | // and create a pseudo stealth address 55 | const randomSk = crypto.randomBytes(32).toString('hex') 56 | const stealthSk = h1( 57 | serialize([randomSk, targetEthAddress]) 58 | ) 59 | 60 | // Opens modal 61 | const estRingIdx = await heiswapInstance 62 | .methods 63 | .getCurrentRingIdx(targetEthAmount) 64 | .call() 65 | 66 | const heiTokenEst = `hei-${targetEthAmount}-${estRingIdx}-${randomSk}` 67 | // Make sure to set heiTokenFinal to null 68 | setModalParams(Object.assign({}, modalParams, { 69 | isOpen: true, 70 | heiTokenEst, 71 | heiTokenFinal: null 72 | })) 73 | 74 | // Append "0x" in front of it, web3 requires it 75 | const stealthPk = bn128.ecMulG(stealthSk).map(x => '0x' + x.toString(16)) 76 | 77 | // Deposit into Ring 78 | try { 79 | const gasPrice = await web3.eth.getGasPrice() 80 | 81 | const depositResult = await heiswapInstance 82 | .methods 83 | .deposit(stealthPk) 84 | .send( 85 | { 86 | from: ethAddress, 87 | value: web3.utils.toWei(targetEthAmount.toString(10), 'ether'), 88 | gasLimit: '800000', 89 | gasPrice 90 | } 91 | ) 92 | 93 | // Get event return value 94 | const depositEventRetVal = depositResult.events.Deposited.returnValues 95 | 96 | // Used to get the index of the ring 97 | const realRingIdx = depositEventRetVal.idx 98 | 99 | // Generate token 100 | // Format is "hei---" 101 | const heiTokenFinal = `hei-${targetEthAmount}-${realRingIdx}-${randomSk}` 102 | 103 | setModalParams(Object.assign(modalParams, { 104 | isOpen: true, 105 | heiTokenFinal, 106 | txHash: depositResult.transactionHash 107 | })) 108 | } catch (exc) { 109 | // TODO: Handle Exception 110 | } 111 | })() 112 | } 113 | } width='100%'> 114 | 115 | Send ETH 116 | {/* } 117 | 118 | 119 | 120 | 121 | 122 | 123 | Deposit ETH and get a token 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | Share the token with your recipient 134 | 135 | 136 | 137 | */} 138 | Deposit your ETH into the pool and get a token that represents the same value. That token can be used by the recipient address to withdraw your deposit. 139 | 143 | { 150 | // For the little checkmark 151 | if (e.target.value.indexOf('0x') === 0 && e.target.value.length === 42) { 152 | e.target.parentNode.classList.add('was-validated') 153 | } else { 154 | e.target.parentNode.classList.remove('was-validated') 155 | } 156 | 157 | setDepForumParams( 158 | Object.assign( 159 | {}, 160 | depForumParams, 161 | { 162 | targetEthAddress: e.target.value, 163 | validEthAddress: e.target.value.indexOf('0x') === 0 && e.target.value.length === 42 164 | }) 165 | ) 166 | }} 167 | /> 168 | 169 | 170 | setCustomRelayerURL(e.target.value)} 456 | /> 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | You will pay a small amount of fees to the relayer for GAS purposes. 465 | 466 | 467 | 468 | : 469 | 470 | 471 | 472 | 473 | You will need some ETH in your withdrawal address to pay for GAS. 474 | 475 | 476 | } 477 | 478 | 481 | 482 | 483 | 484 | 485 | 486 | { 496 | setOpenModal(false) 497 | setWithdrawalState(WITHDRAWALSTATES.Nothing) 498 | }} 499 | /> 500 | 501 | 502 | { getModalDisplay(withdrawalState) } 503 | 504 | 505 | 506 |
507 | ) 508 | } 509 | 510 | export default WithdrawPage 511 | -------------------------------------------------------------------------------- /src/contracts/Migrations.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Migrations", 3 | "abi": [ 4 | { 5 | "constant": true, 6 | "inputs": [], 7 | "name": "last_completed_migration", 8 | "outputs": [ 9 | { 10 | "name": "", 11 | "type": "uint256" 12 | } 13 | ], 14 | "payable": false, 15 | "stateMutability": "view", 16 | "type": "function" 17 | }, 18 | { 19 | "constant": true, 20 | "inputs": [], 21 | "name": "owner", 22 | "outputs": [ 23 | { 24 | "name": "", 25 | "type": "address" 26 | } 27 | ], 28 | "payable": false, 29 | "stateMutability": "view", 30 | "type": "function" 31 | }, 32 | { 33 | "inputs": [], 34 | "payable": false, 35 | "stateMutability": "nonpayable", 36 | "type": "constructor" 37 | }, 38 | { 39 | "constant": false, 40 | "inputs": [ 41 | { 42 | "name": "completed", 43 | "type": "uint256" 44 | } 45 | ], 46 | "name": "setCompleted", 47 | "outputs": [], 48 | "payable": false, 49 | "stateMutability": "nonpayable", 50 | "type": "function" 51 | }, 52 | { 53 | "constant": false, 54 | "inputs": [ 55 | { 56 | "name": "new_address", 57 | "type": "address" 58 | } 59 | ], 60 | "name": "upgrade", 61 | "outputs": [], 62 | "payable": false, 63 | "stateMutability": "nonpayable", 64 | "type": "function" 65 | } 66 | ], 67 | "metadata": "{\"compiler\":{\"version\":\"0.5.8+commit.23d335f2\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"constant\":false,\"inputs\":[{\"name\":\"new_address\",\"type\":\"address\"}],\"name\":\"upgrade\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"last_completed_migration\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"name\":\"\",\"type\":\"address\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"completed\",\"type\":\"uint256\"}],\"name\":\"setCompleted\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}],\"devdoc\":{\"methods\":{}},\"userdoc\":{\"methods\":{}}},\"settings\":{\"compilationTarget\":{\"/home/kendrick/Development/Ethereum/heiswap-dapp/contracts/Migrations.sol\":\"Migrations\"},\"evmVersion\":\"petersburg\",\"libraries\":{},\"optimizer\":{\"enabled\":false,\"runs\":200},\"remappings\":[]},\"sources\":{\"/home/kendrick/Development/Ethereum/heiswap-dapp/contracts/Migrations.sol\":{\"keccak256\":\"0xfdb731592344e2a2890faf03baec7b4bee7057ffba18ba6dbb6eec8db85f8f4c\",\"urls\":[\"bzzr://ddc8801d0a2a7220c2c9bf3881b4921817e72fdd96827ec8be4428fa009ace07\"]}},\"version\":1}", 68 | "bytecode": "0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102ae806100606000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80630900f01014610051578063445df0ac146100955780638da5cb5b146100b3578063fdacd576146100fd575b600080fd5b6100936004803603602081101561006757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061012b565b005b61009d6101f7565b6040518082815260200191505060405180910390f35b6100bb6101fd565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101296004803603602081101561011357600080fd5b8101908080359060200190929190505050610222565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156101f45760008190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156101da57600080fd5b505af11580156101ee573d6000803e3d6000fd5b50505050505b50565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561027f57806001819055505b5056fea165627a7a723058202343afe1ce30c88b3500c16443e2deefbb3e418720f77c60161eac80e1249cc00029", 69 | "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061004c5760003560e01c80630900f01014610051578063445df0ac146100955780638da5cb5b146100b3578063fdacd576146100fd575b600080fd5b6100936004803603602081101561006757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061012b565b005b61009d6101f7565b6040518082815260200191505060405180910390f35b6100bb6101fd565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101296004803603602081101561011357600080fd5b8101908080359060200190929190505050610222565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156101f45760008190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff1660e01b815260040180828152602001915050600060405180830381600087803b1580156101da57600080fd5b505af11580156101ee573d6000803e3d6000fd5b50505050505b50565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561027f57806001819055505b5056fea165627a7a723058202343afe1ce30c88b3500c16443e2deefbb3e418720f77c60161eac80e1249cc00029", 70 | "sourceMap": "34:480:3:-;;;123:50;8:9:-1;5:2;;;30:1;27;20:12;5:2;123:50:3;158:10;150:5;;:18;;;;;;;;;;;;;;;;;;34:480;;;;;;", 71 | "deployedSourceMap": "34:480:3:-;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;34:480:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;347:165;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;347:165:3;;;;;;;;;;;;;;;;;;;:::i;:::-;;82:36;;;:::i;:::-;;;;;;;;;;;;;;;;;;;58:20;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;240:103;;;;;;13:2:-1;8:3;5:11;2:2;;;29:1;26;19:12;2:2;240:103:3;;;;;;;;;;;;;;;;;:::i;:::-;;347:165;223:5;;;;;;;;;;;209:19;;:10;:19;;;205:26;;;409:19;442:11;409:45;;460:8;:21;;;482:24;;460:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8:9:-1;5:2;;;30:1;27;20:12;5:2;460:47:3;;;;8:9:-1;5:2;;;45:16;42:1;39;24:38;77:16;74:1;67:27;5:2;460:47:3;;;;230:1;205:26;347:165;:::o;82:36::-;;;;:::o;58:20::-;;;;;;;;;;;;;:::o;240:103::-;223:5;;;;;;;;;;;209:19;;:10;:19;;;205:26;;;329:9;302:24;:36;;;;205:26;240:103;:::o", 72 | "source": "pragma solidity >=0.4.21 <0.6.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 function upgrade(address new_address) public restricted {\n Migrations upgraded = Migrations(new_address);\n upgraded.setCompleted(last_completed_migration);\n }\n}\n", 73 | "sourcePath": "/home/kendrick/Development/Ethereum/heiswap-dapp/contracts/Migrations.sol", 74 | "ast": { 75 | "absolutePath": "/home/kendrick/Development/Ethereum/heiswap-dapp/contracts/Migrations.sol", 76 | "exportedSymbols": { 77 | "Migrations": [ 78 | 1463 79 | ] 80 | }, 81 | "id": 1464, 82 | "nodeType": "SourceUnit", 83 | "nodes": [ 84 | { 85 | "id": 1408, 86 | "literals": [ 87 | "solidity", 88 | ">=", 89 | "0.4", 90 | ".21", 91 | "<", 92 | "0.6", 93 | ".0" 94 | ], 95 | "nodeType": "PragmaDirective", 96 | "src": "0:32:3" 97 | }, 98 | { 99 | "baseContracts": [], 100 | "contractDependencies": [], 101 | "contractKind": "contract", 102 | "documentation": null, 103 | "fullyImplemented": true, 104 | "id": 1463, 105 | "linearizedBaseContracts": [ 106 | 1463 107 | ], 108 | "name": "Migrations", 109 | "nodeType": "ContractDefinition", 110 | "nodes": [ 111 | { 112 | "constant": false, 113 | "id": 1410, 114 | "name": "owner", 115 | "nodeType": "VariableDeclaration", 116 | "scope": 1463, 117 | "src": "58:20:3", 118 | "stateVariable": true, 119 | "storageLocation": "default", 120 | "typeDescriptions": { 121 | "typeIdentifier": "t_address", 122 | "typeString": "address" 123 | }, 124 | "typeName": { 125 | "id": 1409, 126 | "name": "address", 127 | "nodeType": "ElementaryTypeName", 128 | "src": "58:7:3", 129 | "stateMutability": "nonpayable", 130 | "typeDescriptions": { 131 | "typeIdentifier": "t_address", 132 | "typeString": "address" 133 | } 134 | }, 135 | "value": null, 136 | "visibility": "public" 137 | }, 138 | { 139 | "constant": false, 140 | "id": 1412, 141 | "name": "last_completed_migration", 142 | "nodeType": "VariableDeclaration", 143 | "scope": 1463, 144 | "src": "82:36:3", 145 | "stateVariable": true, 146 | "storageLocation": "default", 147 | "typeDescriptions": { 148 | "typeIdentifier": "t_uint256", 149 | "typeString": "uint256" 150 | }, 151 | "typeName": { 152 | "id": 1411, 153 | "name": "uint", 154 | "nodeType": "ElementaryTypeName", 155 | "src": "82:4:3", 156 | "typeDescriptions": { 157 | "typeIdentifier": "t_uint256", 158 | "typeString": "uint256" 159 | } 160 | }, 161 | "value": null, 162 | "visibility": "public" 163 | }, 164 | { 165 | "body": { 166 | "id": 1420, 167 | "nodeType": "Block", 168 | "src": "144:29:3", 169 | "statements": [ 170 | { 171 | "expression": { 172 | "argumentTypes": null, 173 | "id": 1418, 174 | "isConstant": false, 175 | "isLValue": false, 176 | "isPure": false, 177 | "lValueRequested": false, 178 | "leftHandSide": { 179 | "argumentTypes": null, 180 | "id": 1415, 181 | "name": "owner", 182 | "nodeType": "Identifier", 183 | "overloadedDeclarations": [], 184 | "referencedDeclaration": 1410, 185 | "src": "150:5:3", 186 | "typeDescriptions": { 187 | "typeIdentifier": "t_address", 188 | "typeString": "address" 189 | } 190 | }, 191 | "nodeType": "Assignment", 192 | "operator": "=", 193 | "rightHandSide": { 194 | "argumentTypes": null, 195 | "expression": { 196 | "argumentTypes": null, 197 | "id": 1416, 198 | "name": "msg", 199 | "nodeType": "Identifier", 200 | "overloadedDeclarations": [], 201 | "referencedDeclaration": 1478, 202 | "src": "158:3:3", 203 | "typeDescriptions": { 204 | "typeIdentifier": "t_magic_message", 205 | "typeString": "msg" 206 | } 207 | }, 208 | "id": 1417, 209 | "isConstant": false, 210 | "isLValue": false, 211 | "isPure": false, 212 | "lValueRequested": false, 213 | "memberName": "sender", 214 | "nodeType": "MemberAccess", 215 | "referencedDeclaration": null, 216 | "src": "158:10:3", 217 | "typeDescriptions": { 218 | "typeIdentifier": "t_address_payable", 219 | "typeString": "address payable" 220 | } 221 | }, 222 | "src": "150:18:3", 223 | "typeDescriptions": { 224 | "typeIdentifier": "t_address", 225 | "typeString": "address" 226 | } 227 | }, 228 | "id": 1419, 229 | "nodeType": "ExpressionStatement", 230 | "src": "150:18:3" 231 | } 232 | ] 233 | }, 234 | "documentation": null, 235 | "id": 1421, 236 | "implemented": true, 237 | "kind": "constructor", 238 | "modifiers": [], 239 | "name": "", 240 | "nodeType": "FunctionDefinition", 241 | "parameters": { 242 | "id": 1413, 243 | "nodeType": "ParameterList", 244 | "parameters": [], 245 | "src": "134:2:3" 246 | }, 247 | "returnParameters": { 248 | "id": 1414, 249 | "nodeType": "ParameterList", 250 | "parameters": [], 251 | "src": "144:0:3" 252 | }, 253 | "scope": 1463, 254 | "src": "123:50:3", 255 | "stateMutability": "nonpayable", 256 | "superFunction": null, 257 | "visibility": "public" 258 | }, 259 | { 260 | "body": { 261 | "id": 1429, 262 | "nodeType": "Block", 263 | "src": "199:37:3", 264 | "statements": [ 265 | { 266 | "condition": { 267 | "argumentTypes": null, 268 | "commonType": { 269 | "typeIdentifier": "t_address", 270 | "typeString": "address" 271 | }, 272 | "id": 1426, 273 | "isConstant": false, 274 | "isLValue": false, 275 | "isPure": false, 276 | "lValueRequested": false, 277 | "leftExpression": { 278 | "argumentTypes": null, 279 | "expression": { 280 | "argumentTypes": null, 281 | "id": 1423, 282 | "name": "msg", 283 | "nodeType": "Identifier", 284 | "overloadedDeclarations": [], 285 | "referencedDeclaration": 1478, 286 | "src": "209:3:3", 287 | "typeDescriptions": { 288 | "typeIdentifier": "t_magic_message", 289 | "typeString": "msg" 290 | } 291 | }, 292 | "id": 1424, 293 | "isConstant": false, 294 | "isLValue": false, 295 | "isPure": false, 296 | "lValueRequested": false, 297 | "memberName": "sender", 298 | "nodeType": "MemberAccess", 299 | "referencedDeclaration": null, 300 | "src": "209:10:3", 301 | "typeDescriptions": { 302 | "typeIdentifier": "t_address_payable", 303 | "typeString": "address payable" 304 | } 305 | }, 306 | "nodeType": "BinaryOperation", 307 | "operator": "==", 308 | "rightExpression": { 309 | "argumentTypes": null, 310 | "id": 1425, 311 | "name": "owner", 312 | "nodeType": "Identifier", 313 | "overloadedDeclarations": [], 314 | "referencedDeclaration": 1410, 315 | "src": "223:5:3", 316 | "typeDescriptions": { 317 | "typeIdentifier": "t_address", 318 | "typeString": "address" 319 | } 320 | }, 321 | "src": "209:19:3", 322 | "typeDescriptions": { 323 | "typeIdentifier": "t_bool", 324 | "typeString": "bool" 325 | } 326 | }, 327 | "falseBody": null, 328 | "id": 1428, 329 | "nodeType": "IfStatement", 330 | "src": "205:26:3", 331 | "trueBody": { 332 | "id": 1427, 333 | "nodeType": "PlaceholderStatement", 334 | "src": "230:1:3" 335 | } 336 | } 337 | ] 338 | }, 339 | "documentation": null, 340 | "id": 1430, 341 | "name": "restricted", 342 | "nodeType": "ModifierDefinition", 343 | "parameters": { 344 | "id": 1422, 345 | "nodeType": "ParameterList", 346 | "parameters": [], 347 | "src": "196:2:3" 348 | }, 349 | "src": "177:59:3", 350 | "visibility": "internal" 351 | }, 352 | { 353 | "body": { 354 | "id": 1441, 355 | "nodeType": "Block", 356 | "src": "296:47:3", 357 | "statements": [ 358 | { 359 | "expression": { 360 | "argumentTypes": null, 361 | "id": 1439, 362 | "isConstant": false, 363 | "isLValue": false, 364 | "isPure": false, 365 | "lValueRequested": false, 366 | "leftHandSide": { 367 | "argumentTypes": null, 368 | "id": 1437, 369 | "name": "last_completed_migration", 370 | "nodeType": "Identifier", 371 | "overloadedDeclarations": [], 372 | "referencedDeclaration": 1412, 373 | "src": "302:24:3", 374 | "typeDescriptions": { 375 | "typeIdentifier": "t_uint256", 376 | "typeString": "uint256" 377 | } 378 | }, 379 | "nodeType": "Assignment", 380 | "operator": "=", 381 | "rightHandSide": { 382 | "argumentTypes": null, 383 | "id": 1438, 384 | "name": "completed", 385 | "nodeType": "Identifier", 386 | "overloadedDeclarations": [], 387 | "referencedDeclaration": 1432, 388 | "src": "329:9:3", 389 | "typeDescriptions": { 390 | "typeIdentifier": "t_uint256", 391 | "typeString": "uint256" 392 | } 393 | }, 394 | "src": "302:36:3", 395 | "typeDescriptions": { 396 | "typeIdentifier": "t_uint256", 397 | "typeString": "uint256" 398 | } 399 | }, 400 | "id": 1440, 401 | "nodeType": "ExpressionStatement", 402 | "src": "302:36:3" 403 | } 404 | ] 405 | }, 406 | "documentation": null, 407 | "id": 1442, 408 | "implemented": true, 409 | "kind": "function", 410 | "modifiers": [ 411 | { 412 | "arguments": null, 413 | "id": 1435, 414 | "modifierName": { 415 | "argumentTypes": null, 416 | "id": 1434, 417 | "name": "restricted", 418 | "nodeType": "Identifier", 419 | "overloadedDeclarations": [], 420 | "referencedDeclaration": 1430, 421 | "src": "285:10:3", 422 | "typeDescriptions": { 423 | "typeIdentifier": "t_modifier$__$", 424 | "typeString": "modifier ()" 425 | } 426 | }, 427 | "nodeType": "ModifierInvocation", 428 | "src": "285:10:3" 429 | } 430 | ], 431 | "name": "setCompleted", 432 | "nodeType": "FunctionDefinition", 433 | "parameters": { 434 | "id": 1433, 435 | "nodeType": "ParameterList", 436 | "parameters": [ 437 | { 438 | "constant": false, 439 | "id": 1432, 440 | "name": "completed", 441 | "nodeType": "VariableDeclaration", 442 | "scope": 1442, 443 | "src": "262:14:3", 444 | "stateVariable": false, 445 | "storageLocation": "default", 446 | "typeDescriptions": { 447 | "typeIdentifier": "t_uint256", 448 | "typeString": "uint256" 449 | }, 450 | "typeName": { 451 | "id": 1431, 452 | "name": "uint", 453 | "nodeType": "ElementaryTypeName", 454 | "src": "262:4:3", 455 | "typeDescriptions": { 456 | "typeIdentifier": "t_uint256", 457 | "typeString": "uint256" 458 | } 459 | }, 460 | "value": null, 461 | "visibility": "internal" 462 | } 463 | ], 464 | "src": "261:16:3" 465 | }, 466 | "returnParameters": { 467 | "id": 1436, 468 | "nodeType": "ParameterList", 469 | "parameters": [], 470 | "src": "296:0:3" 471 | }, 472 | "scope": 1463, 473 | "src": "240:103:3", 474 | "stateMutability": "nonpayable", 475 | "superFunction": null, 476 | "visibility": "public" 477 | }, 478 | { 479 | "body": { 480 | "id": 1461, 481 | "nodeType": "Block", 482 | "src": "403:109:3", 483 | "statements": [ 484 | { 485 | "assignments": [ 486 | 1450 487 | ], 488 | "declarations": [ 489 | { 490 | "constant": false, 491 | "id": 1450, 492 | "name": "upgraded", 493 | "nodeType": "VariableDeclaration", 494 | "scope": 1461, 495 | "src": "409:19:3", 496 | "stateVariable": false, 497 | "storageLocation": "default", 498 | "typeDescriptions": { 499 | "typeIdentifier": "t_contract$_Migrations_$1463", 500 | "typeString": "contract Migrations" 501 | }, 502 | "typeName": { 503 | "contractScope": null, 504 | "id": 1449, 505 | "name": "Migrations", 506 | "nodeType": "UserDefinedTypeName", 507 | "referencedDeclaration": 1463, 508 | "src": "409:10:3", 509 | "typeDescriptions": { 510 | "typeIdentifier": "t_contract$_Migrations_$1463", 511 | "typeString": "contract Migrations" 512 | } 513 | }, 514 | "value": null, 515 | "visibility": "internal" 516 | } 517 | ], 518 | "id": 1454, 519 | "initialValue": { 520 | "argumentTypes": null, 521 | "arguments": [ 522 | { 523 | "argumentTypes": null, 524 | "id": 1452, 525 | "name": "new_address", 526 | "nodeType": "Identifier", 527 | "overloadedDeclarations": [], 528 | "referencedDeclaration": 1444, 529 | "src": "442:11:3", 530 | "typeDescriptions": { 531 | "typeIdentifier": "t_address", 532 | "typeString": "address" 533 | } 534 | } 535 | ], 536 | "expression": { 537 | "argumentTypes": [ 538 | { 539 | "typeIdentifier": "t_address", 540 | "typeString": "address" 541 | } 542 | ], 543 | "id": 1451, 544 | "name": "Migrations", 545 | "nodeType": "Identifier", 546 | "overloadedDeclarations": [], 547 | "referencedDeclaration": 1463, 548 | "src": "431:10:3", 549 | "typeDescriptions": { 550 | "typeIdentifier": "t_type$_t_contract$_Migrations_$1463_$", 551 | "typeString": "type(contract Migrations)" 552 | } 553 | }, 554 | "id": 1453, 555 | "isConstant": false, 556 | "isLValue": false, 557 | "isPure": false, 558 | "kind": "typeConversion", 559 | "lValueRequested": false, 560 | "names": [], 561 | "nodeType": "FunctionCall", 562 | "src": "431:23:3", 563 | "typeDescriptions": { 564 | "typeIdentifier": "t_contract$_Migrations_$1463", 565 | "typeString": "contract Migrations" 566 | } 567 | }, 568 | "nodeType": "VariableDeclarationStatement", 569 | "src": "409:45:3" 570 | }, 571 | { 572 | "expression": { 573 | "argumentTypes": null, 574 | "arguments": [ 575 | { 576 | "argumentTypes": null, 577 | "id": 1458, 578 | "name": "last_completed_migration", 579 | "nodeType": "Identifier", 580 | "overloadedDeclarations": [], 581 | "referencedDeclaration": 1412, 582 | "src": "482:24:3", 583 | "typeDescriptions": { 584 | "typeIdentifier": "t_uint256", 585 | "typeString": "uint256" 586 | } 587 | } 588 | ], 589 | "expression": { 590 | "argumentTypes": [ 591 | { 592 | "typeIdentifier": "t_uint256", 593 | "typeString": "uint256" 594 | } 595 | ], 596 | "expression": { 597 | "argumentTypes": null, 598 | "id": 1455, 599 | "name": "upgraded", 600 | "nodeType": "Identifier", 601 | "overloadedDeclarations": [], 602 | "referencedDeclaration": 1450, 603 | "src": "460:8:3", 604 | "typeDescriptions": { 605 | "typeIdentifier": "t_contract$_Migrations_$1463", 606 | "typeString": "contract Migrations" 607 | } 608 | }, 609 | "id": 1457, 610 | "isConstant": false, 611 | "isLValue": false, 612 | "isPure": false, 613 | "lValueRequested": false, 614 | "memberName": "setCompleted", 615 | "nodeType": "MemberAccess", 616 | "referencedDeclaration": 1442, 617 | "src": "460:21:3", 618 | "typeDescriptions": { 619 | "typeIdentifier": "t_function_external_nonpayable$_t_uint256_$returns$__$", 620 | "typeString": "function (uint256) external" 621 | } 622 | }, 623 | "id": 1459, 624 | "isConstant": false, 625 | "isLValue": false, 626 | "isPure": false, 627 | "kind": "functionCall", 628 | "lValueRequested": false, 629 | "names": [], 630 | "nodeType": "FunctionCall", 631 | "src": "460:47:3", 632 | "typeDescriptions": { 633 | "typeIdentifier": "t_tuple$__$", 634 | "typeString": "tuple()" 635 | } 636 | }, 637 | "id": 1460, 638 | "nodeType": "ExpressionStatement", 639 | "src": "460:47:3" 640 | } 641 | ] 642 | }, 643 | "documentation": null, 644 | "id": 1462, 645 | "implemented": true, 646 | "kind": "function", 647 | "modifiers": [ 648 | { 649 | "arguments": null, 650 | "id": 1447, 651 | "modifierName": { 652 | "argumentTypes": null, 653 | "id": 1446, 654 | "name": "restricted", 655 | "nodeType": "Identifier", 656 | "overloadedDeclarations": [], 657 | "referencedDeclaration": 1430, 658 | "src": "392:10:3", 659 | "typeDescriptions": { 660 | "typeIdentifier": "t_modifier$__$", 661 | "typeString": "modifier ()" 662 | } 663 | }, 664 | "nodeType": "ModifierInvocation", 665 | "src": "392:10:3" 666 | } 667 | ], 668 | "name": "upgrade", 669 | "nodeType": "FunctionDefinition", 670 | "parameters": { 671 | "id": 1445, 672 | "nodeType": "ParameterList", 673 | "parameters": [ 674 | { 675 | "constant": false, 676 | "id": 1444, 677 | "name": "new_address", 678 | "nodeType": "VariableDeclaration", 679 | "scope": 1462, 680 | "src": "364:19:3", 681 | "stateVariable": false, 682 | "storageLocation": "default", 683 | "typeDescriptions": { 684 | "typeIdentifier": "t_address", 685 | "typeString": "address" 686 | }, 687 | "typeName": { 688 | "id": 1443, 689 | "name": "address", 690 | "nodeType": "ElementaryTypeName", 691 | "src": "364:7:3", 692 | "stateMutability": "nonpayable", 693 | "typeDescriptions": { 694 | "typeIdentifier": "t_address", 695 | "typeString": "address" 696 | } 697 | }, 698 | "value": null, 699 | "visibility": "internal" 700 | } 701 | ], 702 | "src": "363:21:3" 703 | }, 704 | "returnParameters": { 705 | "id": 1448, 706 | "nodeType": "ParameterList", 707 | "parameters": [], 708 | "src": "403:0:3" 709 | }, 710 | "scope": 1463, 711 | "src": "347:165:3", 712 | "stateMutability": "nonpayable", 713 | "superFunction": null, 714 | "visibility": "public" 715 | } 716 | ], 717 | "scope": 1464, 718 | "src": "34:480:3" 719 | } 720 | ], 721 | "src": "0:515:3" 722 | }, 723 | "legacyAST": { 724 | "absolutePath": "/home/kendrick/Development/Ethereum/heiswap-dapp/contracts/Migrations.sol", 725 | "exportedSymbols": { 726 | "Migrations": [ 727 | 1463 728 | ] 729 | }, 730 | "id": 1464, 731 | "nodeType": "SourceUnit", 732 | "nodes": [ 733 | { 734 | "id": 1408, 735 | "literals": [ 736 | "solidity", 737 | ">=", 738 | "0.4", 739 | ".21", 740 | "<", 741 | "0.6", 742 | ".0" 743 | ], 744 | "nodeType": "PragmaDirective", 745 | "src": "0:32:3" 746 | }, 747 | { 748 | "baseContracts": [], 749 | "contractDependencies": [], 750 | "contractKind": "contract", 751 | "documentation": null, 752 | "fullyImplemented": true, 753 | "id": 1463, 754 | "linearizedBaseContracts": [ 755 | 1463 756 | ], 757 | "name": "Migrations", 758 | "nodeType": "ContractDefinition", 759 | "nodes": [ 760 | { 761 | "constant": false, 762 | "id": 1410, 763 | "name": "owner", 764 | "nodeType": "VariableDeclaration", 765 | "scope": 1463, 766 | "src": "58:20:3", 767 | "stateVariable": true, 768 | "storageLocation": "default", 769 | "typeDescriptions": { 770 | "typeIdentifier": "t_address", 771 | "typeString": "address" 772 | }, 773 | "typeName": { 774 | "id": 1409, 775 | "name": "address", 776 | "nodeType": "ElementaryTypeName", 777 | "src": "58:7:3", 778 | "stateMutability": "nonpayable", 779 | "typeDescriptions": { 780 | "typeIdentifier": "t_address", 781 | "typeString": "address" 782 | } 783 | }, 784 | "value": null, 785 | "visibility": "public" 786 | }, 787 | { 788 | "constant": false, 789 | "id": 1412, 790 | "name": "last_completed_migration", 791 | "nodeType": "VariableDeclaration", 792 | "scope": 1463, 793 | "src": "82:36:3", 794 | "stateVariable": true, 795 | "storageLocation": "default", 796 | "typeDescriptions": { 797 | "typeIdentifier": "t_uint256", 798 | "typeString": "uint256" 799 | }, 800 | "typeName": { 801 | "id": 1411, 802 | "name": "uint", 803 | "nodeType": "ElementaryTypeName", 804 | "src": "82:4:3", 805 | "typeDescriptions": { 806 | "typeIdentifier": "t_uint256", 807 | "typeString": "uint256" 808 | } 809 | }, 810 | "value": null, 811 | "visibility": "public" 812 | }, 813 | { 814 | "body": { 815 | "id": 1420, 816 | "nodeType": "Block", 817 | "src": "144:29:3", 818 | "statements": [ 819 | { 820 | "expression": { 821 | "argumentTypes": null, 822 | "id": 1418, 823 | "isConstant": false, 824 | "isLValue": false, 825 | "isPure": false, 826 | "lValueRequested": false, 827 | "leftHandSide": { 828 | "argumentTypes": null, 829 | "id": 1415, 830 | "name": "owner", 831 | "nodeType": "Identifier", 832 | "overloadedDeclarations": [], 833 | "referencedDeclaration": 1410, 834 | "src": "150:5:3", 835 | "typeDescriptions": { 836 | "typeIdentifier": "t_address", 837 | "typeString": "address" 838 | } 839 | }, 840 | "nodeType": "Assignment", 841 | "operator": "=", 842 | "rightHandSide": { 843 | "argumentTypes": null, 844 | "expression": { 845 | "argumentTypes": null, 846 | "id": 1416, 847 | "name": "msg", 848 | "nodeType": "Identifier", 849 | "overloadedDeclarations": [], 850 | "referencedDeclaration": 1478, 851 | "src": "158:3:3", 852 | "typeDescriptions": { 853 | "typeIdentifier": "t_magic_message", 854 | "typeString": "msg" 855 | } 856 | }, 857 | "id": 1417, 858 | "isConstant": false, 859 | "isLValue": false, 860 | "isPure": false, 861 | "lValueRequested": false, 862 | "memberName": "sender", 863 | "nodeType": "MemberAccess", 864 | "referencedDeclaration": null, 865 | "src": "158:10:3", 866 | "typeDescriptions": { 867 | "typeIdentifier": "t_address_payable", 868 | "typeString": "address payable" 869 | } 870 | }, 871 | "src": "150:18:3", 872 | "typeDescriptions": { 873 | "typeIdentifier": "t_address", 874 | "typeString": "address" 875 | } 876 | }, 877 | "id": 1419, 878 | "nodeType": "ExpressionStatement", 879 | "src": "150:18:3" 880 | } 881 | ] 882 | }, 883 | "documentation": null, 884 | "id": 1421, 885 | "implemented": true, 886 | "kind": "constructor", 887 | "modifiers": [], 888 | "name": "", 889 | "nodeType": "FunctionDefinition", 890 | "parameters": { 891 | "id": 1413, 892 | "nodeType": "ParameterList", 893 | "parameters": [], 894 | "src": "134:2:3" 895 | }, 896 | "returnParameters": { 897 | "id": 1414, 898 | "nodeType": "ParameterList", 899 | "parameters": [], 900 | "src": "144:0:3" 901 | }, 902 | "scope": 1463, 903 | "src": "123:50:3", 904 | "stateMutability": "nonpayable", 905 | "superFunction": null, 906 | "visibility": "public" 907 | }, 908 | { 909 | "body": { 910 | "id": 1429, 911 | "nodeType": "Block", 912 | "src": "199:37:3", 913 | "statements": [ 914 | { 915 | "condition": { 916 | "argumentTypes": null, 917 | "commonType": { 918 | "typeIdentifier": "t_address", 919 | "typeString": "address" 920 | }, 921 | "id": 1426, 922 | "isConstant": false, 923 | "isLValue": false, 924 | "isPure": false, 925 | "lValueRequested": false, 926 | "leftExpression": { 927 | "argumentTypes": null, 928 | "expression": { 929 | "argumentTypes": null, 930 | "id": 1423, 931 | "name": "msg", 932 | "nodeType": "Identifier", 933 | "overloadedDeclarations": [], 934 | "referencedDeclaration": 1478, 935 | "src": "209:3:3", 936 | "typeDescriptions": { 937 | "typeIdentifier": "t_magic_message", 938 | "typeString": "msg" 939 | } 940 | }, 941 | "id": 1424, 942 | "isConstant": false, 943 | "isLValue": false, 944 | "isPure": false, 945 | "lValueRequested": false, 946 | "memberName": "sender", 947 | "nodeType": "MemberAccess", 948 | "referencedDeclaration": null, 949 | "src": "209:10:3", 950 | "typeDescriptions": { 951 | "typeIdentifier": "t_address_payable", 952 | "typeString": "address payable" 953 | } 954 | }, 955 | "nodeType": "BinaryOperation", 956 | "operator": "==", 957 | "rightExpression": { 958 | "argumentTypes": null, 959 | "id": 1425, 960 | "name": "owner", 961 | "nodeType": "Identifier", 962 | "overloadedDeclarations": [], 963 | "referencedDeclaration": 1410, 964 | "src": "223:5:3", 965 | "typeDescriptions": { 966 | "typeIdentifier": "t_address", 967 | "typeString": "address" 968 | } 969 | }, 970 | "src": "209:19:3", 971 | "typeDescriptions": { 972 | "typeIdentifier": "t_bool", 973 | "typeString": "bool" 974 | } 975 | }, 976 | "falseBody": null, 977 | "id": 1428, 978 | "nodeType": "IfStatement", 979 | "src": "205:26:3", 980 | "trueBody": { 981 | "id": 1427, 982 | "nodeType": "PlaceholderStatement", 983 | "src": "230:1:3" 984 | } 985 | } 986 | ] 987 | }, 988 | "documentation": null, 989 | "id": 1430, 990 | "name": "restricted", 991 | "nodeType": "ModifierDefinition", 992 | "parameters": { 993 | "id": 1422, 994 | "nodeType": "ParameterList", 995 | "parameters": [], 996 | "src": "196:2:3" 997 | }, 998 | "src": "177:59:3", 999 | "visibility": "internal" 1000 | }, 1001 | { 1002 | "body": { 1003 | "id": 1441, 1004 | "nodeType": "Block", 1005 | "src": "296:47:3", 1006 | "statements": [ 1007 | { 1008 | "expression": { 1009 | "argumentTypes": null, 1010 | "id": 1439, 1011 | "isConstant": false, 1012 | "isLValue": false, 1013 | "isPure": false, 1014 | "lValueRequested": false, 1015 | "leftHandSide": { 1016 | "argumentTypes": null, 1017 | "id": 1437, 1018 | "name": "last_completed_migration", 1019 | "nodeType": "Identifier", 1020 | "overloadedDeclarations": [], 1021 | "referencedDeclaration": 1412, 1022 | "src": "302:24:3", 1023 | "typeDescriptions": { 1024 | "typeIdentifier": "t_uint256", 1025 | "typeString": "uint256" 1026 | } 1027 | }, 1028 | "nodeType": "Assignment", 1029 | "operator": "=", 1030 | "rightHandSide": { 1031 | "argumentTypes": null, 1032 | "id": 1438, 1033 | "name": "completed", 1034 | "nodeType": "Identifier", 1035 | "overloadedDeclarations": [], 1036 | "referencedDeclaration": 1432, 1037 | "src": "329:9:3", 1038 | "typeDescriptions": { 1039 | "typeIdentifier": "t_uint256", 1040 | "typeString": "uint256" 1041 | } 1042 | }, 1043 | "src": "302:36:3", 1044 | "typeDescriptions": { 1045 | "typeIdentifier": "t_uint256", 1046 | "typeString": "uint256" 1047 | } 1048 | }, 1049 | "id": 1440, 1050 | "nodeType": "ExpressionStatement", 1051 | "src": "302:36:3" 1052 | } 1053 | ] 1054 | }, 1055 | "documentation": null, 1056 | "id": 1442, 1057 | "implemented": true, 1058 | "kind": "function", 1059 | "modifiers": [ 1060 | { 1061 | "arguments": null, 1062 | "id": 1435, 1063 | "modifierName": { 1064 | "argumentTypes": null, 1065 | "id": 1434, 1066 | "name": "restricted", 1067 | "nodeType": "Identifier", 1068 | "overloadedDeclarations": [], 1069 | "referencedDeclaration": 1430, 1070 | "src": "285:10:3", 1071 | "typeDescriptions": { 1072 | "typeIdentifier": "t_modifier$__$", 1073 | "typeString": "modifier ()" 1074 | } 1075 | }, 1076 | "nodeType": "ModifierInvocation", 1077 | "src": "285:10:3" 1078 | } 1079 | ], 1080 | "name": "setCompleted", 1081 | "nodeType": "FunctionDefinition", 1082 | "parameters": { 1083 | "id": 1433, 1084 | "nodeType": "ParameterList", 1085 | "parameters": [ 1086 | { 1087 | "constant": false, 1088 | "id": 1432, 1089 | "name": "completed", 1090 | "nodeType": "VariableDeclaration", 1091 | "scope": 1442, 1092 | "src": "262:14:3", 1093 | "stateVariable": false, 1094 | "storageLocation": "default", 1095 | "typeDescriptions": { 1096 | "typeIdentifier": "t_uint256", 1097 | "typeString": "uint256" 1098 | }, 1099 | "typeName": { 1100 | "id": 1431, 1101 | "name": "uint", 1102 | "nodeType": "ElementaryTypeName", 1103 | "src": "262:4:3", 1104 | "typeDescriptions": { 1105 | "typeIdentifier": "t_uint256", 1106 | "typeString": "uint256" 1107 | } 1108 | }, 1109 | "value": null, 1110 | "visibility": "internal" 1111 | } 1112 | ], 1113 | "src": "261:16:3" 1114 | }, 1115 | "returnParameters": { 1116 | "id": 1436, 1117 | "nodeType": "ParameterList", 1118 | "parameters": [], 1119 | "src": "296:0:3" 1120 | }, 1121 | "scope": 1463, 1122 | "src": "240:103:3", 1123 | "stateMutability": "nonpayable", 1124 | "superFunction": null, 1125 | "visibility": "public" 1126 | }, 1127 | { 1128 | "body": { 1129 | "id": 1461, 1130 | "nodeType": "Block", 1131 | "src": "403:109:3", 1132 | "statements": [ 1133 | { 1134 | "assignments": [ 1135 | 1450 1136 | ], 1137 | "declarations": [ 1138 | { 1139 | "constant": false, 1140 | "id": 1450, 1141 | "name": "upgraded", 1142 | "nodeType": "VariableDeclaration", 1143 | "scope": 1461, 1144 | "src": "409:19:3", 1145 | "stateVariable": false, 1146 | "storageLocation": "default", 1147 | "typeDescriptions": { 1148 | "typeIdentifier": "t_contract$_Migrations_$1463", 1149 | "typeString": "contract Migrations" 1150 | }, 1151 | "typeName": { 1152 | "contractScope": null, 1153 | "id": 1449, 1154 | "name": "Migrations", 1155 | "nodeType": "UserDefinedTypeName", 1156 | "referencedDeclaration": 1463, 1157 | "src": "409:10:3", 1158 | "typeDescriptions": { 1159 | "typeIdentifier": "t_contract$_Migrations_$1463", 1160 | "typeString": "contract Migrations" 1161 | } 1162 | }, 1163 | "value": null, 1164 | "visibility": "internal" 1165 | } 1166 | ], 1167 | "id": 1454, 1168 | "initialValue": { 1169 | "argumentTypes": null, 1170 | "arguments": [ 1171 | { 1172 | "argumentTypes": null, 1173 | "id": 1452, 1174 | "name": "new_address", 1175 | "nodeType": "Identifier", 1176 | "overloadedDeclarations": [], 1177 | "referencedDeclaration": 1444, 1178 | "src": "442:11:3", 1179 | "typeDescriptions": { 1180 | "typeIdentifier": "t_address", 1181 | "typeString": "address" 1182 | } 1183 | } 1184 | ], 1185 | "expression": { 1186 | "argumentTypes": [ 1187 | { 1188 | "typeIdentifier": "t_address", 1189 | "typeString": "address" 1190 | } 1191 | ], 1192 | "id": 1451, 1193 | "name": "Migrations", 1194 | "nodeType": "Identifier", 1195 | "overloadedDeclarations": [], 1196 | "referencedDeclaration": 1463, 1197 | "src": "431:10:3", 1198 | "typeDescriptions": { 1199 | "typeIdentifier": "t_type$_t_contract$_Migrations_$1463_$", 1200 | "typeString": "type(contract Migrations)" 1201 | } 1202 | }, 1203 | "id": 1453, 1204 | "isConstant": false, 1205 | "isLValue": false, 1206 | "isPure": false, 1207 | "kind": "typeConversion", 1208 | "lValueRequested": false, 1209 | "names": [], 1210 | "nodeType": "FunctionCall", 1211 | "src": "431:23:3", 1212 | "typeDescriptions": { 1213 | "typeIdentifier": "t_contract$_Migrations_$1463", 1214 | "typeString": "contract Migrations" 1215 | } 1216 | }, 1217 | "nodeType": "VariableDeclarationStatement", 1218 | "src": "409:45:3" 1219 | }, 1220 | { 1221 | "expression": { 1222 | "argumentTypes": null, 1223 | "arguments": [ 1224 | { 1225 | "argumentTypes": null, 1226 | "id": 1458, 1227 | "name": "last_completed_migration", 1228 | "nodeType": "Identifier", 1229 | "overloadedDeclarations": [], 1230 | "referencedDeclaration": 1412, 1231 | "src": "482:24:3", 1232 | "typeDescriptions": { 1233 | "typeIdentifier": "t_uint256", 1234 | "typeString": "uint256" 1235 | } 1236 | } 1237 | ], 1238 | "expression": { 1239 | "argumentTypes": [ 1240 | { 1241 | "typeIdentifier": "t_uint256", 1242 | "typeString": "uint256" 1243 | } 1244 | ], 1245 | "expression": { 1246 | "argumentTypes": null, 1247 | "id": 1455, 1248 | "name": "upgraded", 1249 | "nodeType": "Identifier", 1250 | "overloadedDeclarations": [], 1251 | "referencedDeclaration": 1450, 1252 | "src": "460:8:3", 1253 | "typeDescriptions": { 1254 | "typeIdentifier": "t_contract$_Migrations_$1463", 1255 | "typeString": "contract Migrations" 1256 | } 1257 | }, 1258 | "id": 1457, 1259 | "isConstant": false, 1260 | "isLValue": false, 1261 | "isPure": false, 1262 | "lValueRequested": false, 1263 | "memberName": "setCompleted", 1264 | "nodeType": "MemberAccess", 1265 | "referencedDeclaration": 1442, 1266 | "src": "460:21:3", 1267 | "typeDescriptions": { 1268 | "typeIdentifier": "t_function_external_nonpayable$_t_uint256_$returns$__$", 1269 | "typeString": "function (uint256) external" 1270 | } 1271 | }, 1272 | "id": 1459, 1273 | "isConstant": false, 1274 | "isLValue": false, 1275 | "isPure": false, 1276 | "kind": "functionCall", 1277 | "lValueRequested": false, 1278 | "names": [], 1279 | "nodeType": "FunctionCall", 1280 | "src": "460:47:3", 1281 | "typeDescriptions": { 1282 | "typeIdentifier": "t_tuple$__$", 1283 | "typeString": "tuple()" 1284 | } 1285 | }, 1286 | "id": 1460, 1287 | "nodeType": "ExpressionStatement", 1288 | "src": "460:47:3" 1289 | } 1290 | ] 1291 | }, 1292 | "documentation": null, 1293 | "id": 1462, 1294 | "implemented": true, 1295 | "kind": "function", 1296 | "modifiers": [ 1297 | { 1298 | "arguments": null, 1299 | "id": 1447, 1300 | "modifierName": { 1301 | "argumentTypes": null, 1302 | "id": 1446, 1303 | "name": "restricted", 1304 | "nodeType": "Identifier", 1305 | "overloadedDeclarations": [], 1306 | "referencedDeclaration": 1430, 1307 | "src": "392:10:3", 1308 | "typeDescriptions": { 1309 | "typeIdentifier": "t_modifier$__$", 1310 | "typeString": "modifier ()" 1311 | } 1312 | }, 1313 | "nodeType": "ModifierInvocation", 1314 | "src": "392:10:3" 1315 | } 1316 | ], 1317 | "name": "upgrade", 1318 | "nodeType": "FunctionDefinition", 1319 | "parameters": { 1320 | "id": 1445, 1321 | "nodeType": "ParameterList", 1322 | "parameters": [ 1323 | { 1324 | "constant": false, 1325 | "id": 1444, 1326 | "name": "new_address", 1327 | "nodeType": "VariableDeclaration", 1328 | "scope": 1462, 1329 | "src": "364:19:3", 1330 | "stateVariable": false, 1331 | "storageLocation": "default", 1332 | "typeDescriptions": { 1333 | "typeIdentifier": "t_address", 1334 | "typeString": "address" 1335 | }, 1336 | "typeName": { 1337 | "id": 1443, 1338 | "name": "address", 1339 | "nodeType": "ElementaryTypeName", 1340 | "src": "364:7:3", 1341 | "stateMutability": "nonpayable", 1342 | "typeDescriptions": { 1343 | "typeIdentifier": "t_address", 1344 | "typeString": "address" 1345 | } 1346 | }, 1347 | "value": null, 1348 | "visibility": "internal" 1349 | } 1350 | ], 1351 | "src": "363:21:3" 1352 | }, 1353 | "returnParameters": { 1354 | "id": 1448, 1355 | "nodeType": "ParameterList", 1356 | "parameters": [], 1357 | "src": "403:0:3" 1358 | }, 1359 | "scope": 1463, 1360 | "src": "347:165:3", 1361 | "stateMutability": "nonpayable", 1362 | "superFunction": null, 1363 | "visibility": "public" 1364 | } 1365 | ], 1366 | "scope": 1464, 1367 | "src": "34:480:3" 1368 | } 1369 | ], 1370 | "src": "0:515:3" 1371 | }, 1372 | "compiler": { 1373 | "name": "solc", 1374 | "version": "0.5.8+commit.23d335f2.Emscripten.clang" 1375 | }, 1376 | "networks": { 1377 | "3": { 1378 | "events": {}, 1379 | "links": {}, 1380 | "address": "0xab2d7A859F5d8D27A1edA4DF82CF2C13D74f20C1", 1381 | "transactionHash": "0xbbe9f86fc5d0b3e2df480471c9976779b3fc000b1767de9f16969b4cbc3f45db" 1382 | } 1383 | }, 1384 | "schemaVersion": "3.0.11", 1385 | "updatedAt": "2019-07-22T10:49:46.640Z", 1386 | "devdoc": { 1387 | "methods": {} 1388 | }, 1389 | "userdoc": { 1390 | "methods": {} 1391 | } 1392 | } -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import './index.css' 4 | import App from './App' 5 | import * as serviceWorker from './serviceWorker' 6 | 7 | ReactDOM.render(, document.getElementById('root')) 8 | 9 | // If you want your app to work offline and load faster, you can change 10 | // unregister() to register() below. Note this comes with some pitfalls. 11 | // Learn more about service workers: https://bit.ly/CRA-PWA 12 | serviceWorker.unregister() 13 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/types/DappGateway.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | export type DappGateway = { 4 | web3: Object, 5 | drizzleUtils: Object, 6 | ethAddress: String, // Current ETH Address 7 | attempted: Boolean, // Have we attempted to connect to web3 and drizzleUtils? 8 | heiswapInstance: Object, // web3.eth.Contract instance (used for calls) 9 | heiswapEvent$: Object // RxJs stream of events 10 | } 11 | -------------------------------------------------------------------------------- /src/utils/AltBn128.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | /* 4 | * alt_bn_128 curve in JavaScript 5 | * Referenced https://github.com/AztecProtocol/aztec-crypto-js/blob/master/bn128/bn128.js 6 | */ 7 | 8 | const EC = require('elliptic') 9 | const crypto = require('crypto') 10 | const BN = require('bn.js') 11 | const { soliditySha3, padLeft } = require('web3-utils') 12 | 13 | // Types 14 | // Since JS doesn't handle BigInteger well, 15 | // the scalar representation will be the a BN object 16 | export type Scalar = BN; 17 | export type Point = [Scalar, Scalar]; 18 | export type Signature = [Scalar, Array, Point]; 19 | 20 | // AltBn128 field properties 21 | const P: Scalar = new BN( 22 | '21888242871839275222246405745257275088696311157297823662689037894645226208583', 23 | 10 24 | ) 25 | const N = new BN( 26 | '21888242871839275222246405745257275088548364400416034343698204186575808495617', 27 | 10 28 | ) 29 | const A = new BN( 30 | '5472060717959818805561601436314318772174077789324455915672259473661306552146', 31 | 10 32 | ) 33 | const G = [new BN(1, 10), new BN(2, 10)] 34 | 35 | // Convinience Numbers 36 | const bnOne = new BN('1', 10) 37 | const bnTwo = new BN('2', 10) 38 | const bnThree = new BN('3', 10) 39 | 40 | // AltBn128 Object 41 | const bn128 = {} 42 | 43 | // ECC Curve 44 | bn128.curve = new EC.curve.short({ 45 | a: '0', 46 | b: '3', 47 | p: P, 48 | n: N, 49 | gRed: false, 50 | g: G 51 | }) 52 | 53 | /** 54 | * BN.js reduction context for bn128 curve group's prime modulus. 55 | */ 56 | bn128.groupReduction = BN.red(bn128.curve.n) 57 | 58 | /** 59 | * Gets a random Scalar. 60 | */ 61 | bn128.randomScalar = (): Scalar => { 62 | return new BN(crypto.randomBytes(32), 16).toRed(bn128.groupReduction) 63 | } 64 | 65 | /** 66 | * Gets a random Point on curve. 67 | */ 68 | bn128.randomPoint = (): Point => { 69 | const recurse = () => { 70 | const x = new BN(crypto.randomBytes(32), 16).toRed(bn128.curve.red) 71 | const y2 = x 72 | .redSqr() 73 | .redMul(x) 74 | .redIAdd(bn128.curve.b) 75 | const y = y2.redSqrt() 76 | if ( 77 | y 78 | .redSqr(y) 79 | .redSub(y2) 80 | .cmp(bn128.curve.a) 81 | ) { 82 | return recurse() 83 | } 84 | return [x, y] 85 | } 86 | return recurse() 87 | } 88 | 89 | /** 90 | * Returns (beta, y) given x 91 | * 92 | * Beta is used to calculate if the Point exists on the curve 93 | */ 94 | bn128.evalCurve = (x: Scalar): [Scalar, Scalar] => { 95 | const beta = x 96 | .mul(x) 97 | .mod(P) 98 | .mul(x) 99 | .mod(P) 100 | .add(bnThree) 101 | .mod(P) 102 | const y = powmod(beta, A, P) 103 | 104 | return [beta, y] 105 | } 106 | 107 | /** 108 | * Calculates a Point given a Scalar 109 | */ 110 | bn128.scalarToPoint = (_x: Scalar): Point => { 111 | let x = _x.mod(N) 112 | 113 | let beta, y, yP 114 | 115 | while (true) { 116 | [beta, y] = bn128.evalCurve(x) 117 | yP = y.mul(y).mod(P) 118 | 119 | if (beta.cmp(yP) === 0) { 120 | return [x, y] 121 | } 122 | x = x.add(bnOne).mod(N) 123 | } 124 | } 125 | 126 | /** 127 | * ECC addition operation 128 | */ 129 | bn128.ecAdd = (point1: Point, point2: Point): Point => { 130 | const p1 = bn128.curve.point(point1[0], point1[1]) 131 | const p2 = bn128.curve.point(point2[0], point2[1]) 132 | const fp = p1.add(p2) 133 | 134 | return [fp.getX(), fp.getY()] 135 | } 136 | 137 | /** 138 | * ECC multiplication operation 139 | */ 140 | bn128.ecMul = (p: Point, s: Scalar): Point => { 141 | const fp = bn128.curve.point(p[0], p[1]).mul(s) 142 | 143 | return [fp.getX(), fp.getY()] 144 | } 145 | 146 | /** 147 | * ECC multiplication operation for G 148 | */ 149 | bn128.ecMulG = (s: Scalar): Point => { 150 | return bn128.ecMul(G, s) 151 | } 152 | 153 | /** 154 | * Ring signature generation 155 | */ 156 | bn128.ringSign = ( 157 | message: String, 158 | publicKeys: Array, 159 | secretKey: Scalar, 160 | secretKeyIdx: int 161 | ): Signature => { 162 | const keyCount: int = publicKeys.length 163 | 164 | let c: Array = Array(keyCount).fill(new BN(0, 10)) 165 | let s: Array = Array(keyCount).fill(new BN(0, 10)) 166 | 167 | // Step 1 168 | let h: Point = h2(serialize(publicKeys)) 169 | let yTilde = bn128.ecMul(h, secretKey) 170 | 171 | // Step 2 172 | let u = bn128.randomScalar() 173 | c[(secretKeyIdx + 1) % keyCount] = h1( 174 | serialize([ 175 | publicKeys, 176 | yTilde, 177 | message, 178 | bn128.ecMul(G, u), 179 | bn128.ecMul(h, u) 180 | ]) 181 | ) 182 | 183 | // Step 3 184 | const indexes = Array(keyCount) 185 | .fill(0) 186 | .map((x, idx) => idx) 187 | .slice(secretKeyIdx + 1, keyCount) 188 | .concat( 189 | Array(secretKeyIdx) 190 | .fill(0) 191 | .map((_x, _idx) => _idx) 192 | ) 193 | 194 | let z1, z2 195 | 196 | indexes.forEach(i => { 197 | s[i] = bn128.randomScalar() 198 | 199 | z1 = bn128.ecAdd(bn128.ecMul(G, s[i]), bn128.ecMul(publicKeys[i], c[i])) 200 | z2 = bn128.ecAdd(bn128.ecMul(h, s[i]), bn128.ecMul(yTilde, c[i])) 201 | 202 | c[(i + 1) % keyCount] = h1( 203 | serialize([publicKeys, yTilde, message, z1, z2]) 204 | ) 205 | }) 206 | 207 | // Step 4 208 | const sci = secretKey.mul(c[secretKeyIdx]).mod(N) 209 | 210 | s[secretKeyIdx] = u.sub(sci) 211 | 212 | // JavaScript negative modulo bug -_- 213 | if (s[secretKeyIdx] < 0) { 214 | s[secretKeyIdx] = s[secretKeyIdx].add(N) 215 | } 216 | 217 | s[secretKeyIdx] = s[secretKeyIdx].mod(N) 218 | 219 | return [c[0], s, yTilde] 220 | } 221 | 222 | /** 223 | * Ring signature verification 224 | */ 225 | bn128.ringVerify = ( 226 | message: String, 227 | publicKeys: Array, 228 | signature: Signature 229 | ): Boolean => { 230 | const keyCount = publicKeys.length 231 | 232 | const [c0, s, yTilde] = signature 233 | let c = c0.clone() 234 | 235 | // Step 1 236 | const h = h2(serialize(publicKeys)) 237 | 238 | let z1, z2 239 | 240 | for (let i = 0; i < keyCount; i++) { 241 | z1 = bn128.ecAdd(bn128.ecMul(G, s[i]), bn128.ecMul(publicKeys[i], c)) 242 | z2 = bn128.ecAdd(bn128.ecMul(h, s[i]), bn128.ecMul(yTilde, c)) 243 | 244 | if (i < keyCount - 1) { 245 | c = h1(serialize([publicKeys, yTilde, message, z1, z2])) 246 | } 247 | } 248 | 249 | return c0.cmp(h1(serialize([publicKeys, yTilde, message, z1, z2]))) === 0 250 | } 251 | 252 | /* Helper Functions */ 253 | 254 | /** 255 | * Efficient powmod implementation. 256 | * 257 | * Reference: https://gist.github.com/HarryR/a6d56a97ba7f1a4ebc43a40ca0f34044#file-longsigh-py-L26 258 | * Returns (a^b) % n 259 | */ 260 | const powmod = (a: Scalar, b: Scalar, n: Scalar): Scalar => { 261 | let c = new BN('0', 10) 262 | let f = new BN('1', 10) 263 | let k = new BN(parseInt(Math.log(b) / Math.log(2)), 10) 264 | 265 | let shiftedK 266 | 267 | while (k >= 0) { 268 | c = c.mul(bnTwo) 269 | f = f.mul(f).mod(n) 270 | 271 | shiftedK = new BN('1' + '0'.repeat(k), 2) 272 | 273 | if (b.and(shiftedK) > 0) { 274 | c = c.add(bnOne) 275 | f = f.mul(a).mod(n) 276 | } 277 | 278 | k = k.sub(bnOne) 279 | } 280 | 281 | return f 282 | } 283 | 284 | /** 285 | * Returns a Scalar repreesntation of the hash of the input. 286 | */ 287 | const h1 = (s: String): Scalar => { 288 | // Want to be compatible with the solidity implementation 289 | // which prepends "0x" by default 290 | if (s.indexOf('0x') !== 0) { 291 | s = '0x' + s 292 | } 293 | 294 | const h = soliditySha3(s).slice(2) // Remove the "0x" 295 | const b = new BN(h, 16) 296 | 297 | return b.mod(N) 298 | } 299 | 300 | /** 301 | * Returns ECC Point of the Scalar representation of the hash 302 | * of the input. 303 | */ 304 | const h2 = (hexStr: String): Point => { 305 | // Note: hexStr should be a string in hexadecimal format! 306 | return bn128.scalarToPoint(h1(hexStr)) 307 | } 308 | 309 | /** 310 | * Serializes the inputs into a hex string 311 | */ 312 | const serialize = (arr: Array): String => { 313 | if (!Array.isArray(arr)) { 314 | // eslint-disable-next-line no-throw-literal 315 | throw 'arr should be of type array' 316 | } 317 | 318 | return arr.reduce((acc, x) => { 319 | if (typeof x === 'string') { 320 | acc = acc + Buffer.from(x).toString('hex') 321 | } else if (Array.isArray(x)) { 322 | acc = acc + serialize(x) 323 | } else if (Buffer.isBuffer(x)) { 324 | acc = acc + x.toString('hex') 325 | } else if (x.getX !== undefined && x.getY !== undefined) { 326 | // Point 327 | acc = acc + padLeft(x.getX().toString(16), 64, '0') 328 | acc = acc + padLeft(x.getY().toString(16), 64, '0') 329 | } else if (x.toString !== undefined) { 330 | acc = acc + padLeft(x.toString(16), 64, '0') 331 | } 332 | 333 | return acc 334 | }, '') 335 | } 336 | 337 | // For testing purposes 338 | // let secretNum = 4; 339 | // let message = "ETH for you and everyone!"; 340 | // let secretKeys = Array(secretNum).fill(0).map(() => bn128.randomScalar()); 341 | // let publicKeys = secretKeys.map(x => bn128.ecMul(G, x)); 342 | // let signIdx = 1; 343 | // let signKey = secretKeys[signIdx]; 344 | // let signature = bn128.ringSign(message, publicKeys, signKey, signIdx); 345 | // console.log("Signature verification: ", bn128.verify(message, publicKeys, signature)) 346 | 347 | // // Print out for easier verification 348 | // // Pass the params to the python "verify" function and it should return True 349 | // console.log("--public keys--"); 350 | // console.log("["); 351 | // publicKeys.map(x => { 352 | // console.log("[" + x[0].toString(10) + ", " + x[1].toString(10) + "],"); 353 | // }); 354 | // console.log("]"); 355 | // console.log("--signature--"); 356 | // console.log("[" + signature[0].toString(10) + ", ["); 357 | // signature[1].map(x => { 358 | // console.log(x + ","); 359 | // }); 360 | // console.log("],["); 361 | // console.log(signature[2][0].toString(10) + ", " + signature[2][1].toString(10)); 362 | // console.log("]]"); 363 | 364 | export { 365 | bn128, 366 | powmod, 367 | h1, 368 | h2, 369 | serialize 370 | } 371 | -------------------------------------------------------------------------------- /src/utils/getWeb3.js: -------------------------------------------------------------------------------- 1 | // Extending https://github.com/trufflesuite/drizzle-utils/blob/master/packages/get-web3/index.js 2 | 3 | const Web3 = require('web3') 4 | 5 | const resolveWeb3 = (resolve, options, isBrowser) => { 6 | let provider 7 | 8 | if (options.customProvider) { 9 | // use custom provider from options object 10 | provider = options.customProvider 11 | } else if (isBrowser && window.ethereum) { 12 | // use `ethereum` object injected by MetaMask 13 | provider = window.ethereum 14 | if (options.requestPermission) window.ethereum.enable() 15 | } else if (isBrowser && typeof window.web3 !== 'undefined') { 16 | // use injected web3 object by legacy dapp browsers 17 | provider = window.web3.currentProvider 18 | } else if (options.fallbackProvider) { 19 | // use fallback provider from options object 20 | provider = options.fallbackProvider 21 | } 22 | 23 | const web3 = new Web3(provider) 24 | resolve(web3) 25 | } 26 | 27 | const getWeb3 = (options = {}) => 28 | new Promise(resolve => { 29 | // handle server-side and React Native environments 30 | const isReactNative = 31 | typeof navigator !== 'undefined' && navigator.product === 'ReactNative' 32 | const isNode = typeof window === 'undefined' 33 | if (isNode || isReactNative) { 34 | return resolveWeb3(resolve, options, false) 35 | } 36 | 37 | // if page is ready, resolve for web3 immediately 38 | if (document.readyState === `complete`) { 39 | return resolveWeb3(resolve, options, true) 40 | } 41 | 42 | // otherwise, resolve for web3 when page is done loading 43 | return window.addEventListener('load', () => 44 | resolveWeb3(resolve, options, true) 45 | ) 46 | }) 47 | 48 | module.exports = getWeb3 49 | -------------------------------------------------------------------------------- /src/utils/helper.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | // Appends 0x in front of hex strings so 4 | // it fits the solidity format 5 | const append0x = (s: String): String => { 6 | if (s.indexOf('0x') !== 0) { 7 | return '0x' + s 8 | } 9 | return s 10 | } 11 | 12 | export { 13 | append0x 14 | } 15 | -------------------------------------------------------------------------------- /test/AltBn128.js: -------------------------------------------------------------------------------- 1 | const AltBn128 = artifacts.require('AltBn128.sol') 2 | 3 | contract('AltBn128', accounts => { 4 | const secretKey = '0x1c28c75b7216693955b3ffe8c601fdfb6dd07b78600eeac48b9954d687090a87' 5 | const publicKey = [ 6 | '0x0fce6aeea309c9487431af3306b49df8f1de2183ac98c59a6e382c0cd56f3b6f', 7 | '0x232e5711e8424a93805b971c1f6be63aa74770f9648601e9bfdc4ad04c28f3bf' 8 | ] 9 | 10 | // Converts Big Number to hex strings 11 | const bn2hex = x => '0x' + x.toString(16).padStart(64, '0') 12 | 13 | it('ecMul', async () => { 14 | const altbn128 = await AltBn128.deployed() 15 | 16 | const point = await altbn128.ecMul(publicKey, secretKey) 17 | const pointHex = [ bn2hex(point[0]), bn2hex(point[1]) ] 18 | 19 | const expectedPoint = [ 20 | '0x1e163d27197822cf07b6fc5a0950721b9f80a7810063c8fa82d7e8f744269aad', 21 | '0x10f82337d1a6fdb0ef44098d066147641e200e34ee6af2d6a4f3064420192f33' 22 | ] 23 | 24 | assert.equal(true, expectedPoint.every(e => pointHex.includes(e))) 25 | }) 26 | 27 | it('ecMulG', async () => { 28 | const altbn128 = await AltBn128.deployed() 29 | 30 | const calculatedPublicKey = await altbn128.ecMulG(secretKey) 31 | const calculatedPublicKeyHex = [ 32 | bn2hex(calculatedPublicKey[0]), 33 | bn2hex(calculatedPublicKey[1]) 34 | ] 35 | 36 | assert.equal(true, publicKey.every(e => calculatedPublicKeyHex.includes(e))) 37 | }) 38 | 39 | it('ecAdd', async () => { 40 | const altbn128 = await AltBn128.deployed() 41 | 42 | const point = await altbn128.ecAdd(publicKey, publicKey) 43 | const pointHex = [ bn2hex(point[0]), bn2hex(point[1]) ] 44 | 45 | const expectedPoint = [ 46 | '0x0726c08a475b0d980e2c0e2d6b92d010f6b4192bdf2c7a2014015504cf39b46c', 47 | '0x0cea253b7abbe43dbb05643f3a9ea936701bb77c10c442b59c1c323dbb8b4a89' 48 | ] 49 | 50 | assert.equal(true, expectedPoint.every(e => pointHex.includes(e))) 51 | }) 52 | 53 | it('onCurve', async () => { 54 | let isOnCurve 55 | 56 | const altbn128 = await AltBn128.deployed() 57 | 58 | isOnCurve = await altbn128.onCurve(publicKey[0], publicKey[1]) 59 | assert.equal(true, isOnCurve) 60 | 61 | // Generator coordinates 62 | isOnCurve = await altbn128.onCurve('0x01', '0x02') 63 | assert.equal(true, isOnCurve) 64 | 65 | isOnCurve = await altbn128.onCurve('0x0', '0x0') 66 | assert.equal(false, isOnCurve) 67 | }) 68 | 69 | it('evalCurve', async () => { 70 | let y 71 | 72 | const altbn128 = await AltBn128.deployed() 73 | 74 | y = await altbn128.evalCurve(secretKey) 75 | yHex = [ bn2hex(y[0]), bn2hex(y[1]) ] 76 | const expectedY = [ 77 | '0x200c701ce7526ffeaafee056172fa3018a7f10c50513023488b17256bf9e029c', 78 | '0x141238261eac80e90649f81745607b70d5776defe23adc09b3cba89a3b578ca8' 79 | ] 80 | 81 | assert.equal(true, yHex.every(e => expectedY.includes(e))) 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /test/LSAG.js: -------------------------------------------------------------------------------- 1 | const LSAG = artifacts.require('LSAG.sol') 2 | 3 | contract('LSAG', accounts => { 4 | // Converts Big Number to hex strings 5 | const bn2hex = x => '0x' + x.toString(16).padStart(64, '0') 6 | 7 | it('intToPoint', async () => { 8 | const lsag = await LSAG.deployed() 9 | 10 | const secretKey = '0x1c28c75b7216693955b3ffe8c601fdfb6dd07b78600eeac48b9954d687090a87' 11 | const publicKey = [ 12 | '0x0fce6aeea309c9487431af3306b49df8f1de2183ac98c59a6e382c0cd56f3b6f', 13 | '0x232e5711e8424a93805b971c1f6be63aa74770f9648601e9bfdc4ad04c28f3bf' 14 | ] 15 | 16 | const point = await lsag.intToPoint(secretKey) 17 | const pointHex = [ bn2hex(point[0]), bn2hex(point[1]) ] 18 | 19 | const expectedPoint = [ 20 | '0x1c28c75b7216693955b3ffe8c601fdfb6dd07b78600eeac48b9954d687090a87', 21 | '0x141238261eac80e90649f81745607b70d5776defe23adc09b3cba89a3b578ca8' 22 | ] 23 | 24 | assert.equal(true, expectedPoint.every(e => pointHex.includes(e))) 25 | }) 26 | 27 | it('verify', async () => { 28 | const lsag = await LSAG.deployed() 29 | 30 | const secretKeys = [ 31 | '0x0e90a24937630c3ade5d52753792decf936f839cc317b9418257da02ee6cf0ab', 32 | '0x1cb0e68ec58bfa7863289b95c6d8eb9d9e66cf9f4804d5ebd346338ebad7fa6e', 33 | '0x100bfa9dbe3631bcfa561f9a87e0e05e8684306a8f7dcf06e9f573985b285f74', 34 | '0x0a5211e6ee38ee31b178c8f8e2b3281a3ddd57de0b24bdb30df4b3e443a87b02' 35 | ] 36 | const publicKeys = [ 37 | ['0x20d9c3e18b9a6c57328ff0a5e19ed198bfa83134eebda6b06cc77e5c264ff0b0', 38 | '0x1176940d44f610d82a73718730671af4bd00c03fa445262436dff38d83b78006'], 39 | ['0x11c4cfafeb9355518b1293f083514c835832584ff443b7466cc1f83a0e22855e', 40 | '0x00dd2f5185175d4ffbe6bcb5106dfbb11d7f254a51337c21f3787aa65ec460d2'], 41 | ['0x2dfa9b9604825f2425523ad824283bc9d9c73af86d7f8878d33321c6c296607c', 42 | '0x0900066caa076333dcdf2a072d48a70412a19d4ee180f953da0f06e4f2ccface'], 43 | ['0x09ca8d27ddcfcb9a681453de9afb97aa81ebc6025423d778b9d5aebfca06c3b9', 44 | '0x275bce6aecf3e5be348a4f328577ced795f97cb6ebb23cc3e9daf8a807926e92'] 45 | ] 46 | 47 | // message = "ETH for you and everyone!" (encoded in byte format) 48 | const message = '0x45544820666f7220796f7520616e642065766572796f6e6521' 49 | 50 | const c0 = '0x16f154c8b054472b27fa5ddfdc6efaef113f287567f0bdfe58a8890d8c6fc4ec' 51 | const s = [ 52 | '0x2374c0249d845fb3d4b24b4eeb50d8a4cdb8fb366095ac6a81f4069620408de9', 53 | '0x27d3e33dfdb5e3f4ca318652c36bb7d425b0c547165cdfc35fef325c1b6d8805', 54 | '0x169defa45ba6aa703487fc0104539991e1af1395c1ef117d344202a62684e15e', 55 | '0x243d34a84942e1d9c1df9b6bc00fa6a073b89c9a4b9fe7959346161ca66a9852' 56 | ] 57 | const keyImage = [ 58 | '0x052f545a6b88959b463c86b280bc201b16eee954b7190512c25624d4a2c8bb4a', 59 | '0x24fbbb0185ad24760408a2d383a1cd8de2be69b6bd52fee38b722927a1d6430d' 60 | ] 61 | 62 | let isVerified 63 | 64 | isVerified = await lsag.verify( 65 | message, 66 | c0, 67 | keyImage, 68 | s, 69 | publicKeys 70 | ) 71 | assert.equal(true, isVerified) 72 | 73 | const invalidMessage = '0x45544820666f7220796f7520616e642065766572796f6e6522' 74 | isVerified = await lsag.verify( 75 | invalidMessage, 76 | c0, 77 | keyImage, 78 | s, 79 | publicKeys 80 | ) 81 | assert.equal(false, isVerified) 82 | }) 83 | }) 84 | -------------------------------------------------------------------------------- /timeline.md: -------------------------------------------------------------------------------- 1 | # Heiswap Rough Timeline 2 | 3 | Jul 2019 - MVP, feedback 4 | Aug 2019 - Iterate, fix bugs, optimize data storage(?) 5 | Sep 2019 - Add types to frontend, refactor to make it more maintainable 6 | Oct 2019 - Write tests for Solidity and frontend + CI/CD pipeline 7 | Nov 2019 - Release stable version 8 | Dec 2019 - Read more on SNARKs / zk-rollups on how to integrate them 9 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | /** 4 | * Use this file to configure your truffle project. It's seeded with some 5 | * common settings for different networks and features like migrations, 6 | * compilation and testing. Uncomment the ones you need or modify 7 | * them to suit your project as necessary. 8 | * 9 | * More information about configuration can be found at: 10 | * 11 | * truffleframework.com/docs/advanced/configuration 12 | * 13 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) 14 | * to sign your transactions before they're sent to a remote public node. Infura accounts 15 | * are available for free at: infura.io/register. 16 | * 17 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 18 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 19 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 20 | * 21 | */ 22 | 23 | const HDWalletProvider = require('truffle-hdwallet-provider'); 24 | // const infuraKey = "fj4jll3k....."; 25 | // 26 | // const fs = require('fs'); 27 | // const mnemonic = fs.readFileSync(".secret").toString().trim(); 28 | 29 | module.exports = { 30 | contracts_build_directory: path.join(__dirname, 'src/contracts'), 31 | 32 | /** 33 | * Networks define how you connect to your ethereum client and let you set the 34 | * defaults web3 uses to send transactions. If you don't specify one truffle 35 | * will spin up a development blockchain for you on port 9545 when you 36 | * run `develop` or `test`. You can ask a truffle command to use a specific 37 | * network from the command line, e.g 38 | * 39 | * $ truffle test --network 40 | */ 41 | 42 | networks: { 43 | // Useful for testing. The `development` name is special - truffle uses it by default 44 | // if it's defined here and no other network is specified at the command line. 45 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 46 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 47 | // options below to some value. 48 | // 49 | development: { 50 | host: '127.0.0.1', // Localhost (default: none) 51 | port: 8545, // Standard Ethereum port (default: none) 52 | network_id: '*' // Any network (default: none) 53 | }, 54 | // Another network with more advanced options... 55 | // advanced: { 56 | // port: 8777, // Custom port 57 | // network_id: 1342, // Custom network 58 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 59 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 60 | // from:
, // Account to send txs from (default: accounts[0]) 61 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 62 | // }, 63 | // Useful for deploying to a public network. 64 | // NB: It's important to wrap the provider as a function. 65 | ropsten: { 66 | provider: () => new HDWalletProvider(process.env.ETH_SK, `https://ropsten.infura.io/v3/${process.env.INFURA_KEY}`), 67 | network_id: 3, // Ropsten's id 68 | gas: 5500000, // Ropsten has a lower block limit than mainnet 69 | confirmations: 2, // # of confs to wait between deployments. (default: 0) 70 | timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 71 | skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 72 | } 73 | // Useful for private networks 74 | // private: { 75 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 76 | // network_id: 2111, // This network is yours, in the cloud. 77 | // production: true // Treats this network as if it was a public net. (default: false) 78 | // } 79 | }, 80 | 81 | // Set default mocha options here, use special reporters etc. 82 | mocha: { 83 | // timeout: 100000 84 | }, 85 | 86 | // Configure your compilers 87 | compilers: { 88 | solc: { 89 | version: '0.5.8' // Fetch exact version from solc-bin (default: truffle's version) 90 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 91 | // settings: { // See the solidity docs for advice about optimization and evmVersion 92 | // optimizer: { 93 | // enabled: false, 94 | // runs: 200 95 | // }, 96 | // evmVersion: "byzantium" 97 | // } 98 | } 99 | } 100 | } 101 | --------------------------------------------------------------------------------