├── .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 | [](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 | You need to enable JavaScript to run this app.
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 |
135 | Heiswap
136 |
137 |
138 |
139 |
140 | {dappGateway.ethAddress === null ? (
141 | {
143 | if (dappGateway.ethAddress === null) {
144 | (async () => {
145 | if (!await initDappGateway()) {
146 | setweb3StatusModal(true)
147 | }
148 | })()
149 | }
150 | }}
151 | >
152 | Connect
153 |
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 |
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 |
205 |
206 |
207 |
208 |
209 |
210 | {
211 | modalParams.heiTokenFinal === null
212 | ?
213 | :
214 | }
215 |
216 |
217 |
218 | {
219 | modalParams.heiTokenFinal === null
220 | ? 'Depositing ETH... make sure you have confirmed the deposit in your wallet'
221 | : ETH deposited!
222 | }
223 |
224 |
225 |
Next up
226 |
227 | Send this token to your recipient. They'll need it to withdraw their funds.
228 |
229 |
230 |
{}} />
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 | There is no way to recover a lost token. If you lose this token, you'll lose your ETH.
241 |
242 |
243 | { setModalParams(Object.assign({}, modalParams, { acknowledgeClose: e.target.checked })) }}
247 | />
248 | {
249 | modalParams.acknowledgeClose
250 | ? {
255 | // Only allow close if tx is complete
256 | // and user acknowledged close
257 | if (modalParams.heiToken !== null && modalParams.acknowledgeClose) {
258 | setModalParams(Object.assign({}, modalParams, { isOpen: false }))
259 | }
260 | }}
261 | >Close
262 | : Confirm you've copied the token
268 | }
269 |
270 |
271 |
272 |
273 |
274 |
275 | )
276 | }
277 |
278 | export default DepositPage
279 |
--------------------------------------------------------------------------------
/src/components/StatusPage.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React, { useState } from 'react'
3 | import { Form, Button, Modal, Card, Box, Loader, Text, Pill } from 'rimble-ui'
4 | import { DappGateway } from '../types/DappGateway'
5 |
6 | type StatusPageModalParams = {
7 | isOpen: Boolean,
8 | invalidToken: Boolean,
9 | ringHash: String,
10 | maxParticipants: Number,
11 | depositedParticipants: Number,
12 | withdrawnParticipants: Number,
13 | blocksLeftForForceClose: Number
14 | }
15 |
16 | const StatusPage = (props: { dappGateway: DappGateway, noWeb3: Boolean, noContractInstance: Boolean }) => {
17 | const { dappGateway } = props
18 |
19 | const [heiToken, setHeiToken] = useState('')
20 |
21 | const [modalParams: StatusPageModalParams, setModalParams] = useState({
22 | isOpen: false,
23 | invalidToken: false,
24 | ringHash: null,
25 | maxParticipants: null,
26 | depositedParticipants: null,
27 | withdrawnParticipants: null
28 | })
29 |
30 | // Disable buttons etc if web3 isn't injected
31 | const { noWeb3, noContractInstance } = props
32 |
33 | return (
34 |
35 |
94 | setHeiToken(e.target.value)}
101 | />
102 |
103 |
104 | Check Ring Status
105 |
106 |
107 |
108 |
109 | {
119 | setModalParams(Object.assign({}, modalParams, { isOpen: false }))
120 | }}
121 | />
122 |
123 |
124 |
125 | {
126 | modalParams.invalidToken
127 | ?
Invalid hei-token
128 | : modalParams.withdrawnParticipants === null
129 | ?
130 | :
131 |
132 | Ring status: {
133 | modalParams.depositedParticipants >= modalParams.maxParticipants || modalParams.ringHash.length === 66
134 | ? 'Closed'
135 | : modalParams.blocksLeftForForceClose > 0
136 | ? `On-going (${modalParams.blocksLeftForForceClose} blocks till manual intervention allowed)`
137 | : `On-going ${modalParams.depositedParticipants > 1 ? '(Intervention allowed)' : ''}`
138 | }
139 |
140 | Ring participants: {`${modalParams.depositedParticipants}/${modalParams.maxParticipants}`}
141 |
142 | {
143 | modalParams.depositedParticipants <= 1
144 | ? Privacy not guaranteed
145 | : modalParams.depositedParticipants <= 3
146 | ? Privacy somewhat guaranteed
147 | : Privacy strongly guaranteed
148 | }
149 |
150 |
151 | }
152 |
153 |
154 |
155 |
156 |
157 | )
158 | }
159 |
160 | export default StatusPage
161 |
--------------------------------------------------------------------------------
/src/components/Web3StatusModal.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import React from 'react'
3 | import {
4 | Flex,
5 | Box,
6 | Button,
7 | Modal,
8 | Card
9 | } from 'rimble-ui'
10 |
11 | type ModalProps = {
12 | isOpen: Boolean,
13 | setIsOpen: Function,
14 | children: any
15 | }
16 |
17 | const StatusModal = (props: ModalProps) => {
18 | const { isOpen, setIsOpen } = props
19 |
20 | return (
21 |
22 |
23 | setIsOpen(false)}
33 | />
34 |
35 |
36 | { props.children }
37 |
38 |
39 |
46 | setIsOpen(false)}>
47 | Close
48 |
49 |
50 |
51 |
52 | )
53 | }
54 |
55 | export default StatusModal
56 |
--------------------------------------------------------------------------------
/src/components/WithdrawPage.js:
--------------------------------------------------------------------------------
1 | // @flow
2 | import BN from 'bn.js'
3 | import { Scalar, Point, serialize, h1, bn128 } from '../utils/AltBn128'
4 | import { append0x } from '../utils/helper'
5 | import React, { useState } from 'react'
6 | import { Form, Radio, Field, Flex, Input, Blockie, EthAddress, Link, Icon, Modal, Flash, Card, Box, Button, Loader, Text, Heading, Tooltip } from 'rimble-ui'
7 | import { DappGateway } from '../types/DappGateway'
8 | import axios from 'axios'
9 |
10 | // BigNumber 0
11 | const bnZero = new BN('0', 10)
12 |
13 | // Possible states
14 | const WITHDRAWALSTATES = {
15 | RelayerUnreachable: -3,
16 | UnknownError: -2,
17 | Nothing: -1,
18 | CorruptedToken: 0,
19 | RingNotClosed: 1,
20 | RingNotEnoughParticipantsToClose: 2,
21 | ForceClosingRing: 3,
22 | FailedCloseRing: 4,
23 | SuccessCloseRing: 5,
24 | InvalidRing: 6,
25 | InvalidSignature: 7,
26 | SignatureUsed: 8,
27 | Withdrawn: 9
28 | }
29 |
30 | const WithdrawPage = (props: { dappGateway: DappGateway, noWeb3: Boolean, noContractInstance: Boolean }) => {
31 | const { dappGateway } = props
32 |
33 | const [ringParticipants, setRingParticipants] = useState({
34 | deposited: 0,
35 | withdrawn: 0,
36 | participantsRequired: 0
37 | })
38 | const [unknownErrorStr, setUnknownErrorStr] = useState('')
39 | const [txReceipt, setTxReceipt] = useState(null)
40 | const [heiToken, setHeiToken] = useState('')
41 | const [useRelayer, setUseRelayer] = useState(true)
42 | const [useDefaultRelayer, setUseDefaultRelayer] = useState(true)
43 | const [customerRelayerURL, setCustomRelayerURL] = useState('')
44 | const [openModal, setOpenModal] = useState(false)
45 | const [withdrawalState, setWithdrawalState] = useState(WITHDRAWALSTATES.Nothing)
46 |
47 | const { noWeb3, noContractInstance } = props
48 |
49 | const getModalDisplay = (ws) => {
50 | if (ws === WITHDRAWALSTATES.Nothing) {
51 | return
52 |
53 |
54 | Withdrawing ETH...
55 | Remember to confirm this withdrawal in your wallet.
56 |
57 | } else if (ws === WITHDRAWALSTATES.ForceClosingRing) {
58 | return
59 |
60 |
61 |
62 | Closing pool...
63 | Remember to confirm this in your wallet.
64 |
65 |
66 | } else if (ws === WITHDRAWALSTATES.CorruptedToken) {
67 | return This token doesn't look right. Check you've pasted it correctly and try again.
68 | } else if (ws === WITHDRAWALSTATES.SuccessCloseRing) {
69 | return (
70 |
71 |
72 | Pool closed
73 | Your ETH is now ready to withdraw.
74 |
75 |
76 | )
77 | } else if (ws === WITHDRAWALSTATES.FailedCloseRing) {
78 | return (
79 |
80 |
81 | Couldn't close pool
82 | If you recently tried closing the pool, it may still be closing in the background. Please wait a few minutes and try your withdrawal again.
83 |
84 |
85 | )
86 | } else if (ws === WITHDRAWALSTATES.RingNotClosed) {
87 | return (
88 |
89 |
90 | Not Enough Participants
91 | To get your ETH, you'll need to wait for more participants to join.
92 |
93 | The pool currently has {ringParticipants.deposited}/{ringParticipants.participantsRequired} ETH participants(s).
94 |
95 |
96 |
97 | setOpenModal(false)}
99 | width={1}>Wait
100 |
101 |
102 |
103 |
104 |
105 | )
106 | } else if (ws === WITHDRAWALSTATES.RingNotEnoughParticipantsToClose) {
107 | return (
108 |
109 |
110 | Couldn't close pool
111 | There's not enough ETH in this pool right now to make your withdrawal private. Please try again later.
112 |
113 |
114 | )
115 | } else if (ws === WITHDRAWALSTATES.InvalidRing) {
116 | return (
117 |
118 | Invalid signature.
119 |
120 | )
121 | } else if (ws === WITHDRAWALSTATES.InvalidSignature) {
122 | return (
123 |
124 | Wrong account
125 | There's no ETH in the pool for:
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | Try changing your Ethereum account.
135 |
136 | )
137 | } else if (ws === WITHDRAWALSTATES.SignatureUsed) {
138 | return (
139 |
140 | Old token
141 | This token's already been used to withdraw ETH.
142 |
143 | )
144 | } else if (ws === WITHDRAWALSTATES.Withdrawn) {
145 | return (
146 |
147 |
148 |
149 | ETH withdrawn
150 |
154 | View on etherscan
155 |
156 |
157 |
158 | )
159 | } else if (ws === WITHDRAWALSTATES.RelayerUnreachable) {
160 | return (
161 |
162 | Unable to reach relayer
163 | Try using a different relayer, you can also setup your own relayer
164 |
165 | )
166 | }
167 |
168 | return (
169 |
170 | Unknown Error Occured
171 |
172 | Please open a new issue with the error description below.
173 | Description: { unknownErrorStr }
174 |
175 |
176 | )
177 | }
178 |
179 | return (
180 |
181 |
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 |
--------------------------------------------------------------------------------