├── .gitignore ├── .npmignore ├── .DS_Store ├── dist └── .DS_Store ├── .babelrc ├── src ├── services │ ├── subprovider.js │ ├── nonceTracker.js │ ├── httpSubprovider.js │ └── wsSubprovider.js └── index.js ├── package.json ├── webpack.config.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmb84/fuel-web3-provider/HEAD/.DS_Store -------------------------------------------------------------------------------- /dist/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmb84/fuel-web3-provider/HEAD/dist/.DS_Store -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "test": { 4 | "presets": ["@babel/preset-env"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/services/subprovider.js: -------------------------------------------------------------------------------- 1 | const createPayload = require('web3-provider-engine/util/create-payload.js') 2 | 3 | class Subprovider { 4 | setEngine (engine) { 5 | const self = this 6 | self.engine = engine 7 | engine.on('block', function (block) { 8 | self.currentBlock = block 9 | }) 10 | } 11 | handleRequest (payload, next, end) { 12 | throw new Error('Subproviders should override `handleRequest`.') 13 | } 14 | emitPayload (payload, cb) { 15 | const self = this 16 | self.engine.sendAsync(createPayload(payload), cb) 17 | } 18 | } 19 | 20 | module.exports = Subprovider 21 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fuel-web3-provider", 3 | "version": "1.3.0", 4 | "description": "Web3 provider for interacting with meta-tx fueling service", 5 | "main": "./dist/fuelWeb3Provider.node.js", 6 | "browser": "./dist/fuelWeb3Provider.js", 7 | "scripts": { 8 | "build": "npm run build-dist", 9 | "build-dist": "webpack --config webpack.config.js", 10 | "build-dist-prod": "webpack --config webpack.prod.js", 11 | "test": "jest --forceExit --coverage --verbose" 12 | }, 13 | "author": "Abdallah-Hossayn Mokhtar Bacha ", 14 | "license": "AGPL-3.0", 15 | "dependencies": { 16 | "@babel/core": "^7.0.0-beta.51", 17 | "@babel/preset-env": "^7.0.0-beta.51", 18 | "axios": "^0.18.0", 19 | "babel-loader": "^8.0.0-beta.4", 20 | "bignumber.js": "^7.2.1", 21 | "bluebird": "^3.5.1", 22 | "crypto-js": "^3.1.9-1", 23 | "eth-sig-util": "^1.2.1", 24 | "ethereumjs-util": "^5.1.1", 25 | "ethjs": "^0.2.6", 26 | "js-scrypt": "^0.2.0", 27 | "left-pad": "^1.3.0", 28 | "request": "^2.85.0", 29 | "solidity-sha3": "^0.4.1", 30 | "uport-identity": "^2.0.1", 31 | "util": "^0.11.0", 32 | "web3": "^1.0.0-beta.33", 33 | "web3-eth-abi": "^1.0.0-beta.35", 34 | "web3-provider-engine": "^14.0.5", 35 | "webpack-cli": "^3.0.8", 36 | "xmlhttprequest": "^1.8.0" 37 | }, 38 | "devDependencies": { 39 | "babel-cli": "^6.26.0", 40 | "babel-preset-env": "^1.7.0", 41 | "chai": "^4.1.2", 42 | "nodemon": "^1.13.0", 43 | "source-map-loader": "^0.2.3", 44 | "standard": "^11.0.1", 45 | "webpack": "^4.14.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/services/nonceTracker.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const Subprovider = require('./subprovider') 3 | const Transaction = require('ethereumjs-tx') 4 | const ethUtil = require('ethereumjs-util') 5 | const blockTagForPayload = require('web3-provider-engine/util/rpc-cache-utils').blockTagForPayload 6 | 7 | class NonceTrackerSubprovider extends Subprovider { 8 | constructor (opts) { 9 | super() 10 | this.nonceCache = {} 11 | this.handleRequest = this.handleRequest.bind(this) 12 | } 13 | handleRequest (payload, next, end) { 14 | const self = this 15 | switch (payload.method) { 16 | case 'eth_getTransactionCount': 17 | const blockTag = blockTagForPayload(payload) 18 | const address = payload.params[0].toLowerCase() 19 | const cachedResult = self.nonceCache[address] 20 | if (blockTag === 'pending') { 21 | if (cachedResult) { 22 | end(null, cachedResult) 23 | } else { 24 | next((error, result, cb) => { 25 | if (error) { return cb() } 26 | if (self.nonceCache[address] === undefined) { 27 | self.nonceCache[address] = result 28 | } 29 | cb() 30 | }) 31 | } 32 | } else { 33 | next() 34 | } 35 | return 36 | case 'eth_sendRawTransaction': 37 | next((error, result, cb) => { 38 | if (error) { 39 | return cb() 40 | } 41 | const rawTx = payload.params[0].metaSignedTx 42 | const tx = new Transaction(Buffer.from(ethUtil.stripHexPrefix(rawTx), 'hex')) 43 | const address = '0x' + tx.to.toString('hex') 44 | let nonce = ethUtil.bufferToInt(tx.nonce) 45 | nonce++ 46 | let hexNonce = nonce.toString(16) 47 | if (hexNonce.length % 2) { 48 | hexNonce = '0' + hexNonce 49 | } 50 | hexNonce = '0x' + hexNonce 51 | self.nonceCache[address] = hexNonce 52 | cb() 53 | }) 54 | return 55 | default: 56 | next() 57 | } 58 | } 59 | } 60 | 61 | module.exports = NonceTrackerSubprovider 62 | -------------------------------------------------------------------------------- /src/services/httpSubprovider.js: -------------------------------------------------------------------------------- 1 | const xhr = require('xhr') 2 | const inherits = require('util').inherits 3 | const createPayload = require('web3-provider-engine/util/create-payload.js') 4 | const Subprovider = require('./subprovider.js') 5 | const JsonRpcError = require('json-rpc-error') 6 | 7 | 8 | module.exports = RpcSource 9 | 10 | inherits(RpcSource, Subprovider) 11 | 12 | function RpcSource(opts) { 13 | this.rpcUrl = opts.rpcUrl 14 | this.fuelUrl = opts.fuelUrl 15 | } 16 | 17 | RpcSource.prototype.handleRequest = function(payload, next, end){ 18 | // overwrite id to conflict with other concurrent users 19 | let newPayload = createPayload(payload) 20 | 21 | 22 | if (payload.method === 'eth_sendRawTransaction') { 23 | newPayload = payload.params[0] 24 | newPayload.jsonRpcReponse = true 25 | newPayload.id = payload.id 26 | } 27 | 28 | xhr({ 29 | uri: payload.method === ('eth_sendRawTransaction') ? (this.fuelUrl) : (this.rpcUrl), 30 | method: 'POST', 31 | headers: { 32 | 'Accept': 'application/json', 33 | 'Content-Type': 'application/json', 34 | }, 35 | body: JSON.stringify(newPayload), 36 | rejectUnauthorized: false, 37 | }, (err, res, body) => { 38 | if (err) return end(new JsonRpcError.InternalError(err)) 39 | // check for error code 40 | switch (res.statusCode) { 41 | case 405: 42 | return end(new JsonRpcError.MethodNotFound()) 43 | case 504: // Gateway timeout 44 | let msg = `Gateway timeout. The request took too long to process. ` 45 | msg += `This can happen when querying logs over too wide a block range.` 46 | const err = new Error(msg) 47 | return end(new JsonRpcError.InternalError(err)) 48 | default: 49 | if (res.statusCode != 200) { 50 | return end(new JsonRpcError.InternalError(res.body)) 51 | } 52 | } 53 | 54 | // parse response 55 | let data 56 | try { 57 | data = JSON.parse(body) || body 58 | } catch (error) { 59 | console.error(error.stack) 60 | return end(new JsonRpcError.InternalError(err)) 61 | } 62 | if (data.error) return end(data.error) 63 | 64 | end(null, data.result) 65 | }) 66 | 67 | } 68 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const webpack = require('webpack') 4 | const path = require('path') 5 | 6 | // Plugin Setup 7 | const globalsPlugin = new webpack.DefinePlugin({ 8 | __DEV__: JSON.stringify(JSON.parse(process.env.BUILD_DEV || 'true')), 9 | 'process.env': { NODE_ENV: JSON.stringify('development') } 10 | }) 11 | 12 | const libraryName = 'fuelWeb3Provider' 13 | 14 | const serverConfig = { 15 | entry: { [libraryName]: './src/index.js' }, 16 | devtool: 'source-map', 17 | output: { 18 | filename: '[name].node.js', 19 | library: libraryName, 20 | libraryTarget: 'umd', 21 | umdNamedDefine: true 22 | }, 23 | 'target': 'node', // To work with node 24 | module: { 25 | rules: [ 26 | { test: /\.(t|j)sx?$/, use: { loader: 'babel-loader' } }, 27 | // addition - add source-map support 28 | { enforce: 'pre', test: /\.js$/, loader: 'source-map-loader' } 29 | ] 30 | }, 31 | node: { 32 | process: false, // To work with node 33 | console: false, 34 | fs: 'empty', 35 | net: 'empty', 36 | tls: 'empty' 37 | }, 38 | resolve: { 39 | modules: ['./src', 'node_modules'], 40 | extensions: ['.ts', '.js', '.json'], 41 | alias: { 42 | 'fsevents': path.join(__dirname, './nil.js'), 43 | 'original-fs': path.join(__dirname, './nil.js'), 44 | 'scrypt': 'js-scrypt', 45 | 'fs': path.join(__dirname, './src/nil.js'), 46 | 'swarm-js': path.resolve(__dirname, './node_modules/swarm-js/lib/api-browser.js'), 47 | 'fs-promise': path.join(__dirname, './src/nil.js') 48 | } 49 | }, 50 | plugins: [globalsPlugin, 51 | new webpack.DefinePlugin({ 52 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV) 53 | }), 54 | new webpack.IgnorePlugin(/^electron$/)] 55 | } 56 | 57 | const clientConfig = { 58 | entry: { [libraryName]: './src/index.js' }, 59 | devtool: 'source-map', 60 | output: { 61 | filename: '[name].js', 62 | library: libraryName, 63 | libraryTarget: 'umd', 64 | umdNamedDefine: true 65 | }, 66 | target: 'web', 67 | module: { 68 | rules: [ 69 | { test: /\.(t|j)sx?$/, use: { loader: 'babel-loader' } }, 70 | // addition - add source-map support 71 | { enforce: 'pre', test: /\.js$/, loader: 'source-map-loader' } 72 | ] 73 | }, 74 | node: { 75 | console: false, 76 | fs: 'empty', 77 | net: 'empty', 78 | tls: 'empty' 79 | }, 80 | resolve: { 81 | modules: ['./src', 'node_modules'], 82 | extensions: ['.ts', '.js', '.json'] 83 | }, 84 | plugins: [globalsPlugin] 85 | } 86 | 87 | module.exports = [ clientConfig, serverConfig ] 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.com/Frontier-project/FrontierJS.svg?token=DJeMzxJJncp3nRaEUuxH&branch=master)](https://travis-ci.com/Frontier-project/FrontierJS) 2 | 3 | # fuel-web3-provider 4 | 5 | ## Integrate Fuel in your Dapp 6 | 7 | FuelWeb3Provider provides a simple way for you to integrate Fuel service into your javascript application. 8 | 9 | ## What is Fuel? 10 | 11 | Fuel is strongly inspired by [Sensui](https://github.com/ConsenSys/lambda-sensui), the [uPort](https://uport.me) transactions funding service, and his meta-transaction system conceived by Dr. Christian Lundkvist. Sensui works just for uPort users, Fuel enable developers to add a funding system to his Dapp and so make transactions feesless for the end user. 12 | 13 | ## Install 14 | 15 | You can install the library via `npm`: 16 | 17 | ```javascript 18 | npm i fuel-web3-provider 19 | ``` 20 | 21 | ## Smart Contract Compatibality 22 | 23 | As it very well explained [here](https://github.com/uport-project/uport-identity/blob/develop/docs/reference/txRelay.md) your smart contract need some change in order to support meta-tx. 24 | 25 | 1. Add sender param sender to all functions and modifiers that include a reference to msg.sender a. This param should be the first param of the function call 26 | 2. All references to msg.sender should be changed to sender 27 | 3. Add a modifier onlyAuthorized to all functions changed in this manner a. Allows the relay contract to call these functions. Otherwise, anyone could claim to be whomever they wanted to be b. Also allows users to call these functions themselves without meta-tx with the use of checkMessageData function 28 | 29 | ## Config 30 | 31 | In your application you must first configure your FrontierJS object. The constructor uses the following parameters in order to work: 32 | 33 | | Param | Description | 34 | | ------------------ | -------------------------------------------------------------------- | 35 | | `privateKey` | Your private Ethreum key | 36 | | `rpcUrl` | An Ethreum node RPC uri, providing access to Ethereum for now it should be websocket | 37 | | `fuelUrl` | The uri of the fuel server | 38 | | `network` | Mainnet, Rinkeby, Kovan, or Ropsten | 39 | | `txRelayAddress` | The address of the contract relayer | 40 | | `txSenderAddress` | The address of the sender of the transaction, the gas provider | 41 | | `whiteListAddress` | The white listed address | 42 | 43 | ## Usage 44 | 45 | In order to use it, just import the library and pass the previous params in the constructor. 46 | By default the library is loaded with the following value, so you just have to pass a private key and then you can start to use Fuel. 47 | 48 | ```javascript 49 | import Web3 from 'web3'; 50 | import FuelProvider from 'web3-fuel-provider'; 51 | import targetContractAbi from './targetContractAbi.json'; 52 | 53 | const fuelProvider = new FuelProvider( 54 | { 55 | privateKey: "YOUR_PRIVATE_KEY", 56 | rpcUrl: 'wss://rinkeby.infura.io/ws', 57 | fuelUrl: 'https://bagas.app/api/relay', 58 | network: 'rinkeby', 59 | txRelayAddress: '0xda8c6dce9e9a85e6f9df7b09b2354da44cb48331', 60 | txSenderAddress: '0x00B8FBD65D61b7DFe34b9A3Bb6C81908d7fFD541', 61 | whiteListAddress: '0x0000000000000000000000000000000000000000' 62 | } 63 | ) 64 | 65 | ``` 66 | 67 | And then pass the provider to web3 use it in the normal way. 68 | 69 | ```javascript 70 | const web3 = new Web3(fuelProvider.start()); 71 | const targetContract = new web3.eth.Contract( 72 | targetContractAbi, 73 | '0xaaf1bbc703a6f9ebe8f8c171f4dc0d60c8b4b1b8' 74 | ); 75 | 76 | targetContract.methods 77 | .register('0x' + config.address, 1) 78 | .send({ 79 | from: '0x' + config.address 80 | }) 81 | .on('error', error => { 82 | console.log(error); 83 | }) 84 | .on('transactionHash', transactionHash => { 85 | console.log('This the transactionHash', transactionHash); 86 | }); 87 | ``` 88 | 89 | [Here](https://github.com/ahmb84/fuel-node-example) you will can find a reference implementation. 90 | 91 | ## Demo 92 | 93 | ![Alt Text](https://media.giphy.com/media/5BPWeEFH2Aqo4Xv6no/giphy.gif) 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/services/wsSubprovider.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const Subprovider = require('./subprovider') 3 | const WebSocket = window.WebSocket 4 | const axios = require('axios') 5 | const Backoff = require('backoff') 6 | const EventEmitter = require('events') 7 | const createPayload = require('web3-provider-engine/util/create-payload') 8 | 9 | class FuelSubprovider extends Subprovider { 10 | constructor ({ rpcUrl, fuelUrl, debug }) { 11 | super() 12 | EventEmitter.call(this) 13 | this._backoff = Backoff.exponential({ 14 | randomisationFactor: 0.2, 15 | maxDelay: 5000 16 | }) 17 | this._connectTime = null 18 | this._log = debug ? (...args) => console.info(console, ['[WSProvider]', ...args]) : () => { } 19 | this._pendingRequests = new Map() 20 | this._socket = null 21 | this._unhandledRequests = [] 22 | this._rpcUrl = rpcUrl 23 | this._fuelUrl = fuelUrl 24 | this._axios = axios.create({ 25 | baseURL: fuelUrl, 26 | headers: { 'Content-Type': 'application/json' } 27 | }) 28 | this._handleSocketClose = this._handleSocketClose.bind(this) 29 | this._handleSocketMessage = this._handleSocketMessage.bind(this) 30 | this._handleSocketOpen = this._handleSocketOpen.bind(this) 31 | // Called when a backoff timeout has finished. Time to try reconnecting. 32 | this._backoff.on('ready', () => { 33 | this._openSocket() 34 | }) 35 | this._openSocket() 36 | } 37 | handleRequest (payload, next, end) { 38 | if (!this._socket || this._socket.readyState !== WebSocket.OPEN) { 39 | this._unhandledRequests.push(Array.from(arguments)) 40 | this._log('Socket not open. Request queued.') 41 | return 42 | } 43 | if (payload.method === 'eth_sendRawTransaction') { 44 | this._pendingRequests.set(payload.id, [payload, end]) 45 | const newPayload = payload.params[0] 46 | newPayload.jsonRpcReponse = true 47 | newPayload.id = payload.id 48 | this._axios.post('', newPayload) 49 | } else { 50 | this._pendingRequests.set(payload.id, [payload, end]) 51 | const newPayload = createPayload(payload) 52 | delete newPayload.origin 53 | this._socket.send(JSON.stringify(newPayload)) 54 | this._log(`Sent: ${newPayload.method} #${newPayload.id}`) 55 | } 56 | } 57 | _handleSocketClose ({ reason, code }) { 58 | this._log(`Socket closed, code ${code} (${reason || 'no reason'})`) 59 | // If the socket has been open for longer than 5 seconds, reset the backoff 60 | if (this._connectTime && Date.now() - this._connectTime > 5000) { 61 | this._backoff.reset() 62 | } 63 | this._socket.removeEventListener('close', this._handleSocketClose) 64 | this._socket.removeEventListener('message', this._handleSocketMessage) 65 | this._socket.removeEventListener('open', this._handleSocketOpen) 66 | this._socket = null 67 | this._backoff.backoff() 68 | } 69 | _handleSocketMessage (message) { 70 | let payload 71 | try { 72 | payload = JSON.parse(message.data) 73 | } catch (e) { 74 | this._log('Received a message that is not valid JSON:', payload) 75 | return 76 | } 77 | if (payload.id === undefined) { 78 | return this.emit('data', null, payload) 79 | } 80 | if (!this._pendingRequests.has(payload.id)) { 81 | return 82 | } 83 | const [originalReq, end] = this._pendingRequests.get(payload.id) 84 | this._pendingRequests.delete(payload.id) 85 | this._log(`Received: ${originalReq.method} #${payload.id}`) 86 | if (payload.error) { 87 | return end(new Error(payload.error.message)) 88 | } 89 | end(null, payload.result) 90 | } 91 | _handleSocketOpen () { 92 | this._log('Socket open.') 93 | this._connectTime = Date.now() 94 | // Any pending requests need to be resent because our session was lost 95 | // and will not get responses for them in our new session. 96 | this._pendingRequests.forEach((value) => this._unhandledRequests.push(value)) 97 | this._pendingRequests.clear() 98 | const unhandledRequests = this._unhandledRequests.splice(0, this._unhandledRequests.length) 99 | unhandledRequests.forEach((request) => { 100 | this.handleRequest.apply(this, request) 101 | }) 102 | } 103 | _openSocket () { 104 | this._log('Opening socket...') 105 | this._socket = new WebSocket(this._rpcUrl) 106 | this._socket.addEventListener('close', this._handleSocketClose) 107 | this._socket.addEventListener('message', this._handleSocketMessage) 108 | this._socket.addEventListener('open', this._handleSocketOpen) 109 | this._axios.interceptors.response.use((response) => { 110 | this._handleSocketMessage({ data: JSON.stringify(response.data) }) 111 | }, (error) => { 112 | return Promise.reject(error) 113 | }) 114 | } 115 | } 116 | // multiple inheritance 117 | Object.assign(FuelSubprovider.prototype, EventEmitter.prototype) 118 | 119 | module.exports = FuelSubprovider 120 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const BlueBird = require('bluebird') 2 | const Transaction = require('ethereumjs-tx') 3 | const UportIdentity = require('uport-identity') 4 | const Web3 = require('web3') 5 | const ProviderEngine = require('web3-provider-engine') 6 | const CacheSubprovider = require('web3-provider-engine/subproviders/cache.js') 7 | const FixtureSubprovider = require('web3-provider-engine/subproviders/fixture.js') 8 | const FilterSubprovider = require('web3-provider-engine/subproviders/filters.js') 9 | const HookedWalletSubprovider = require('web3-provider-engine/subproviders/hooked-wallet.js') 10 | const SubscriptionSubprovider = require('web3-provider-engine/subproviders/subscriptions') 11 | const NonceTrackerSubprovider = require('./services/nonceTracker') 12 | const WsSubprovider = require('./services/wsSubprovider') 13 | const HttpSubprovider = require('./services/httpSubprovider.js') 14 | 15 | const engine = new ProviderEngine() 16 | const TxRelaySigner = require('./services/eth-signer/txRelaySigner') 17 | const txRelayArtifact = UportIdentity.TxRelay.v2 18 | const KeyPair = require('./services/eth-signer/generators/keyPair') 19 | 20 | class Provider { 21 | constructor ( 22 | { privateKey, 23 | web3Provider, 24 | rpcUrl = 'https://rinkeby.infura.io/', 25 | fuelUrl = 'http://localhost:5000/relay', 26 | network = 'rinkeby', 27 | txRelayAddress = '0xda8c6dce9e9a85e6f9df7b09b2354da44cb48331', 28 | txSenderAddress = '0x00B8FBD65D61b7DFe34b9A3Bb6C81908d7fFD541', 29 | whiteListAddress = '0x0000000000000000000000000000000000000000' 30 | } 31 | ) { 32 | this.rpcUrl = rpcUrl 33 | this.fuelUrl = fuelUrl 34 | this.network = network 35 | this.txRelayAddress = txRelayAddress 36 | this.txSenderAddress = txSenderAddress 37 | this.whiteListAddress = whiteListAddress 38 | this.isWeb3Provided = web3Provider !== undefined 39 | this.web3 = ((this.isWeb3Provided === true) ? new Web3(web3Provider.currentProvider) : new Web3(rpcUrl)) 40 | this.asyncWeb3 = BlueBird.promisifyAll(this.web3.eth) 41 | this.TxRelay = BlueBird.promisifyAll(new this.web3.eth.Contract(txRelayArtifact.abi, txRelayAddress)) 42 | this.senderKeyPair = {} 43 | if (this.isWeb3Provided === true) { 44 | this.senderKeyPair.address = window.web3.eth.defaultAccount 45 | } else if (privateKey !== undefined) { 46 | this.senderKeyPair = KeyPair.fromPrivateKey(privateKey) 47 | } else { 48 | throw new Error('A web instance, or a private key, should be passed') 49 | } 50 | this.txRelaySigner = new TxRelaySigner( 51 | this.senderKeyPair, 52 | this.txRelayAddress, 53 | this.txSenderAddress, 54 | this.whiteListAddress 55 | ) 56 | this.signFunction = async (hash) => { 57 | const sig = await this.asyncWeb3.signAsync(hash, this.senderKeyPair.address) 58 | return sig 59 | } 60 | this.start = this.start 61 | } 62 | start () { 63 | engine.addProvider( 64 | new FixtureSubprovider({ 65 | web3_clientVersion: 'ProviderEngine/v0.0.0/javascript', 66 | net_listening: true, 67 | eth_hashrate: '0x00', 68 | eth_mining: false, 69 | eth_syncing: true 70 | }) 71 | ) 72 | engine.addProvider(new CacheSubprovider()) 73 | engine.addProvider(new FilterSubprovider()) 74 | engine.addProvider(new NonceTrackerSubprovider()) 75 | // engine.addProvider(new VmSubprovider()) 76 | engine.addProvider( 77 | new HookedWalletSubprovider({ 78 | getAccounts: (cb) => { 79 | console.log(this.senderKeyPair.address) 80 | cb(null, [this.senderKeyPair.address]) 81 | }, 82 | getPrivateKey: (cb) => { 83 | cb(null, this.senderKeyPair.privateKey) 84 | }, 85 | signTransaction: async (txParams, callback) => { 86 | const txRelayNonce = await this.TxRelay.methods 87 | .getNonce(this.senderKeyPair.address).call() 88 | txParams.nonce = Web3.utils.toHex(txRelayNonce) 89 | const tx = new Transaction(txParams) 90 | const rawTx = '0x' + tx.serialize().toString('hex') 91 | let metaSignedTx 92 | if (this.isWeb3Provided === true) { 93 | metaSignedTx = await this.txRelaySigner.signRawTx(rawTx, this.signFunction) 94 | } else { 95 | metaSignedTx = await this.txRelaySigner.signRawTx(rawTx) 96 | } 97 | const params = { 98 | metaNonce: txParams.nonce, 99 | metaSignedTx, 100 | blockchain: this.network 101 | } 102 | console.log(params) 103 | callback(null, params) 104 | } 105 | }) 106 | ) 107 | 108 | const connectionType = getConnectionType(this.rpcUrl) 109 | 110 | if (connectionType === 'ws') { 111 | const filterSubprovider = new FilterSubprovider() 112 | engine.addProvider(filterSubprovider) 113 | engine.addProvider( 114 | new WsSubprovider({ 115 | rpcUrl: this.rpcUrl, 116 | fuelUrl: this.fuelUrl 117 | }) 118 | ) 119 | } else { 120 | const filterAndSubsSubprovider = new SubscriptionSubprovider() 121 | filterAndSubsSubprovider.on('data', (err, notification) => { 122 | engine.emit('data', err, notification) 123 | }) 124 | engine.addProvider(filterAndSubsSubprovider) 125 | 126 | engine.addProvider(new HttpSubprovider({ 127 | rpcUrl: this.rpcUrl, 128 | fuelUrl: this.fuelUrl 129 | })) 130 | } 131 | engine.on('error', error => { 132 | console.error(error.stack) 133 | }) 134 | engine.start() 135 | return engine 136 | } 137 | } 138 | 139 | function getConnectionType (rpcUrl) { 140 | if (!rpcUrl) return undefined 141 | 142 | const protocol = rpcUrl.split(':')[0] 143 | switch (protocol) { 144 | case 'http': 145 | case 'https': 146 | return 'http' 147 | case 'ws': 148 | case 'wss': 149 | return 'ws' 150 | default: 151 | throw new Error(`ProviderEngine - unrecognized protocol in "${rpcUrl}"`) 152 | } 153 | } 154 | 155 | module.exports = Provider 156 | --------------------------------------------------------------------------------