├── .github └── workflows │ └── nodejs_test.yml ├── .gitignore ├── LICENSE ├── README.md ├── lerna.json ├── package-lock.json ├── package.json └── packages ├── api ├── .gitignore ├── .npmignore ├── README.md ├── package.json └── src │ ├── index.js │ └── test │ └── index.js ├── batch ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── README.md ├── package-lock.json ├── package.json └── src │ ├── index.js │ └── test │ └── index.js ├── client ├── .gitignore ├── .npmignore ├── README.md ├── browser │ ├── discord.svg │ ├── favicon.png │ ├── index.html │ └── index.js ├── config │ └── react-native.js ├── package-lock.json ├── package.json └── src │ ├── app.js │ ├── cli.js │ ├── config.browser.js │ ├── config.local.js │ ├── exit.js │ ├── faucet.js │ ├── faucetPlugin.js │ ├── fetch.js │ ├── full.js │ ├── index.js │ ├── keyToWallet.js │ ├── local.js │ ├── oracle.js │ ├── recover.js │ ├── retrieveBonds.js │ ├── test │ ├── artifacts │ │ └── multisig.js │ └── index.js │ ├── testing-leader.js │ ├── testing.js │ └── wallet.js ├── contracts ├── package.json └── src │ ├── builds │ ├── ERC20.json │ ├── ERC20.json.yul │ ├── Fuel.json │ ├── Fuel.json.yul │ ├── Funnel.json │ ├── Funnel.json.yul │ ├── HTLC.json │ ├── HTLC.json.yul │ ├── OwnedProxy.json │ ├── OwnedProxy.json.yul │ ├── Proxy.json │ ├── Proxy.json.yul │ ├── ProxyBypass.json │ ├── ProxyBypass.json.yul │ ├── Revert.json │ └── Revert.json.yul │ ├── deployments │ ├── Fuel.json │ └── Numbers.json │ └── index.js ├── database ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── README.md ├── package-lock.json ├── package.json └── src │ ├── index.js │ └── test │ └── index.js ├── down ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── README.md ├── package-lock.json ├── package.json └── src │ ├── copy.js │ ├── index.js │ ├── local.js │ ├── simulate.js │ └── test │ ├── copy.js │ ├── index.js │ ├── local.js │ ├── move.js │ └── simulate.js ├── encoding ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── README.md ├── package-lock.json ├── package.json └── src │ ├── index.js │ └── test │ └── index.js ├── environment ├── .npmignore ├── README.md ├── package-lock.json ├── package.json └── src │ ├── compile.js │ ├── deploy.js │ ├── index.js │ └── test │ └── index.js ├── gasprice ├── .eslintrc.js ├── README.md ├── package-lock.json ├── package.json └── src │ ├── index.js │ └── test │ └── index.js ├── interface ├── .eslintrc.js ├── .npmignore ├── README.md ├── package-lock.json ├── package.json └── src │ ├── index.js │ └── test │ └── index.js ├── key ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── README.md ├── package-lock.json ├── package.json └── src │ ├── index.js │ └── test │ └── index.js ├── logic ├── .gitignore ├── .npmignore ├── README.md ├── package.json └── src │ ├── account.js │ ├── assets.js │ ├── balance.js │ ├── bondRetrieval.js │ ├── genesis.js │ ├── history.js │ ├── index.js │ ├── mempool.js │ ├── operatorsToWallets.js │ ├── process.js │ ├── produce.js │ ├── profile.js │ ├── reconcileMempoolBalance.js │ ├── requests.js │ ├── restore.js │ ├── root.js │ ├── sync.js │ ├── test │ ├── artifacts │ │ ├── LeaderSelection.json │ │ ├── Multisig.json │ │ └── TokenReleaseFactory.json │ ├── config.local.js │ ├── correctness.js │ ├── defaults.js │ ├── genesis.js │ ├── in-flight.js │ ├── index.js │ ├── leader.js │ ├── mempool.js │ ├── outputFromMetadata.js │ ├── produce.js │ ├── prover.js │ ├── recieving.js │ ├── root-force.js │ ├── sync.js │ ├── third-party-production.js │ └── transact.js │ ├── transact.js │ ├── transactions.js │ └── withdraw.js ├── protocol ├── .gitignore ├── .npmignore ├── README.md ├── browser │ └── index.html ├── package-lock.json ├── package.json └── src │ ├── addons.js │ ├── block.js │ ├── deposit.js │ ├── eip712.js │ ├── index.js │ ├── inputs.js │ ├── merkle.js │ ├── metadata.js │ ├── outputs.js │ ├── root.js │ ├── state.js │ ├── test │ ├── addons.js │ ├── block.js │ ├── eip712.js │ ├── erc20.json │ ├── index.js │ ├── inputs.js │ ├── metadata.js │ ├── outputs.js │ ├── root.js │ ├── state.js │ ├── token.js │ ├── transaction.js │ ├── withdraw.js │ └── witness.js │ ├── token.js │ ├── transaction.js │ ├── withdraw.js │ └── witness.js ├── refill ├── .eslintrc.js ├── README.md ├── package-lock.json ├── package.json └── src │ ├── Multisend.sol │ ├── index.js │ └── test │ └── index.js ├── rolled ├── .gitignore ├── .npmignore ├── README.md ├── package.json └── src │ ├── index.js │ └── test │ └── index.js ├── rpc ├── .gitignore ├── .npmignore ├── README.md ├── package.json └── src │ ├── index.js │ └── test │ └── index.js ├── struct ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── README.md ├── package-lock.json ├── package.json └── src │ ├── index.js │ └── test │ ├── index.js │ └── memory.js ├── utils ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── README.md ├── package-lock.json ├── package.json └── src │ ├── index.js │ └── test │ └── index.js └── wallet ├── .gitignore ├── .npmignore ├── README.md ├── browser ├── deposit.js ├── index.html ├── index.js ├── load.js ├── local.js └── rinkeby.js ├── config └── webpack.js ├── package.json └── src ├── abi.json ├── index.d.ts ├── index.js └── test ├── deposit.js ├── htlc.js ├── index.js └── transfer.js /.github/workflows/nodejs_test.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: 9 | - master 10 | tags: 11 | - v* 12 | pull_request: 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | matrix: 21 | node-version: [10.x, 12.x] 22 | 23 | steps: 24 | - uses: actions/checkout@v2 25 | - name: Use Node.js ${{ matrix.node-version }} 26 | uses: actions/setup-node@v1 27 | with: 28 | node-version: ${{ matrix.node-version }} 29 | - run: npm install 30 | - run: npm run bootstrap 31 | - run: | 32 | nohup npm run --prefix ./packages/client start-testing & sleep 10 33 | - run: npm test 34 | timeout-minutes: 150 35 | 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | packages/**/node_modules 3 | packages/**/.nyc_output 4 | packages/**/.testdb 5 | fuel 6 | lerna-debug.log 7 | ./lerna-debug.log 8 | .nyc_output 9 | coverage 10 | .husky -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fuel-js 2 | 3 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 4 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.com/invite/xfpK4Pe) 5 | 6 | ## Abstract 7 | 8 | This repository contains all the necessary working Fuel javascript infrastructure. 9 | 10 | ## Usage 11 | 12 | Please consult the SDK documentation: [docs.fuel.sh](https://docs.fuel.sh/v1.0.0/SDK/Wallet.html) 13 | 14 | ## Install 15 | 16 | ``` 17 | lerna bootstrap 18 | ``` 19 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*" 4 | ], 5 | "version": "independent" 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "root", 3 | "private": true, 4 | "scripts": { 5 | "test": "lerna run --stream --concurrency=1 test", 6 | "test-conc": "lerna run --stream --concurrency=1 test", 7 | "test2": "nyc --reporter=json lerna run test --parallel", 8 | "bootstrap": "lerna bootstrap --no-ci --ignore-prepublish", 9 | "badge": "istanbul-badges-readme --coverageDir='./coverage'", 10 | "build": "lerna bootstrap --no-ci" 11 | }, 12 | "devDependencies": { 13 | "dot-prop": ">=4.2.1", 14 | "fs-readfile-promise": "^3.0.1", 15 | "husky": "^6.0.0", 16 | "istanbul-badges-readme": "1.0.5", 17 | "lerna": "^3.22.1", 18 | "nyc": "^15.1.0", 19 | "write": "^2.0.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/api/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/api/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | -------------------------------------------------------------------------------- /packages/api/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/api 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/api", 3 | "version": "0.3.2", 4 | "description": "A simple API wrapper for the Fuel API.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "node src/test" 8 | }, 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/fuellabs/fuel-js.git" 15 | }, 16 | "keywords": [ 17 | "fuel", 18 | "js", 19 | "api" 20 | ], 21 | "dependencies": { 22 | "@fuel-js/interface": "^0.2.0", 23 | "@fuel-js/protocol": "^0.4.6", 24 | "@fuel-js/utils": "^0.1.4" 25 | }, 26 | "devDependencies": { 27 | "@fuel-js/environment": "^0.1.0" 28 | }, 29 | "author": "Nick Dodson ", 30 | "license": "Apache-2.0", 31 | "bugs": { 32 | "url": "https://github.com/fuellabs/fuel-js/issues" 33 | }, 34 | "homepage": "https://github.com/fuellabs/fuel-js#readme" 35 | } 36 | -------------------------------------------------------------------------------- /packages/api/src/test/index.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const Api = require('../index'); 3 | const { outputs } = require('@fuel-js/protocol'); 4 | 5 | module.exports = test('api', async t => { 6 | try { 7 | 8 | const api = new Api('mainnet', { 9 | // url: 'http://localhost:3000', 10 | }); 11 | 12 | t.ok(await api.getState(), 'get state'); 13 | t.ok(await api.getBlockByHeight(0), 'block by height'); 14 | t.ok(await api.getTokenId(utils.emptyAddress), 'token id'); 15 | t.ok(await api.getToken(1), 'token address'); 16 | t.ok(await api.getAddress(0), 'address registered'); 17 | t.ok(await api.getState(), 'get state'); 18 | t.ok(await api.getTokenMetadata(1), 'get token'); 19 | 20 | const api2 = new Api('mainnet', { 21 | }); 22 | const userProfile = await api2.getProfile('0x8328d1693d693130f141812b5fa3e46602abcb45'); 23 | const firstTransactionId = userProfile.history[0].transactionId; 24 | const firstTransaction = await api.getTransactionByHash(firstTransactionId); 25 | const lastOutput = firstTransaction.outputs[2]; 26 | const returnData = lastOutput; 27 | 28 | return; 29 | 30 | const _profile = await api.getProfile('0xD2a8dD8F9F4371b636BFE8dd036772957a5D425C'); 31 | t.ok(_profile, 'profile'); 32 | 33 | console.log(_profile); 34 | 35 | console.log(await api.getAccount('0xD2a8dD8F9F4371b636BFE8dd036772957a5D425C')); 36 | 37 | return; 38 | 39 | t.ok(await api.getBlockByHeight(5), 'block by height'); 40 | t.ok(await api.getBlockByHeight(10), 'block by height'); 41 | 42 | const tx = await api.getTransactionByHash('0x87195fd8eba32a1d363f85551c7a3ca1d9ab27cd64746c0eadd1f7ad37f31c77'); 43 | 44 | t.ok(tx, 'tx by hash'); 45 | 46 | const depositProfile = await api.getProfile('0x1dF62f291b2E969fB0849d99D9Ce41e2F137006e'); 47 | 48 | console.log(depositProfile); 49 | 50 | const profile = await api.getProfile('0x0ea6b5edc8905c85514b3676703f1bfe6ec260ad'); 51 | 52 | // const profile2 = await api.getProfile('0x87065ef77dd63220c0bf30cb6f322646bb9659e5'); 53 | 54 | // console.log(profile2.history); 55 | 56 | t.ok(profile); 57 | 58 | t.ok(await api.getTokenId(utils.emptyAddress), 'token id'); 59 | 60 | t.ok(await api.getToken(1), 'token address'); 61 | 62 | t.ok(await api.getAddress(0), 'address registered'); 63 | 64 | t.ok(await api.getState(), 'get state'); 65 | 66 | t.equal(await api.getReturn('0x184681347bc6f928228c2dd7b97f9847ca218ca29bc06a38317deab5801ca114', 2), '0xdeafbeef', 'return data'); 67 | 68 | } catch (testError) { throw new utils.ByPassError(testError); } 69 | }); 70 | -------------------------------------------------------------------------------- /packages/batch/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | }, 14 | parserOptions: { 15 | ecmaVersion: 2018, 16 | }, 17 | rules: { 18 | 'arrow-parens': ['off'], 19 | 'import/no-extraneous-dependencies': ['error', { devDependencies: ['./src/test/*'] }], 20 | 'no-await-in-loop': ['off'], 21 | 'no-restricted-syntax': ['off'], 22 | camelcase: ['off'], 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /packages/batch/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/batch/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | .nyc_output -------------------------------------------------------------------------------- /packages/batch/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/batch 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/batch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/batch", 3 | "version": "0.1.6", 4 | "description": "A batch get utility for leveldown.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "nyc node src/test", 8 | "lint": "eslint src/index.js", 9 | "prepublish": "npm test && npm run lint" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/fuellabs/fuel-js.git" 14 | }, 15 | "publishConfig": { 16 | "access": "public" 17 | }, 18 | "keywords": [ 19 | "fuel", 20 | "js", 21 | "interface" 22 | ], 23 | "author": "Nick Dodson ", 24 | "license": "Apache-2.0", 25 | "bugs": { 26 | "url": "https://github.com/fuellabs/fuel-js/issues" 27 | }, 28 | "homepage": "https://github.com/fuellabs/fuel-js#readme", 29 | "devDependencies": { 30 | "@fuel-js/environment": "^0.1.0", 31 | "@fuel-js/database": "^0.1.0", 32 | "leveldown": "^5.6.0", 33 | "memdown": "^5.1.0", 34 | "stream-to-array": "^2.3.0", 35 | "eslint": "^7.6.0", 36 | "nyc": "^15.1.0", 37 | "eslint-config-airbnb-base": "^14.2.0", 38 | "eslint-plugin-import": "^2.21.2" 39 | }, 40 | "dependencies": { 41 | "@fuel-js/utils": "^0.1.0", 42 | "@fuel-js/encoding": "^0.1.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/batch/src/index.js: -------------------------------------------------------------------------------- 1 | const utils = require('@fuel-js/utils'); 2 | const encoding = require('@fuel-js/encoding'); 3 | 4 | async function batch(db = {}, keys = [], options = {}) { 5 | try { 6 | if (typeof db.supports.mysql === 'object') { 7 | const { table } = db.supports.mysql; 8 | 9 | // eslint-disable-next-line 10 | const query = keys.map(key => 'SELECT value FROM ' 11 | // eslint-disable-next-line 12 | + table 13 | // eslint-disable-next-line 14 | + ' WHERE `key`=' + db.supports.mysql.escape(encoding.keyEncoding.encode(key)) + ';').join(''); 15 | 16 | // eslint-disable-next-line 17 | return await new Promise((resolve, reject) => db.supports.mysql._query(query, 18 | (error, results) => { 19 | if (error) { 20 | return reject(error); 21 | } 22 | 23 | return resolve(results.map((row, i) => { 24 | if (row.length === 0) { 25 | return reject(new Error('Invalid Batch Selection')); 26 | } 27 | 28 | const rowData = Array.isArray(row) 29 | ? row[0] 30 | : row; 31 | 32 | return { 33 | key: encoding.keyEncoding.decode(encoding.keyEncoding.encode(keys[i])), 34 | value: encoding.valueEncoding.decode(rowData.value), 35 | }; 36 | })); 37 | }, 38 | options.transact)); 39 | } 40 | 41 | // normal batch get sequence 42 | const results = []; 43 | for (const key of keys) { 44 | results.push({ 45 | key: encoding.keyEncoding.decode(encoding.keyEncoding.encode(key)), 46 | value: await db.get(key, options), 47 | }); 48 | } 49 | 50 | return results; 51 | } catch (error) { 52 | throw new utils.ByPassError(error); 53 | } 54 | } 55 | 56 | module.exports = batch; 57 | -------------------------------------------------------------------------------- /packages/batch/src/test/index.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | 3 | const db = require('@fuel-js/database'); 4 | const MemDown = require('memdown'); 5 | const streamToArray = require('stream-to-array'); 6 | const batch = require('../index'); 7 | 8 | module.exports = test('check mysql preforms like any other keystore', async t => { 9 | const mysqldb = db(new MemDown()); 10 | const memorydb = db(new MemDown()); 11 | 12 | await mysqldb.clear(); 13 | await memorydb.clear(); 14 | 15 | await mysqldb.put(['0xaa'], '0xbb'); 16 | await memorydb.put(['0xaa'], '0xbb'); 17 | 18 | const mysqlstream = await streamToArray(await mysqldb.createReadStream()); 19 | const memorystream = await streamToArray(await memorydb.createReadStream()); 20 | 21 | t.equalHex(mysqlstream[0].key, utils.RLP.encode(['0xaa'])); 22 | t.equalHex(memorystream[0].key, utils.RLP.encode(['0xaa'])); 23 | t.equalHex(mysqlstream[0].value, '0xbb'); 24 | t.equalHex(memorystream[0].value, '0xbb'); 25 | 26 | await mysqldb.put(['0xcc'], '0xee'); 27 | await memorydb.put(['0xcc'], '0xee'); 28 | 29 | const mysqlBatch = await batch(mysqldb, [ ['0xcc'], ['0xaa'] ]); 30 | const memoryBatch = await batch(memorydb, [ ['0xcc'], ['0xaa'] ]); 31 | 32 | t.equalHex(mysqlBatch[0].value, '0xee'); 33 | t.equalHex(memoryBatch[0].value, '0xee'); 34 | 35 | t.equalHex(mysqlBatch[1].value, '0xbb'); 36 | t.equalHex(memoryBatch[1].value, '0xbb'); 37 | 38 | t.catch(batch(), 'empty batch'); 39 | 40 | try { 41 | await batch(mysqldb, [ ['0xcc'], ['0xff'] ]); 42 | } catch (error) { 43 | t.equal(typeof error, 'object', 'invalid batch get sql'); 44 | } 45 | 46 | try { 47 | await batch(memorydb, [ ['0xcc'], ['0xff'] ]); 48 | } catch (error) { 49 | t.equal(typeof error, 'object', 'invalid batch get'); 50 | } 51 | 52 | const fakeMysql = (opts = {}) => ({ 53 | supports: { 54 | mysql: { 55 | table: 'table', 56 | escape: value => value, 57 | _query: (query, callback) => { 58 | if (opts.causeError) return callback(new Error('error!')); 59 | if (opts.noLength) return callback(null, [[], []]); 60 | 61 | callback(null, [[{ 62 | key: '0xcc', 63 | value: Buffer.from('bb', 'hex'), 64 | }], 65 | [{ 66 | key: '0xaa', 67 | value: Buffer.from('dd', 'hex'), 68 | }]]); 69 | }, 70 | }, 71 | }, 72 | }); 73 | 74 | const fakeMysql2 = (opts = {}) => ({ 75 | supports: { 76 | mysql: { 77 | table: 'table', 78 | escape: value => value, 79 | _query: (query, callback) => { 80 | if (opts.causeError) return callback(new Error('error!')); 81 | if (opts.noLength) return callback(null, [[], []]); 82 | 83 | callback(null, [{ 84 | key: '0xcc', 85 | value: Buffer.from('bb', 'hex'), 86 | }]); 87 | }, 88 | }, 89 | }, 90 | }); 91 | 92 | t.ok(await batch(fakeMysql(), [ ['0xcc'], ['0xaa'] ]), 'mysql batch'); 93 | t.catch(batch(fakeMysql({ causeError: true }), [ ['0xcc'], ['0xaa'] ]), 'mysql batch'); 94 | t.catch(batch(fakeMysql({ noLength: true }), [ ['0xcc'], ['0xaa'] ]), 'mysql batch'); 95 | 96 | t.ok(await batch(fakeMysql2(), [ ['0xcc'], ['0xaa'] ]), 'mysql batch 2'); 97 | t.catch(batch(fakeMysql2({ causeError: true }), [ ['0xcc'], ['0xaa'] ]), 'mysql batch 2'); 98 | t.catch(batch(fakeMysql2({ noLength: true }), [ ['0xcc'], ['0xaa'] ]), 'mysql batch 2'); 99 | 100 | await mysqldb.close(); 101 | }); 102 | -------------------------------------------------------------------------------- /packages/client/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .fueldb 4 | .fueldb-goerli 5 | .fueldb-rinkeby 6 | .fueldb-ropsten 7 | .fueldb-mainnet 8 | .fueldb-kovan 9 | .fueldb-unspecified 10 | .cache 11 | .fuel-wallet.json 12 | .fueldb-remote-unspecified 13 | ./.fueldb-remote-unspecified 14 | old 15 | geth -------------------------------------------------------------------------------- /packages/client/.npmignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .fueldb 4 | .fueldb-goerli 5 | .fueldb-rinkeby 6 | .fueldb-ropsten 7 | .fueldb-mainnet 8 | .fueldb-kovan 9 | .fueldb-unspecified 10 | .cache 11 | .fuel-wallet.json 12 | .fueldb-remote-unspecified 13 | ./.fueldb-remote-unspecified 14 | geth -------------------------------------------------------------------------------- /packages/client/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/client 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | build status 7 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.com/invite/xfpK4Pe) 8 | 9 | ## Usage 10 | 11 | Please consult the SDK documentation: 12 | 13 | [docs.fuel.sh](https://docs.fuel.sh) 14 | 15 | ## Build from Source 16 | 17 | ```bash 18 | git clone https://github.com/FuelLabs/fuel-js 19 | cd fuel-js/packages/client 20 | npm install 21 | npm run build-bin 22 | ``` 23 | 24 | Then select the distro for your environment i.e. `./dist/client-linux --help` 25 | 26 | ## Running from Source 27 | 28 | You may run Fuel with NodeJS vanilla. 29 | 30 | ```bash 31 | node src/index.js --help 32 | ``` 33 | 34 | ## Recommended Go-Ethereum (Geth) Settings 35 | 36 | ``` 37 | geth --http 38 | ``` 39 | 40 | These are the recommended settings for `geth` which by default will host the go-ethereum RPC at `http://localhost:8545`. If you are still having trouble connection, try adjusting your `--http.cors` settings. 41 | -------------------------------------------------------------------------------- /packages/client/browser/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FuelLabs/fuel-js/c55cd985d5384f074608d506d13f89c8a04a28b2/packages/client/browser/favicon.png -------------------------------------------------------------------------------- /packages/client/config/react-native.js: -------------------------------------------------------------------------------- 1 | import { NativeModules, Platform } from 'react-native'; 2 | import { toByteArray } from 'base64-js'; 3 | 4 | const { RNSecureRandom } = NativeModules; 5 | 6 | export function generateSecureRandom(length) { 7 | if (Platform.OS !== 'ios' && Platform.OS !== 'android') { 8 | throw Error('react-native-securerandom is currently only available for iOS and Android'); 9 | } 10 | 11 | if (!RNSecureRandom || !RNSecureRandom.generateSecureRandomAsBase64) { 12 | throw Error('react-native-securerandom is not properly linked'); 13 | } 14 | 15 | return RNSecureRandom.generateSecureRandomAsBase64(length).then(base64 => toByteArray(base64)); 16 | } -------------------------------------------------------------------------------- /packages/client/src/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | const meow = require('meow'); 4 | const chalk = require('chalk'); 5 | 6 | function cli() { 7 | return meow(` 8 | 9 | ⚡ fuel [options] 10 | 11 | ${chalk.grey(`Options:`)} 12 | 13 | -n, --network the ethereum network "mainnet"; default "mainnet" 14 | -r, --rpc a standard Ethereum RPC provider (i.e. local go-ethereum) 15 | -i, --infura an Infura service API key (--network must also be specified) 16 | -es, --etherscan an Etherscan service API key (--network must also be specified) 17 | -e, --environment use the environment variables to specify node paramaters 18 | -w, --wallet path to a pbkdf2 encrypted Ethers wallet JSON file; default ".fuel-wallet.json" 19 | -c, --clear clears the leveldb store 20 | -s, --serve starts a local Fuel RPC server on http://localhost:3000; default false 21 | -p, --port specify the Fuel RPC server port; default 3000 22 | -cr, --cors cors domain for the Fuel RPC server; default http://localhost:1234 23 | -o, --oracle start a price feed oracle for stablecoins and ether 24 | -f, --faucet start a test network faucet 25 | 26 | ${chalk.grey(`Examples:`)} 27 | 28 | ${chalk.cyan(` $ fuel --network="rinkeby" --rpc="http://localhost:8545"`)} 29 | `, { 30 | package: {}, 31 | description: false, 32 | flags: { 33 | environment: { 34 | type: 'boolean', 35 | alias: 'e' 36 | }, 37 | network: { 38 | type: 'string', 39 | alias: 'n', 40 | }, 41 | infura: { 42 | type: 'string', 43 | alias: 'i', 44 | }, 45 | etherscan: { 46 | type: 'string', 47 | alias: 'es', 48 | }, 49 | wallet: { 50 | type: 'string', 51 | alias: 'w', 52 | }, 53 | maxMempoolAge: { 54 | type: 'string', 55 | alias: 'ma', 56 | }, 57 | minRootSize: { 58 | type: 'string', 59 | alias: 'ms', 60 | }, 61 | db_path: { 62 | type: 'string', 63 | alias: 'db', 64 | }, 65 | cors: { 66 | type: 'string', 67 | alias: 'cr', 68 | }, 69 | faucet_wallet: { 70 | type: 'string', 71 | alias: 'fw', 72 | }, 73 | producer_address: { 74 | type: 'string', 75 | alias: 'pa', 76 | }, 77 | release: { 78 | type: 'boolean', 79 | alias: 're', 80 | }, 81 | remote_production: { 82 | type: 'boolean', 83 | alias: 'rep', 84 | }, 85 | deploy: { 86 | type: 'boolean', 87 | alias: 'd', 88 | }, 89 | produce: { 90 | type: 'boolean', 91 | alias: 'p', 92 | }, 93 | clear: { 94 | type: 'boolean', 95 | alias: 'c', 96 | }, 97 | forceClear: { 98 | type: 'boolean', 99 | alias: 'fc', 100 | }, 101 | rpc: { 102 | type: 'string', 103 | alias: 'r', 104 | }, 105 | privateKey: { 106 | type: 'string', 107 | alias: 'pk', 108 | }, 109 | clear_and_stop: { 110 | type: 'boolean', 111 | alias: 'cs', 112 | }, 113 | recover: { 114 | type: 'string', 115 | alias: 'rec', 116 | }, 117 | nosync: { 118 | type: 'boolean', 119 | alias: 'ns', 120 | }, 121 | port: { 122 | type: 'string', 123 | alias: 'p', 124 | }, 125 | proxy: { 126 | type: 'boolean', 127 | alias: 'p', 128 | }, 129 | oracle: { 130 | type: 'boolean', 131 | alias: 'o', 132 | }, 133 | scanSize: { 134 | type: 'string', 135 | alias: 'ss', 136 | }, 137 | } 138 | }); 139 | } 140 | 141 | module.exports = cli; 142 | -------------------------------------------------------------------------------- /packages/client/src/config.browser.js: -------------------------------------------------------------------------------- 1 | const utils = require('@fuel-js/utils'); 2 | const database = require('@fuel-js/database'); 3 | const { local } = require('@fuel-js/down'); 4 | const ethers = require('ethers'); 5 | const memdown = require('memdown'); 6 | const leveljs = require('level-js'); 7 | const { Fuel, deployments } = require('@fuel-js/contracts'); 8 | const { v1 } = deployments; 9 | 10 | function definedOr(value, or) { 11 | return typeof value !== "undefined" ? value : or; 12 | } 13 | 14 | // The confirguation object for the Fuel lambdas 15 | function config(opts = process.env, _prefix = 'fuel_v1_') { 16 | const network = opts.network || opts[_prefix + 'network']; 17 | const resolve = key => definedOr(opts[key], 18 | (opts[_prefix + network + '_' + key] 19 | || opts[_prefix + 'default_' + key])); 20 | 21 | // local provider 22 | let provider = opts.provider; 23 | 24 | // Network requirement 25 | utils.assert(network, '--network must be specified'); 26 | utils.assert(network === 'rinkeby', 'only rinkeby network supported'); 27 | utils.assert(provider || resolve('infura') || resolve('etherscan') || resolve('rpc'), 'you must specify a provider URL or key, either Infura, Etherscan or an RPC URL'); 28 | 29 | if (!opts.provider) { 30 | if (resolve('infura') || resolve('etherscan')) { 31 | provider = ethers.getDefaultProvider(network, { 32 | etherscan: resolve('etherscan'), 33 | infura: resolve('infura'), 34 | }); 35 | } else { 36 | provider = new ethers.providers.JsonRpcProvider(resolve('rpc'), network); 37 | } 38 | } 39 | 40 | const date = (d) => { 41 | return `${d.toISOString().slice(0, 10)} ${d.toLocaleTimeString()}`; 42 | } 43 | 44 | // return config object 45 | return { 46 | emit: () => {}, // emmitter for new outputs 47 | continue: () => { return opts.loop.continue; }, 48 | console: { 49 | error: error => (opts.console || console).log(`fuel-${date(new Date())}-ERROR! : ${error.message}`), 50 | log: message => (opts.console || console).log(`fuel-${date(new Date())} : ${message}`), 51 | }, 52 | prompt: { 53 | confirm: () => true, 54 | }, 55 | clear: resolve('clear'), 56 | produce: false, 57 | network: utils.getNetwork(network), 58 | archive: false, 59 | gas_limit: 4000000, // default gas limit 60 | confirmations: 6, // required block confirmations 61 | block_time: 13 * 1000, 62 | blockHeight: resolve('blockHeight'), 63 | db: database(local(leveljs('fueldb-' + network))), 64 | contract: new ethers.Contract(opts.contract || v1[network], Fuel.abi, provider), // selected Fuel contract object 65 | provider, // provider object 66 | operators: resolve('operators'), // 0 is main operator, the rest are for root deployment 67 | }; 68 | } 69 | 70 | module.exports = config; 71 | -------------------------------------------------------------------------------- /packages/client/src/exit.js: -------------------------------------------------------------------------------- 1 | const readline = require('readline'); 2 | 3 | // no op method 4 | const noop = () => {}; 5 | 6 | // handle CNTRL+C exits 7 | function exit(method = noop) { 8 | // readline setup 9 | readline.emitKeypressEvents(process.stdin); 10 | 11 | // key press 12 | process.stdin.on('keypress', (ch, key) => { 13 | if (key && key.ctrl && key.name == 'c') { 14 | console.log('exiting sync loop...'); 15 | method(); 16 | } 17 | }); 18 | 19 | // on clean exit 20 | process.on('SIGINT', method); 21 | process.on("SIGTERM", method); 22 | process.on("SIGINT", method); 23 | process.on("exit", method); 24 | 25 | // raw mode 26 | if (typeof process.stdin.setRawMode === 'function') { 27 | process.stdin.setRawMode(true); 28 | } 29 | } 30 | 31 | module.exports = exit; 32 | -------------------------------------------------------------------------------- /packages/client/src/faucet.js: -------------------------------------------------------------------------------- 1 | const utils = require('@fuel-js/utils'); 2 | const schema = require('@fuel-js/interface'); 3 | const { requests } = require('@fuel-js/logic'); 4 | const { Wallet } = require('@fuel-js/wallet'); 5 | 6 | // Amount to faucet per address. 7 | const faucetAmount = utils.parseEther('1000.0'); 8 | 9 | // Faucet method. 10 | async function faucet(config = {}) { 11 | // Stop nicely. 12 | if (!config.continue()) { 13 | return; 14 | } 15 | 16 | // Start a wallet instance. 17 | config.wallet = new Wallet(null, { 18 | network: config.networkSpecified, 19 | privateKey: (config.faucetOperator || {}).privateKey || config.operators, 20 | path: config.wallet_path || null, 21 | transact: config.transact || null, 22 | // spendProtection: true, 23 | // transact: (unsigned = '0x', witnesses = '0x') => logic.transact(unsigned, witnesses, 0, result), 24 | }); 25 | 26 | // start loop 27 | config.console.log(`Begining faucet sequence on ${ 28 | config.network.name 29 | } with wallet ${ 30 | config.wallet.address 31 | }`); 32 | 33 | let loop = true; 34 | let start = 0; 35 | let startNonce = 0; 36 | 37 | // get first token 38 | let token = null; 39 | try { 40 | token = await config.db.get([ schema.db.token, 1 ]); 41 | } catch (noTokenYet) { 42 | return; 43 | } 44 | 45 | // sync the wallet 46 | await config.wallet.sync(); 47 | 48 | // starting balance 49 | config.console.log(`starting balance ${utils.formatUnits( 50 | await config.wallet.balance(token, { sync: false }), 'ether')} ether`); 51 | 52 | // loop 53 | while (loop) { 54 | try { 55 | // Request processing on the remote DB. 56 | const entries = await requests(start, startNonce, utils.timestamp(), config); 57 | 58 | // processing 59 | config.console.log(`processing ${entries.length} addresses`); 60 | 61 | // addresses 62 | for (const { timestamp, nonce, address } of entries) { 63 | if (address === '0x') continue; 64 | await config.wallet.transfer(token, address, faucetAmount, { 65 | sync: false, 66 | }); 67 | start = timestamp; 68 | startNonce = nonce; 69 | } 70 | 71 | // processing 72 | config.console.log(`processed ${entries.length} addresses`); 73 | 74 | // Batch delete these keys, onto the next, on remote db. 75 | await config.db.batch(entries.map(entry => ({ 76 | type: 'del', 77 | key: entry.key, 78 | })), { remote: true }); 79 | } catch (loopError) { 80 | config.console.error(loopError); 81 | } 82 | 83 | // If stop, then break the loop. 84 | if (!config.continue() || config.faucet_no_cycle) { 85 | break; 86 | } 87 | 88 | // If loop we wait, otherwise just stop process. 89 | if (loop) { 90 | await utils.wait(1000); 91 | } 92 | } 93 | } 94 | 95 | module.exports = faucet; 96 | -------------------------------------------------------------------------------- /packages/client/src/faucetPlugin.js: -------------------------------------------------------------------------------- 1 | // This will run a full simulation node locally (w/ faucet, local chain and environment)x 2 | const faucet = require('./faucet'); 3 | const logic = require('@fuel-js/logic'); 4 | 5 | // The faucet plugin, that will run once sync has completed. 6 | async function faucetPlugin(state = {}, config = {}) { 7 | // Log the process. 8 | config.console.log(`Faucet sequence plugin running...`); 9 | 10 | // Transact. 11 | const transact = (unsigned = '0x', witnesses = '0x') => { 12 | return logic.transact( 13 | unsigned, 14 | witnesses, 15 | 0, 16 | config, 17 | ); 18 | }; 19 | 20 | // So here we run the faucet as a sync plugin. 21 | try { 22 | // Start the fauceting sequence. 23 | await faucet({ 24 | ...config, 25 | transact, 26 | faucet_no_cycle: true, 27 | wallet_path: 'http://localhost:' + config.port, // so the wallet will ping the local db RPC endpoint 28 | }); 29 | } catch (faucetError) { 30 | config.console.error('Faucet Error:'); 31 | config.console.error(faucetError); 32 | } 33 | } 34 | 35 | module.exports = faucetPlugin; -------------------------------------------------------------------------------- /packages/client/src/fetch.js: -------------------------------------------------------------------------------- 1 | const utils = require('@fuel-js/utils'); 2 | 3 | async function fetchRetry(path = '', obj = {}, opts = {}) { 4 | try { 5 | const maximumRetries = opts.retries || 10; 6 | let output = null; 7 | let error = null; 8 | 9 | // manage 502 retries if they occur 10 | for (var retry = 0; retry < maximumRetries; retry++) { 11 | try { 12 | output = await utils.fetch(path, obj); 13 | break; 14 | } catch (retryError) { 15 | // error 16 | error = retryError; 17 | 18 | // the lambda needs to be woken up.. 19 | if (retryError.statusCode !== 502) { 20 | throw new utils.ByPassError(retryError); 21 | break; 22 | } 23 | 24 | // wait 1 second than try again.. 25 | await utils.wait(1000); 26 | } 27 | } 28 | 29 | // if no output after 10 tries, than result the error 30 | if (!output) { 31 | throw new utils.ByPassError(error); 32 | } 33 | 34 | return output; 35 | } catch (fetchError) { 36 | throw new utils.ByPassError(fetchError); 37 | } 38 | } 39 | 40 | // _fetch, fetch data from the api provider, Returns: Array, RLP Decoded 41 | async function fetch (obj = {}, opts = {}) { 42 | try { 43 | const path = opts.path || '/get'; 44 | 45 | // Root api path 46 | const root = `https://${opts.network}.api.fuel.sh/v1`; 47 | 48 | // fetch 49 | const { 50 | error, 51 | result, 52 | } = await fetchRetry(`${root}${path}`, obj, opts); 53 | 54 | // if any error 55 | if (error) throw new Error(error); 56 | 57 | // Decode RLP 58 | return utils.RLP.decode(result); 59 | } catch (error) { 60 | throw new utils.ByPassError(error); 61 | } 62 | } 63 | 64 | module.exports = fetch; -------------------------------------------------------------------------------- /packages/client/src/full.js: -------------------------------------------------------------------------------- 1 | // This will run a full simulation node locally (w/ faucet, local chain and environment) 2 | const app = require('./app'); 3 | const faucetPlugin = require('./faucetPlugin'); 4 | 5 | // Start the main app loop. 6 | app({ 7 | plugin: faucetPlugin, 8 | }) 9 | .then(console.log) 10 | .catch(console.error); -------------------------------------------------------------------------------- /packages/client/src/index.js: -------------------------------------------------------------------------------- 1 | const app = require('./app'); 2 | 3 | // Start the main app loop. 4 | app() 5 | .then(console.log) 6 | .catch(console.error); -------------------------------------------------------------------------------- /packages/client/src/keyToWallet.js: -------------------------------------------------------------------------------- 1 | const write = require('write'); 2 | const read = require('fs-readfile-promise'); 3 | const prompts = require('prompts'); 4 | const ethers = require('ethers'); 5 | const utils = require('@fuel-js/utils'); 6 | 7 | // default wallet path 8 | const defaultPath = '.fuel-wallet.json'; 9 | 10 | // Convert a key to a wallet. 11 | async function keyToWallet(flags = {}, env = {}) { 12 | try { 13 | const provided = flags.wallet; 14 | const privateKey = flags.privateKey; 15 | 16 | // Producing wallet file for private key. 17 | console.log('Producing wallet file for private key.'); 18 | 19 | // Check private key. 20 | utils.assert(utils.hexDataLength(privateKey) === 32, 21 | 'please provide a valid private key'); 22 | 23 | // Path selection and wallet setup. 24 | const _path = provided || defaultPath; 25 | let _wallet = new ethers.Wallet(privateKey); 26 | let json = null; 27 | 28 | // attempt to load, decrypto or create an Ethers wallet 29 | try { 30 | // ask for password 31 | const { password } = await prompts({ 32 | type: 'password', 33 | name: 'password', 34 | message: 'Please select an 8+ char password for this private key wallet file.', 35 | }); 36 | 37 | // length check 38 | if (password.length < 8) { 39 | throw new Error('password must be more than 8 characters'); 40 | } 41 | 42 | // encrypto wallet JSON again 43 | json = await _wallet.encrypt(password); 44 | } catch (readError) { 45 | console.error(readError); 46 | } 47 | 48 | // write the JSON to the appropriate path 49 | await write(_path, json); 50 | 51 | // Wallet log. 52 | console.log(`Wallet file produced to: ${_path}`); 53 | 54 | // return wallet object descrypted into memory 55 | return _wallet; 56 | } catch (error) { 57 | throw new Error(error.message); 58 | } 59 | } 60 | 61 | module.exports = keyToWallet; 62 | -------------------------------------------------------------------------------- /packages/client/src/local.js: -------------------------------------------------------------------------------- 1 | const app = require('./app'); 2 | const ethers = require('ethers'); 3 | const utils = require('@fuel-js/utils'); 4 | const { ERC20, Fuel } = require('@fuel-js/contracts'); 5 | 6 | (async () => { 7 | await app({ 8 | localDeployment: async (flags = {}) => { 9 | // If deploy. 10 | console.log('Using RPC provider: ' + flags.rpc); 11 | const provider = new ethers.providers.JsonRpcProvider(flags.rpc); 12 | const signer = provider.getSigner(); 13 | const account = (await provider.listAccounts())[0]; 14 | const factory = new ethers.ContractFactory(Fuel.abi, Fuel.bytecode, signer); 15 | const tx = await factory.deploy( 16 | account, 17 | 70, // finalization delay at 20 18 | 4, 19 | 4, 20 | utils.parseEther('1.0'), 21 | "Fuel", 22 | "1.1.0", 23 | 0, 24 | utils.keccak256('0xdeadbeaf'), 25 | ); 26 | 27 | const contract = await tx.deployed(); 28 | 29 | async function increaseBlock() { 30 | try { 31 | await signer.sendTransaction({ 32 | to: account, 33 | value: 1, 34 | data: '0x', 35 | }); 36 | } catch (err) {console.log(err);} 37 | } 38 | 39 | async function plugin() { 40 | await increaseBlock(); 41 | } 42 | 43 | return { 44 | provider, 45 | contract: contract.address, 46 | increaseBlock, 47 | plugin, 48 | }; 49 | }, 50 | }) 51 | .then(console.log) 52 | .catch(console.error); 53 | 54 | return; 55 | 56 | })(); 57 | 58 | /* 59 | // Start the main app loop. 60 | 61 | */ -------------------------------------------------------------------------------- /packages/client/src/recover.js: -------------------------------------------------------------------------------- 1 | const utils = require('@fuel-js/utils'); 2 | 3 | /// @dev this will allow a database recovery from dump. 4 | async function recover(dumpLocation = '', config = {}) { 5 | // Get the entries from the simulated DB. 6 | let dbEntries = JSON.parse(await config.read( 7 | dumpLocation, 8 | )); 9 | 10 | config.console.log(`Recovering ${dbEntries.length} entires`); 11 | 12 | // 20k per batch, safley 256 per payload for now, ~1.3 minutes per 100k writes 13 | const batchSize = 20000; 14 | const payloadSize = 256; 15 | const numberOfRetries = 10; 16 | 17 | // Make entries in db. 18 | for (var retry = 0; retry < numberOfRetries; retry++) { 19 | try { 20 | // We attempt a large 20k batch size first, if it fails, than we try smaller batch sizes 21 | const batchAttemptSize = Math.round(batchSize / (retry + 1)); 22 | 23 | // attempt 20k batchs 24 | for (var bindex = 0; bindex < dbEntries.length; bindex += batchAttemptSize) { 25 | const _entries = dbEntries.slice(bindex, bindex + batchAttemptSize); 26 | let batches = []; 27 | 28 | // build the payloads 29 | for (var index = 0; index < _entries.length; index += payloadSize) { 30 | batches.push(config.db.batch(_entries.slice(index, index + payloadSize))); 31 | } 32 | 33 | // send entire batch and payloads in parallel 34 | await Promise.all(batches); 35 | } 36 | 37 | // if all is well, break retry cycle, carry on.. 38 | break; 39 | } catch (error) { 40 | config.console.log(`Error while processing, waiting.. ${error.message}`); 41 | 42 | // wait 10 seconds for potentially more connections 43 | await utils.wait(10000); 44 | } 45 | } 46 | 47 | config.console.log(`Restored successfully: ${dbEntries.length} entries.`); 48 | } 49 | 50 | // Export dump. 51 | module.exports = recover; -------------------------------------------------------------------------------- /packages/client/src/retrieveBonds.js: -------------------------------------------------------------------------------- 1 | const protocol = require('@fuel-js/protocol'); 2 | 3 | // @dev Retrieve bonds from a given contract. 4 | async function retrieveBonds(address = null, config = {}) { 5 | // The Fuel contract. 6 | let contract = config.contract; 7 | 8 | // If a secondary address specified, then use this. 9 | if (address) { 10 | // Attahc new address. 11 | contract = contract.attach(address); 12 | } 13 | 14 | // Attahc operator. 15 | contract = contract.connect(config.connectedOperator); 16 | 17 | // Scan through all logs 18 | const logs = await contract.provider.getLogs({ 19 | fromBlock: 0, 20 | toBlock: 'latest', 21 | address: contract.address, 22 | topics: contract.filters.BlockCommitted(null, null, null, null, null).topics, 23 | }); 24 | 25 | // Go through each log. 26 | for (const log of logs) { 27 | // Decode log. 28 | const parsedLog = contract.interface.parseLog(log); 29 | 30 | // Block header. 31 | const blockHeader = new protocol.block.BlockHeader({ 32 | ...parsedLog.values, 33 | blockNumber: log.blockNumber, 34 | }); 35 | 36 | // Skip genesis block. 37 | if (blockHeader.properties.height().get().lte(0)) { 38 | continue; 39 | } 40 | 41 | // If not continue, stop. 42 | if (!config.continue()) { 43 | break; 44 | } 45 | 46 | // Stub for tx. 47 | let retrievalTx = null; 48 | 49 | // Options. 50 | const txOptions = { 51 | gasLimit: 4000000, 52 | }; 53 | 54 | // Retrieval. 55 | try { 56 | // If the operator is a proxy, handle that, otherwise just normal transaciton. 57 | if (config.proxy) { 58 | // Commit transaction encoded data. 59 | const bondWithdraw = contract.interface.functions.bondWithdraw.encode([ 60 | blockHeader.encodePacked(), 61 | ]); 62 | 63 | // Set block commitment. 64 | const txValue = 0; 65 | retrievalTx = await config.proxy.transact( 66 | contract.address, 67 | txValue, // no value. 68 | bondWithdraw, 69 | txOptions, 70 | ); 71 | } else { 72 | // Retrieval transaction. 73 | retrievalTx = await contract.bondWithdraw( 74 | blockHeader.encodePacked(), 75 | txOptions, 76 | ); 77 | } 78 | 79 | // Wait for retrieval. 80 | retrievalTx = await retrievalTx.wait(); 81 | } catch (retrievalError) { 82 | config.console.error(`Bond retreival error during transaction for block ${ 83 | blockHeader.properties.height().get().toNumber() 84 | }`); 85 | config.console.error(retrievalError); 86 | } 87 | } 88 | } 89 | 90 | module.exports = retrieveBonds; -------------------------------------------------------------------------------- /packages/client/src/test/index.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | 3 | module.exports = test('client', async t => { 4 | t.ok(1, 'client'); 5 | }); 6 | -------------------------------------------------------------------------------- /packages/client/src/wallet.js: -------------------------------------------------------------------------------- 1 | const prompts = require('prompts'); 2 | const ethers = require('ethers'); 3 | const { sign } = require('crypto'); 4 | const { promises: fs } = require('fs') 5 | 6 | // default wallet path 7 | const defaultPath = '.fuel-wallet.json'; 8 | 9 | // wallet method 10 | async function wallet(flags = {}, environment = {}) { 11 | try { 12 | const provided = flags.wallet; 13 | const _path = provided || defaultPath; 14 | let _wallet = null; 15 | let json = null; 16 | 17 | // if the user provided a wallet path, try to read it 18 | if (provided) { 19 | try { 20 | json = await fs.readFile(_path, 'utf8'); 21 | } catch (readError) { 22 | throw new Error('Error reading wallet path: ' + readError.message); 23 | } 24 | } 25 | // if no wallet was provided, attempt to read the default path wallet 26 | if (!provided) { 27 | try { 28 | json = await fs.readFile(_path, 'utf8'); 29 | } catch (_) { 30 | // ignore error 31 | } 32 | } 33 | 34 | // attempt to load, decrypto or create an Ethers wallet 35 | try { 36 | const message = json ? 37 | 'Please enter your Fuel wallet decryption passphrase' : 38 | 'Please enter a Fuel wallet encryption passphrase'; 39 | 40 | // Password. 41 | let password = ''; 42 | 43 | // Get environmental password if not available. 44 | try { 45 | if (environment.fuel_v1_default_password) { 46 | password = environment.fuel_v1_default_password; 47 | 48 | console.log('Wallet password detected in the environment'); 49 | } else { 50 | // ask for password 51 | const promptResult = await prompts({ 52 | type: 'password', 53 | name: 'password', 54 | message, 55 | }); 56 | 57 | password = promptResult.password; 58 | } 59 | } catch (promptError) { 60 | console.error('Password prompt error' + promptError.message); 61 | throw new Error(error.message); 62 | } 63 | 64 | // length check, only for new wallets 65 | if (!json && (password || '').length < 8) { 66 | throw new Error('password must be more than 8 characters'); 67 | } 68 | 69 | // if no wallet present, attempt to load the JSON from password 70 | // otherwise create wallet 71 | if (json) { 72 | _wallet = await ethers.Wallet.fromEncryptedJson(json, password); 73 | } else { 74 | _wallet = ethers.Wallet.createRandom(); 75 | } 76 | 77 | // encrypt and write to file if new wallet 78 | if (!json) { 79 | json = await _wallet.encrypt(password); 80 | await fs.writeFile(_path, json); 81 | } 82 | } catch (readError) { 83 | console.error('Wallet decryption error: ' + readError.message); 84 | throw new Error(); 85 | } 86 | 87 | // return wallet object descrypted into memory 88 | return _wallet; 89 | } catch (error) { 90 | throw new Error(error.message); 91 | } 92 | } 93 | 94 | module.exports = wallet; 95 | -------------------------------------------------------------------------------- /packages/contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/contracts", 3 | "version": "0.1.8", 4 | "description": "Fuel protocol implemented in Yul+ for the Ethereum Virtual Machine", 5 | "main": "src/index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/fuellabs/fuel.git" 9 | }, 10 | "keywords": [ 11 | "fuel", 12 | "ethereum", 13 | "optimistic", 14 | "rollup" 15 | ], 16 | "publishConfig": { 17 | "access": "public" 18 | }, 19 | "author": "Nick Dodson ", 20 | "license": "Apache-2.0", 21 | "bugs": { 22 | "url": "https://github.com/fuellabs/fuel/issues" 23 | }, 24 | "homepage": "https://github.com/fuellabs/fuel#readme" 25 | } 26 | -------------------------------------------------------------------------------- /packages/contracts/src/builds/ERC20.json: -------------------------------------------------------------------------------- 1 | { 2 | "bytecode": "0x603f193801600138821038831417101561001e57600060005260206000fd5b604081604039506040516060518160005260006020528060406000205550506102f78061004e600039806000f350fe60003681608037600160e01b6080510463a9059cbb8114610081576323b872dd81146100955763095ea7b381146100ab5763dd62ed3e81146100f8576306fdde038114610114576395d89b418114610119576354fd4d50811461011e5763313ce5678114610123576370a0823181146101305761007c82836101b6565b610144565b61009060a451608451336101ce565b610144565b6100a660c45160a4516084516101ce565b610144565b60a4516100bc6001608451336102d3565b5560a4518252608451337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925602085a360018252602082f3610144565b610108600160a4516084516102d3565b548252602082f3610144565b610144565b610144565b610144565b60128252602082f3610144565b61013c826084516102bd565b548252602082f35b5050006102f6565b6000828214838311171561015f57600190505b5b92915050565b60008282019050600182821183831417101561018757600060005260206000fd5b5b92915050565b6000828203905060018282108383141710156101af57600060005260206000fd5b5b92915050565b60018110156101c9578160005260206000fd5b5b5050565b6000818152806020526040812054828252816020528360405260408220606052604060402054610200600133866102d3565b546102126001888514898611176101b6565b60001981141533861415161561024d576102366002610231898561014c565b6101b6565b610240878361018e565b61024b8588886102d3565b555b5050610259858261018e565b61026383856102bd565b55506102798461027383866102bd565b54610166565b61028382856102bd565b5583815282827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602084a360018152602081f3505b505050565b6000818152826020526040812090505b92915050565b6000818152836020528260405260408120606052604060402090505b9392505050565b", 3 | "abi": [ 4 | "constructor(address owner, uint256 totalSupply)", 5 | "transfer(address owner, uint256 amount) public returns (bool success)", 6 | "transferFrom(address source, address destination, uint amount) public returns (bool success)", 7 | "approve(address destination, uint256 amount) public returns (bool success)", 8 | "allowance(address source, address owner) public view returns (uint256 allowance)", 9 | "name() public view returns (string)", 10 | "symbol() public view returns (string)", 11 | "version() public view returns (string)", 12 | "decimals() public view returns (uint8)", 13 | "balanceOf(address owner) public view returns (uint256 balance)", 14 | "event Approval(address indexed source, address indexed destination, uint256 amount)", 15 | "event Transfer(address indexed source, address indexed destination, uint amount)" 16 | ], 17 | "errors": { 18 | "insufficient-balance": "0x01", 19 | "0x01": "insufficient-balance", 20 | "insufficient-allowance": "0x02", 21 | "0x02": "insufficient-allowance" 22 | } 23 | } -------------------------------------------------------------------------------- /packages/contracts/src/builds/Funnel.json: -------------------------------------------------------------------------------- 1 | { 2 | "bytecode": "0x6062600d60003960626000f3fe6000602060358239338151141560335760803614156030573681823780816044603c8485515af11515602f578081fd5b5b33ff5b50", 3 | "abi": [], 4 | "errors": {} 5 | } -------------------------------------------------------------------------------- /packages/contracts/src/builds/Funnel.json.yul: -------------------------------------------------------------------------------- 1 | object "Funnel" { 2 | code { 3 | // Sizes: 66 + 32 = 98.s 4 | datacopy(0, dataoffset("Runtime"), 98) 5 | return(0, 98) 6 | } 7 | object "Runtime" { 8 | code { 9 | // Copy owner set in constructor to memory. 10 | // 66 - 13 = 53 11 | codecopy(0, 53, 32) 12 | 13 | // Check owner is caller. 14 | if eq(mload(0), caller()) { 15 | 16 | // If calldata is correct length proceed with call. 17 | if eq(calldatasize(), 128) { 18 | 19 | // Copy calldata to memory. 20 | calldatacopy(0, 0, calldatasize()) 21 | 22 | // Make outward call to destination, first word is destination, the next 68 is the call. 23 | if iszero(call(gas(), mload(0), 0, 60, 68, 0, 0)) { 24 | // If the call failed, revert tx. 25 | revert(0, 0) 26 | } 27 | } 28 | 29 | // Send ether to caller and self-destruct regardless. 30 | selfdestruct(caller()) 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /packages/contracts/src/builds/HTLC.json: -------------------------------------------------------------------------------- 1 | { 2 | "bytecode": "0x61033660208101600182821183831417101561002057600060005260206000fd5b8061002f600039806000f35050fe602061000b81386102c8565b600082828239805191506004818237600160e01b815104632b660100811461005e5763a49f2c25811461012e57635524d548811461023e5763cbf9fe5f811461025a5763570ca73581146102755761027c565b60a06004833760a082208451608051827f10906fae603eebfac53ddc0f103bee8a044dd7643c425c7a90f921dfa15ef62c8687a26100a6816100a08488610308565b546102a0565b6100b360073389146102f0565b82600081146100f4576370a0823187523089526100da60098a896024601c8c8a5af16102f0565b6100ef600a6100ea848a51610286565b6102f0565b61010a565b6101096008610104843031610286565b6102f0565b5b506001610118856001610308565b55806101248488610308565b555050505061027c565b60a06004833760a08220807f6eec2dd2382427616d4ea7ef183b16091feac4e2e63c8b55f25215f132df8d148485a2608051431160405161016f60a061031e565b148451608051885161018b82610185838b610308565b546102c8565b6101a1600b61019b896001610308565b546102f0565b6101cd7f657870697265642d6f722d707265696d616765000000000000000000000000008688176102f0565b8515156102195781600081146102045763a9059cbb8a52848c52836040526101ff600c8b8c6044601c8f895af16102f0565b610217565b61021660048b8c8d8e898b5af16102f0565b5b505b80610224838b610308565b5550505050505082610237826001610308565b555061027c565b836004833761024f82516001610308565b5482528382f361027c565b836004833761026a825183610308565b5482528382f361027c565b8282528382f35b5050505000610335565b6000828214838311171561029957600190505b5b92915050565b6000828201905060018282118383141710156102c157600060005260206000fd5b5b92915050565b6000828203905060018282108383141710156102e957600060005260206000fd5b5b92915050565b6001811015610303578160005260206000fd5b5b5050565b6000818152826020526040812090505b92915050565b600060208160208460025afa50805190505b919050565b", 3 | "abi": [ 4 | "constructor(address operator)", 5 | "transfer(address,uint256)", 6 | "register(address owner,\n address token,\n bytes32 digest,\n uint256 expiry,\n uint256 amount)", 7 | "balanceOf(address)", 8 | "release(address owner,\n address token,\n bytes32 digest,\n uint256 expiry,\n uint256 amount,\n bytes32 preimage)", 9 | "registered(bytes32 hash) view returns (bool notReleased)", 10 | "locked(address token) view returns (uint256 balanceLocked)", 11 | "operator() view returns (address operator)", 12 | "event Registered(bytes32 indexed hash)", 13 | "event Released(bytes32 indexed hash)" 14 | ], 15 | "errors": { 16 | "amount": "0x01", 17 | "0x01": "amount", 18 | "owner": "0x02", 19 | "0x02": "owner", 20 | "token": "0x03", 21 | "0x03": "token", 22 | "ether-transfer": "0x04", 23 | "0x04": "ether-transfer", 24 | "erc20-call-transfer": "0x05", 25 | "0x05": "erc20-call-transfer", 26 | "erc20-return-transfer": "0x06", 27 | "0x06": "erc20-return-transfer", 28 | "caller-operator": "0x07", 29 | "0x07": "caller-operator", 30 | "ether-balance-underflow": "0x08", 31 | "0x08": "ether-balance-underflow", 32 | "balance-call": "0x09", 33 | "0x09": "balance-call", 34 | "balance-underflow": "0x0a", 35 | "0x0a": "balance-underflow", 36 | "invalid-hash": "0x0b", 37 | "0x0b": "invalid-hash", 38 | "transfer-call": "0x0c", 39 | "0x0c": "transfer-call" 40 | } 41 | } -------------------------------------------------------------------------------- /packages/contracts/src/builds/OwnedProxy.json: -------------------------------------------------------------------------------- 1 | { 2 | "bytecode": "0x603f193801600138821038831417101561001e57600060005260206000fd5b600060208282398051815561014a915060408201600183821184831417101561004957818252602082fd5b8061005783398082f3505050fe36151561000857005b60206100148138610109565b600082828239805191508054600480838437600160e01b83510463c4627c5d81146100575763776d1a0181146100a657631e77933e81146100c0578384fd6100d5565b84331461006960018286331417610131565b3683863761007f60026001548751148317610131565b506100a1600385866060516100968b6040516100e1565b8b518a515af1610131565b6100d5565b6100b282863314610131565b8582853783516001556100d5565b6100cc82863314610131565b85828537835184555b50505050505000610149565b60008282019050600182821183831417101561010257600060005260206000fd5b5b92915050565b60008282039050600182821083831417101561012a57600060005260206000fd5b5b92915050565b6001811015610144578160005260206000fd5b5b5050565b", 3 | "abi": [ 4 | "constructor(address hot, address cold)", 5 | "transact(address destination, uint256 value, bytes data)", 6 | "setTarget(address target)", 7 | "change(address hot)" 8 | ], 9 | "errors": { 10 | "caller": "0x01", 11 | "0x01": "caller", 12 | "target": "0x02", 13 | "0x02": "target", 14 | "call": "0x03", 15 | "0x03": "call", 16 | "caller-cold": "0x04", 17 | "0x04": "caller-cold" 18 | } 19 | } -------------------------------------------------------------------------------- /packages/contracts/src/builds/Proxy.json: -------------------------------------------------------------------------------- 1 | { 2 | "bytecode": "0x603d80600e600039806000f350fe600036818237601f1936016001368210368314171015602057818252602082fd5b600182838360208687515af11015603a5760018252602082fd5b5050", 3 | "abi": [], 4 | "errors": { 5 | "proxy-call-failed": "0x01", 6 | "0x01": "proxy-call-failed" 7 | } 8 | } -------------------------------------------------------------------------------- /packages/contracts/src/builds/Proxy.json.yul: -------------------------------------------------------------------------------- 1 | object "Proxy" { 2 | code { 3 | datacopy(0, dataoffset("Runtime"), datasize("Runtime")) 4 | return(0, datasize("Runtime")) 5 | } 6 | object "Runtime" { 7 | code { 8 | function safeAdd(x, y) -> z { 9 | z := add(x, y) 10 | require(or(eq(z, x), gt(z, x)), 0) 11 | } 12 | 13 | function safeSub(x, y) -> z { 14 | z := sub(x, y) 15 | require(or(eq(z, x), lt(z, x)), 0) 16 | } 17 | 18 | function safeMul(x, y) -> z { 19 | if gt(y, 0) { 20 | z := mul(x, y) 21 | require(eq(div(z, y), x), 0) 22 | } 23 | } 24 | 25 | function safeDiv(x, y) -> z { 26 | require(gt(y, 0), 0) 27 | z := div(x, y) 28 | } 29 | 30 | function require(arg, message) { 31 | if lt(arg, 1) { 32 | mstore(0, message) 33 | revert(0, 32) 34 | } 35 | } 36 | 37 | calldatacopy(0, 0, calldatasize()) 38 | require(call(gas(), mload(0), 0, 32, safeSub(calldatasize(), 32), 0, 0), 0x01) 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /packages/contracts/src/builds/ProxyBypass.json: -------------------------------------------------------------------------------- 1 | { 2 | "bytecode": "0x604380600e600039806000f350fe600036818237601f1936016001368210368314171015602057818252602082fd5b6020828260208586515af1825191506001811015603f57818352602083fd5b505050", 3 | "abi": [], 4 | "errors": {} 5 | } -------------------------------------------------------------------------------- /packages/contracts/src/builds/ProxyBypass.json.yul: -------------------------------------------------------------------------------- 1 | object "ProxyBypass" { 2 | code { 3 | datacopy(0, dataoffset("Runtime"), datasize("Runtime")) 4 | return(0, datasize("Runtime")) 5 | } 6 | object "Runtime" { 7 | code { 8 | function safeAdd(x, y) -> z { 9 | z := add(x, y) 10 | require(or(eq(z, x), gt(z, x)), 0) 11 | } 12 | 13 | function safeSub(x, y) -> z { 14 | z := sub(x, y) 15 | require(or(eq(z, x), lt(z, x)), 0) 16 | } 17 | 18 | function safeMul(x, y) -> z { 19 | if gt(y, 0) { 20 | z := mul(x, y) 21 | require(eq(div(z, y), x), 0) 22 | } 23 | } 24 | 25 | function safeDiv(x, y) -> z { 26 | require(gt(y, 0), 0) 27 | z := div(x, y) 28 | } 29 | 30 | function require(arg, message) { 31 | if lt(arg, 1) { 32 | mstore(0, message) 33 | revert(0, 32) 34 | } 35 | } 36 | 37 | calldatacopy(0, 0, calldatasize()) 38 | let result := call(gas(), mload(0), 0, 32, safeSub(calldatasize(), 32), 0, 32) 39 | require(result, mload(0)) 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /packages/contracts/src/builds/Revert.json: -------------------------------------------------------------------------------- 1 | { 2 | "bytecode": "0x60fd8061000f600039806000f350fe600036818237600160e01b8151046370a08231811460425763a9059cbb8114604f5763352e65528114605c57634c97815b811460a357603e60058360e5565b60df565b6113888252602082f360df565b605860018360e5565b60df565b3660048337815160205160405163f9609f08855281602052806040525050608c600284856044601c88875af160e5565b609e600384856044601c88875af160e5565b5060df565b3660048337815160205163cc4c0b4b8452806020525060cb600284856024601c88875af160e5565b60dd600484856024601c88875af160e5565b505b505060fc565b600181101560f7578160005260206000fd5b5b5050565b", 3 | "abi": [ 4 | "balanceOf(address owner) public view returns (uint256 balance)", 5 | "transfer(address owner, uint256 amount) public returns (bool success)", 6 | "doubleDeposit(address fuel, address owner, address token)", 7 | "deposit(address owner, address token)", 8 | "doubleCommitWitnesss(address fuel, bytes32 witness)", 9 | "commitWitness(bytes32)" 10 | ], 11 | "errors": { 12 | "transfer": "0x01", 13 | "0x01": "transfer", 14 | "call": "0x02", 15 | "0x02": "call", 16 | "per-block": "0x03", 17 | "0x03": "per-block", 18 | "already-witnessed": "0x04", 19 | "0x04": "already-witnessed", 20 | "invalid-sig": "0x05", 21 | "0x05": "invalid-sig" 22 | } 23 | } -------------------------------------------------------------------------------- /packages/contracts/src/builds/Revert.json.yul: -------------------------------------------------------------------------------- 1 | object "Revert" { 2 | code { 3 | datacopy(0, dataoffset("Runtime"), datasize("Runtime")) 4 | return(0, datasize("Runtime")) 5 | } 6 | object "Runtime" { 7 | code { 8 | function require(arg, message) { 9 | if lt(arg, 1) { 10 | mstore(0, message) 11 | revert(0, 32) 12 | } 13 | } 14 | 15 | function mslice(position, length) -> result { 16 | result := div(mload(position), exp(2, sub(256, mul(length, 8)))) 17 | } 18 | 19 | calldatacopy(0, 0, calldatasize()) 20 | 21 | switch mslice(0, 4) 22 | 23 | /// @dev Return a valid balance. 24 | case 0x70a08231 { 25 | mstore(0, 5000) 26 | return (0, 32) 27 | } 28 | 29 | /// @dev Revert on transfer. 30 | case 0xa9059cbb { 31 | require(0, 0x01) 32 | } 33 | 34 | /// @dev Attempt double deposit (i.e. same block.). 35 | case 0x352e6552 { 36 | calldatacopy(0, 4, calldatasize()) 37 | let fuelAddress := mload(0) 38 | let ownerAddress := mload(32) 39 | let tokenAddress := mload(64) 40 | 41 | // We commit our first witness. 42 | mstore(0, 0xf9609f08) mstore(add(0,32), ownerAddress) mstore(add(0,64), tokenAddress) 43 | require(call(gas(), fuelAddress, 0, 28, 68, 0, 0), 0x02) 44 | 45 | // Attempt same deposit in the same block. should throw. 46 | require(call(gas(), fuelAddress, 0, 28, 68, 0, 0), 0x03) 47 | } 48 | 49 | /// @dev Attempt double witness commitment (i.e. same block, same data, same caller). 50 | case 0x4c97815b { 51 | calldatacopy(0, 4, calldatasize()) 52 | let fuelAddress := mload(0) 53 | let witness := mload(32) 54 | 55 | // We commit our first witness. 56 | mstore(0, 0xcc4c0b4b) mstore(add(0,32), witness) 57 | require(call(gas(), fuelAddress, 0, 28, 36, 0, 0), 0x02) 58 | 59 | // In the same block we commit the same witness, this should fail, we flag: already-committed. 60 | require(call(gas(), fuelAddress, 0, 28, 36, 0, 0), 0x04) 61 | } 62 | 63 | /// @dev Invaid Signature. 64 | default { 65 | require(0, 0x05) 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /packages/contracts/src/deployments/Fuel.json: -------------------------------------------------------------------------------- 1 | { 2 | "v1": { 3 | "mainnet": "0x6880f6Fd960D1581C2730a451A22EED1081cfD72", 4 | "rinkeby": "0xb171b7202A833D97A3D69412AA365DE4AB6e4545" 5 | } 6 | } -------------------------------------------------------------------------------- /packages/contracts/src/deployments/Numbers.json: -------------------------------------------------------------------------------- 1 | { 2 | "v1": { 3 | "mainnet": "11787727", 4 | "rinkeby": "8012426" 5 | } 6 | } -------------------------------------------------------------------------------- /packages/contracts/src/index.js: -------------------------------------------------------------------------------- 1 | const Funnel = require('./builds/Funnel.json'); 2 | const Fuel = require('./builds/Fuel.json'); 3 | const OwnedProxy = require('./builds/OwnedProxy.json'); 4 | const HTLC = require('./builds/HTLC.json'); 5 | const Proxy = require('./builds/Proxy.json'); 6 | const ERC20 = require('./builds/ERC20.json'); 7 | const deployments = require('./deployments/Fuel.json'); 8 | 9 | module.exports = { 10 | Funnel, 11 | Fuel, 12 | OwnedProxy, 13 | HTLC, 14 | Proxy, 15 | ERC20, 16 | deployments, 17 | }; -------------------------------------------------------------------------------- /packages/database/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | }, 14 | parserOptions: { 15 | ecmaVersion: 2018, 16 | }, 17 | rules: { 18 | 'no-restricted-syntax': ['off'], 19 | 'no-plusplus': ['off'], 20 | 'arrow-parens': ['off'], 21 | 'import/no-extraneous-dependencies': ['error', { devDependencies: ['./src/test/*'] }], 22 | camelcase: ['off'], 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /packages/database/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/database/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | -------------------------------------------------------------------------------- /packages/database/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/database 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/database/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/database", 3 | "version": "0.1.0", 4 | "description": "The fuel-js levelup database wrapper.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "nyc node src/test", 8 | "lint": "eslint src/index.js", 9 | "prepublish": "npm test && npm run lint" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/fuellabs/fuel-js.git" 14 | }, 15 | "publishConfig": { 16 | "access": "public" 17 | }, 18 | "keywords": [ 19 | "fuel", 20 | "js", 21 | "interface" 22 | ], 23 | "author": "Nick Dodson ", 24 | "license": "Apache-2.0", 25 | "bugs": { 26 | "url": "https://github.com/fuellabs/fuel-js/issues" 27 | }, 28 | "homepage": "https://github.com/fuellabs/fuel-js#readme", 29 | "devDependencies": { 30 | "@fuel-js/environment": "^0.1.0", 31 | "leveldown": "^5.6.0", 32 | "memdown": "^5.1.0", 33 | "stream-to-array": "^2.3.0", 34 | "eslint": "^7.6.0", 35 | "nyc": "^15.1.0", 36 | "eslint-config-airbnb-base": "^14.2.0", 37 | "eslint-plugin-import": "^2.21.2" 38 | }, 39 | "dependencies": { 40 | "@fuel-js/encoding": "^0.1.0", 41 | "encoding-down": "^6.3.0", 42 | "levelup": "^4.4.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/database/src/index.js: -------------------------------------------------------------------------------- 1 | // @description database wrapper for leveldown 2 | const levelup = require('levelup'); 3 | const encoding = require('@fuel-js/encoding'); 4 | const encode = require('encoding-down'); 5 | 6 | function db(down) { 7 | return levelup(encode(down, encoding)); 8 | } 9 | 10 | module.exports = db; 11 | -------------------------------------------------------------------------------- /packages/database/src/test/index.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const memdown = require('memdown'); 3 | const database = require('../index'); 4 | 5 | module.exports = test('database', async t => { 6 | 7 | const db = database(memdown()); 8 | await db.put('0xaa', '0xbb'); 9 | t.equal(await db.get('0xaa'), '0xbb', 'put check'); 10 | 11 | await db.close(); 12 | 13 | }); 14 | -------------------------------------------------------------------------------- /packages/down/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | }, 14 | parserOptions: { 15 | ecmaVersion: 2018, 16 | }, 17 | rules: { 18 | 'arrow-parens': ['off'], 19 | 'import/no-extraneous-dependencies': ['error', { devDependencies: ['./src/test/*'] }], 20 | 'no-await-in-loop': ['off'], 21 | 'no-plusplus': ['off'], 22 | 'global-require': ['off'], 23 | 'func-names': ['off'], 24 | camelcase: ['off'], 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /packages/down/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .testdb2 3 | .testdb3 4 | .testdb8 5 | -------------------------------------------------------------------------------- /packages/down/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | .testdb2 3 | .testdb3 4 | .testdb8 5 | -------------------------------------------------------------------------------- /packages/down/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/down 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/down/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/down", 3 | "version": "0.1.5", 4 | "description": "The Fuel-JS common down implimentations.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "npm run clean && nyc node src/test", 8 | "lint": "eslint src/**", 9 | "prepublish": "npm test && npm run lint", 10 | "clean": "rm -rf .testdb && rm -rf .testdb3" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/fuellabs/fuel-js.git" 15 | }, 16 | "publishConfig": { 17 | "access": "public" 18 | }, 19 | "keywords": [ 20 | "fuel", 21 | "js", 22 | "interface" 23 | ], 24 | "author": "Nick Dodson ", 25 | "license": "Apache-2.0", 26 | "bugs": { 27 | "url": "https://github.com/fuellabs/fuel-js/issues" 28 | }, 29 | "homepage": "https://github.com/fuellabs/fuel-js#readme", 30 | "devDependencies": { 31 | "@fuel-js/environment": "^0.1.0", 32 | "@fuel-js/database": "^0.1.0", 33 | "@fuel-js/key": "^0.1.0", 34 | "leveldown": "^5.6.0", 35 | "memdown": "^5.1.0", 36 | "stream-to-array": "^2.3.0", 37 | "nyc": "^15.1.0", 38 | "eslint-config-airbnb-base": "^14.2.0", 39 | "eslint": "^7.2.0", 40 | "eslint-plugin-import": "^2.21.2" 41 | }, 42 | "dependencies": { 43 | "@fuel-js/encoding": "^0.1.0", 44 | "abstract-leveldown": "^6.3.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/down/src/copy.js: -------------------------------------------------------------------------------- 1 | // @description leveldown remote, reads from local, puts / dels / batch / on both dbs 2 | /* eslint no-underscore-dangle: 0 */ 3 | /* eslint consistent-return: 0 */ 4 | const { AbstractLevelDOWN } = require('abstract-leveldown'); 5 | const util = require('util'); 6 | 7 | // Constructor 8 | function CopyDown(remote, local) { 9 | if (!(this instanceof CopyDown)) return new CopyDown(remote, local); 10 | 11 | AbstractLevelDOWN.call(this, { 12 | ...(remote.supports || {}), 13 | ...(local.supports || {}), 14 | promises: true, 15 | buffer: true, 16 | remote, 17 | local, 18 | }); 19 | this.remote = remote; 20 | this.local = local; 21 | } 22 | 23 | // Our new prototype inherits from AbstractLevelDOWN 24 | util.inherits(CopyDown, AbstractLevelDOWN); 25 | 26 | function openIfNew(db, options, callback) { 27 | if (db.status === 'new') return db.open(options, callback); 28 | process.nextTick(callback); 29 | } 30 | 31 | function closeIfOpen(db, callback) { 32 | if (db.status === 'open') return db.close(callback); 33 | process.nextTick(callback); 34 | } 35 | 36 | CopyDown.prototype._open = function (options, callback) { 37 | const self = this; 38 | openIfNew(self.local, options, err => { 39 | if (!err) { 40 | return openIfNew(self.remote, options, callback); 41 | } 42 | return callback(err); 43 | }); 44 | }; 45 | 46 | CopyDown.prototype._close = function (callback) { 47 | const self = this; 48 | closeIfOpen(self.local, err => { 49 | if (!err) { 50 | return closeIfOpen(self.remote, callback); 51 | } 52 | return callback(err); 53 | }); 54 | }; 55 | 56 | CopyDown.prototype._put = function (key, value, options, callback) { 57 | const self = this; 58 | if (options.remote) return self.remote._put(key, value, options, callback); 59 | if (options.local) return self.local._put(key, value, options, callback); 60 | 61 | self.local._put(key, value, options, (error, result) => { 62 | if (error) { return callback(error, result); } 63 | self.remote._put(key, value, options, callback); 64 | }); 65 | }; 66 | 67 | CopyDown.prototype._get = function (key, options, callback) { 68 | const self = this; 69 | if (options.remote) return self.remote._get(key, options, callback); 70 | if (options.local) return self.local._get(key, options, callback); 71 | 72 | self.local._get(key, options, callback); 73 | }; 74 | 75 | CopyDown.prototype._del = function (key, options, callback) { 76 | const self = this; 77 | if (options.remote) return self.remote._del(key, options, callback); 78 | if (options.local) return self.local._del(key, options, callback); 79 | 80 | self.local._del(key, options, (error, result) => { 81 | if (error) { return callback(error, result); } 82 | self.remote._del(key, options, callback); 83 | }); 84 | }; 85 | 86 | CopyDown.prototype._clear = function (options, callback) { 87 | const self = this; 88 | if (options.remote) return self.remote._clear(options, callback); 89 | if (options.local) return self.local._clear(options, callback); 90 | 91 | self.local._clear(options, (error, result) => { 92 | if (error) { return callback(error, result); } 93 | self.remote._clear(options, callback); 94 | }); 95 | }; 96 | 97 | CopyDown.prototype._batch = function (array, options, callback) { 98 | const self = this; 99 | if (options.remote) return self.remote._batch(array, options, callback); 100 | if (options.local) return self.local._batch(array, options, callback); 101 | 102 | self.local._batch(array, options, (error, result) => { 103 | if (error) { return callback(error, result); } 104 | self.remote._batch(array, options, callback); 105 | }); 106 | }; 107 | 108 | CopyDown.prototype._iterator = function (options) { 109 | if (options.remote) return this.remote._iterator(options); 110 | if (options.local) return this.local._iterator(options); 111 | 112 | return this.local._iterator(options); 113 | }; 114 | 115 | module.exports = CopyDown; 116 | -------------------------------------------------------------------------------- /packages/down/src/index.js: -------------------------------------------------------------------------------- 1 | const copy = require('./copy'); 2 | const simulate = require('./simulate'); 3 | const local = require('./local'); 4 | 5 | module.exports = { 6 | copy, 7 | simulate, 8 | local, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/down/src/local.js: -------------------------------------------------------------------------------- 1 | // @description leveldown will store an additional key for each write for rewdinabilitiy 2 | /* eslint no-underscore-dangle: 0 */ 3 | /* eslint consistent-return: 0 */ 4 | const { AbstractLevelDOWN } = require('abstract-leveldown'); 5 | const util = require('util'); 6 | 7 | function LocalDown(down = {}) { 8 | if (!(this instanceof LocalDown)) return new LocalDown(down); 9 | this.store = down; 10 | AbstractLevelDOWN.call(this, { 11 | stream: true, 12 | local: down, 13 | }); 14 | } 15 | 16 | util.inherits(LocalDown, AbstractLevelDOWN); 17 | 18 | function openIfNew(db, options, callback) { 19 | if (db.status === 'new') return db.open(options, callback); 20 | process.nextTick(callback); 21 | } 22 | 23 | function closeIfOpen(db, callback) { 24 | if (db.status === 'open') return db.close(callback); 25 | process.nextTick(callback); 26 | } 27 | 28 | LocalDown.prototype._open = function (options, callback) { 29 | openIfNew(this.store, options, callback); 30 | }; 31 | 32 | LocalDown.prototype._close = function (callback) { 33 | closeIfOpen(this.store, callback); 34 | }; 35 | 36 | LocalDown.prototype._put = function (key, value, options, callback) { 37 | const self = this; 38 | self.store._put(key, value, options, callback); 39 | }; 40 | 41 | LocalDown.prototype._get = function (key, options, callback) { 42 | this.store._get(key, options, callback); 43 | }; 44 | 45 | LocalDown.prototype._del = function (key, options, callback) { 46 | const self = this; 47 | self.store._del(key, options, callback); 48 | }; 49 | 50 | LocalDown.prototype._clear = function (options, callback) { 51 | this.store._clear(options, callback); 52 | }; 53 | 54 | LocalDown.prototype._batch = function (array, options, callback) { 55 | this.store._batch(array, options, callback); 56 | }; 57 | 58 | LocalDown.prototype._iterator = function (options) { 59 | return this.store._iterator(options); 60 | }; 61 | 62 | module.exports = LocalDown; 63 | -------------------------------------------------------------------------------- /packages/down/src/test/index.js: -------------------------------------------------------------------------------- 1 | (async () => { 2 | await require('./copy'); 3 | await require('./simulate'); 4 | await require('./local'); 5 | })(); 6 | -------------------------------------------------------------------------------- /packages/down/src/test/local.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const database = require('@fuel-js/database'); 3 | const Key = require('@fuel-js/key'); 4 | const memdown = require('memdown'); 5 | const leveldown = require('leveldown'); 6 | const copydown = require('../copy'); 7 | const localdown = require('../local'); 8 | 9 | let index = 0; 10 | const schema = { 11 | test: Key(index++, 'uint32 blockNumber'), 12 | unfinalized: Key(index++, 'uint32 blockNumber', 'bytes32 hash'), 13 | sort: Key(index++, 'uint32 blockNumber', 'uint32 token', 'bytes32 hash'), 14 | }; 15 | 16 | module.exports = test('localdown', async t => { 17 | const checkEmpty = database(localdown()); 18 | t.ok(checkEmpty.supports.local, 'empty supports local'); 19 | 20 | const local2 = leveldown('.testdb3'); 21 | const remote = leveldown('.testdb2'); 22 | const localDOWN = localdown(local2); 23 | const db = database(copydown(remote, localDOWN)); 24 | const db2 = database(localDOWN); 25 | t.ok(db2.supports.local, 'supports local'); 26 | 27 | const mem1 = memdown(); 28 | // const mem2 = memdown(); 29 | 30 | const checkOpen = database(localdown(local2)); 31 | t.ok(checkOpen.supports.local, 'local'); 32 | 33 | const closeIfOpen = database(localdown(mem1)); 34 | const closeIfOpen2 = database(localdown(mem1)); 35 | await closeIfOpen.close(); 36 | await closeIfOpen2.close(); 37 | t.ok(closeIfOpen.status !== 'open', 'close if open'); 38 | t.ok(closeIfOpen2.status !== 'open', 'close if open'); 39 | 40 | const localPutDB = database(localdown(memdown())); 41 | await localPutDB.put([schema.test, 42300], '0xaa', 'put local'); 42 | t.equal(await localPutDB.get([schema.test, 42300]), '0xaa', 'local get'); 43 | await localPutDB.del([schema.test, 42300]); 44 | await t.catch(localPutDB.get([schema.test, 42300]), 'local del correct'); 45 | 46 | await localPutDB.batch([{ 47 | type: 'put', 48 | key: '0xaa', 49 | value: '0xbb', 50 | }, { 51 | type: 'del', 52 | key: '0xaa', 53 | }, { 54 | type: 'put', 55 | key: '0xcc', 56 | value: '0xbb', 57 | }]); 58 | await t.catch(localPutDB.get('0xaa'), 'local del correct'); 59 | t.equal(await localPutDB.get('0xcc'), '0xbb', 'local get'); 60 | 61 | await db.clear(); 62 | 63 | t.equal(typeof db.supports.local, 'object', 'local'); 64 | t.equal(typeof db.supports.remote, 'object', 'remote'); 65 | 66 | await db.put([schema.test, 42300], '0xaa', 'put'); 67 | 68 | t.equal(await db.get([schema.test, 42300]), '0xaa', 'db get'); 69 | t.equal(await db.get([schema.test, 42300], { remote: true }), 70 | '0xaa', 'remote get'); 71 | t.equal(await db.get([schema.test, 42300], { local: true }), 72 | '0xaa', 'local get'); 73 | 74 | await db.del([schema.test, 42300], { remote: true }); 75 | 76 | t.equal(await db.get([schema.test, 42300], { local: true }), 77 | '0xaa', 'local get'); 78 | await t.catch(db.get([schema.test, 42300], { remote: true }), 'remote get'); 79 | 80 | await db.put([schema.sort, 0, '0x0a', utils.emptyBytes32], '0xaa'); 81 | await db.put([schema.sort, 0, '0x0c', utils.emptyBytes32], '0x0c'); 82 | await db.put([schema.sort, 3, '0x0e', utils.emptyBytes32], '0x0e'); 83 | await db.put([schema.sort, 0, '0x0a', utils.keccak256('0xbb')], '0xaa'); 84 | 85 | await db.close(); 86 | }); 87 | -------------------------------------------------------------------------------- /packages/down/src/test/move.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const database = require('@fuel-js/database'); 3 | const memdown = require('memdown'); 4 | const streamToArray = require('stream-to-array'); 5 | const copydown = require('../copy'); 6 | 7 | // will copy the localdb to the remote db 8 | async function copy(config = {}) { 9 | try { 10 | await config.db.clear({ 11 | remote: true, 12 | }); 13 | 14 | let loop = true; 15 | let lastKey = null; 16 | 17 | while (loop) { 18 | const entries = await streamToArray(config.db.createReadStream({ 19 | ...(lastKey ? { gt: lastKey } : {}), 20 | limit: 3500, 21 | local: true, 22 | })); 23 | 24 | // stop here.. 25 | if (!entries.length) { 26 | loop = false; 27 | return; 28 | } 29 | 30 | // last key out 31 | lastKey = utils.RLP.decode(entries[entries.length - 1].key); 32 | 33 | // remote put these into the db 34 | await config.db.batch(entries.map(entry => ({ 35 | type: 'put', 36 | key: utils.RLP.encode(utils.RLP.decode(entry.key)), 37 | value: entry.value, 38 | })), { remote: true }); 39 | } 40 | } catch (copyError) { 41 | throw new utils.ByPassError(copyError); 42 | } 43 | } 44 | 45 | module.exports = test('copy', async t => { 46 | const remote = memdown(); 47 | const local = memdown(); 48 | const db = database(copydown(remote, local)); 49 | const localdb = database(local); 50 | const remotedb = database(remote); 51 | 52 | let localLength = (await streamToArray(localdb.createReadStream())).length; 53 | let remoteLength = (await streamToArray(remotedb.createReadStream())).length; 54 | 55 | t.equal(localLength, 0, 'local length'); 56 | t.equal(remoteLength, 0, 'remote length'); 57 | 58 | await copy({ db }); 59 | 60 | for (let i = 0; i < 20000; i++) { 61 | await localdb.put(['0xaa', '0xbbbb', utils.hexlify(i)], '0xaabb'); 62 | } 63 | 64 | localLength = (await streamToArray(localdb.createReadStream())).length; 65 | remoteLength = (await streamToArray(remotedb.createReadStream())).length; 66 | 67 | t.equal(localLength, 20000, 'local length'); 68 | t.equal(remoteLength, 0, 'remote length'); 69 | 70 | await copy({ db }); 71 | 72 | localLength = (await streamToArray(localdb.createReadStream())).length; 73 | remoteLength = (await streamToArray(remotedb.createReadStream())).length; 74 | 75 | t.equal(localLength, 20000, 'local length'); 76 | t.equal(remoteLength, 20000, 'remote length'); 77 | 78 | for (let i = 0; i < 20000; i++) { 79 | t.equal('0xaabb', await remotedb.get(['0xaa', '0xbbbb', utils.hexlify(i)])); 80 | } 81 | 82 | await db.close(); 83 | }); 84 | -------------------------------------------------------------------------------- /packages/encoding/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | }, 14 | parserOptions: { 15 | ecmaVersion: 2018, 16 | }, 17 | rules: { 18 | 'no-restricted-syntax': ['off'], 19 | 'no-plusplus': ['off'], 20 | 'arrow-parens': ['off'], 21 | 'import/no-extraneous-dependencies': ['error', { devDependencies: ['./src/test/*'] }], 22 | camelcase: ['off'], 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /packages/encoding/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nyc_output -------------------------------------------------------------------------------- /packages/encoding/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | .nyc_output 3 | .eslinterc.js -------------------------------------------------------------------------------- /packages/encoding/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/encoding 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/encoding/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/encoding", 3 | "version": "0.1.1", 4 | "description": "The common leveldown encoding utilities for fuel.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "nyc node src/test", 8 | "lint": "eslint src/index.js", 9 | "prepublish": "npm test && npm run lint" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/fuellabs/fuel-js.git" 14 | }, 15 | "publishConfig": { 16 | "access": "public" 17 | }, 18 | "keywords": [ 19 | "fuel", 20 | "js", 21 | "interface" 22 | ], 23 | "author": "Nick Dodson ", 24 | "license": "Apache-2.0", 25 | "bugs": { 26 | "url": "https://github.com/fuellabs/fuel-js/issues" 27 | }, 28 | "homepage": "https://github.com/fuellabs/fuel-js#readme", 29 | "devDependencies": { 30 | "@fuel-js/environment": "^0.1.0", 31 | "@fuel-js/interface": "^0.1.0", 32 | "@fuel-js/struct": "^0.1.0", 33 | "leveldown": "^5.6.0", 34 | "memdown": "^5.1.0", 35 | "stream-to-array": "^2.3.0", 36 | "eslint": "^7.6.0", 37 | "nyc": "^15.1.0", 38 | "eslint-config-airbnb-base": "^14.2.0", 39 | "eslint-plugin-import": "^2.21.2" 40 | }, 41 | "dependencies": { 42 | "@fuel-js/utils": "^0.1.0" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/encoding/src/index.js: -------------------------------------------------------------------------------- 1 | const utils = require('@fuel-js/utils'); 2 | 3 | // @description encoding-down options 4 | const encoding = { 5 | keyEncoding: { 6 | type: 'rlp', 7 | encode: (v, opts = {}) => { 8 | if (opts.bypassEncoding) return v; 9 | if (Array.isArray(v)) { 10 | // eslint-disable-next-line 11 | if (v[0]._interfaceKey) { 12 | return Buffer.from(utils.RLP.encode(v[0].encode(v.slice(1))).slice(2), 'hex'); 13 | } 14 | return Buffer.from(utils.RLP.encode(v).slice(2), 'hex'); 15 | } 16 | return Buffer.from(v.slice(2), 'hex'); 17 | }, 18 | // eslint-disable-next-line 19 | decode: v => '0x' + v.toString('hex'), 20 | buffer: true, 21 | }, 22 | valueEncoding: { 23 | type: 'rlp', 24 | encode: (v, opts = {}) => { 25 | if (opts.bypassEncoding) return v; 26 | // eslint-disable-next-line 27 | if (v._isStruct === true) return Buffer.from(v.encodeRLP().slice(2), 'hex'); 28 | return Buffer.from(utils.RLP.encode(v).slice(2), 'hex'); 29 | }, 30 | decode: v => { 31 | try { 32 | // eslint-disable-next-line 33 | return utils.RLP.decode('0x' + v.toString('hex')); 34 | } catch (error) { 35 | // eslint-disable-next-line 36 | return '0x' + v.toString('hex'); 37 | } 38 | }, 39 | buffer: true, 40 | }, 41 | }; 42 | 43 | module.exports = encoding; 44 | -------------------------------------------------------------------------------- /packages/encoding/src/test/index.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const schema = require('@fuel-js/interface'); 3 | const { struct } = require('@fuel-js/struct'); 4 | const encoding = require('../index'); 5 | 6 | module.exports = test('encoding', async t => { 7 | 8 | t.ok(encoding, 'available'); 9 | t.throws(() => encoding.keyEncoding.encode(), 'empty'); 10 | t.throws(() => encoding.keyEncoding.decode(), 'empty'); 11 | t.throws(() => encoding.valueEncoding.encode(), 'empty'); 12 | t.throws(() => encoding.valueEncoding.decode(), 'empty'); 13 | 14 | t.equal(encoding.keyEncoding.encode(0, { bypassEncoding: true }), 0, 'bypass encode'); 15 | t.equal(encoding.keyEncoding.encode(['0xaa']), 16 | Buffer.from(utils.RLP.encode(['0xaa']).slice(2), 'hex'), 'array hex'); 17 | 18 | const complexKey = [schema.db.balance, utils.emptyAddress, '0x01' ]; 19 | const complexKeyRLP = utils.RLP.encode(schema.db.balance.encode(complexKey.slice(1))); 20 | t.equal(encoding.keyEncoding.encode(complexKey), 21 | Buffer.from(complexKeyRLP.slice(2), 'hex'), 'array hex'); 22 | 23 | t.equal(encoding.keyEncoding.decode(Buffer.from('aa', 'hex')), '0xaa', 'decode'); 24 | 25 | t.equal(encoding.valueEncoding.encode(0, { bypassEncoding: true }), 26 | 0, 'bypass decode'); 27 | const TestStruct = struct(`uint256 value`); 28 | const testStruct = new TestStruct({ value: 0 }); 29 | t.equal(encoding.valueEncoding.encode(testStruct), 30 | Buffer.from(testStruct.encodeRLP().slice(2), 'hex'), 'bypass decode'); 31 | t.equal(encoding.valueEncoding.encode('0xaa'), 32 | Buffer.from(utils.RLP.encode('0xaa').slice(2), 'hex'), 'bypass decode'); 33 | t.equal(encoding.valueEncoding.decode(Buffer.from(utils.RLP.encode('0xaa').slice(2), 'hex')), 34 | '0xaa', 'bypass decode'); 35 | 36 | }); 37 | -------------------------------------------------------------------------------- /packages/environment/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | -------------------------------------------------------------------------------- /packages/environment/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/environment 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/environment/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/environment", 3 | "version": "0.1.6", 4 | "description": "Common Fuel testing and deployent utils.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "node src/test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/fuellabs/fuel-js.git" 12 | }, 13 | "publishConfig": { 14 | "access": "public" 15 | }, 16 | "keywords": [ 17 | "fuel", 18 | "js", 19 | "interface" 20 | ], 21 | "author": "Nick Dodson ", 22 | "license": "Apache-2.0", 23 | "bugs": { 24 | "url": "https://github.com/fuellabs/fuel-js/issues" 25 | }, 26 | "homepage": "https://github.com/fuellabs/fuel-js#readme", 27 | "dependencies": { 28 | "@fuel-js/utils": "^0.1.0", 29 | "bn.js": "^5.1.1", 30 | "ethers": "^4.0.47", 31 | "ganache-core": "2.10.2", 32 | "solc": "0.6.6", 33 | "yulp": "^0.2.3", 34 | "zora": "^3.1.8", 35 | "fs-readfile-promise": "^3.0.1", 36 | "write": "^2.0.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/environment/src/compile.js: -------------------------------------------------------------------------------- 1 | const yulp = require('yulp'); 2 | const solc = require('solc'); 3 | const readFile = require('fs-readfile-promise'); 4 | const write = require('write'); 5 | const fs = require('fs'); 6 | 7 | // Parse and Compile 8 | async function compile(config) { 9 | try { 10 | let yulpResult = null; 11 | let yulpError = null; 12 | 13 | try { 14 | yulpResult = yulp.compile(await readFile(config.in, 'utf8'), config.base ? fs : null, config.base); 15 | } catch (yulpErrors) { 16 | console.error(yulpErrors); 17 | throw new Error(yulpErrors); 18 | } 19 | 20 | console.log('yul+ compiled'); 21 | 22 | let output = JSON.parse(solc.compile(JSON.stringify({ 23 | "language": "Yul", 24 | "sources": { "Target.yul": { "content": yulp.print(yulpResult.results) } }, 25 | "settings": { 26 | "outputSelection": { "*": { "*": ["*"], "": [ "*" ] } }, 27 | "optimizer": { 28 | "enabled": true, 29 | "runs": 0, 30 | "details": { 31 | "yul": true 32 | } 33 | } 34 | } 35 | }))); 36 | 37 | // is errors in compiling 38 | const isErrors = (output.errors || []) 39 | .filter(v => v.type !== 'Warning').length; 40 | 41 | // Is errors 42 | if (isErrors || yulpError) { 43 | console.error('Compiling errors', output.errors); 44 | 45 | process.exit(); 46 | return; 47 | } 48 | 49 | // Contract Code 50 | const bytecode = '0x' + output.contracts['Target.yul'][config.object] 51 | .evm.bytecode.object; 52 | 53 | const result = { 54 | bytecode, 55 | abi: yulpResult.signatures.map(v => v.abi.slice(4, -1)) 56 | .concat(yulpResult.topics.map(v => v.abi.slice(6, -1))), 57 | errors: yulpResult.errors, 58 | // yul: yulp.print(yulpResult.results), 59 | }; 60 | 61 | // Output as a file 62 | if (config.out) { 63 | // should be a json file 64 | let outin = {}; 65 | 66 | try { 67 | outin = JSON.parse(await readFile(config.out, 'utf8')); 68 | } catch (err) {} 69 | 70 | await write(config.out, JSON.stringify({ ...outin, ...result }, null, 2)); 71 | await write(config.out + '.yul', yulp.print(yulpResult.results)); 72 | } 73 | 74 | // Contract Bytecode 75 | return result; 76 | } catch (error) { 77 | console.error('Compiling error while building Fuel bytecode', error); 78 | process.exit(); 79 | } 80 | } 81 | 82 | // Export methods 83 | module.exports = compile; 84 | -------------------------------------------------------------------------------- /packages/environment/src/deploy.js: -------------------------------------------------------------------------------- 1 | // @description node based deployment process 2 | const { Wallet, ContractFactory, providers } = require('ethers'); 3 | const utils = require('@fuel-js/utils'); 4 | 5 | async function deploy({ privateKey, chainId, providerURL, gasLimit, methods, bytecode }) { 6 | try { 7 | const wallet = new Wallet(privateKey, new providers.JsonRpcProvider(providerURL)); 8 | const factory = new ContractFactory(methods, bytecode, wallet); 9 | 10 | const contract = await factory.deploy(wallet.address, { 11 | gasLimit: gasLimit || 4000000, 12 | }); 13 | 14 | return await contract.deployed(); 15 | } catch (error) { 16 | throw new utils.ByPassError(error); 17 | } 18 | } 19 | 20 | module.exports = deploy; 21 | -------------------------------------------------------------------------------- /packages/environment/src/test/index.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('../index'); 2 | 3 | (async () => { 4 | 5 | await test('environment', async t => { 6 | 7 | await t.ok(t.getOverrides()); 8 | 9 | t.setOverrides({ 10 | gasPrice: '0xaabbaa', 11 | }); 12 | 13 | await t.ok(t.getOverrides()); 14 | 15 | await t.ok(t.getProvider()); 16 | 17 | await t.equalBig(0, 0); 18 | 19 | await t.equalBig(0, utils.bigNumberify(0)); 20 | 21 | await t.ok('ok'); 22 | 23 | await t.equalBig(45, utils.bigNumberify(45)); 24 | 25 | await t.equalHex('0xaa', '0xAA'); 26 | 27 | await t.equalBig(0, await t.getProvider().getBlockNumber()); 28 | 29 | }); 30 | 31 | })(); 32 | -------------------------------------------------------------------------------- /packages/gasprice/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | }, 14 | parserOptions: { 15 | ecmaVersion: 2018, 16 | }, 17 | rules: { 18 | 'no-restricted-syntax': ['off'], 19 | 'no-plusplus': ['off'], 20 | 'arrow-parens': ['off'], 21 | 'import/no-extraneous-dependencies': ['error', { devDependencies: ['./src/test/*'] }], 22 | camelcase: ['off'], 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /packages/gasprice/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/gasprice 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/gasprice/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/gasprice", 3 | "version": "0.1.1", 4 | "description": "Sample transactions from recent Ethereum blocks to determine average fast/safe/network/medium/low gas prices.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "nyc node src/test", 8 | "lint": "eslint src/index.js", 9 | "prepublish": "npm test && npm run lint" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/fuellabs/fuel-js.git" 14 | }, 15 | "publishConfig": { 16 | "access": "public" 17 | }, 18 | "keywords": [ 19 | "fuel", 20 | "js", 21 | "interface" 22 | ], 23 | "author": "Nick Dodson ", 24 | "license": "Apache-2.0", 25 | "bugs": { 26 | "url": "https://github.com/fuellabs/fuel-js/issues" 27 | }, 28 | "homepage": "https://github.com/fuellabs/fuel-js#readme", 29 | "devDependencies": { 30 | "ethers": "^4.0.47", 31 | "@fuel-js/environment": "^0.1.0", 32 | "eslint": "^7.6.0", 33 | "nyc": "^15.1.0", 34 | "eslint-config-airbnb-base": "^14.2.0", 35 | "eslint": "^7.2.0", 36 | "eslint-plugin-import": "^2.21.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/gasprice/src/index.js: -------------------------------------------------------------------------------- 1 | function sample(arr) { 2 | return arr[Math.floor(Math.random() * arr.length)]; 3 | } 4 | 5 | function median(array) { 6 | const items = [...array].sort((a, b) => a.sub(b)); 7 | const mid = items.length / 2; 8 | return mid % 1 9 | ? items[mid - 0.5] 10 | : (items[mid - 1].add(items[mid])).div(2); 11 | } 12 | 13 | // gasPrice, use an ethers provider object 14 | // and it will do the rest, opts { sampleSize, sampleTarget } 15 | async function gasPrice(provider = {}, opts = {}) { 16 | try { 17 | let transactionHashes = []; 18 | const gasPrices = []; 19 | const height = await provider.getBlockNumber(); 20 | 21 | // make block promises, this can eventually be done in parallel 22 | for (let blockNumber = height; 23 | (blockNumber > 0 && transactionHashes.length < (opts.sampleTarget || 15)); 24 | blockNumber--) { 25 | // eslint-disable-next-line 26 | const block = await provider.getBlock(blockNumber); 27 | 28 | // do 10 sample attempts 29 | for (let i = 0; i < (opts.sampleSize || 5); i++) { 30 | if (block.transactions && (block.transactions || {}).length > 0) { 31 | const randomSample = sample(block.transactions); 32 | 33 | if (randomSample) { 34 | transactionHashes.push(randomSample); 35 | } 36 | } 37 | } 38 | 39 | // remove deuplicates 40 | transactionHashes = [...new Set(transactionHashes)]; 41 | } 42 | 43 | // transactionPrices 44 | const transactionPrices = []; 45 | 46 | // go through transaction hashes 47 | for (const transactionHash of transactionHashes) { 48 | if (transactionHash) { 49 | transactionPrices.push(provider.getTransaction(transactionHash)); 50 | } 51 | } 52 | 53 | // grab all prices in parallel 54 | (await Promise.all(transactionPrices)).forEach(transaction => { 55 | gasPrices.push(transaction.gasPrice); 56 | }); 57 | 58 | // get network gas price 59 | const network = await provider.getGasPrice(); 60 | 61 | // add the network price in there as well 62 | gasPrices.push(network); 63 | 64 | // median 65 | const medianPrice = median(gasPrices); 66 | 67 | // sorted 68 | const sorted = gasPrices.sort((a, b) => a.sub(b)); 69 | 70 | return { 71 | network, 72 | fast: sorted.slice(-1)[0], 73 | safe: [network, medianPrice].sort((a, b) => a.sub(b)).slice(-1)[0], 74 | median: medianPrice, 75 | low: sorted[0], 76 | }; 77 | } catch (error) { 78 | throw new Error(error.message); 79 | } 80 | } 81 | 82 | module.exports = gasPrice; 83 | -------------------------------------------------------------------------------- /packages/gasprice/src/test/index.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const ethers = require('ethers'); 3 | const gasPrice = require('../index'); 4 | 5 | module.exports = test('gasPrice', async t => { 6 | // this produce will throw an error 7 | const fakeProducer = { 8 | getBlockNumber: () => { throw new Error('yammed'); }, 9 | }; 10 | await t.catch(gasPrice(fakeProducer), 'throw check'); 11 | await t.catch(gasPrice(), 'no producer checks'); 12 | 13 | // attempt with no txs 14 | const preTransactionPrices = await gasPrice(t.getProvider()); 15 | console.log('network', utils.formatUnits(preTransactionPrices.network, 'gwei')); 16 | console.log('fast', utils.formatUnits(preTransactionPrices.fast, 'gwei')); 17 | console.log('safe', utils.formatUnits(preTransactionPrices.safe, 'gwei')); 18 | console.log('median', utils.formatUnits(preTransactionPrices.median, 'gwei')); 19 | console.log('low', utils.formatUnits(preTransactionPrices.low, 'gwei')); 20 | 21 | // simulate some block production 22 | for (var i = 0; i < 20; i++) { 23 | await t.getWallets()[0].sendTransaction({ 24 | ...t.getOverrides(), 25 | to: utils.emptyAddress, 26 | value: 4500, 27 | }); 28 | await t.getWallets()[0].sendTransaction({ 29 | ...t.getOverrides(), 30 | to: utils.emptyAddress, 31 | value: 4500, 32 | }); 33 | await t.increaseBlock(2); 34 | } 35 | 36 | // attempt with options 37 | const prices = await gasPrice(t.getProvider({ sampleSize: 3, sampleTarget: 10 })); 38 | console.log('network', utils.formatUnits(prices.network, 'gwei')); 39 | console.log('fast', utils.formatUnits(prices.fast, 'gwei')); 40 | console.log('safe', utils.formatUnits(prices.safe, 'gwei')); 41 | console.log('median', utils.formatUnits(prices.median, 'gwei')); 42 | console.log('low', utils.formatUnits(prices.low, 'gwei')); 43 | 44 | // attempt to get gas price with transactions and no options 45 | const localPrices = await gasPrice(t.getProvider()); 46 | console.log('network', utils.formatUnits(localPrices.network, 'gwei')); 47 | console.log('fast', utils.formatUnits(localPrices.fast, 'gwei')); 48 | console.log('safe', utils.formatUnits(localPrices.safe, 'gwei')); 49 | console.log('median', utils.formatUnits(localPrices.median, 'gwei')); 50 | console.log('low', utils.formatUnits(localPrices.low, 'gwei')); 51 | }); 52 | -------------------------------------------------------------------------------- /packages/interface/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | }, 14 | parserOptions: { 15 | ecmaVersion: 2018, 16 | }, 17 | rules: { 18 | 'no-plusplus': ['off'], 19 | 'arrow-parens': ['off'], 20 | 'import/no-extraneous-dependencies': ['error', { devDependencies: ['./src/test/*'] }], 21 | camelcase: ['off'], 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /packages/interface/.npmignore: -------------------------------------------------------------------------------- 1 | .nyc_output 2 | .eslintrc.js -------------------------------------------------------------------------------- /packages/interface/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/interface 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/interface/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/interface", 3 | "version": "0.2.3", 4 | "description": "Fuel protocol interfaces.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "nyc node src/test", 8 | "lint": "eslint src/index.js", 9 | "prepublish": "npm test && npm run lint" 10 | }, 11 | "publishConfig": { 12 | "access": "public" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/fuellabs/fuel-js.git" 17 | }, 18 | "keywords": [ 19 | "fuel", 20 | "js", 21 | "protocol" 22 | ], 23 | "author": "Nick Dodson ", 24 | "license": "Apache-2.0", 25 | "bugs": { 26 | "url": "https://github.com/fuellabs/fuel-js/issues" 27 | }, 28 | "devDependencies": { 29 | "@fuel-js/environment": "^0.1.0", 30 | "eslint": "^7.6.0", 31 | "nyc": "^15.1.0", 32 | "eslint-config-airbnb-base": "^14.2.0", 33 | "eslint-plugin-import": "^2.21.2" 34 | }, 35 | "dependencies": { 36 | "@fuel-js/key": "^0.1.2" 37 | }, 38 | "homepage": "https://github.com/fuellabs/fuel-js#readme" 39 | } 40 | -------------------------------------------------------------------------------- /packages/interface/src/test/index.js: -------------------------------------------------------------------------------- 1 | const { test } = require('@fuel-js/environment'); 2 | const { db } = require('../index'); 3 | 4 | module.exports = test('interface', async t => { 5 | 6 | t.ok(db, 'available'); 7 | 8 | }); 9 | -------------------------------------------------------------------------------- /packages/key/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | }, 14 | parserOptions: { 15 | ecmaVersion: 2018, 16 | }, 17 | rules: { 18 | 'arrow-parens': ['off'], 19 | 'import/no-extraneous-dependencies': ['error', { devDependencies: ['./src/test/*'] }], 20 | camelcase: ['off'], 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/key/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/key/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | .nyc_output -------------------------------------------------------------------------------- /packages/key/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/key 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/key/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/key", 3 | "version": "0.1.2", 4 | "description": "The Fuel-js databasing key.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "nyc node src/test", 8 | "lint": "eslint src/index.js", 9 | "prepublish": "npm test && npm run lint" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/fuellabs/fuel-js.git" 14 | }, 15 | "publishConfig": { 16 | "access": "public" 17 | }, 18 | "keywords": [ 19 | "fuel", 20 | "js", 21 | "interface" 22 | ], 23 | "author": "Nick Dodson ", 24 | "license": "Apache-2.0", 25 | "bugs": { 26 | "url": "https://github.com/fuellabs/fuel-js/issues" 27 | }, 28 | "homepage": "https://github.com/fuellabs/fuel-js#readme", 29 | "devDependencies": { 30 | "@fuel-js/environment": "^0.1.0", 31 | "eslint": "^7.6.0", 32 | "nyc": "^15.1.0", 33 | "eslint-config-airbnb-base": "^14.2.0", 34 | "eslint": "^7.2.0", 35 | "eslint-plugin-import": "^2.21.2" 36 | }, 37 | "dependencies": { 38 | "@fuel-js/utils": "^0.1.0", 39 | "@fuel-js/rolled": "^0.1.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/key/src/index.js: -------------------------------------------------------------------------------- 1 | const coder = require('@fuel-js/rolled'); 2 | const utils = require('@fuel-js/utils'); 3 | 4 | function Key(key, ...arr) { 5 | if (!(this instanceof Key)) return new Key(key, ...arr); 6 | const self = this; 7 | 8 | // eslint-disable-next-line 9 | self._interfaceKey = true; 10 | self.key = key; 11 | 12 | self.sizes = (['uint8 index'].concat(arr)) 13 | .map(type => coder.parse(utils.parseParamType(type)).size); 14 | 15 | self.encode = values => { 16 | const keyAndValues = [self.key].concat(values); 17 | utils.assert(keyAndValues.length === self.sizes.length, 'invalid key length'); 18 | return keyAndValues.map((value, i) => { 19 | const hexed = utils.hexlify(value); 20 | 21 | // check overflow 22 | if (((hexed.length - 2) / 2) > self.sizes[i]) { 23 | utils.assert(0, `key-length-overflow-${((hexed.length - 2) / 2)}-${self.sizes[i]}-${hexed}`); 24 | } 25 | 26 | return utils.hexZeroPad(hexed, self.sizes[i]); 27 | }); 28 | }; 29 | } 30 | 31 | module.exports = Key; 32 | -------------------------------------------------------------------------------- /packages/key/src/test/index.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const Key = require('../index'); 3 | 4 | module.exports = test('key', async t => { 5 | 6 | const key = Key(1, 'uint8 type', 'uint32 num'); 7 | const encoded = key.encode(['0x33', 22]); 8 | 9 | t.equalBig(encoded[0], '0x01'); 10 | t.equalBig(encoded[1], '0x33'); 11 | t.equalHex(encoded[2], '0x00000016'); 12 | 13 | const key2 = Key(4, 'uint8 type', 'bytes32 num'); 14 | const encoded2 = key2.encode(['0x33', utils.bigNumberify(22)]); 15 | 16 | t.equalBig(encoded2[0], '0x04'); 17 | t.equalBig(encoded2[1], '0x33'); 18 | t.equalHex(encoded2[2], '0x0000000000000000000000000000000000000000000000000000000000000016'); 19 | 20 | const key3 = Key(4, 'uint8 type'); 21 | t.throws(() => key3.encode(['0x3333']), 'key-length-overflow'); 22 | }); 23 | -------------------------------------------------------------------------------- /packages/logic/.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .fueldb 4 | .testdb 5 | .fueldb-cache 6 | .fueldb-goerli 7 | .fueldb-rinkeby 8 | .fueldb-ropsten 9 | .fueldb-mainnet 10 | .fueldb-kovan 11 | .fueldb-unspecified 12 | -------------------------------------------------------------------------------- /packages/logic/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | .fueldb-unspecified 3 | .nyc_output 4 | .out -------------------------------------------------------------------------------- /packages/logic/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/logic 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/logic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/logic", 3 | "version": "0.6.7", 4 | "description": "Essential fuel-js logic for clients.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "rm -rf .fueldb-cache && nyc node src/test" 8 | }, 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/fuellabs/fuel-js.git" 15 | }, 16 | "keywords": [ 17 | "fuel", 18 | "js", 19 | "fuel" 20 | ], 21 | "author": "Nick Dodson ", 22 | "license": "Apache-2.0", 23 | "bugs": { 24 | "url": "https://github.com/fuellabs/fuel-js/issues" 25 | }, 26 | "dependencies": { 27 | "@fuel-js/interface": "^0.2.3", 28 | "@fuel-js/protocol": "^0.4.6", 29 | "@fuel-js/batch": "^0.1.6", 30 | "@fuel-js/database": "^0.1.0", 31 | "@fuel-js/struct": "^0.1.7", 32 | "@fuel-js/down": "^0.1.5", 33 | "@fuel-js/utils": "^0.1.4", 34 | "ethers": "^4.0.47", 35 | "memdown": "^5.1.0", 36 | "stream-to-array": "^2.3.0" 37 | }, 38 | "devDependencies": { 39 | "@fuel-js/contracts": "^0.1.8", 40 | "@fuel-js/environment": "0.1.4", 41 | "eslint": "^7.6.0", 42 | "write": "^2.0.0", 43 | "nyc": "^15.1.0", 44 | "eslint-config-airbnb-base": "^14.2.0", 45 | "ethers": "^4.0.47", 46 | "elliptic": ">=6.5.4", 47 | "eslint-plugin-import": "^2.21.2" 48 | }, 49 | "homepage": "https://github.com/fuellabs/fuel-js#readme" 50 | } 51 | -------------------------------------------------------------------------------- /packages/logic/src/account.js: -------------------------------------------------------------------------------- 1 | // get all account inputs from db 2 | const utils = require('@fuel-js/utils'); 3 | const interface = require('@fuel-js/interface'); 4 | const streamToArray = require('stream-to-array'); 5 | 6 | async function account( 7 | owner = '0x', 8 | timeMin = '0x00', 9 | timeMax = '0xFFFFFFFFFFFFFFFF', 10 | token = null, 11 | proof = false, 12 | config = {}) { 13 | try { 14 | const token_min = token ? token : '0x00'; 15 | const token_max = token ? token : '0xFFFFFFFF'; 16 | 17 | const inputs = (await streamToArray(config.db.createReadStream({ 18 | gte: interface.db.owner.encode([ owner, token_min, timeMin, 0, 0, utils.min_num ]), 19 | lte: interface.db.owner.encode([ owner, token_max, timeMax, 4, 1, utils.max_num ]), 20 | limit: config.account_limit || 1000, 21 | remote: true, 22 | }))) 23 | .map(data => { 24 | // old api 25 | if (!proof) { 26 | // owner: Key(index++, 'address owner', 'uint8 type', 'uint8 isWithdrawal', 'uint64 timestamp', 'bytes32 inputHash'), 27 | // owner: Key(index++, 'address owner', 'uint32 token', 'uint64 timestamp', 'uint8 type', 'uint8 isWithdrawal', 'bytes32 inputHash'), 28 | // return utils.RLP.decode(data.key); 29 | const decoded = utils.RLP.decode(data.key); 30 | return ['0x00', decoded[1], decoded[4], decoded[5], decoded[3], decoded[6]]; 31 | } 32 | 33 | // if not proof 34 | return [ 35 | utils.RLP.decode(data.key), 36 | data.value, 37 | ]; 38 | }); 39 | 40 | return inputs; 41 | } catch (error) { 42 | throw new utils.ByPassError(error); 43 | } 44 | } 45 | 46 | module.exports = account; 47 | -------------------------------------------------------------------------------- /packages/logic/src/assets.js: -------------------------------------------------------------------------------- 1 | // get all account inputs from db 2 | const utils = require('@fuel-js/utils'); 3 | const interface = require('@fuel-js/interface'); 4 | const streamToArray = require('stream-to-array'); 5 | const protocol = require('@fuel-js/protocol'); 6 | 7 | // This would just have to give both balances, but that's okay. 8 | // It simply decides to look at either the mempool or sync balance. 9 | async function assets(owner = '0x', token = '0x', limit = 128, config = {}) { 10 | try { 11 | // Go through all the users balances and grab all of them. 12 | return (await streamToArray(config.db.createReadStream({ 13 | gte: interface.db.balance.encode([ owner, token ]), 14 | lte: interface.db.balance.encode([ owner, '0xFFFFFFFF' ]), 15 | limit: Math.min(parseInt(limit, 10), 128), 16 | remote: true, 17 | }))) 18 | .map(data => { 19 | // each onchain asset 20 | const [_index, _owner, _token] = utils.RLP.decode(data.key); 21 | 22 | // Decode the balance. 23 | const balObject = protocol.addons.Balance(data.value); 24 | 25 | // Balance. 26 | let balance = null; 27 | 28 | // This will return the final balance. 29 | if (balObject.properties.transactionHashId() 30 | .get() !== utils.emptyBytes32) { 31 | balance = balObject.properties.mempoolBalance() 32 | .get(); 33 | } else { 34 | balance = balObject.properties.syncBalance() 35 | .get(); 36 | } 37 | 38 | // return the token and the balance for each asset 39 | return [_token, balance]; 40 | }); 41 | } catch (error) { 42 | throw new utils.ByPassError(error); 43 | } 44 | } 45 | 46 | module.exports = assets; 47 | -------------------------------------------------------------------------------- /packages/logic/src/genesis.js: -------------------------------------------------------------------------------- 1 | const utils = require('@fuel-js/utils'); 2 | 3 | // Block numbers. 4 | const blockNumbers = { 5 | "v1": { 6 | "mainnet": 11787727, 7 | "rinkeby": 8012426 8 | } 9 | }; 10 | 11 | // Return the genesis Ethereum block for this contract. 12 | async function genesis(config = {}) { 13 | try { 14 | let fromBlock = 0; 15 | 16 | // Mainnet patch. 17 | if (config.network.chainId === 1) { 18 | fromBlock = blockNumbers.v1.mainnet; 19 | } 20 | 21 | // Rinkeby patch. 22 | if (config.network.name === 'rinkeby') { 23 | fromBlock = blockNumbers.v1.rinkeby; 24 | } 25 | 26 | // Get logs. 27 | const logs = await config.provider.getLogs({ 28 | fromBlock, 29 | toBlock: 'latest', 30 | address: config.contract.address, 31 | topics: config.contract.filters.BlockCommitted(null, null, null, null, 0, null).topics, 32 | }); 33 | 34 | // Return the genesis block number log. 35 | return logs[0].blockNumber; 36 | } catch (error) { 37 | throw new utils.ByPassError(error); 38 | } 39 | } 40 | 41 | module.exports = genesis; 42 | -------------------------------------------------------------------------------- /packages/logic/src/history.js: -------------------------------------------------------------------------------- 1 | // get all account inputs from db 2 | const utils = require('@fuel-js/utils'); 3 | const interface = require('@fuel-js/interface'); 4 | const batch = require('@fuel-js/batch'); 5 | const streamToArray = require('stream-to-array'); 6 | 7 | // get the transaction history of a specific owner / account, include up to 10 proofs if necssary 8 | async function history(opts = {}, config = {}) { 9 | try { 10 | const { 11 | owner = '0x', 12 | nonce = '0xFFFFFFFF', 13 | timestamp = '0xFFFFFFFFFFFFFFFF', 14 | transactionId = utils.max_num, 15 | limit = 64, 16 | include = false, 17 | } = opts; 18 | 19 | /* 20 | Old settings not in descending. 21 | lte: interface.db.archiveOwner.encode([ owner, timestamp, transactionId ]), 22 | gte: interface.db.archiveOwner.encode([ owner, '0x00', utils.min_num ]), 23 | limit: Math.min(parseInt(limit, 10), 64), 24 | remote: true, 25 | */ 26 | 27 | // Get transactions from range proof up to 64 -- descending order. 28 | let transactions = await streamToArray(config.db.createReadStream({ 29 | gte: interface.db.archiveOwner.encode([ owner, timestamp, transactionId ]), 30 | lte: interface.db.archiveOwner.encode([ owner, '0x00', utils.min_num ]), 31 | limit: Math.min(parseInt(limit, 10), 64), 32 | remote: true, 33 | reverse: true, 34 | })); 35 | 36 | // setup default proofs 37 | let proofs = []; 38 | 39 | // Retrieved [tx hash] => timestamp. 40 | let retrieved = {}; 41 | 42 | // Always take the most recent one. 43 | transactions.forEach(data => { 44 | const [ _index, _owner, _timestamp, _transactionId ] = utils.RLP.decode(data.key); 45 | 46 | // Retrieved and account for mempool timestamp (i.e. zero.). 47 | if (utils.bigNumberify(_timestamp).gt(retrieved[_transactionId] || 0)) { 48 | // set retrieved hash. 49 | retrieved[_transactionId] = _timestamp; 50 | } 51 | }); 52 | 53 | // Filter only the most recent tx. 54 | const filteredTransactions = transactions.filter(data => { 55 | const [ _index, _owner, _timestamp, _transactionId ] = utils.RLP.decode(data.key); 56 | 57 | // Return true if it's the latest tx. 58 | if (retrieved[_transactionId] === _timestamp) { 59 | return true; 60 | } 61 | 62 | // Return false. 63 | return false; 64 | }); 65 | 66 | // include up to 10 tx proofs using batch 1 round trip 67 | try { 68 | if (include && transactions.length) { 69 | proofs = await batch(config.db, filteredTransactions.map(data => { 70 | return [ interface.db.transactionId, data.value ]; 71 | }), { remote: true }); 72 | } 73 | } catch (err) {} 74 | 75 | // return transactions parsed, timestamp, transactionId, proof if available 76 | return filteredTransactions.map((data, index) => { 77 | // parse the key 78 | const [ _index, _owner, _timestamp, _transactionId ] = utils.RLP.decode(data.key); 79 | 80 | // include transaction proof if avaiable 81 | const proof = include && proofs[index] ? [ proofs[index].value ] : []; 82 | 83 | // return timestamp and transactionId 84 | return [ _timestamp, data.value, ...proof ]; 85 | }); 86 | } catch (error) { 87 | throw new utils.ByPassError(error); 88 | } 89 | } 90 | 91 | module.exports = history; 92 | -------------------------------------------------------------------------------- /packages/logic/src/index.js: -------------------------------------------------------------------------------- 1 | const transact = require('./transact'); 2 | const sync = require('./sync'); 3 | const mempool = require('./mempool'); 4 | const balance = require('./balance'); 5 | const genesis = require('./genesis'); 6 | const produce = require('./produce'); 7 | const root = require('./root'); 8 | const assets = require('./assets'); 9 | const profile = require('./profile'); 10 | const transactions = require('./transactions'); 11 | const account = require('./account'); 12 | const withdraw = require('./withdraw'); 13 | const requests = require('./requests'); 14 | 15 | module.exports = { 16 | transact, 17 | sync, 18 | mempool, 19 | balance, 20 | genesis, 21 | produce, 22 | root, 23 | assets, 24 | transactions, 25 | profile, 26 | account, 27 | withdraw, 28 | requests, 29 | }; 30 | -------------------------------------------------------------------------------- /packages/logic/src/mempool.js: -------------------------------------------------------------------------------- 1 | // get data from the mempool 2 | const utils = require('@fuel-js/utils'); 3 | const interface = require('@fuel-js/interface'); 4 | const protocol = require('@fuel-js/protocol'); 5 | const streamToArray = require('stream-to-array'); 6 | 7 | async function mempool(opts = {}, config = {}) { 8 | try { 9 | const { 10 | minTimestamp = 0, 11 | minNonce = '0x00', 12 | minTransactionId = utils.min_num, 13 | maxTimestamp = utils.timestamp(), 14 | maxNonce = '0xFFFFFFFF', 15 | maxTransactionId = utils.max_num, 16 | limit = 1000, 17 | } = opts; 18 | 19 | const transactions = (await streamToArray(config.db.createReadStream({ 20 | gte: interface.db.mempool.encode([ minTimestamp, minNonce, minTransactionId ]), 21 | lte: interface.db.mempool.encode([ maxTimestamp, maxNonce, maxTransactionId ]), 22 | limit, 23 | remote: true, 24 | }))) 25 | .map(data => { 26 | // Timestamp and TransactionId from the key 27 | const [index, timestamp, nonce, transactionHashId] = utils.RLP.decode(data.key); 28 | 29 | // We get the Transaction 30 | return { 31 | timestamp, 32 | transactionHashId, 33 | end: { 34 | timestamp, 35 | nonce, 36 | transactionId: transactionHashId, 37 | }, 38 | transaction: protocol.transaction._Transaction( 39 | data.value, 40 | null, 41 | protocol.addons.Transaction, 42 | ), 43 | }; 44 | }); 45 | 46 | return transactions || []; 47 | } catch (error) { 48 | throw new utils.ByPassError(error); 49 | } 50 | } 51 | 52 | module.exports = mempool; 53 | -------------------------------------------------------------------------------- /packages/logic/src/operatorsToWallets.js: -------------------------------------------------------------------------------- 1 | const utils = require('@fuel-js/utils'); 2 | const ethers = require('ethers'); 3 | 4 | // default root producers 5 | const defaultRootProducers = 8; 6 | 7 | // Convert Seed / Comma Seperated Operators to usable Wallets 8 | function operatorsToWallets(config = {}) { 9 | // Operators specified 10 | utils.assert(config.operators, 'no operators specified'); 11 | 12 | // Is array 13 | if (Array.isArray(config.operators)) { 14 | return config.operators.map(privateKey => { 15 | return new ethers.Wallet(privateKey, config.provider); 16 | }).slice(0, 128); 17 | } 18 | 19 | // Just a single private key 20 | if (config.operators.indexOf(',') === -1 21 | && config.operators.indexOf('0x') === 0) { 22 | return [ 23 | new ethers.Wallet(config.operators, config.provider), 24 | ]; 25 | } 26 | 27 | // Is a comma seperated list 28 | if (config.operators.indexOf(',') === 0) { 29 | return config.operators.trim().split(',').map(privateKey => { 30 | return new ethers.Wallet(privateKey.trim(), config.provider); 31 | }).slice(0, 128); 32 | } 33 | 34 | // Is a mnemonic seed phrase 35 | let wallets = []; 36 | for (var i = 0; i < defaultRootProducers; i++) { 37 | const _wallet = new ethers.Wallet.fromMnemonic( 38 | config.operators, 39 | "m/44'/60'/0'/1/" + i, 40 | ); 41 | wallets.push(_wallet.connect(config.provider)); 42 | } 43 | 44 | // Return wallets 45 | return wallets; 46 | } 47 | 48 | module.exports = operatorsToWallets; -------------------------------------------------------------------------------- /packages/logic/src/profile.js: -------------------------------------------------------------------------------- 1 | const utils = require('@fuel-js/utils'); 2 | const interface = require('@fuel-js/interface'); 3 | const streamToArray = require('stream-to-array'); 4 | const getAssets = require('./assets'); 5 | const getHistory = require('./history'); 6 | 7 | async function getOwnerId(owner = '0x', config = {}) { 8 | try { 9 | return await config.db.get([ interface.db.addressId, owner ]); 10 | } catch (ownerIdError) { 11 | return '0x'; 12 | } 13 | } 14 | 15 | // get user deposits 16 | async function getDeposits(opts = {}, config = {}) { 17 | // stream deposits 18 | const deposits = await streamToArray(config.db.createReadStream({ 19 | lte: interface.db.depositArchive.encode([ opts.owner, opts.token, opts.blockNumber ]), 20 | gte: interface.db.depositArchive.encode([ opts.owner, '0x00', '0x00' ]), 21 | limit: Math.min(parseInt(opts.limit, 10), 64), 22 | remote: true, 23 | })); 24 | 25 | // return deposits data 26 | return deposits.map(data => data.value); 27 | } 28 | 29 | // max address 30 | const maxAddress = `0xffffffffffffffffffffffffffffffffffffffff`; 31 | 32 | function getProfile(opts = {}, config = {}) { 33 | const { 34 | owner = '0x', 35 | token = '0x00', 36 | timestamp = '0xFFFFFFFFFFFFFFFF', 37 | blockNumber = '0xFFFFFFFF', 38 | depositToken = maxAddress, 39 | transactionId = utils.max_num, 40 | include = true, 41 | limit = 64, 42 | } = opts; 43 | 44 | return Promise.all([ 45 | getAssets(owner, token, limit, config), 46 | getHistory({ owner, timestamp, transactionId, include, limit }, config), 47 | getOwnerId(owner, config), 48 | getDeposits({ owner, token: depositToken, blockNumber, limit }, config), 49 | ]); 50 | } 51 | 52 | module.exports = getProfile; 53 | -------------------------------------------------------------------------------- /packages/logic/src/requests.js: -------------------------------------------------------------------------------- 1 | // get all account inputs from db 2 | const utils = require('@fuel-js/utils'); 3 | const interface = require('@fuel-js/interface'); 4 | const streamToArray = require('stream-to-array'); 5 | 6 | async function requests(timeMin = '0x00', 7 | nonceMin = '0x00', 8 | timeMax = '0xFFFFFFFFFFFFFFFF', 9 | config = {}) { 10 | try { 11 | let entries = []; 12 | 13 | const inputs = (await streamToArray(config.db.createReadStream({ 14 | gte: interface.db.faucet.encode([ timeMin, nonceMin ]), 15 | lte: interface.db.faucet.encode([ timeMax, '0xFFFFFFFF' ]), 16 | limit: 8, 17 | remote: true, 18 | }))) 19 | .map(data => { 20 | const decoded = utils.RLP.decode(data.key); 21 | entries.push({ 22 | key: data.key, 23 | timestamp: decoded[1], 24 | nonce: decoded[2], 25 | address: data.value, 26 | }); 27 | }); 28 | 29 | return entries; 30 | } catch (error) { 31 | throw new utils.ByPassError(error); 32 | } 33 | } 34 | 35 | module.exports = requests; 36 | -------------------------------------------------------------------------------- /packages/logic/src/test/config.local.js: -------------------------------------------------------------------------------- 1 | const utils = require('@fuel-js/utils'); 2 | const database = require('@fuel-js/database'); 3 | const { copy } = require('@fuel-js/down'); 4 | const ethers = require('ethers'); 5 | const memdown = require('memdown'); 6 | const { ERC20 } = require('@fuel-js/contracts'); 7 | const write = require('write'); 8 | 9 | // The confirguation object for the Fuel lambdas 10 | function config(opts = process.env, _prefix = 'fuel_v1_', noresolution = false) { 11 | const network = opts.network || opts[_prefix + 'network']; 12 | const resolve = key => noresolution 13 | ? opts[key] 14 | : (opts[_prefix + network + '_' + key] || opts[_prefix + 'default_' + key]); 15 | 16 | // local provider 17 | const provider = opts.provider || ethers.getDefaultProvider(network, { 18 | infura: resolve('infura'), 19 | }); 20 | 21 | // Setup ERC20 contract wrapper. 22 | const erc20 = new ethers.Contract( 23 | utils.emptyAddress, 24 | ERC20.abi, 25 | provider, 26 | ); 27 | 28 | // If testing environment. 29 | let noconsole = null; 30 | if (process.env.FUEL_ENV === 'testing') { 31 | noconsole = { 32 | log: () => {}, 33 | error: console.error, 34 | }; 35 | } 36 | 37 | // return config object 38 | return { 39 | archive: true, 40 | rootLengthTarget: opts.rootLengthTarget, 41 | produce: opts.produce, 42 | minimumTransactionsPerRoot: opts.minimumTransactionsPerRoot || 0, 43 | emit: () => {}, // emmitter for new outputs 44 | console: { 45 | log: () => {}, 46 | error: console.error, 47 | } || console, 48 | erc20: opts.erc20 || erc20, 49 | increaseBlock: opts.increaseBlock, 50 | feeEnforcement: opts.feeEnforcement, 51 | bypassBlockProducer: true, 52 | stopForOverflow: opts.stopForOverflow, 53 | continue: opts.continue, 54 | network: utils.getNetwork(network), 55 | gasPrice: () => Promise.resolve('0x12a05f2000'), // 80 gwei 56 | gas_limit: 4000000, // default gas limit 57 | confirmations: 0, // required block confirmations 58 | block_time: 0, // 13 * 1000, 59 | release: opts.release || false, 60 | db: database(copy(memdown(), memdown())), 61 | proxy: opts.proxy || null, 62 | contract: opts.contract || {}, // new ethers.Contract(opts.contract || v1[network], abi.Fuel, provider), // selected Fuel contract object 63 | provider, // provider object 64 | write, 65 | operators: opts.operators || [], // 0 is main operator, the rest are for root deployment 66 | }; 67 | } 68 | 69 | module.exports = config; 70 | -------------------------------------------------------------------------------- /packages/logic/src/test/defaults.js: -------------------------------------------------------------------------------- 1 | const { utils } = require('@fuel-js/environment'); 2 | 3 | // defaults 4 | const defaults = (producer, bondSize = utils.parseEther('1.0')) => [ 5 | producer, 6 | 20, 7 | 20, 8 | 20, 9 | bondSize, 10 | "Fuel", 11 | "1.1.0", 12 | 1, 13 | utils.emptyBytes32 14 | ]; 15 | 16 | module.exports = defaults; 17 | -------------------------------------------------------------------------------- /packages/logic/src/test/genesis.js: -------------------------------------------------------------------------------- 1 | const { test } = require('@fuel-js/environment'); 2 | const defaults = require('./defaults'); 3 | const config = require('./config.local'); 4 | const genesis = require('../genesis'); 5 | const { Fuel } = require('@fuel-js/contracts'); 6 | 7 | module.exports = test('genesis', async t => { 8 | try { 9 | 10 | // check if genesis function grabs correct starting Fuel block number 11 | const producer = t.getWallets()[0].address; 12 | const contract = await t.deploy(Fuel.abi, Fuel.bytecode, defaults(producer)); 13 | const settings = config({ 14 | network: 'unspecified', 15 | provider: t.getProvider(), 16 | contract, 17 | }); 18 | 19 | const deployBlockNumber = (await contract.deployTransaction.wait()).blockNumber; 20 | 21 | t.ok(deployBlockNumber > 0, 'block real'); 22 | t.equalBig(deployBlockNumber, await genesis(settings), 'block number'); 23 | 24 | } catch (testError) { console.error(testError); } 25 | }); 26 | -------------------------------------------------------------------------------- /packages/logic/src/test/index.js: -------------------------------------------------------------------------------- 1 | (async () => { 2 | 3 | // run all tests 4 | await require('./sync'); 5 | await require('./genesis'); 6 | await require('./mempool'); 7 | await require('./sync'); 8 | await require('./produce'); 9 | await require('./transact'); 10 | await require('./prover'); 11 | await require('./correctness'); 12 | await require('./root-force'); 13 | await require('./third-party-production'); 14 | await require('./in-flight'); 15 | 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/logic/src/test/mempool.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const protocol = require('@fuel-js/protocol'); 3 | const mempool = require('../mempool'); 4 | const config = require('./config.local'); 5 | const interface = require('@fuel-js/interface'); 6 | 7 | module.exports = test('mempool', async t => { 8 | try { 9 | const settings = config({ 10 | network: 'unspecified', 11 | provider: t.getProvider(), 12 | }); 13 | 14 | let middle = null; 15 | 16 | // put transactions into db 17 | for (var tx = 0; tx < 1000; tx++) { 18 | const key = [ 19 | interface.db.mempool, 20 | utils.timestamp(), 21 | utils.hexlify(tx), 22 | utils.randomBytes(32), 23 | ]; 24 | const value = protocol.transaction._Transaction({}); 25 | await settings.db.put(key, value); 26 | 27 | if (tx === 500) { 28 | middle = { key, value }; 29 | } 30 | } 31 | 32 | let txs = await mempool({ 33 | minTimestamp: 0, 34 | minNonce: 0, 35 | minTransactionId: 0, 36 | limit: 0, 37 | }, settings); 38 | 39 | t.equal(txs.length, 0, 'grabbed right amount of items'); 40 | 41 | txs = await mempool({ 42 | minTimestamp: 0, 43 | minNonce: 0, 44 | minTransactionId: 0, 45 | limit: 20, 46 | }, settings); 47 | 48 | t.equal(txs.length, 20, 'grabbed right amount of items'); 49 | 50 | txs = await mempool({ 51 | minTimestamp: 0, 52 | minNonce: 0, 53 | minTransactionId: 0, 54 | limit: 3000, 55 | }, settings); 56 | 57 | t.equal(txs.length, 1000, 'grabbed right amount of items'); 58 | 59 | t.equalBig(txs[500].end.timestamp, middle.key[1], 'timestamp'); 60 | t.equalBig(txs[500].end.nonce, middle.key[2], 'nonce'); 61 | t.equalHex(txs[500].end.transactionId, middle.key[3], 'transactionId'); 62 | 63 | } catch (testError) { console.error(testError); } 64 | }); 65 | -------------------------------------------------------------------------------- /packages/logic/src/test/outputFromMetadata.js: -------------------------------------------------------------------------------- 1 | const protocol = require('@fuel-js/protocol'); 2 | const utils = require('@fuel-js/utils'); 3 | 4 | /// @notice Proof from Metadata and Input. 5 | /// @notice Proof from Metadata and Input. 6 | async function outputFromMetadata({ metadata, config }) { 7 | const block = await protocol.block.BlockHeader.fromLogs( 8 | metadata.properties.blockHeight().get().toNumber(), 9 | config.contract, 10 | ); 11 | 12 | utils.assert(block.properties, 'block-not-found'); 13 | 14 | const rootIndex = metadata.properties.rootIndex().get().toNumber(); 15 | const roots = block.properties.roots().get(); 16 | 17 | // Check the root index isn't invalid. 18 | utils.assert(roots[rootIndex], 'roots-index-overflow'); 19 | 20 | // Get the root from logs. 21 | const rootHash = roots[rootIndex]; 22 | const logs = await config.contract.provider.getLogs({ 23 | fromBlock: 0, 24 | toBlock: 'latest', 25 | address: config.contract.address, 26 | topics: config.contract.filters.RootCommitted(rootHash).topics, 27 | }); 28 | 29 | // Check root is real. 30 | utils.assert(logs.length > 0, 'no-root-available'); 31 | 32 | // Parse log and build root struct. 33 | const log = config.contract.interface.parseLog(logs[0]); 34 | const root = new protocol.root.RootHeader({ ...log.values }); 35 | 36 | // Get the root transaction data. 37 | const transactionData = await config.provider 38 | .getTransaction(logs[0].transactionHash); 39 | const calldata = config.contract.interface.parseTransaction(transactionData) 40 | .args[3]; 41 | 42 | // attempt to decode the root from data. 43 | const transactions = protocol.root.decodePacked(calldata); 44 | 45 | // Selected transaction index. 46 | const transactionIndex = metadata.properties.transactionIndex().get(); 47 | 48 | // Check index overflow. 49 | utils.assert(transactions[transactionIndex], 'transaction-index'); 50 | 51 | // Check transaction output overflow. 52 | const transaction = protocol.transaction 53 | .decodePacked(transactions[transactionIndex]); 54 | 55 | // Check output index overflow. 56 | const outputIndex = metadata.properties.outputIndex().get(); 57 | 58 | // Output index overflow check. 59 | utils.assert(transaction.outputs[outputIndex], 'output-index-overflow'); 60 | 61 | // Output in question. 62 | return transaction.outputs[outputIndex]; 63 | } 64 | 65 | module.exports = outputFromMetadata; -------------------------------------------------------------------------------- /packages/logic/src/transactions.js: -------------------------------------------------------------------------------- 1 | // get all account inputs from db 2 | const utils = require('@fuel-js/utils'); 3 | const interface = require('@fuel-js/interface'); 4 | const streamToArray = require('stream-to-array'); 5 | 6 | async function transactions(blockHeight = 0, rootIndex = 0, config = {}) { 7 | try { 8 | return (await streamToArray(config.db.createReadStream({ 9 | gte: interface.db.transactionMetadata.encode([ blockHeight, rootIndex, 0 ]), 10 | lte: interface.db.transactionMetadata.encode([ blockHeight, rootIndex, 2048 ]), 11 | limit: 2048, 12 | remote: true, 13 | }))) 14 | .map(data => data.value); 15 | } catch (error) { 16 | throw new utils.ByPassError(error); 17 | } 18 | } 19 | 20 | module.exports = transactions; 21 | -------------------------------------------------------------------------------- /packages/protocol/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .cache 4 | old -------------------------------------------------------------------------------- /packages/protocol/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | node_modules 3 | dist 4 | .cache 5 | old 6 | .nyc_output 7 | browser -------------------------------------------------------------------------------- /packages/protocol/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/protocol 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/protocol/browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/protocol/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/protocol", 3 | "version": "0.4.6", 4 | "description": "Fuel protocol structures and logic.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "browser": "parcel browser/index.html", 8 | "test": "nyc node src/test --exclude src/test/*.js", 9 | "lint": "eslint src/index.js" 10 | }, 11 | "publishConfig": { 12 | "access": "public" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/fuellabs/fuel-js.git" 17 | }, 18 | "keywords": [ 19 | "fuel", 20 | "js", 21 | "protocol" 22 | ], 23 | "author": "Nick Dodson ", 24 | "license": "Apache-2.0", 25 | "bugs": { 26 | "url": "https://github.com/fuellabs/fuel-js/issues" 27 | }, 28 | "dependencies": { 29 | "@fuel-js/struct": "^0.1.7", 30 | "@fuel-js/utils": "^0.1.4" 31 | }, 32 | "devDependencies": { 33 | "@fuel-js/contracts": "^0.1.2", 34 | "eth-sig-util": "*", 35 | "regenerator-runtime": "^0.13.7", 36 | "@fuel-js/environment": "^0.1.0", 37 | "ethers": "^4.0.47", 38 | "eslint": "^7.6.0", 39 | "nyc": "^15.1.0", 40 | "eslint-config-airbnb-base": "^14.2.0", 41 | "eslint-plugin-import": "^2.21.2" 42 | }, 43 | "homepage": "https://github.com/fuellabs/fuel-js#readme" 44 | } 45 | -------------------------------------------------------------------------------- /packages/protocol/src/addons.js: -------------------------------------------------------------------------------- 1 | const { struct } = require('@fuel-js/struct'); 2 | const metadata = require('./metadata'); 3 | const inputs = require('./inputs'); 4 | 5 | const UTXO = struct(` 6 | uint64 timestamp, 7 | uint32 blockHeight, 8 | uint8 rootIndex, 9 | uint32 transactionIndex, 10 | uint32 outputIndex, 11 | uint32 blockNumber 12 | `); 13 | 14 | const Deposit = struct(` 15 | uint64 timestamp, 16 | bytes32 transactionHash 17 | `); 18 | 19 | const BlockHeader = struct(` 20 | uint64 timestamp, 21 | bytes32 transactionHash 22 | `); 23 | 24 | const RootHeader = struct(` 25 | uint64 timestamp, 26 | address blockProducer, 27 | uint32 rightmostIndex, 28 | uint32 blockHeight, 29 | uint32 blockNumber, 30 | uint8 rootIndex, 31 | bytes32 transactionHash 32 | `); 33 | 34 | const Transaction = struct(` 35 | bytes1[] transaction, 36 | uint32 blockHeight, 37 | uint8 rootIndex, 38 | uint32 transactionIndex, 39 | uint8 inputsLength, 40 | uint8 outputsLength, 41 | uint8 witnessesLength, 42 | uint32 blockNumber, 43 | uint64 timestamp, 44 | bytes32[] data, 45 | uint32 signatureFeeToken, 46 | uint256 signatureFee, 47 | bytes32[] spendableOutputs, 48 | bytes[][] deltas, 49 | bytes[][] outputProofs, 50 | bytes[][] inputProofs, 51 | bytes1[] inputTypes 52 | `); 53 | 54 | const Commitment = struct(` 55 | uint64 startTimestamp, 56 | uint64 startNonce, 57 | bytes32 startTransactionId, 58 | uint64 endTimestamp, 59 | uint64 endNonce, 60 | bytes32 endTransactionId, 61 | uint32 blockHeight, 62 | uint32 blockNumber, 63 | bytes32 transactionHash, 64 | bytes32[] roots 65 | `); 66 | 67 | // This is used for third party block production. 68 | const CommitmentWait = struct(` 69 | uint64 time, 70 | bytes32[] roots, 71 | bytes[][] processed 72 | `); 73 | 74 | function metadataFromProofs(_inputs = [], proofs = []) { 75 | let result = []; 76 | 77 | for (var i = 0; i < _inputs.length; i++) { 78 | if (_inputs[i].properties.type().get().toNumber() === inputs.InputTypes.Deposit) { 79 | result.push(metadata.MetadataDeposit(proofs[i].object())); 80 | } else if (_inputs[i].properties.type().get().toNumber() === inputs.InputTypes.Root) { 81 | const addon = proofs[i].getAddon(); 82 | result.push(metadata.Metadata(RootHeader(addon.object ? addon.object() : {}).object())); 83 | } else { 84 | const addon = proofs[i].getAddon(); 85 | result.push(metadata.Metadata(UTXO(addon.object ? addon.object() : {}).object())); 86 | } 87 | } 88 | 89 | return result; 90 | } 91 | 92 | // Scan point struct. 93 | // This is the point or tx last scanned from the mempool. 94 | const ScanPoint = struct(` 95 | uint64 minTimestamp, 96 | uint64 minNonce, 97 | bytes32 minTransactionId 98 | `); 99 | 100 | // This is the balance struct used for tracking. 101 | const Balance = struct(` 102 | uint256 syncBalance, 103 | uint256 mempoolBalance, 104 | bytes32 transactionHashId 105 | `); 106 | 107 | // Delta changes. 108 | const Delta = struct(` 109 | uint256 amount, 110 | uint32 token, 111 | address account, 112 | uint8 isIncrease 113 | `); 114 | 115 | module.exports = { 116 | UTXO, 117 | RootHeader, 118 | BlockHeader, 119 | ScanPoint, 120 | Deposit, 121 | Balance, 122 | Delta, 123 | Transaction, 124 | Commitment, 125 | CommitmentWait, 126 | metadataFromProofs, 127 | }; 128 | -------------------------------------------------------------------------------- /packages/protocol/src/block.js: -------------------------------------------------------------------------------- 1 | const { struct } = require('@fuel-js/struct'); 2 | const utils = require('@fuel-js/utils'); 3 | const root = require('./root'); 4 | 5 | const TRANSACTION_ROOTS_MAX = 128; 6 | const Not_Finalized = false; 7 | const Finalized = true; 8 | const No_Assertion = 2; 9 | 10 | const BlockHeader = struct(` 11 | address producer, 12 | bytes32 previousBlockHash, 13 | uint256 height, 14 | uint256 blockNumber, 15 | uint256 numTokens, 16 | uint256 numAddresses, 17 | bytes32[**] roots 18 | `); 19 | 20 | function rootIndex(block, root) { 21 | return block.properties.roots().get().indexOf(root.keccak256Packed()); 22 | } 23 | 24 | BlockHeader.fromLogs = async function (height, contract) { 25 | try { 26 | const logs = await contract.provider.getLogs({ 27 | fromBlock: 0, 28 | toBlock: 'latest', 29 | address: contract.address, 30 | topics: contract.filters.BlockCommitted(null, null, null, null, height).topics, 31 | }); 32 | 33 | // get the latest log. 34 | const raw = logs.pop(); 35 | const log = contract.interface.parseLog(raw); 36 | 37 | return new BlockHeader({ ...log.values, blockNumber: raw.blockNumber }); 38 | } catch (error) { 39 | throw new utils.ByPassError(error); 40 | } 41 | }; 42 | 43 | const genesis = '0x00'; 44 | 45 | module.exports = { 46 | ...root, 47 | BlockHeader, 48 | genesis, 49 | rootIndex, 50 | TRANSACTION_ROOTS_MAX, 51 | Not_Finalized, 52 | No_Assertion, 53 | }; 54 | -------------------------------------------------------------------------------- /packages/protocol/src/deposit.js: -------------------------------------------------------------------------------- 1 | const { struct } = require('@fuel-js/struct'); 2 | 3 | const Deposit = struct(` 4 | address owner, 5 | uint256 token, 6 | uint256 blockNumber, 7 | uint256 value 8 | `); 9 | 10 | module.exports = { 11 | Deposit, 12 | }; 13 | -------------------------------------------------------------------------------- /packages/protocol/src/eip712.js: -------------------------------------------------------------------------------- 1 | const utils = require('@fuel-js/utils'); 2 | 3 | // @description EIP712 typed structures 4 | const types = { 5 | EIP712Domain: [ 6 | { name: "name", type: "string" }, 7 | { name: "version", type: "string" }, 8 | { name: "chainId", type: "uint256" }, 9 | { name: "contract", type: "address" }, 10 | ], 11 | Transaction: [ 12 | { name: "transaction", type: "bytes" }, 13 | ], 14 | }; 15 | 16 | // @description domain typehash 17 | const DomainStruct = utils.keccak256(utils.solidityPack( 18 | ['string'], ['EIP712Domain(string name,string version,uint256 chainId,address contract)'] 19 | )); 20 | 21 | // @description Registration hash 22 | const TransactionStruct = utils.keccak256(utils.solidityPack( 23 | ['string'], ['Transaction(bytes transaction)'] 24 | )); 25 | 26 | // @description default options 27 | const defaults = ['Fuel', '1.1.0', 1]; 28 | 29 | // @description domain hash creation 30 | function eip712Domain(opts = {}) { 31 | // @descript EIP712 Domain Data 32 | const data = { 33 | name: defaults[0], 34 | version: defaults[1], 35 | chainId: opts.chainId || defaults[2], 36 | ...opts, 37 | }; 38 | 39 | // @description EIP712 Domain Hash 40 | const hash = utils.keccak256(utils.abi.encode( 41 | ['bytes32', 'bytes32', 'bytes32', 'uint256', 'address'], [ 42 | DomainStruct, 43 | utils.keccak256(utils.solidityPack(['string'], [data.name])), // name 44 | utils.keccak256(utils.solidityPack(['string'], [data.version])), // version 1 45 | data.chainId, // chain id homestead (mainnet) 46 | data.contract, // salt 47 | ] 48 | )); 49 | 50 | return { hash, data }; 51 | } 52 | 53 | // @description produce hash from EIP712 message 54 | function hash({ transactionHashId, unsigned, contract, chainId }, opts = {}) { 55 | // compute domain hash 56 | const domain = eip712Domain({ contract: contract.address, chainId, ...opts }); 57 | 58 | // produce registration hash 59 | const TransactionHash = utils.keccak256(utils.abi.encode( 60 | ['bytes32', 'bytes32'], 61 | [ 62 | TransactionStruct, 63 | unsigned ? unsigned.keccak256Packed() : transactionHashId, 64 | ], 65 | )); 66 | 67 | // release hash 68 | const release = utils.keccak256(utils.solidityPack( 69 | ['string', 'bytes32', 'bytes32'], [ 70 | "\x19\x01", 71 | domain.hash, 72 | TransactionHash, 73 | ], 74 | )); 75 | 76 | // message 77 | const message = { 78 | transaction: unsigned ? unsigned.encodePacked() : '0x00', 79 | }; 80 | 81 | // Return typed data object 82 | const typedData = { types, domain: domain.data, primaryType: "Transaction", message }; 83 | 84 | // return compliance hashes, typeData structure for MM, and registration hash 85 | return { typedData, hash: release, message: TransactionHash }; 86 | } 87 | 88 | module.exports = { 89 | hash, 90 | types, 91 | eip712Domain, 92 | TransactionStruct, 93 | DomainStruct, 94 | defaults, 95 | }; 96 | -------------------------------------------------------------------------------- /packages/protocol/src/index.js: -------------------------------------------------------------------------------- 1 | const block = require('./block'); 2 | const deposit = require('./deposit'); 3 | const inputs = require('./inputs'); 4 | const metadata = require('./metadata'); 5 | const outputs = require('./outputs'); 6 | const root = require('./root'); 7 | const transaction = require('./transaction'); 8 | const witness = require('./witness'); 9 | const state = require('./state'); 10 | const addons = require('./addons'); 11 | const eip712 = require('./eip712'); 12 | const token = require('./token'); 13 | const withdraw = require('./withdraw'); 14 | 15 | module.exports = { 16 | block, 17 | deposit, 18 | metadata, 19 | inputs, 20 | outputs, 21 | root, 22 | transaction, 23 | witness, 24 | state, 25 | addons, 26 | eip712, 27 | token, 28 | withdraw, 29 | }; 30 | -------------------------------------------------------------------------------- /packages/protocol/src/inputs.js: -------------------------------------------------------------------------------- 1 | const { struct } = require('@fuel-js/struct'); 2 | const utils = require('@fuel-js/utils'); 3 | 4 | const INPUTS_MAX = 8; 5 | 6 | const InputTypes = { 7 | Transfer: 0, 8 | Deposit: 1, 9 | HTLC: 2, 10 | Root: 3, 11 | }; 12 | 13 | const Input = struct( 14 | `uint8 type, uint8 witnessReference`, 15 | opts => ({ ...opts, type: InputTypes.Transfer }) 16 | ); 17 | 18 | const InputTransfer = struct( 19 | `uint8 type, uint8 witnessReference`, 20 | opts => ({ ...opts, type: InputTypes.Transfer }) 21 | ); 22 | 23 | const InputHTLC = struct( 24 | `uint8 type, uint8 witnessReference, bytes32 preImage`, 25 | opts => ({ ...opts, type: InputTypes.HTLC }) 26 | ); 27 | 28 | const InputDeposit = struct( 29 | `uint8 type, uint8 witnessReference, address owner`, 30 | opts => ({ ...opts, type: InputTypes.Deposit }) 31 | ); 32 | 33 | const InputRoot = struct( 34 | `uint8 type, uint8 witnessReference`, 35 | opts => ({ ...opts, type: InputTypes.Root }) 36 | ); 37 | 38 | const InputStructs = [InputTransfer, InputDeposit, InputHTLC, InputRoot]; 39 | 40 | const isDeposit = _input => { 41 | return _input.object().type === InputTypes.Deposit; 42 | }; 43 | 44 | function decodePacked(data = '0x') { 45 | let result = []; 46 | let pos = 0; 47 | 48 | for (;pos < utils.hexDataLength(data);) { 49 | let decoder = null; 50 | const kind = parseInt(utils.hexDataSub(data, pos, 1), 16); 51 | 52 | switch (kind) { 53 | case InputTypes.Transfer: 54 | decoder = InputTransfer; 55 | break; 56 | 57 | case InputTypes.Deposit: 58 | decoder = InputDeposit; 59 | break; 60 | 61 | case InputTypes.HTLC: 62 | decoder = InputHTLC; 63 | break; 64 | 65 | case InputTypes.Root: 66 | decoder = InputRoot; 67 | break; 68 | 69 | default: 70 | utils.assert(0, 'invalid-input-type'); 71 | } 72 | 73 | const input = decoder.decodePacked(utils.hexDataSub(data, pos)); 74 | pos += input.sizePacked(); 75 | result.push(input); 76 | } 77 | 78 | utils.assert(pos === utils.hexDataLength(data), 'inputs-length-mismatch'); 79 | utils.assert(result.length > 0, 'inputs-length-underflow'); 80 | utils.assert(result.length <= INPUTS_MAX, 'inputs-length-overflow'); 81 | 82 | return result; 83 | } 84 | 85 | module.exports = { 86 | InputStructs, 87 | InputTypes, 88 | Input, 89 | InputTransfer, 90 | InputHTLC, 91 | InputDeposit, 92 | InputRoot, 93 | isDeposit, 94 | decodePacked, 95 | INPUTS_MAX, 96 | }; 97 | -------------------------------------------------------------------------------- /packages/protocol/src/root.js: -------------------------------------------------------------------------------- 1 | const { struct } = require('@fuel-js/struct'); 2 | const utils = require('@fuel-js/utils'); 3 | const transaction = require('./transaction'); 4 | const { merkleTreeRoot } = require('./merkle'); 5 | 6 | const MAX_ROOT_SIZE = 57600; 7 | const MaxTransactionsInRoot = 2048; 8 | const MIN_ROOT_SIZE = 44; 9 | 10 | const RootHeader = struct(` 11 | address rootProducer, 12 | bytes32 merkleTreeRoot, 13 | bytes32 commitmentHash, 14 | uint256 rootLength, 15 | uint256 feeToken, 16 | uint256 fee 17 | `); 18 | 19 | const dataFromLog = async (log = {}, contract = {}) => { 20 | try { 21 | const transaction = await contract.provider.getTransaction(log.transactionHash); 22 | return contract.interface.parseTransaction(transaction).args[3]; 23 | } catch (error) { 24 | throw new utils.ByPassError(error); 25 | } 26 | }; 27 | 28 | const decodePacked = (data = '0x') => { 29 | const dataLength = utils.hexDataLength(data); 30 | let transactions = []; 31 | let pos = 0; 32 | 33 | for (;pos < dataLength;) { 34 | const length = utils.hexToInt(utils.hexDataSub(data, pos, 2)) + 2; 35 | 36 | utils.assert(length > transaction.TransactionSizeMinimum, "transaction-length-underflow"); 37 | utils.assert(length < transaction.TransactionSizeMaximum, "transaction-length-overflow"); 38 | 39 | transactions.push(utils.hexDataSub(data, pos, length)); 40 | pos += length; 41 | } 42 | 43 | utils.assert(pos === dataLength, "net-length-overflow"); 44 | utils.assert(transactions.length > 0, 'transaction-index-underflow'); 45 | utils.assert(transactions.length < MaxTransactionsInRoot, 'transaction-index-overflow'); 46 | 47 | return transactions; 48 | }; 49 | 50 | RootHeader.fromLogs = async function (indexOrRoot, block = {}, contract = {}, transactions = false) { 51 | try { 52 | const logs = await contract.provider.getLogs({ 53 | fromBlock: 0, 54 | toBlock: 'latest', 55 | address: contract.address, 56 | topics: contract.filters.RootCommitted(!block 57 | ? indexOrRoot 58 | : block.properties.roots().get()[indexOrRoot]).topics, 59 | }); 60 | 61 | const log = contract.interface.parseLog(logs[0]); 62 | return new RootHeader({ ...log.values }); 63 | } catch (error) { 64 | throw new utils.ByPassError(error); 65 | } 66 | }; 67 | 68 | RootHeader.fromLogsByIndex = async function (index, block, contract, transactions = false) { 69 | return RootHeader.fromLogs(index, block, contract, transactions); 70 | }; 71 | 72 | RootHeader.fromLogsByHash = async function (hash, contract, transactions = false) { 73 | return RootHeader.fromLogs(hash, null, contract, transactions); 74 | }; 75 | 76 | const Leaf = struct('bytes1[**] data'); 77 | const transactions = leafs => '0x' + leafs.map(v => v.encodePacked().slice(2)).join(''); 78 | const encodePacked = transactions; 79 | 80 | module.exports = { 81 | RootHeader, 82 | Leaf, 83 | merkleTreeRoot, 84 | transactions, 85 | MAX_ROOT_SIZE, 86 | MaxTransactionsInRoot, 87 | encodePacked, 88 | decodePacked, 89 | dataFromLog, 90 | }; 91 | -------------------------------------------------------------------------------- /packages/protocol/src/state.js: -------------------------------------------------------------------------------- 1 | const { struct } = require('@fuel-js/struct'); 2 | 3 | const State = struct(` 4 | uint32 numAddresses, 5 | uint32 numTokens, 6 | uint32 blockNumber, 7 | uint32 blockHeight, 8 | uint32 penalty, 9 | uint32 transactions, 10 | uint32 trades 11 | `); 12 | 13 | module.exports = { 14 | State, 15 | }; 16 | -------------------------------------------------------------------------------- /packages/protocol/src/test/addons.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const addons = require('../addons'); 3 | const inputs = require('../inputs'); 4 | const outputs = require('../outputs'); 5 | const root = require('../root'); 6 | const deposit = require('../deposit'); 7 | 8 | module.exports = test('addons', async t => { 9 | 10 | t.ok(addons, 'addons available'); 11 | 12 | const txInputs = [ 13 | inputs.InputTransfer({}), 14 | inputs.InputRoot({}), 15 | inputs.InputDeposit({}), 16 | ]; 17 | const txProofs = [ 18 | outputs.UTXO({}, null, addons.UTXO), 19 | root.RootHeader({}, null, addons.RootHeader), 20 | deposit.Deposit({ 21 | blockNumber: 2, 22 | }), 23 | ]; 24 | 25 | txProofs[0].setAddon(addons.UTXO({ 26 | blockHeight: 4, 27 | })); 28 | txProofs[1].setAddon(addons.RootHeader({ 29 | blockHeight: 3, 30 | })); 31 | 32 | const empty = addons.metadataFromProofs(); 33 | t.ok(empty, 'empty'); 34 | 35 | const metadata = addons.metadataFromProofs(txInputs, txProofs); 36 | 37 | t.equal(metadata.length, 3, 'length'); 38 | t.equalBig(metadata[0].properties.blockHeight().get(), 4, 'utxo check'); 39 | t.equalBig(metadata[1].properties.blockHeight().get(), 3, 'root check'); 40 | t.equalBig(metadata[2].properties.blockNumber().get(), 2, 'deposit check'); 41 | 42 | }); 43 | -------------------------------------------------------------------------------- /packages/protocol/src/test/block.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const { Fuel } = require('@fuel-js/contracts'); 3 | const block = require('../block'); 4 | const root = require('../root'); 5 | 6 | // defaults 7 | const defaults = (producer, bondSize = utils.parseEther('1.0')) => [ 8 | producer, 9 | 20, 10 | 20, 11 | 20, 12 | bondSize, 13 | "Fuel", 14 | "1.0.0", 15 | 1, 16 | utils.emptyBytes32 17 | ]; 18 | 19 | module.exports = test('block', async t => { 20 | 21 | const blockHeader = block.BlockHeader({ 22 | roots: [utils.keccak256('0xaa'), utils.keccak256('0xbb'), utils.keccak256('0xcc')], 23 | }); 24 | const rootHeader = { keccak256Packed: () => utils.keccak256('0xbb') }; 25 | 26 | t.equal(block.rootIndex(blockHeader, rootHeader), 1, 'root index'); 27 | 28 | const producer = t.getWallets()[0].address; 29 | const contract = await t.deploy(Fuel.abi, Fuel.bytecode, defaults(producer)); 30 | 31 | const genesis = await block.BlockHeader.fromLogs(0, contract); 32 | 33 | t.equalBig(genesis.properties.height().get(), 0, 'gen height'); 34 | 35 | await t.catch(block.BlockHeader.fromLogs(1, contract), 'block log overflow'); 36 | 37 | }); 38 | -------------------------------------------------------------------------------- /packages/protocol/src/test/eip712.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const eip712 = require('../eip712'); 3 | const sigUtil = require('eth-sig-util'); 4 | // require('regenerator-runtime'); 5 | 6 | module.exports = test('eip712', async t => { 7 | 8 | const txData = '0x00100000000000000000000000000000000000420001011006058d15e1762814744454ffbca8f134e81b292c6200c5cbe0c283f200010100084af0a763bb1c006414744454ffbca8f134e81b292c6200c5cbe0c283f2081092c8a81313513a6bdbeabd729250947fa0fec0d72700c4816d41872764bd83165c1646e3113a833a6d4ffeb069ca11a0f8ebb0dd45b602f7fc1ede3ea579bc1c49901d45fb43185910addc361f6742ae1d6bfcfeff7b174393744c25f68b0a2c37b3e403d32067f9010216b35f64e616bb6f5a0255d22584aa72da739f74452e25ab4fe4db10a22f63b035140d1cf16b37294e7339a0012dc75c500e9f582d2ebf0c5357ea0362603400b7d52495770a4f7f4ccf5ceb45e288d8586b1c6e75309962fa42dd24173ea78426cdef741aeacc9ecff34c46f5f7c92b042041a83734da920c546f8a5fc49d4126e5f7606c4c3a08b29f0c9d47f4e190df4859ba3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'; 9 | 10 | const { typedData, hash } = eip712.hash({ 11 | transactionHashId: utils.keccak256(txData), 12 | unsigned: { encodePacked: () => txData, keccak256Packed: () => utils.keccak256(txData) }, 13 | contract: { address: utils.emptyAddress }, 14 | chainId: 3, 15 | }); 16 | const utilHash = '0x' + sigUtil.TypedDataUtils.sign(typedData).toString('hex'); 17 | 18 | /* 19 | await window.web3.currentProvider.enable(); 20 | 21 | web3.currentProvider.sendAsync({ 22 | jsonrpc: "2.0", 23 | method: "eth_signTypedData_v4", 24 | params: [ 25 | '0x744454FFbca8f134E81B292C6200c5cbE0c283F2', 26 | JSON.stringify(typedData), 27 | ] 28 | }, function (err, result) { 29 | if (err) { 30 | console.error(err); 31 | } 32 | 33 | console.log('recover', utils.recoverAddress(hash, result.result)); 34 | 35 | console.log(result); 36 | }); 37 | 38 | console.log('mine', hash, utilHash); 39 | */ 40 | 41 | }); 42 | -------------------------------------------------------------------------------- /packages/protocol/src/test/index.js: -------------------------------------------------------------------------------- 1 | module.exports = (async () => { 2 | 3 | await require('./inputs'); 4 | await require('./outputs'); 5 | await require('./root'); 6 | await require('./transaction'); 7 | await require('./witness'); 8 | await require('./block'); 9 | await require('./addons'); 10 | await require('./eip712'); 11 | await require('./witness'); 12 | await require('./withdraw'); 13 | await require('./state'); 14 | await require('./metadata'); 15 | 16 | })(); 17 | -------------------------------------------------------------------------------- /packages/protocol/src/test/inputs.js: -------------------------------------------------------------------------------- 1 | const { test, utils, BN, accounts } = require('@fuel-js/environment'); 2 | const inputs = require('../inputs'); 3 | const struct = require('@fuel-js/struct'); 4 | 5 | module.exports = test('inputs', async t => { 6 | const data = '0x000100040101c5d2460186f7233c927e7db2dcc703c0e500b600'; 7 | const decoded = inputs.decodePacked(data); 8 | 9 | t.equalBig(decoded[0].object().type, 0, 'type'); 10 | t.equalBig(decoded[0].object().witnessReference, 1, 'witnessReference'); 11 | t.equalBig(decoded[1].object().type, 0, 'type'); 12 | t.equalBig(decoded[1].object().witnessReference, 4, 'witnessReference'); 13 | t.equalBig(decoded[2].object().type, 1, 'type'); 14 | t.equalBig(decoded[2].object().witnessReference, 1, 'witnessReferece'); 15 | t.equalHex(decoded[2].object().owner, '0xc5d2460186f7233c927e7db2dcc703c0e500b600', 'owner'); 16 | 17 | const invalidType = '0x0400'; 18 | t.throws(() => inputs.decodePacked(invalidType), 'invalid type'); 19 | 20 | const invalidSize = '0x00'; 21 | t.throws(() => inputs.decodePacked(invalidSize), 'invalid size'); 22 | 23 | const lengthUnderflow = '0x'; 24 | t.throws(() => inputs.decodePacked(lengthUnderflow), 'length underflow'); 25 | 26 | const lengthOverflow = '0x000100010001000100010001000100010001'; 27 | t.throws(() => inputs.decodePacked(lengthOverflow), 'length overflow'); 28 | 29 | const txInputs = [ 30 | inputs.Input(), 31 | inputs.InputTransfer(), 32 | inputs.InputDeposit(), 33 | inputs.InputHTLC(), 34 | inputs.InputRoot(), 35 | ]; 36 | 37 | t.equal(inputs.isDeposit(txInputs[2]), true, 'is deposit'); 38 | 39 | const decodedInputs = inputs.decodePacked(struct.combine(txInputs)); 40 | 41 | t.equalBig(decodedInputs[0].properties.type().get(), inputs.InputTypes.Transfer, 'type check'); 42 | t.equalBig(decodedInputs[1].properties.type().get(), inputs.InputTypes.Transfer, 'type check'); 43 | t.equalBig(decodedInputs[2].properties.type().get(), inputs.InputTypes.Deposit, 'type check'); 44 | t.equalBig(decodedInputs[3].properties.type().get(), inputs.InputTypes.HTLC, 'type check'); 45 | t.equalBig(decodedInputs[4].properties.type().get(), inputs.InputTypes.Root, 'type check'); 46 | 47 | t.throws(() => inputs.decodePacked(), 'empty decode'); 48 | }); 49 | -------------------------------------------------------------------------------- /packages/protocol/src/test/metadata.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const struct = require('@fuel-js/struct'); 3 | const metadata = require('../metadata'); 4 | const inputs = require('../inputs'); 5 | 6 | module.exports = test('metadata', async t => { 7 | 8 | t.throw(() => metadata.decodePacked(), 'metadata-underflow'); 9 | t.throw(() => metadata.decodePacked('0x'), 'metadata-underflow'); 10 | 11 | const txMetadata = [ 12 | metadata.Metadata(), 13 | ]; 14 | const txInputs = [ 15 | inputs.Input(), 16 | ]; 17 | 18 | const single = metadata.decodePacked(struct.combine(txMetadata), txInputs); 19 | 20 | t.equal(single.length, 1, 'single length'); 21 | 22 | const txMetadata2 = [ 23 | metadata.Metadata(), 24 | metadata.MetadataDeposit(), 25 | ]; 26 | const txInputs2 = [ 27 | inputs.Input(), 28 | inputs.InputDeposit(), 29 | ]; 30 | 31 | const double = metadata.decodePacked(struct.combine(txMetadata2), txInputs2); 32 | 33 | t.equal(double.length, 2, 'double length'); 34 | 35 | const txMetadataFull = [ 36 | metadata.Metadata(), 37 | metadata.MetadataDeposit(), 38 | metadata.Metadata(), 39 | metadata.MetadataDeposit(), 40 | metadata.Metadata(), 41 | metadata.MetadataDeposit(), 42 | metadata.Metadata(), 43 | metadata.MetadataDeposit(), 44 | ]; 45 | const txInputsFull = [ 46 | inputs.Input(), 47 | inputs.InputDeposit(), 48 | inputs.Input(), 49 | inputs.InputDeposit(), 50 | inputs.Input(), 51 | inputs.InputDeposit(), 52 | inputs.Input(), 53 | inputs.InputDeposit(), 54 | ]; 55 | 56 | const full = metadata.decodePacked(struct.combine(txMetadataFull), txInputsFull); 57 | 58 | t.equal(full.length, 8, 'double length'); 59 | 60 | }); 61 | -------------------------------------------------------------------------------- /packages/protocol/src/test/root.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const { Fuel } = require('@fuel-js/contracts'); 3 | const root = require('../root'); 4 | 5 | // defaults 6 | const defaults = (producer, bondSize = utils.parseEther('1.0')) => [ 7 | producer, 8 | 20, 9 | 20, 10 | 20, 11 | bondSize, 12 | "Fuel", 13 | "1.0.0", 14 | 1, 15 | utils.emptyBytes32, 16 | ]; 17 | 18 | module.exports = test('root', async t => { 19 | 20 | t.throw(() => root.decodePacked('0x'), "transaction-index-underflow"); 21 | t.throw(() => root.decodePacked('0x002a'), "transaction-length-underflow"); 22 | t.throw(() => root.decodePacked('0x0044'), 'hex-data-overflow'); 23 | 24 | const normal = root.Leaf({ data: utils.randomBytes(400) }); 25 | 26 | t.equalHex(normal.encodePacked(), root.decodePacked(normal.encodePacked())[0], 'single'); 27 | 28 | const packed = root.encodePacked([normal, normal, normal]); 29 | const decoded = root.decodePacked(packed); 30 | 31 | t.equalHex(decoded[0], normal.encodePacked(), 'check'); 32 | t.equalHex(decoded[1], normal.encodePacked(), 'check'); 33 | t.equalHex(decoded[2], normal.encodePacked(), 'check'); 34 | t.equalBig(decoded.length, 3, 'length'); 35 | 36 | const valid = root.Leaf({ data: utils.randomBytes(160) }); 37 | const validPacked = root.encodePacked((new Array(root.MaxTransactionsInRoot - 1)).fill(valid)); 38 | const decodeValidPacked = root.decodePacked(validPacked); 39 | 40 | const overflowPacked = root.encodePacked((new Array(root.MaxTransactionsInRoot)).fill(valid)); 41 | 42 | t.throw(() => root.decodePacked(overflowPacked), 'transaction-index-overflow'); 43 | 44 | const producer = t.getWallets()[0].address; 45 | const contract = await t.deploy(Fuel.abi, Fuel.bytecode, defaults(producer)); 46 | 47 | const merkleRoot = utils.emptyBytes32; 48 | const fee = 0; 49 | const feeToken = 0; 50 | const transactions = utils.hexZeroPad('0x00', 350); 51 | 52 | let rootTx = await contract.commitRoot(merkleRoot, feeToken, fee, transactions, t.getOverrides()); 53 | rootTx = await rootTx.wait(); 54 | 55 | await t.catch(root.dataFromLog(), 'empty data from log'); 56 | t.equal(await root.dataFromLog(rootTx.logs[0], contract), transactions, 'data from log'); 57 | 58 | // RootHeader.fromLogs = async function (indexOrRoot, block = {}, contract = {}, transactions = false) 59 | 60 | const header = await root.RootHeader.fromLogs(rootTx.events[0].args.root, null, contract, false); 61 | 62 | t.equal(header.keccak256Packed(), rootTx.events[0].args.root, 'root header correct'); 63 | 64 | await t.catch(root.RootHeader.fromLogs(utils.emptyBytes32, null, { 65 | provider: { 66 | getLogs: () => { throw new Error('err') }, 67 | }, 68 | }, false), 'check throw'); 69 | 70 | const headerFromIndex = await root.RootHeader.fromLogsByIndex(0, { 71 | properties: { 72 | roots: () => ({ 73 | get: () => [ 74 | rootTx.events[0].args.root, 75 | ], 76 | }), 77 | }, 78 | }, contract, false); 79 | 80 | t.equal(headerFromIndex.keccak256Packed(), rootTx.events[0].args.root, 'headerFromIndex header correct'); 81 | 82 | const fromHash = await root.RootHeader.fromLogsByHash(rootTx.events[0].args.root, contract, false); 83 | 84 | t.equal(fromHash.keccak256Packed(), rootTx.events[0].args.root, 'fromHash header correct'); 85 | 86 | }); 87 | -------------------------------------------------------------------------------- /packages/protocol/src/test/state.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const state = require('../state'); 3 | 4 | module.exports = test('state', async t => { 5 | 6 | }); 7 | -------------------------------------------------------------------------------- /packages/protocol/src/test/token.js: -------------------------------------------------------------------------------- 1 | const { bytecode } = require('./erc20.json'); 2 | const { test, utils } = require('@fuel-js/environment'); 3 | const { ERC20 } = require('@fuel-js/contracts'); 4 | const token = require('../token'); 5 | 6 | const abi = [ 7 | ...ERC20.abi, 8 | 'constructor(uint256 chainId_) public', 9 | ]; 10 | 11 | module.exports = test('token', async t => { 12 | // check if sync dbs the right values 13 | const producer = t.getWallets()[0].address; 14 | 15 | // Setup erc20. 16 | const totalSupply = utils.bigNumberify('0xFFFFFFFFFFFFFFFFFFFFFF'); 17 | const erc20 = await t.deploy(abi, bytecode, [ 18 | producer, 19 | totalSupply, 20 | ]); 21 | 22 | // Erc20. 23 | const someErc20 = erc20.attach(utils.emptyAddress); 24 | 25 | // Fake config object. 26 | const config = { 27 | erc20: someErc20, 28 | }; 29 | 30 | const etherData = await token.encodeTokenMetadata( 31 | utils.emptyAddress, 32 | config, 33 | ); 34 | 35 | const noErc20Data = await token.encodeTokenMetadata( 36 | erc20.address, 37 | { ...config, erc20: null }, 38 | ); 39 | 40 | const erc20Data = await token.encodeTokenMetadata( 41 | erc20.address, 42 | config, 43 | ); 44 | 45 | const decodedEther = await token.decodeTokenMetadata( 46 | etherData 47 | ); 48 | 49 | const noErc20Decode = await token.decodeTokenMetadata( 50 | noErc20Data 51 | ); 52 | 53 | const erc20Decode = await token.decodeTokenMetadata( 54 | erc20Data 55 | ); 56 | 57 | t.equal(decodedEther.name, 'ether'); 58 | t.equalBig(decodedEther.decimals, 18); 59 | t.equal(noErc20Decode.name, ''); 60 | t.equalBig(noErc20Decode.decimals, 0); 61 | t.equal(erc20Decode.name, 'Fake Dai Stablecoin'); 62 | t.equal(erc20Decode.symbol, 'FDAI'); 63 | t.equalBig(erc20Decode.decimals, 18); 64 | 65 | }); -------------------------------------------------------------------------------- /packages/protocol/src/test/transaction.js: -------------------------------------------------------------------------------- 1 | const { test, utils, BN, accounts } = require('@fuel-js/environment'); 2 | const transaction = require('../transaction'); 3 | const inputs = require('../inputs'); 4 | const outputs = require('../outputs'); 5 | const metadata = require('../metadata'); 6 | const witness = require('../witness'); 7 | const root = require('../root'); 8 | const { combine } = require('@fuel-js/struct'); 9 | 10 | module.exports = test('transaction', async t => { 11 | 12 | const valid = await transaction.Transaction({ 13 | override: true, 14 | inputs: [ 15 | inputs.InputTransfer({}), 16 | inputs.InputTransfer({}), 17 | inputs.InputDeposit({}), 18 | inputs.InputTransfer({}), 19 | inputs.InputTransfer({}), 20 | inputs.InputTransfer({}), 21 | inputs.InputTransfer({}), 22 | inputs.InputTransfer({ witnessReference: 0 }), 23 | ], 24 | data: [ 25 | utils.emptyBytes32, 26 | utils.emptyBytes32, 27 | utils.emptyBytes32, 28 | utils.emptyBytes32, 29 | utils.emptyBytes32, 30 | utils.emptyBytes32, 31 | utils.emptyBytes32, 32 | utils.emptyBytes32, 33 | ], 34 | outputs: [ 35 | outputs.OutputTransfer({ 36 | token: '0x00', 37 | amount: 50000, 38 | owner: t.wallets[0].address, 39 | }), 40 | outputs.OutputWithdraw({ 41 | token: '0x00', 42 | amount: 102, 43 | owner: '0x00', 44 | }), 45 | outputs.OutputHTLC({ 46 | token: '0x00', 47 | amount: 3001, 48 | owner: t.wallets[0].address, 49 | digest: utils.emptyBytes32, 50 | expiry: 48, 51 | }), 52 | outputs.OutputReturn({ 53 | data: ['0xaa'], 54 | }), 55 | ], 56 | metadata: [ 57 | metadata.Metadata({}), 58 | metadata.Metadata({}), 59 | metadata.MetadataDeposit({}), 60 | metadata.Metadata({}), 61 | metadata.Metadata({ blockHeight: 4 }), 62 | metadata.Metadata({}), 63 | metadata.Metadata({}), 64 | metadata.Metadata({}), 65 | ], 66 | witnesses: [ 67 | witness._Signature({ v: 21 }), 68 | ], 69 | }); 70 | 71 | const encoded = valid.encodePacked(); 72 | const decoded = transaction.decodePacked(encoded); 73 | 74 | t.equalBig(decoded.metadata[4].properties.blockHeight().hex(), 4, 'metadata-check'); 75 | t.equalBig(decoded.outputs[1].properties.amount().hex(), 102, 'amount-check'); 76 | t.equalBig(decoded.witnesses[0].properties.v().hex(), 21, 'witness-version'); 77 | t.equalBig(decoded.inputs[7].properties.witnessReference().hex(), 0, 'witness-reference'); 78 | 79 | const invalid2 = await transaction.Transaction({ 80 | override: true, 81 | inputs: [ 82 | inputs.InputTransfer({}), 83 | inputs.InputTransfer({}), 84 | inputs.InputDeposit({}), 85 | inputs.InputTransfer({}), 86 | inputs.InputTransfer({}), 87 | inputs.InputTransfer({}), 88 | inputs.InputTransfer({}), 89 | inputs.InputTransfer({ witnessReference: 0 }), 90 | ], 91 | data: [ 92 | utils.emptyBytes32, 93 | utils.emptyBytes32, 94 | utils.emptyBytes32, 95 | utils.emptyBytes32, 96 | utils.emptyBytes32, 97 | utils.emptyBytes32, 98 | utils.emptyBytes32, 99 | utils.emptyBytes32, 100 | ], 101 | outputs: [ 102 | ], 103 | metadata: [ 104 | metadata.Metadata({}), 105 | metadata.Metadata({}), 106 | metadata.MetadataDeposit({}), 107 | metadata.Metadata({}), 108 | metadata.Metadata({ blockHeight: 4 }), 109 | metadata.Metadata({}), 110 | metadata.Metadata({}), 111 | metadata.Metadata({}), 112 | ], 113 | witnesses: [ 114 | witness._Signature({ v: 21 }), 115 | ], 116 | }); 117 | 118 | const encoded2 = invalid2.encodePacked(); 119 | try { 120 | const decoded2 = transaction.decodePacked(encoded2); 121 | } catch (invalid) { 122 | } 123 | 124 | }); 125 | -------------------------------------------------------------------------------- /packages/protocol/src/test/withdraw.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const withdraw = require('../withdraw'); 3 | 4 | module.exports = test('withdraw', async t => { 5 | 6 | }); 7 | -------------------------------------------------------------------------------- /packages/protocol/src/test/witness.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const witness = require('../witness'); 3 | 4 | module.exports = test('witness', async t => { 5 | 6 | }); 7 | -------------------------------------------------------------------------------- /packages/protocol/src/token.js: -------------------------------------------------------------------------------- 1 | const { struct } = require('@fuel-js/struct'); 2 | const utils = require('@fuel-js/utils'); 3 | 4 | // Token metadata. 5 | const Token = struct(` 6 | bytes name, 7 | bytes symbol, 8 | bytes version, 9 | address addr, 10 | uint256 decimals 11 | `); 12 | 13 | // Bytes utils. 14 | function toUtf8Bytes(text) { 15 | return utils.hexlify(utils.toUtf8Bytes(text)); 16 | } 17 | 18 | // Utils. 19 | function toUtf8String(hex) { 20 | return utils.toUtf8String(utils.arrayify(hex)); 21 | } 22 | 23 | // The Ether Token metadata. 24 | const EtherToken = Token({ 25 | name: toUtf8Bytes('ether'), 26 | symbol: toUtf8Bytes('eth'), 27 | version: toUtf8Bytes('1.1.0'), 28 | addr: utils.emptyAddress, 29 | decimals: 18, 30 | }); 31 | 32 | /// @dev A very fault taulerant token metadata collection mechanism. 33 | /// @return Token 34 | async function encodeTokenMetadata(token = '0x', config = {}) { 35 | if (token === utils.emptyAddress) return EtherToken; 36 | 37 | // Empty token data. 38 | const data = { 39 | name: '0x', 40 | symbol: '0x', 41 | version: '0x', 42 | addr: token, 43 | decimals: 0, 44 | }; 45 | 46 | // If no ERC20 is configured. 47 | if (!config.erc20) return Token(data); 48 | 49 | // Attach erc20 50 | const contract = config.erc20.attach(token); 51 | 52 | // Get the ERC20 data from the contract. 53 | try { 54 | data.name = toUtf8Bytes(await contract.name()); 55 | } catch (nameError) {} 56 | 57 | try { 58 | data.symbol = toUtf8Bytes(await contract.symbol()); 59 | } catch (symbolError) {} 60 | 61 | try { 62 | data.version = toUtf8Bytes(await contract.version()); 63 | } catch (versionError) {} 64 | 65 | try { 66 | data.decimals = await contract.decimals(); 67 | } catch (decimalError) {} 68 | 69 | // Final return. 70 | return Token(data); 71 | } 72 | 73 | /// @dev A very fault taulerant token metadata collection mechanism. 74 | /// @return Object 75 | function decodeTokenMetadata(tokenStruct = {}) { 76 | return { 77 | name: toUtf8String(tokenStruct.properties.name().get()), 78 | version: toUtf8String(tokenStruct.properties.version().get()), 79 | decimals: tokenStruct.properties.decimals().get(), 80 | symbol: toUtf8String(tokenStruct.properties.symbol().get()), 81 | addr: tokenStruct.properties.addr().get(), 82 | }; 83 | } 84 | 85 | // Exports. 86 | module.exports = { 87 | Token, 88 | encodeTokenMetadata, 89 | decodeTokenMetadata, 90 | }; -------------------------------------------------------------------------------- /packages/protocol/src/withdraw.js: -------------------------------------------------------------------------------- 1 | const { struct } = require('@fuel-js/struct'); 2 | const utils = require('@fuel-js/utils'); 3 | 4 | const WithdrawProof = struct( 5 | `uint256 rootIndex, 6 | bytes32 transactionLeafHash, 7 | uint256 outputIndex` 8 | ); 9 | 10 | const Withdraw = struct(`address account, 11 | address tokenAddress, 12 | uint256 amount, 13 | uint256 blockHeight, 14 | uint256 rootIndex, 15 | bytes32 transactionLeafHash, 16 | uint8 outputIndex, 17 | uint256 transactionIndex`); 18 | 19 | function computeWithdrawId(rootIndex, transactionLeafHash, outputIndex) { 20 | return utils.keccak256( 21 | utils.hexZeroPad(utils.hexlify(rootIndex), 32) 22 | + utils.hexZeroPad(transactionLeafHash, 32).slice(2) 23 | + utils.hexZeroPad(utils.hexlify(outputIndex), 32).slice(2) 24 | ); 25 | } 26 | 27 | module.exports = { 28 | WithdrawProof, 29 | Withdraw, 30 | computeWithdrawId, 31 | }; 32 | -------------------------------------------------------------------------------- /packages/refill/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | }, 14 | parserOptions: { 15 | ecmaVersion: 2018, 16 | }, 17 | rules: { 18 | 'no-restricted-syntax': ['off'], 19 | 'no-plusplus': ['off'], 20 | 'arrow-parens': ['off'], 21 | 'import/no-extraneous-dependencies': ['error', { devDependencies: ['./src/test/*'] }], 22 | camelcase: ['off'], 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /packages/refill/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/refill 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/refill/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/refill", 3 | "version": "0.1.0", 4 | "description": "Using a single address, keep other address balances filled up with Ether.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "nyc node src/test", 8 | "lint": "eslint src/index.js", 9 | "prepublish": "npm test && npm run lint" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/fuellabs/fuel-js.git" 14 | }, 15 | "publishConfig": { 16 | "access": "public" 17 | }, 18 | "keywords": [ 19 | "fuel", 20 | "js", 21 | "interface" 22 | ], 23 | "author": "Nick Dodson ", 24 | "license": "Apache-2.0", 25 | "bugs": { 26 | "url": "https://github.com/fuellabs/fuel-js/issues" 27 | }, 28 | "homepage": "https://github.com/fuellabs/fuel-js#readme", 29 | "devDependencies": { 30 | "@fuel-js/environment": "^0.1.0", 31 | "ethers": "^4.0.47", 32 | "eslint": "^7.6.0", 33 | "nyc": "^15.1.0", 34 | "eslint-config-airbnb-base": "^14.2.0", 35 | "eslint": "^7.2.0", 36 | "eslint-plugin-import": "^2.21.2" 37 | }, 38 | "dependencies": { 39 | "ethers": "^4.0.47" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/refill/src/Multisend.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | contract Multisend { 4 | constructor(uint256[] memory amounts, address payable[] memory recipients) public payable { 5 | require(amounts.length == recipients.length); 6 | for (uint256 i = 0; i < recipients.length; i++) { 7 | recipients[i].transfer(amounts[i]); 8 | } 9 | selfdestruct(msg.sender); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/refill/src/index.js: -------------------------------------------------------------------------------- 1 | const ethers = require('ethers'); 2 | 3 | // Mulisend bytecode 4 | const bytecode = '0x60806040526040516102023803806102028339818101604052604081101561002657600080fd5b810190808051604051939291908464010000000082111561004657600080fd5b8382019150602082018581111561005c57600080fd5b825186602082028301116401000000008211171561007957600080fd5b8083526020830192505050908051906020019060200280838360005b838110156100b0578082015181840152602081019050610095565b50505050905001604052602001805160405193929190846401000000008211156100d957600080fd5b838201915060208201858111156100ef57600080fd5b825186602082028301116401000000008211171561010c57600080fd5b8083526020830192505050908051906020019060200280838360005b83811015610143578082015181840152602081019050610128565b50505050905001604052505050805182511461015e57600080fd5b60008090505b81518110156101e75781818151811061017957fe5b602002602001015173ffffffffffffffffffffffffffffffffffffffff166108fc8483815181106101a657fe5b60200260200101519081150290604051600060405180830381858888f193505050501580156101d9573d6000803e3d6000fd5b508080600101915050610164565b503373ffffffffffffffffffffffffffffffffffffffff16fffe'; 5 | 6 | // Send data 7 | const abi = ['constructor(uint256[] amounts, address[] recipients)']; 8 | 9 | // refill, will use the wallet account to refill all accounts in array at target balance of Ether 10 | async function refill(wallet = {}, accounts = [], targetBalance = 0, opts = { 11 | gasLimit: 6000000, 12 | }) { 13 | try { 14 | let total = ethers.utils.bigNumberify(0); 15 | const amounts = []; 16 | const recipients = []; 17 | 18 | // get all balances 19 | const balances = await Promise.all(accounts 20 | .map(account => wallet.provider.getBalance(account))); 21 | 22 | // go through balances 23 | let index = 0; 24 | for (const balance of balances) { 25 | // if balance is less than tagret, add to amounts / recipients list 26 | if (balance.lt(targetBalance)) { 27 | recipients.push(accounts[index]); 28 | amounts.push(targetBalance.sub(balance)); 29 | total = total.add(targetBalance.sub(balance)); 30 | } 31 | 32 | // increase index 33 | index += 1; 34 | } 35 | 36 | const factory = new ethers.ContractFactory(abi, bytecode, wallet); 37 | const contract = await factory.deploy(amounts, recipients, { 38 | ...opts, 39 | value: total, 40 | }); 41 | 42 | // wait for transaction to process. 43 | return await contract.deployTransaction.wait(); 44 | } catch (error) { 45 | throw new Error(error.message); 46 | } 47 | } 48 | 49 | module.exports = refill; 50 | -------------------------------------------------------------------------------- /packages/refill/src/test/index.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const ethers = require('ethers'); 3 | const refill = require('../index'); 4 | 5 | module.exports = test('gasPrice', async t => { 6 | const fakeWalletProvider = { 7 | provider: { 8 | getBalance: () => { throw new Error('yammed'); }, 9 | }, 10 | }; 11 | await t.catch(refill(fakeWalletProvider, [utils.emptyAddress], {})); 12 | await t.catch(refill()); 13 | 14 | let accounts = (new Array(128)) 15 | .fill(0) 16 | .map(v => (new ethers.Wallet(utils.randomBytes(32), t.getProvider()))); 17 | const addresses = accounts.map(v => v.address); 18 | 19 | for (const account of addresses) { 20 | t.equalBig(await t.getProvider().getBalance(account), 0, 'balance empty'); 21 | } 22 | 23 | const target = utils.parseEther('1.0'); 24 | 25 | await refill(t.getWallets()[0], addresses, target); 26 | 27 | for (const account of addresses) { 28 | t.equalBig(await t.getProvider().getBalance(account), target, 'balance full'); 29 | } 30 | 31 | const acc = new ethers.Wallet(accounts[47].privateKey, t.getProvider()); 32 | 33 | const rec = await acc.sendTransaction({ 34 | to: utils.emptyAddress, 35 | value: utils.parseEther('.30'), 36 | gasLimit: 30000, 37 | gasPrice: await t.getProvider().getGasPrice(), 38 | }); 39 | await rec.wait(); 40 | 41 | t.ok((await t.getProvider().getBalance(addresses[47])).eq(target) !== true, 'balance not 1 ether'); 42 | 43 | await refill(t.getWallets()[0], addresses, target); 44 | 45 | for (const account of addresses) { 46 | t.equalBig(await t.getProvider().getBalance(account), target, 'balance full'); 47 | } 48 | }); 49 | -------------------------------------------------------------------------------- /packages/rolled/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/rolled/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | -------------------------------------------------------------------------------- /packages/rolled/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/rolled 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/rolled/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/rolled", 3 | "version": "0.1.5", 4 | "description": "A rolled-abi implimentation.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "node src/test" 8 | }, 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/fuellabs/fuel-js.git" 15 | }, 16 | "keywords": [ 17 | "fuel", 18 | "js", 19 | "fuel" 20 | ], 21 | "author": "Nick Dodson ", 22 | "license": "Apache-2.0", 23 | "bugs": { 24 | "url": "https://github.com/fuellabs/fuel-js/issues" 25 | }, 26 | "devDependencies": { 27 | "@fuel-js/environment": "^0.1.0" 28 | }, 29 | "dependencies": { 30 | "@fuel-js/utils": "^0.1.0" 31 | }, 32 | "homepage": "https://github.com/fuellabs/fuel-js#readme" 33 | } 34 | -------------------------------------------------------------------------------- /packages/rolled/src/test/index.js: -------------------------------------------------------------------------------- 1 | const { test, utils, BN } = require('@fuel-js/environment'); 2 | const { encode, decode, Coder } = require('../index'); 3 | 4 | module.exports = test('coder test', async t => { 5 | t.equalHex(encode(['bytes1'], ['0xaa']), '0xaa'); 6 | t.equalHex(encode(['bytes32'], [utils.hexZeroPad('0xaa', 32)]), utils.hexZeroPad('0xaa', 32)); 7 | 8 | t.equalHex(encode(['uint8'], [0]), '0x00'); 9 | t.equalHex(encode(['uint8'], ['0xaa']), '0xaa'); 10 | t.equalHex(encode(['uint256'], ['0xaa']), utils.hexZeroPad('0xaa', 32)); 11 | 12 | t.equalHex(encode(['address'], ['0xaa']), utils.hexZeroPad('0xaa', 20)); 13 | 14 | t.equalHex(encode(['bytes1[]'], [['0xaa']]), '0x0001aa'); 15 | t.equalHex(encode(['bytes1[]'], [['0xaa', '0xbb']]), '0x0002aabb'); 16 | t.equalHex(encode(['bytes1[]'], [['0xaa', '0xbb', '0xee']]), '0x0003aabbee'); 17 | 18 | t.equalHex(encode(['bytes2[]'], [['0xaaaa']]), '0x0001aaaa'); 19 | t.equalHex(encode(['bytes2[]'], [['0xaaaa', '0xbbbb']]), '0x0002aaaabbbb'); 20 | t.equalHex(encode(['bytes2[]'], [['0xaaaa', '0xbbbb', '0xeeee']]), '0x0003aaaabbbbeeee'); 21 | 22 | t.equalHex(encode(['tuple(uint8)'], [['0x11']]), '0x11'); 23 | t.equalHex(encode(['tuple(uint8, bytes2)'], [['0x11', '0xeeee']]), '0x11eeee'); 24 | t.equalHex(encode(['tuple(uint8, tuple(uint16))'], [['0x11', ['0xeeee']]]), '0x11eeee'); 25 | 26 | t.equalHex(encode(['tuple(uint8[])'], [[['0x11']]]), '0x000111'); 27 | t.equalHex(encode(['tuple(uint8[], bytes2)'], [[['0x11'], '0xeeee']]), '0x000111eeee'); 28 | t.equalHex(encode(['tuple(uint8, tuple(uint16[]))'], [['0x11', [['0xeeee']]]]), '0x110001eeee'); 29 | 30 | t.equalHex(encode(['bytes1[3]'], ['0xaabbcc']), '0xaabbcc'); 31 | t.equalHex(encode(['bytes1[*]'], [['0xaa', '0xbb', '0xcc']]), '0x03aabbcc'); 32 | t.equal(decode(['bytes1[*]'], '0x03aabbcc'), [['0xaa', '0xbb', '0xcc']]); 33 | t.equal(decode(['bytes1[***]'], '0x000003aabbcc'), [['0xaa', '0xbb', '0xcc']]); 34 | 35 | t.equal(decode(['uint8', 'bytes1[*]'], '0xaa05bbccddeeff'), ['0xaa', ['0xbb', '0xcc', '0xdd', '0xee', '0xff']], 'basix'); 36 | 37 | t.equalHex(encode(['bytes1[*][***]'], [[['0xaa', '0xbb'], ['0xaa', '0xbb']]]), '0x02000002aabb000002aabb', 'complex'); 38 | 39 | // t.throw(() => decode(['bytes1'], '0x'), 'decode-offset-mismatch'); 40 | // t.throw(() => decode(['bytes1'], '0xaabb'), 'decode-offset-mismatch'); 41 | // t.throw(() => decode(['uint8', 'bytes1[*]'], '0xaa05bbccddee'), 'decode-offset-mismatch'); 42 | 43 | const coder = new Coder(['uint8', 'bytes1[*]']); 44 | t.equal(coder.decode('0xaa05bbccddeeff'), ['0xaa', ['0xbb', '0xcc', '0xdd', '0xee', '0xff']], 'coder basix'); 45 | 46 | const coder2 = new Coder(['bytes1[*][***]']); 47 | t.equalHex(coder2.encode([[['0xaa', '0xbb'], ['0xaa', '0xbb']]]), '0x02000002aabb000002aabb', 'coder complex'); 48 | 49 | t.equalHex(coder2.encode([[['0xaa', '0xbb'], ['0xaa', '0xbb']]]), '0x02000002aabb000002aabb', 'coder complex'); 50 | 51 | }); 52 | -------------------------------------------------------------------------------- /packages/rpc/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/rpc/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | -------------------------------------------------------------------------------- /packages/rpc/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/api 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/rpc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/rpc", 3 | "version": "0.1.0", 4 | "description": "A simple RPC interface the Fuel API.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "node src/test" 8 | }, 9 | "publishConfig": { 10 | "access": "public" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/fuellabs/fuel-js.git" 15 | }, 16 | "keywords": [ 17 | "fuel", 18 | "js", 19 | "api" 20 | ], 21 | "dependencies": { 22 | "@fuel-js/interface": "^0.2.0", 23 | "@fuel-js/protocol": "^0.4.6", 24 | "@fuel-js/utils": "^0.1.4" 25 | }, 26 | "devDependencies": { 27 | "@fuel-js/environment": "^0.1.0" 28 | }, 29 | "author": "Nick Dodson ", 30 | "license": "Apache-2.0", 31 | "bugs": { 32 | "url": "https://github.com/fuellabs/fuel-js/issues" 33 | }, 34 | "homepage": "https://github.com/fuellabs/fuel-js#readme" 35 | } 36 | -------------------------------------------------------------------------------- /packages/rpc/src/test/index.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const Api = require('../index'); 3 | 4 | module.exports = test('rpc', async t => { 5 | try { 6 | 7 | const api = new Api('rinkeby'); 8 | 9 | t.ok(await api.getState(), 'get state'); 10 | t.ok(await api.getBlockByHeight(0), 'block by height'); 11 | t.ok(await api.getTokenId(utils.emptyAddress), 'token id'); 12 | t.ok(await api.getToken(1), 'token address'); 13 | t.ok(await api.getAddress(0), 'address registered'); 14 | t.ok(await api.getState(), 'get state'); 15 | t.ok(await api.getTokenMetadata(1), 'get token'); 16 | 17 | } catch (testError) { throw new utils.ByPassError(testError); } 18 | }); 19 | -------------------------------------------------------------------------------- /packages/struct/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | }, 14 | parserOptions: { 15 | ecmaVersion: 2018, 16 | }, 17 | rules: { 18 | 'func-names': ['off'], 19 | 'arrow-parens': ['off'], 20 | 'import/no-extraneous-dependencies': ['error', { devDependencies: ['./src/test/*'] }], 21 | camelcase: ['off'], 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /packages/struct/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /packages/struct/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | -------------------------------------------------------------------------------- /packages/struct/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/struct 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | ## Usage 9 | 10 | Please consult the SDK documentation: 11 | 12 | [docs.fuel.sh](https://docs.fuel.sh) 13 | -------------------------------------------------------------------------------- /packages/struct/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/struct", 3 | "version": "0.1.7", 4 | "description": "A universal struct for Ethereum related javascript tasks with rolled-abi support.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "nyc node src/test", 8 | "lint": "eslint src/index.js --fix", 9 | "prepublish": "npm test && npm run lint" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/fuellabs/fuel-js.git" 14 | }, 15 | "publishConfig": { 16 | "access": "public" 17 | }, 18 | "keywords": [ 19 | "fuel", 20 | "js", 21 | "interface" 22 | ], 23 | "author": "Nick Dodson ", 24 | "license": "Apache-2.0", 25 | "bugs": { 26 | "url": "https://github.com/fuellabs/fuel-js/issues" 27 | }, 28 | "homepage": "https://github.com/fuellabs/fuel-js#readme", 29 | "devDependencies": { 30 | "@fuel-js/environment": "^0.1.0", 31 | "eslint": "^7.2.0", 32 | "nyc": "^15.1.0", 33 | "eslint-config-airbnb-base": "^14.2.0", 34 | "eslint-plugin-import": "^2.21.2" 35 | }, 36 | "dependencies": { 37 | "@ethersproject/abi": "^5.0.5", 38 | "@fuel-js/rolled": "^0.1.5", 39 | "@fuel-js/utils": "^0.1.1" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/struct/src/test/memory.js: -------------------------------------------------------------------------------- 1 | const utils = require('@fuel-js/utils'); 2 | const struct1 = require('../index'); 3 | 4 | const SomeStruct1 = struct1.struct(` 5 | bytes32 nick1, 6 | bytes32 nick2, 7 | bytes32 nick3, 8 | bytes32 nick4, 9 | bytes32 nick5, 10 | bytes32 nick6, 11 | uint8 cool7 12 | `); 13 | 14 | utils.logMemory(); 15 | 16 | let arr = []; 17 | for (var i = 0; i < 1000000; i++) { 18 | arr.push(new SomeStruct1({ 19 | nick1: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 20 | nick2: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 21 | nick3: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 22 | nick4: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 23 | nick5: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 24 | nick6: '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 25 | cool7: 1, 26 | })); 27 | } 28 | 29 | utils.logMemory(); 30 | -------------------------------------------------------------------------------- /packages/utils/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | commonjs: true, 5 | es6: true, 6 | }, 7 | extends: [ 8 | 'airbnb-base', 9 | ], 10 | globals: { 11 | Atomics: 'readonly', 12 | SharedArrayBuffer: 'readonly', 13 | }, 14 | parserOptions: { 15 | ecmaVersion: 2018, 16 | }, 17 | rules: { 18 | 'arrow-parens': ['off'], 19 | 'import/no-extraneous-dependencies': ['error', { devDependencies: ['./src/test/*'] }], 20 | camelcase: ['off'], 21 | }, 22 | }; 23 | -------------------------------------------------------------------------------- /packages/utils/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | .nyc_output 4 | -------------------------------------------------------------------------------- /packages/utils/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | -------------------------------------------------------------------------------- /packages/utils/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/utils 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/xfpK4Pe) 7 | 8 | | Statements | Branches | Functions | Lines | 9 | | ------------------------- | ----------------------- | ------------------------ | -------------------- | 10 | | ![Statements](#branches#) | ![Branches](#branches#) | ![Functions](#branches#) | ![Lines](#branches#) | 11 | 12 | ## Usage 13 | 14 | Please consult the SDK documentation: 15 | 16 | [docs.fuel.sh](https://docs.fuel.sh) 17 | -------------------------------------------------------------------------------- /packages/utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/utils", 3 | "version": "0.1.4", 4 | "description": "Common Fuel utilites.", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "nyc node src/test", 8 | "lint": "eslint src/index.js", 9 | "prepublish": "npm test && npm run lint" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/fuellabs/fuel-js.git" 14 | }, 15 | "publishConfig": { 16 | "access": "public" 17 | }, 18 | "keywords": [ 19 | "fuel", 20 | "js", 21 | "interface" 22 | ], 23 | "author": "Nick Dodson ", 24 | "license": "Apache-2.0", 25 | "bugs": { 26 | "url": "https://github.com/fuellabs/fuel-js/issues" 27 | }, 28 | "homepage": "https://github.com/fuellabs/fuel-js#readme", 29 | "devDependencies": { 30 | "@fuel-js/environment": "0.1.0", 31 | "eslint": "^7.6.0", 32 | "nyc": "^15.1.0", 33 | "eslint-config-airbnb-base": "^14.2.0", 34 | "eslint-plugin-import": "^2.21.2" 35 | }, 36 | "dependencies": { 37 | "bn.js": "^5.1.1", 38 | "ethers": "^4.0.47" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /packages/wallet/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .cache 3 | dist 4 | umd 5 | dist 6 | stats.json 7 | -------------------------------------------------------------------------------- /packages/wallet/.npmignore: -------------------------------------------------------------------------------- 1 | src/test 2 | .cache 3 | browser 4 | dist 5 | stats.json 6 | config 7 | -------------------------------------------------------------------------------- /packages/wallet/README.md: -------------------------------------------------------------------------------- 1 | # @fuel-js/wallet 2 | 3 | > Fuel is a stateless "Layer-2" system for ERC20 transfers and swaps designed for interoperable performance, scale and efficiency. 4 | 5 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) 6 | build status 7 | [![Community](https://img.shields.io/badge/chat%20on-discord-orange?&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.com/invite/xfpK4Pe) 8 | 9 | ## Usage 10 | 11 | Please consult the SDK documentation: 12 | 13 | [docs.fuel.sh](https://docs.fuel.sh) 14 | -------------------------------------------------------------------------------- /packages/wallet/browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packages/wallet/browser/index.js: -------------------------------------------------------------------------------- 1 | const regeneratorRuntime = require("regenerator-runtime"); 2 | // const Api = require('@fuel-js/api'); 3 | // const fuel = require('../dist/fuel.umd.min.js'); 4 | const fuel = require('../src/index'); 5 | // import fuel from "http://localhost:1234/fuel.umd.js"; 6 | 7 | (async () => { 8 | const pk = fuel.utils.hexlify(fuel.utils.randomBytes(32)); 9 | 10 | // create a new wallet with a new key 11 | const wallet = new fuel.Wallet(window.ethereum, { 12 | // privateKey: pk, 13 | network: 'rinkeby', 14 | }); 15 | 16 | console.log('starting faucet for wallet', wallet.address); 17 | 18 | // get yourself some fake Moons 19 | await wallet.faucet(); 20 | 21 | // establish faucet token id 22 | const faucetToken = fuel.constants.faucetToken; 23 | 24 | // get your fake moons balance 25 | console.log('my balance: ', 26 | await wallet.balance(faucetToken, { format: 'ether' })); 27 | 28 | // send yourself some faucet token 29 | const tx = await wallet.transfer(faucetToken, wallet.address, '4.5', { units: 'ether' }); 30 | 31 | // get your fake moons balance 32 | console.log('check out your tx: https://rinkeby.fuel.sh/tx/' + tx.transactionId); 33 | })(); 34 | -------------------------------------------------------------------------------- /packages/wallet/browser/load.js: -------------------------------------------------------------------------------- 1 | const regeneratorRuntime = require("regenerator-runtime"); 2 | // const Api = require('@fuel-js/api'); 3 | // const fuel = require('../dist/fuel.umd.min.js'); 4 | const fuel = require('../src/index'); 5 | 6 | // import fuel from "http://localhost:1234/fuel.umd.js"; 7 | 8 | (async () => { 9 | const pk = fuel.utils.hexlify(fuel.utils.randomBytes(32)); 10 | 11 | const network = 'rinkeby'; 12 | 13 | // create a new wallet with a new key 14 | const wallet = new fuel.Wallet(null, { 15 | privateKey: pk, 16 | network, 17 | }); 18 | 19 | console.log('starting faucet for wallet', wallet.address); 20 | 21 | // get yourself some fake Moons 22 | await wallet.faucet(); 23 | 24 | // establish faucet token id 25 | const faucetToken = fuel.constants.faucetToken; 26 | 27 | // Create 5 wallets. 28 | const wallets = (new Array(20)).fill(0).map(() => new fuel.Wallet(null, { 29 | privateKey: fuel.utils.hexlify(fuel.utils.randomBytes(32)), 30 | network, 31 | })); 32 | 33 | // Wallet of wallets. 34 | console.log('setting up wallets'); 35 | for (const _wallet of wallets) { 36 | // send yourself some faucet token Tx to each address. 37 | await wallet.transfer(faucetToken, _wallet.address, '1', { 38 | units: 'ether', 39 | }); 40 | } 41 | 42 | // 100 per wallet. 43 | async function sendLots(_wallet) { 44 | console.log('starting send load for wallet', _wallet.address); 45 | 46 | for (var k = 0; k < 100; k++) { 47 | // send yourself some faucet token 48 | const tx = await _wallet.transfer(faucetToken, fuel.utils.emptyAddress, '.01', { 49 | units: 'ether', 50 | }); 51 | 52 | // get your fake moons balance 53 | console.log(`check out your tx: https://${network}.fuel.sh/tx/${tx.transactionId}`); 54 | } 55 | } 56 | 57 | // For each wallet send a bunch of tx.s 58 | await Promise.all( 59 | wallets.map(sendLots), 60 | ); 61 | 62 | // get your fake moons balance 63 | console.log('my balance: ', 64 | await wallet.balance(faucetToken, { format: 'ether' })); 65 | 66 | for (var k = 0; k < 100; k++) { 67 | 68 | // send yourself some faucet token 69 | const tx = await wallet.transfer(faucetToken, fuel.utils.emptyAddress, '.1', { 70 | units: 'ether', 71 | }); 72 | 73 | // get your fake moons balance 74 | console.log(`check out your tx: https://${network}.fuel.sh/tx/${tx.transactionId}`); 75 | 76 | } 77 | 78 | })(); 79 | -------------------------------------------------------------------------------- /packages/wallet/browser/local.js: -------------------------------------------------------------------------------- 1 | const regeneratorRuntime = require("regenerator-runtime"); 2 | const fuel = require('../src/index'); 3 | 4 | (async () => { 5 | // Create a new wallet with a new key 6 | const wallet = new fuel.Wallet(window.ethereum, { 7 | network: 'rinkeby', 8 | path: 'http://localhost:3000', 9 | }); 10 | 11 | await wallet.sync(); 12 | 13 | // await wallet.faucet(); 14 | 15 | console.log(await wallet.transfer( 16 | 1, 17 | wallet.address, 18 | fuel.utils.parseEther('1'), 19 | )); 20 | 21 | /* 22 | const pk = fuel.utils.hexlify(fuel.utils.randomBytes(32)); 23 | const wallet2 = new fuel.Wallet(window.ethereum, { 24 | privateKey: pk, 25 | network: 'mainnet', 26 | }); 27 | 28 | console.log('private key', pk); 29 | 30 | console.log(await wallet.transfer( 31 | 0, 32 | wallet2.address, 33 | fuel.utils.parseEther('.0002'), 34 | { 35 | htlc: true, 36 | preImage: fuel.utils.keccak256('0xdeadbead'), 37 | expiry: 5000000, 38 | }, 39 | )); 40 | 41 | const daiAddress = '0x6b175474e89094c44da98b954eedeac495271d0f'; 42 | const usdcAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'; 43 | 44 | */ 45 | /* 46 | console.log( 47 | await wallet.deposit( 48 | usdcAddress, 49 | '0x1312d00', // fuel.utils.parseEther('20'), 50 | { 51 | gasLimit: 300000, 52 | }, 53 | ), 54 | ); 55 | 56 | console.log(await wallet.transfer( 57 | daiAddress, 58 | '0xD2a8dD8F9F4371b636BFE8dd036772957a5D425C', 59 | fuel.utils.parseEther('.00002'))); 60 | 61 | */ 62 | /* 63 | console.log(await wallet.transfer( 64 | daiAddress, 65 | '0xD2a8dD8F9F4371b636BFE8dd036772957a5D425C', 66 | fuel.utils.parseEther('.00002'))); 67 | 68 | console.log(await wallet.transfer( 69 | daiAddress, 70 | '0xD2a8dD8F9F4371b636BFE8dd036772957a5D425C', 71 | fuel.utils.parseEther('.00002'))); 72 | 73 | /* 74 | console.log( 75 | await wallet.deposit( 76 | fuel.utils.emptyAddress, 77 | fuel.utils.parseEther('.001'), 78 | { 79 | gasLimit: 150000, 80 | }, 81 | ), 82 | ); 83 | */ 84 | 85 | })(); 86 | -------------------------------------------------------------------------------- /packages/wallet/browser/rinkeby.js: -------------------------------------------------------------------------------- 1 | const regeneratorRuntime = require("regenerator-runtime"); 2 | const fuel = require('../src/index'); 3 | const Api = require('@fuel-js/api'); 4 | const { setInterval, setTimeout } = require("timers"); 5 | 6 | (async () => { 7 | // Create a new wallet with a new key 8 | const wallet = new fuel.Wallet(window.ethereum, { 9 | network: 'rinkeby', 10 | }); 11 | 12 | wallet.on('deposit-funnel-receipt', console.log); 13 | wallet.on('deposit-commmitment-receipt', console.log); 14 | wallet.on('deposit', console.log); 15 | 16 | const api = new Api('rinkeby'); 17 | 18 | await wallet.sync(); 19 | 20 | await wallet.faucet(); 21 | 22 | console.log(await wallet.transfer( 23 | 1, 24 | '0x73813909482106190c0e0Fa220028d7787c221Dc', 25 | await wallet.balance(1), 26 | )); 27 | 28 | await wallet.sync(); 29 | 30 | })(); -------------------------------------------------------------------------------- /packages/wallet/config/webpack.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const production = process.env.NODE_ENV === 'production'; 3 | 4 | module.exports = { 5 | entry: './src/index.js', 6 | output: { 7 | library: 'fuel', 8 | libraryTarget: 'umd', 9 | filename: `fuel.umd${production ? '.min' : ''}.js`, 10 | path: path.resolve(__dirname, '../umd'), 11 | }, 12 | optimization: { 13 | minimize: production, 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /packages/wallet/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fuel-js/wallet", 3 | "version": "0.5.9", 4 | "description": "Common Fuel utilites.", 5 | "main": "src/index.js", 6 | "types": "src/index.d.ts", 7 | "scripts": { 8 | "start": "parcel browser/index.html", 9 | "build": "npm run build:min && npm run build:unmin", 10 | "build:min": "NODE_ENV=production webpack --config ./config/webpack.js", 11 | "build:unmin": "webpack --config ./config/webpack.js", 12 | "profile": "webpack --profile --json > stats.json", 13 | "prepublish": "npm run build", 14 | "test": "node src/test" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/fuellabs/fuel-js.git" 19 | }, 20 | "publishConfig": { 21 | "access": "public" 22 | }, 23 | "keywords": [ 24 | "fuel", 25 | "js", 26 | "interface" 27 | ], 28 | "author": "Nick Dodson ", 29 | "license": "Apache-2.0", 30 | "bugs": { 31 | "url": "https://github.com/fuellabs/fuel-js/issues" 32 | }, 33 | "homepage": "https://github.com/fuellabs/fuel-js#readme", 34 | "devDependencies": { 35 | "@fuel-js/api": "0.2.9", 36 | "@fuel-js/environment": "^0.1.6", 37 | "regenerator-runtime": "^0.13.7", 38 | "webpack": "^4.20.2", 39 | "webpack-cli": "^3.1.2" 40 | }, 41 | "dependencies": { 42 | "@fuel-js/contracts": "^0.1.8", 43 | "@fuel-js/database": "^0.1.0", 44 | "@fuel-js/interface": "^0.2.0", 45 | "@fuel-js/protocol": "^0.4.6", 46 | "@fuel-js/struct": "^0.1.5", 47 | "@fuel-js/utils": "^0.1.2", 48 | "bn.js": "^5.1.1", 49 | "ethers": "^4.0.47", 50 | "memdown": "^5.1.0", 51 | "pubnub": "^4.28.4", 52 | "elliptic": ">=6.5.4", 53 | "stream-to-array": "^2.3.0" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/wallet/src/abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | "transfer(address,uint256)", 3 | "balanceOf(address) external view returns (uint256)", 4 | "deposit(address account, address token) external", 5 | "commitRoot(bytes32 merkleTreeRoot, uint256 token,\n uint256 fee, bytes transactions) external", 6 | "commitBlock(uint32 minimum, bytes32 minimumHash, uint32 height, bytes32[] roots) external", 7 | "commitWitness(bytes32 transactionId) external", 8 | "commitAddress(address addr) external returns (uint256 id)", 9 | "withdraw(bytes proof) external", 10 | "funnel(address account) external view returns (address)", 11 | "tokenId(address token) external view returns (uint256 tokenId)", 12 | "addressId(address owner) external view returns (uint256 addressId)", 13 | "BOND_SIZE() external view returns (uint256)", 14 | "FINALIZATION_DELAY() external view returns (uint256)", 15 | "SUBMISSION_DELAY() external view returns (uint256)", 16 | "rootBlockNumberAt(bytes32 root) external view returns (uint256 blockNumber)", 17 | "event TokenIndexed(address indexed token, uint256 indexed id)", 18 | "event AddressIndexed(address indexed owner, uint256 indexed id)", 19 | "event FraudCommitted(uint256 indexed previousTip, uint256 indexed currentTip, uint256 indexed fraudCode)", 20 | "event BlockCommitted(\n address producer,\n uint256 numTokens,\n uint256 numAddresses,\n bytes32 indexed previousBlockHash,\n uint256 indexed height,\n bytes32[] roots)", 21 | "event RootCommitted(\n bytes32 indexed root,\n address rootProducer,\n uint256 feeToken,\n uint256 fee,\n uint256 rootLength,\n bytes32 indexed merkleTreeRoot,\n bytes32 indexed commitmentHash)", 22 | "event WitnessCommitted(\n address indexed owner,\n uint256 blockNumber,\n bytes32 indexed transactionId)", 23 | "event DepositMade(address indexed owner, uint32 indexed token, uint256 value)", 24 | "event WithdrawalMade(\n address indexed owner,\n address tokenAddress,\n uint256 amount,\n uint32 indexed blockHeight,\n uint32 rootIndex,\n bytes32 indexed transactionLeafHash,\n uint8 outputIndex,\n uint32 transactionIndex)" 25 | ] 26 | -------------------------------------------------------------------------------- /packages/wallet/src/index.d.ts: -------------------------------------------------------------------------------- 1 | import BN from 'bn.js'; 2 | 3 | interface WalletOptions { 4 | key?: string; 5 | network?: string; 6 | } 7 | 8 | export class Wallet { 9 | public address: string; 10 | 11 | constructor(provider: any, options?: any); 12 | 13 | balance(token: any, options?: any): Promise; 14 | deposit(token: any, amount: any, opts?: any): Promise; 15 | transfer(token: any, recipient: any, amount: any, opts?: any): Promise; 16 | estimateGasCost(token: any, recipient: any, amount: any, opts?: any): Promise; 17 | faucet(): Promise; 18 | _tokenId(token: any): Promise; 19 | _token(id: number): Promise; 20 | 21 | sync(): Promise; 22 | withdraw(token: any, amount: any, opts?: any): Promise; 23 | retrieve(token: any, opts?: any): Promise; 24 | fee(token: any): Promise; 25 | 26 | on(name: string, cb: Function); 27 | off(name: string); 28 | } 29 | 30 | interface Utils { 31 | parseEther(eth: any): string; 32 | formatEther(wei: any, unit: string): string; 33 | } 34 | 35 | export const utils: Utils; 36 | -------------------------------------------------------------------------------- /packages/wallet/src/test/htlc.js: -------------------------------------------------------------------------------- 1 | const { test, utils } = require('@fuel-js/environment'); 2 | const schema = require('@fuel-js/interface'); 3 | const Api = require('@fuel-js/api'); 4 | const ethers = require('ethers'); 5 | const fuel = require('../index'); 6 | 7 | module.exports = test('htlc', async t => { 8 | const network = 'unspecified'; 9 | const path = 'http://localhost:3000'; 10 | const jsonProvider = new ethers.providers.JsonRpcProvider('http://localhost:8545'); 11 | 12 | // Setup Addresses 13 | const producer = t.wallets[0].connect(jsonProvider); 14 | const cold = t.wallets[1].connect(jsonProvider); 15 | const userA = t.wallets[2].connect(jsonProvider); 16 | const userB = t.wallets[3].connect(jsonProvider); 17 | 18 | // Special API key. 19 | const apiKey = utils.hexDataSlice( 20 | utils.keccak256('0xbebebe'), 21 | 12, 22 | 32, 23 | ); 24 | 25 | const api = new Api(network, { 26 | url: 'http://localhost:3000', 27 | }); 28 | 29 | const walletB = new fuel.Wallet(jsonProvider, { 30 | privateKey: utils.hexlify(utils.randomBytes(32)), 31 | network, 32 | path, 33 | apiKey, 34 | }); 35 | 36 | console.log('wallet b address', walletB.address); 37 | 38 | await walletB.sync(); 39 | 40 | await walletB.faucet(); 41 | 42 | const preImageA = utils.keccak256('0xdeadbeaf'); 43 | 44 | t.ok(await walletB.transfer( 45 | 1, 46 | walletB.address, 47 | utils.parseEther('500'), 48 | { 49 | htlc: true, 50 | preImage: preImageA, 51 | expiry: (await api.getState()) 52 | .properties.blockNumber().get() 53 | .add(500), 54 | }, 55 | ), 'transfer full to self'); 56 | 57 | await walletB.sync(); 58 | 59 | t.equalBig(await walletB.balance(1), utils.parseEther('500'), 60 | 'wallet balance after HTLC'); 61 | 62 | t.equalBig(await walletB.balance(1, { 63 | htlc: true, 64 | preimages: [preImageA], 65 | }), utils.parseEther('1000'), 'wallet balance after HTLC'); 66 | 67 | t.ok(await walletB.transfer( 68 | 1, 69 | walletB.address, 70 | utils.parseEther('1000'), 71 | { 72 | preimages: [preImageA], 73 | }, 74 | ), 'transfer full to self after htlc'); 75 | 76 | t.equalBig(await walletB.balance(1), utils.parseEther('1000'), 77 | 'wallet balance after HTLC'); 78 | 79 | walletB.off(); 80 | 81 | await walletB.db.close(); 82 | }); 83 | -------------------------------------------------------------------------------- /packages/wallet/src/test/index.js: -------------------------------------------------------------------------------- 1 | (async () => { 2 | 3 | // run all tests 4 | // await require('./deposit'); 5 | await require('./htlc'); 6 | await require('./transfer'); 7 | 8 | })(); 9 | --------------------------------------------------------------------------------