├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── babel.config.js
├── lerna-debug.log
├── lerna.json
├── package-lock.json
├── package.json
├── packages
├── bitcoin
│ ├── README.md
│ ├── __tests__
│ │ └── bitcoin.test.js
│ ├── lib
│ │ └── bitcoin.js
│ ├── package-lock.json
│ ├── package.json
│ ├── prepare.js
│ ├── yarn-error.log
│ └── yarn.lock
├── core
│ ├── README.md
│ ├── __tests__
│ │ └── core.test.js
│ ├── lib
│ │ ├── core.js
│ │ ├── migrations
│ │ │ ├── index.js
│ │ │ └── migrator.js
│ │ ├── models
│ │ │ ├── Account.js
│ │ │ ├── AccountAction.js
│ │ │ ├── AuthorizedApp.js
│ │ │ ├── Blockchains.js
│ │ │ ├── Contact.js
│ │ │ ├── CreditCard.js
│ │ │ ├── Explorer.js
│ │ │ ├── Identity.js
│ │ │ ├── Keychain.js
│ │ │ ├── Keypair.js
│ │ │ ├── Locale.js
│ │ │ ├── Meta.js
│ │ │ ├── Network.js
│ │ │ ├── Permission.js
│ │ │ ├── Scatter.js
│ │ │ ├── Settings.js
│ │ │ ├── Token.js
│ │ │ ├── api
│ │ │ │ ├── ApiActions.js
│ │ │ │ └── index.js
│ │ │ ├── errors
│ │ │ │ ├── Error.js
│ │ │ │ ├── ErrorTypes.js
│ │ │ │ └── index.js
│ │ │ ├── hardware
│ │ │ │ ├── ExternalWallet.js
│ │ │ │ └── index.js
│ │ │ ├── histories
│ │ │ │ ├── HistoricAction.js
│ │ │ │ ├── HistoricExchange.js
│ │ │ │ ├── HistoricTransfer.js
│ │ │ │ ├── History.js
│ │ │ │ └── index.js
│ │ │ └── index.js
│ │ ├── plugins
│ │ │ ├── Plugin.js
│ │ │ ├── PluginRepository.js
│ │ │ ├── PluginTypes.js
│ │ │ ├── defaults
│ │ │ │ ├── index.js
│ │ │ │ └── interface.js
│ │ │ └── index.js
│ │ ├── services
│ │ │ ├── apis
│ │ │ │ ├── ApiService.js
│ │ │ │ ├── BackendApiService.js
│ │ │ │ ├── ExchangeService.js
│ │ │ │ ├── PriceService.js
│ │ │ │ └── index.js
│ │ │ ├── apps
│ │ │ │ ├── AppsService.js
│ │ │ │ ├── PermissionService.js
│ │ │ │ └── index.js
│ │ │ ├── blockchain
│ │ │ │ ├── AccountService.js
│ │ │ │ ├── BalanceService.js
│ │ │ │ ├── ExplorerService.js
│ │ │ │ ├── NetworkService.js
│ │ │ │ ├── ResourceService.js
│ │ │ │ ├── TransferService.js
│ │ │ │ └── index.js
│ │ │ ├── index.js
│ │ │ ├── secure
│ │ │ │ ├── HardwareService.js
│ │ │ │ ├── KeyPairService.js
│ │ │ │ ├── PasswordService.js
│ │ │ │ ├── QRService.js
│ │ │ │ ├── Seeder.js
│ │ │ │ ├── SigningService.js
│ │ │ │ └── index.js
│ │ │ └── utility
│ │ │ │ ├── ContactService.js
│ │ │ │ ├── EventService.js
│ │ │ │ ├── Framework.js
│ │ │ │ ├── IdentityService.js
│ │ │ │ ├── SingletonService.js
│ │ │ │ ├── SocketService.js
│ │ │ │ ├── StoreService.js
│ │ │ │ ├── TokenService.js
│ │ │ │ └── index.js
│ │ ├── store
│ │ │ ├── constants.js
│ │ │ └── index.js
│ │ └── util
│ │ │ ├── Crypto.js
│ │ │ ├── DateHelpers.js
│ │ │ ├── Hasher.js
│ │ │ ├── Http.js
│ │ │ ├── IdGenerator.js
│ │ │ ├── Mnemonic.js
│ │ │ ├── ObjectHelpers.js
│ │ │ ├── TestingHelper.js
│ │ │ └── index.js
│ ├── package-lock.json
│ ├── package.json
│ ├── prepare.js
│ └── yarn.lock
├── eosio
│ ├── README.md
│ ├── __tests__
│ │ ├── api.test.js
│ │ └── eosio.test.js
│ ├── lib
│ │ ├── api.js
│ │ └── eosio.js
│ ├── package-lock.json
│ ├── package.json
│ ├── prepare.js
│ ├── yarn-error.log
│ └── yarn.lock
├── ethereum
│ ├── README.md
│ ├── __tests__
│ │ └── ethereum.test.js
│ ├── lib
│ │ ├── erc20.json
│ │ └── ethereum.js
│ ├── package-lock.json
│ ├── package.json
│ ├── prepare.js
│ └── yarn.lock
├── fio
│ ├── README.md
│ ├── __tests__
│ │ └── fio.test.js
│ ├── lib
│ │ └── fio.js
│ ├── package-lock.json
│ ├── package.json
│ ├── prepare.js
│ ├── yarn-error.log
│ └── yarn.lock
└── tron
│ ├── README.md
│ ├── __tests__
│ └── tron.test.js
│ ├── lib
│ ├── trc20.json
│ └── tron.js
│ ├── package-lock.json
│ ├── package.json
│ ├── prepare.js
│ └── yarn.lock
├── scripts
├── copy-files.js
├── prepare.js
└── prepublish.js
├── webpack.config.js
├── yarn-error.log
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | node_modules/
3 | .env
4 | .DS_Store
5 | *.iml
6 | dist
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .*
2 | **/webpack.config.js
3 | node_modules
4 | src
5 | test
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 GetScatter
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WalletPack
2 |
3 | This is a wallet building SDK which takes care of all of the heavy lifting for creating blockchain wallets.
4 |
5 | Currently being used in Scatter Desktop, Scatter Mobile, and Scatter Bridge.
6 |
7 |
8 |
9 | ## Setup
10 |
11 | Install the core plus any blockchains you want to support
12 | ```
13 | npm i -S @walletpack/core @walletpack/eosio @walletpack/ethereum @walletpack/bitcoin @walletpack/tron
14 | ```
15 |
16 | ### Call initialize first.
17 |
18 | ```js
19 |
20 | import WalletPack from '@walletpack/core';
21 |
22 | const eventListener = (type, data) => {
23 | console.log('event', type, data);
24 | switch(type){
25 | case 'popout': break;
26 | case 'firewalled': break;
27 | case 'no_certs': break;
28 | }
29 | console.log('event', type, data);
30 | };
31 | WalletPack.initialize(
32 | // --------------------------------------------
33 | // blockchains & blockchain plugins
34 | {
35 | blockchains:{
36 | EOSIO:'eos',
37 | ETH:'eth',
38 | // TRX:'trx',
39 | BTC:'btc',
40 | },
41 | plugins:[
42 | require('@walletpack/eosio').default,
43 | require('@walletpack/ethereum').default,
44 | // require('@walletpack/tron').default,
45 | require('@walletpack/bitcoin').default,
46 | ]
47 | },
48 | // --------------------------------------------
49 | // store
50 | {
51 | state:{},
52 | commit:(key, val) => {},
53 | dispatch:(key, val) => {}
54 | },
55 | // --------------------------------------------
56 | // security
57 | {
58 | getSalt:() => '',
59 | get:() => () => '',
60 | set:(_seed) => () => '',
61 | clear:() => () => '',
62 | },
63 | // --------------------------------------------
64 | // framework
65 | {
66 | getVersion:WebHelpers.getVersion,
67 | pushNotification:WebHelpers.pushNotificationMethod(),
68 | },
69 | // --------------------------------------------
70 | // events
71 | eventListener,
72 | // --------------------------------------------
73 | // optionals
74 | {
75 | // Enables websocket based 3rd party app support
76 | socketService:SocketService,
77 |
78 | // Allows you to override private key provision with
79 | // external services
80 | publicToPrivate:async publicKey => {
81 | return false;
82 | },
83 | // Allows you to have custom signers instead of key provision,
84 | // which means you can sign on completely separate processes instead
85 | // of giving the private key to the renderer process
86 | signer:async (network, publicKey, payload, arbitrary = false, isHash = false) => {
87 | return sign(...);
88 | }
89 | }
90 | );
91 | ```
92 |
93 |
94 |
95 |
96 | ## Store/state requirements
97 | These properties and methods must be available on the injected store manager.
98 |
99 |
100 | ```js
101 | store:{
102 | state:{
103 | dappLogos:{},
104 | dappData:{},
105 | resources:{},
106 | scatter:null,
107 | balances:{},
108 | prices:{},
109 | history:[],
110 | priceData:{},
111 | },
112 | commit:(key, val) => {},
113 | dispatch:(key, value){},
114 | }
115 | ```
116 |
117 |
118 | #### dispatch
119 | This is an action handler that pre-processing commits to the state.
120 | [An example of these are here](https://github.com/GetScatter/ScatterDesktop/blob/core-extrapolation/src/store/actions.js)
121 | (_Some of these could possibly be put into the core library_)
122 |
123 | #### commit
124 | **must be synchronous**
125 | This is the actual commiter to the state which changes state values.
126 | [An example of these are here](https://github.com/GetScatter/ScatterDesktop/blob/core-extrapolation/src/store/mutations.js)
127 |
128 |
129 |
130 |
131 | ----------------------------
132 |
133 | ## Reaching blockchain plugins
134 |
135 | `/src/plugins/defaults/interface.js` has common methods between each blockchain plugin.
136 | Instead of tapping directly into the plugin itself you can grab the singleton plugin based on the blockchain required and
137 | then process methods on it.
138 |
139 | ```js
140 | import PluginRepository from ...
141 | PluginRepository.plugin(Blockchains.EOSIO).method(...);
142 | ```
143 |
144 |
145 | ----------------------------
146 |
147 |
148 |
149 |
150 |
151 |
152 | Some constants for the docs below:
153 | - `$API = "https://api.get-scatter.com/v1/"`
154 |
155 | ## Services breakdown
156 | These are some of the important services in Scatter, and brief explanations of what they do and how to use them.
157 |
158 | **Note: All ScatterCore methods are static**.
159 |
160 |
161 | ----------------------------
162 |
163 | ### ApiService
164 |
165 | This service handles all of the incoming messages from external applications.
166 | You should never actually have to handle this service manually in the application, as all of the methods will be called
167 | from the messages in the SocketService you provide.
168 |
169 | The flow is as follows.
170 |
171 | `app -> socket -> api handler -> openPopOut -> api handler -> socket -> app`
172 |
173 | [To see a live example of this happening see this](https://github.com/GetScatter/ScatterDesktop/blob/core-extrapolation/src/services/SocketService.js#L24)
174 | [And check out also the low level socket service](https://github.com/GetScatter/ScatterDesktop/blob/core-extrapolation/electron.js#L339)
175 |
176 |
177 |
178 |
179 | ----------------------------
180 |
181 | ### PriceService
182 | This service (and the price data) keeps itself up to date using a recursive timeout. You should never have to
183 | fetch prices manually.
184 |
185 | #### `PriceService.getCurrencies()`
186 | This fetches the available fiat currency ticker symbols from `$API/currencies`.
187 | - Example result: `["USD","EUR","CNY","GBP","JPY","CAD","CHF","AUD"]`
188 |
189 | #### `PriceService.getCurrencyPrices()`
190 | This fetches the available fiat currency prices from `$API/currencies/prices`. These are prices in relation to USD.
191 | - Example result: `{"USD":1,"EUR":0.887901,"CNY":6.877801,"GBP":0.799055,"JPY":107.956006,"CAD":1.304397,"CHF":0.98455,"AUD":1.42273}`
192 |
193 | #### `PriceService.loadPriceTimelineData()`
194 | This fetches a timeline of price data from `$API/prices/timeline` for the past 24 hours.
195 | It will automatically insert the returned data into the `state` under `priceData` in the form of `{prices, yesterday, today}`
196 |
197 | #### `PriceService.getTotal(totals, displayCurrency, bypassDisplayToken, displayToken)`
198 | Returns formatted totals based on the entire balances inside of a user's accounts.
199 |
200 | ```js
201 | // Return format
202 | ----------------------------
203 | return Token.fromJson({
204 | symbol:this.fiatSymbol(displayCurrency),
205 | amount:total.toFixed(2),
206 | })
207 |
208 | // Examples
209 | -----------------------------
210 | // Returns the total fiat balance
211 | PriceService.getTotal(BalanceService.totalBalances(false).totals)
212 |
213 | // Returns the total token balance
214 | return PriceService.getTotal(BalanceService.totalBalances(false).totals, null, false, state.scatter.settings.displayToken);
215 | ```
216 |
217 | #### `PriceService.fiatSymbol(currency = StoreService.get().state.scatter.settings.displayCurrency)`
218 | Returns an ascii currency sign ($/¥/€/£) instead of a ticker (USD/CNY/EUR/etc).
219 |
220 |
221 |
222 | ----------------------------
223 |
224 |
225 | ### AppsService
226 | This service fills itself using the SingletonService which is instantiated once when opening a Scatter wallet.
227 | All app data is available on `state.dappData`
228 |
229 | #### `AppsService.getAppData(origin)`
230 | Returns formatted data based on the applink (origin/fqdn) of the apps trying to interact with Scatter.
231 | If the app doesn't exist on the `state.dappData` then it will return a formatted result regardless.
232 |
233 | ```js
234 | // Return structure
235 | {
236 | applink:origin,
237 | type:'',
238 | name:origin,
239 | description:'',
240 | logo:'',
241 | url:'',
242 | }
243 | ```
244 |
245 | #### `AppsService.categories(selectedCategory = null)`
246 | Returns a list of categories available based on the `state.dappData`.
247 | This is a simple helper method that loops over the dapps and aggregates the `.type` param.
248 |
249 | #### `AppsService.appsByCategory(selectedCategory = null)`
250 | Returns all the apps available with a given category.
251 |
252 | #### `AppsService.appsByTerm(terms)`
253 | Returns all the apps available with a given search terms.
254 |
255 | #### `AppsService.linkedApps(terms = '', categoryFilter = null)`
256 | Returns all of the apps that are **linked** in the user's Scatter.
257 | These are apps that the user already has permissions for (My Apps).
258 |
259 |
260 | ----------------------------
261 |
262 |
263 | ### PermissionService
264 | This service handles everything to do with application permissions, including whitelists.
265 | A lot of the handling is internal for the library but below are some methods that will need to be
266 | integrated into components.
267 |
268 | #### `PermissionService.removeAllPermissions()`
269 | Removes every single permission that the user has. This includes all application permissions and whitelists.
270 |
271 | #### `PermissionService.removeAllPermissionsFor(origin)`
272 | Removes every permission for a given origin/applink
273 |
274 | #### `PermissionService.removePermission(permission)`
275 | Removes a given permission
276 |
277 |
278 |
279 |
280 |
281 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | const EXPORTS = {
2 | "presets": ["@babel/preset-env"],
3 | "plugins": []
4 | };
5 |
6 | if(process.env.WALLETPACK_TESTING){
7 | EXPORTS.plugins.push(["@babel/transform-async-to-generator"]);
8 | // This is used for tests, since the import path structures
9 | // are different for the packages internally.
10 | EXPORTS.plugins.push(["module-resolver", {
11 | "alias": {
12 | "^@walletpack/core/(.+)": ([, name]) => {
13 | // Catching absolute lib imports
14 | if(name.indexOf('lib') > -1) name = name.replace('lib/', '');
15 | // Prefixing includes
16 | return `./packages/core/lib/${name}`
17 | }
18 | }
19 | }])
20 | } else {
21 | EXPORTS.plugins.push(["@babel/transform-runtime"]);
22 | }
23 |
24 | module.exports = EXPORTS;
25 |
--------------------------------------------------------------------------------
/lerna-debug.log:
--------------------------------------------------------------------------------
1 | 51 error Error: Command failed: git push --follow-tags --no-verify origin master
2 | 51 error
3 | 51 error at makeError (C:\Work\Libraries\walletpack\node_modules\execa\index.js:174:9)
4 | 51 error at Promise.all.then.arr (C:\Work\Libraries\walletpack\node_modules\execa\index.js:278:16)
5 |
--------------------------------------------------------------------------------
/lerna.json:
--------------------------------------------------------------------------------
1 | {
2 | "packages": [
3 | "packages/*"
4 | ],
5 | "command": {
6 | "publish": {
7 | "ignoreChanges": [ "*.md" ]
8 | },
9 | "bootstrap": {
10 | "npmClientArgs": ["--no-package-lock"]
11 | }
12 | },
13 | "version": "independent"
14 | }
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "walletpack",
3 | "private": true,
4 | "scripts": {
5 | "postinstall": "lerna bootstrap",
6 | "build": "lerna exec -- babel --config-file ../../babel.config.js --out-dir dist lib && node scripts/copy-files",
7 | "links": "lerna exec -- yarn link",
8 | "publish": "node ./scripts/prepublish.js && lerna publish",
9 | "testfile": "cross-env WALLETPACK_TESTING=true mocha --require @babel/register --require @babel/polyfill --exit --timeout 1000000",
10 | "test": "npm run testfile \"./packages/**/__tests__/**/*.test.js\"",
11 | "pack": "webpack-cli --mode=production --display-error-details"
12 | },
13 | "dependencies": {},
14 | "devDependencies": {
15 | "@babel/cli": "^7.4.4",
16 | "@babel/core": "^7.6.4",
17 | "@babel/plugin-transform-async-to-generator": "^7.5.0",
18 | "@babel/plugin-transform-runtime": "^7.6.2",
19 | "@babel/polyfill": "^7.6.0",
20 | "@babel/preset-env": "^7.4.5",
21 | "@babel/register": "^7.5.5",
22 | "@babel/runtime": "^7.0.0",
23 | "babel-loader": "^8.0.2",
24 | "babel-plugin-module-resolver": "^3.2.0",
25 | "babel-preset-minify": "^0.5.0-alpha.3cc09dcf",
26 | "chai": "^4.1.2",
27 | "concurrently": "^4.0.1",
28 | "cross-env": "^6.0.3",
29 | "isomorphic-fetch": "^2.2.1",
30 | "lerna": "^3.15.0",
31 | "mocha": "^6.2.2",
32 | "webpack": "^4.17.2",
33 | "webpack-cli": "^3.1.0"
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/packages/bitcoin/README.md:
--------------------------------------------------------------------------------
1 | # `bitcoin`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const bitcoin = require('bitcoin');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/bitcoin/__tests__/bitcoin.test.js:
--------------------------------------------------------------------------------
1 | // 'use strict';
2 | //
3 | // const bitcoin = require('..');
4 | //
5 | // describe('bitcoin', () => {
6 | // it('needs tests');
7 | // });
8 |
--------------------------------------------------------------------------------
/packages/bitcoin/lib/bitcoin.js:
--------------------------------------------------------------------------------
1 | import Plugin from '@walletpack/core/plugins/Plugin';
2 | import * as PluginTypes from '@walletpack/core/plugins/PluginTypes';
3 | import {Blockchains} from '@walletpack/core/models/Blockchains'
4 | import Network from "@walletpack/core/models/Network";
5 | import Token from "@walletpack/core/models/Token";
6 | import ObjectHelpers from "@walletpack/core/util/ObjectHelpers";
7 | import KeyPairService from "@walletpack/core/services/secure/KeyPairService";
8 | import StoreService from "@walletpack/core/services/utility/StoreService";
9 | import * as Actions from "@walletpack/core/models/api/ApiActions";
10 | import {GET, POST} from "@walletpack/core/services/apis/BackendApiService";
11 | import EventService from "@walletpack/core/services/utility/EventService";
12 | import SigningService from "@walletpack/core/services/secure/SigningService";
13 |
14 | const bitcoin = require('bitcoinjs-lib');
15 |
16 |
17 | const SELECTED_CHAIN = 0;
18 | const SELECTED_NETWORK = SELECTED_CHAIN === 0 ? bitcoin.networks.bitcoin : bitcoin.networks.testnet;
19 |
20 |
21 | const EXPLORER = {
22 | "name":"Blockcypher",
23 | "account":"https://live.blockcypher.com/btc/address/{x}",
24 | "transaction":"https://live.blockcypher.com/btc/tx/{x}",
25 | "block":"https://live.blockcypher.com/btc/block/{x}"
26 | };
27 |
28 | export default class BTC extends Plugin {
29 |
30 | constructor(){ super(Blockchains.BTC, PluginTypes.BLOCKCHAIN_SUPPORT) }
31 |
32 | bip(){ return `44'/0'/0'/0/`}
33 | bustCache(){ }
34 | defaultExplorer(){ return EXPLORER; }
35 | accountFormatter(account){ return `${account.publicKey}` }
36 | returnableAccount(account){ return { address:account.publicKey, blockchain:Blockchains.BTC }}
37 |
38 | contractPlaceholder(){ return '...'; }
39 |
40 | checkNetwork(network){
41 | return Promise.race([
42 | new Promise(resolve => setTimeout(() => resolve(null), 2000)),
43 | //TODO:
44 | new Promise(resolve => setTimeout(() => resolve(true), 10)),
45 | ])
46 | }
47 |
48 | getEndorsedNetwork(){
49 | return new Network('Bitcoin Mainnet', 'https', 'btcnodes.get-scatter.com', 443, Blockchains.BTC, '1')
50 | }
51 |
52 | isEndorsedNetwork(network){
53 | const endorsedNetwork = this.getEndorsedNetwork();
54 | return network.blockchain === Blockchains.BTC && network.chainId === endorsedNetwork.chainId;
55 | }
56 |
57 | async getChainId(network){
58 | return 1;
59 | }
60 |
61 | usesResources(){ return false; }
62 | hasAccountActions(){ return false; }
63 |
64 | accountsAreImported(){ return false; }
65 | isValidRecipient(address){ return this.validPublicKey(address); }
66 | privateToPublic(privateKey){
67 | const pubkey = (() => {
68 | if(typeof privateKey === 'string') return bitcoin.ECPair.fromWIF(privateKey, SELECTED_NETWORK);
69 | else return bitcoin.ECPair.fromPrivateKey(privateKey, {network:SELECTED_NETWORK});
70 | })().publicKey;
71 |
72 | const { address } = bitcoin.payments.p2pkh({ pubkey, network:SELECTED_NETWORK })
73 | return address;
74 | }
75 |
76 | validPrivateKey(privateKey){
77 | return (typeof privateKey === 'string' ? privateKey : this.bufferToHexPrivate(privateKey)).length === 52;
78 | }
79 |
80 | validPublicKey(publicKey){
81 | return publicKey.length === 34;
82 | }
83 |
84 | bufferToHexPrivate(buffer){
85 | return bitcoin.ECPair.fromPrivateKey(Buffer.from(buffer), {network:SELECTED_NETWORK}).toWIF()
86 | }
87 |
88 | hexPrivateToBuffer(privateKey){
89 | return bitcoin.ECPair.fromWIF(privateKey, SELECTED_NETWORK).privateKey;
90 | }
91 |
92 |
93 | bufferToHexPublicKeyOrAddress(buffer){
94 | return bitcoin.payments.p2pkh({ pubkey:buffer }).address;
95 | }
96 |
97 | hasUntouchableTokens(){ return false; }
98 |
99 | async balanceFor(account){
100 | return GET(`btc/balance/${account.publicKey}`).then(res => {
101 | const token = this.defaultToken().clone();
102 | token.amount = parseInt(res[account.publicKey].final_balance) / 100000000;
103 | return token;
104 | });
105 | }
106 |
107 | async balancesFor(account){
108 | const balance = await this.balanceFor(account);
109 | return balance ? [balance] : [];
110 | }
111 |
112 | defaultDecimals(){ return 8; }
113 | defaultToken(){ return new Token(Blockchains.BTC, 'btc', 'BTC', 'BTC', this.defaultDecimals(), '1') }
114 |
115 | actionParticipants(payload){
116 | return ObjectHelpers.flatten(
117 | payload.messages
118 | .map(message => message.authorization)
119 | );
120 | }
121 |
122 | async transfer({account, to, amount, promptForSignature = true}){
123 | amount = amount * 100000000;
124 |
125 | try {
126 | return new Promise(async (resolve, reject) => {
127 | const txb = new bitcoin.TransactionBuilder(SELECTED_NETWORK);
128 | txb.setVersion(1);
129 |
130 | // The amount you are sending to the recipient.
131 | txb.addOutput(to, amount);
132 |
133 | // Calculating unspent inputs
134 | const utxos = await GET(`btc/unspent/${account.publicKey}`).then(x => x.unspent_outputs).catch(() => null);
135 | if(!utxos) return resolve({error:`There was a problem loading UTXOs for ${account.publicKey}`});
136 |
137 | let inputs = 0;
138 | for (let utx of utxos) {
139 | txb.addInput(utx.tx_hash_big_endian, utx.tx_output_n);
140 | inputs += utx.value;
141 | if (inputs >= amount) break;
142 | }
143 |
144 |
145 | // Calculating the fee
146 | let bestFee = await GET(`fees`).then(x => x.btc).catch(() => null);
147 | if(!bestFee) return resolve({error:`Couldn't get fee`});
148 | // Sats * bytes
149 | const fee = (txb.buildIncomplete().toHex().length * bestFee);
150 |
151 | // Returning unspent to sender.
152 | const change = inputs - (amount + fee);
153 | if(change < 0) return resolve({error:`Insufficient BTC: ${inputs}. (Most likely due to fees, you need to leave ${fee} worth)`});
154 | if (change) txb.addOutput(account.publicKey, change);
155 |
156 | const payload = { transaction:{from:account.publicKey, to, amount:amount / 100000000, fee:fee / 100000000}, unsigned:txb.buildIncomplete().toHex(),
157 | blockchain:Blockchains.BTC, network:account.network(), requiredFields:{}, abi:null };
158 | const signed = promptForSignature
159 | ? await this.signerWithPopup(payload, account, x => resolve(x), null)
160 | : await SigningService.sign(account.network(), payload.unsigned, account.publicKey, false, false);
161 |
162 | if(!signed) return;
163 |
164 | await POST(`btc/pushtx`, {signed}).then(res => {
165 | if(res.indexOf('Transaction Submitted') > -1){
166 | resolve({txid:bitcoin.Transaction.fromHex(signed).getId()});
167 | } else {
168 | resolve({error:res});
169 | }
170 | }).catch(error => {
171 | console.error(error);
172 | resolve({error})
173 | });
174 | })
175 | } catch(e){
176 | console.error(e);
177 | resolve({error:e});
178 | }
179 |
180 |
181 | }
182 |
183 | async signer(transaction, publicKey, arbitrary = false, isHash = false, privateKey = null){
184 | try {
185 | // TODO: No hardware support yet.
186 | // if(account && KeyPairService.isHardware(publicKey))
187 | // return await HardwareService.sign(account, transaction);
188 |
189 | if(!privateKey) privateKey = await KeyPairService.publicToPrivate(publicKey);
190 | if(!privateKey) return;
191 |
192 | if(typeof privateKey === 'string') privateKey = this.hexPrivateToBuffer(privateKey);
193 | const key = bitcoin.ECPair.fromPrivateKey(Buffer.from(privateKey), {network:SELECTED_NETWORK});
194 | const txb = bitcoin.TransactionBuilder.fromTransaction(bitcoin.Transaction.fromHex(transaction), SELECTED_NETWORK)
195 |
196 | if(Object.keys(txb.__PREV_TX_SET).length > 1){
197 | Object.keys(txb.__PREV_TX_SET).map((x,i) => {
198 | txb.sign(i, key);
199 | })
200 | } else txb.sign(0, key);
201 | return txb.build().toHex();
202 | } catch(e){
203 | console.error(e);
204 | return null;
205 | }
206 | }
207 |
208 | async signerWithPopup(payload, account, rejector, token = null){
209 | return new Promise(async resolve => {
210 | payload.messages = [{
211 | data:payload.transaction,
212 | code:payload.transaction.to,
213 | type:'transfer',
214 | authorization:payload.transaction.from
215 | }];
216 | payload.identityKey = StoreService.get().state.scatter.keychain.identities[0].publicKey;
217 | payload.participants = [account];
218 | payload.network = account.network();
219 | payload.origin = 'Scatter';
220 | const request = {
221 | payload,
222 | origin:payload.origin,
223 | blockchain:Blockchains.BTC,
224 | requiredFields:{},
225 | type:Actions.SIGN,
226 | id:1,
227 | };
228 |
229 | EventService.emit('popout', request).then( async ({result}) => {
230 | if(!result || (!result.accepted || false)) return rejector({error:'Could not get signature'});
231 |
232 | // TODO: No hardware
233 | // let signature = null;
234 | // if(KeyPairService.isHardware(account.publicKey)){
235 | // signature = await HardwareService.sign(account, payload);
236 | // } else signature = await this.signer(payload.transaction, account.publicKey, true);
237 |
238 | const signature = await SigningService.sign(payload.network, payload.unsigned, account.publicKey, true);
239 | if(!signature) return rejector({error:'Could not get signature'});
240 | resolve(signature);
241 | })
242 | })
243 | }
244 |
245 | async requestParser(transaction, network){
246 | throw new Error("Bitcoin not yet supported externally")
247 | }
248 | }
249 |
--------------------------------------------------------------------------------
/packages/bitcoin/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@walletpack/bitcoin",
3 | "version": "1.0.56",
4 | "description": "> TODO: description",
5 | "author": "GetScatter Ltd. 2019",
6 | "homepage": "",
7 | "license": "ISC",
8 | "main": "bitcoin.js",
9 | "files": [
10 | "dist",
11 | "prepare.js"
12 | ],
13 | "scripts": {
14 | "install": "node prepare.js",
15 | "test": "echo \"Error: run tests from root\" && exit 1"
16 | },
17 | "dependencies": {
18 | "@walletpack/core": "^1.0.51",
19 | "bitcoinjs-lib": "^5.1.2"
20 | },
21 | "gitHead": "70bd19f93b503618a79eb519eef082bfc40b16d7",
22 | "publishConfig": {
23 | "access": "public"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/packages/bitcoin/prepare.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const rimraf = require("rimraf");
4 |
5 | const paths = __dirname.split(path.sep);
6 | const parent = paths[paths.length-2];
7 |
8 | if(parent === 'packages') return;
9 |
10 | try {
11 | rimraf.sync("./__tests__");
12 | const files = fs.readdirSync(`./dist`);
13 | files.map(file => {
14 | if(fs.existsSync(`./${file}`)) rimraf.sync(`./${file}`);
15 | fs.renameSync(`./dist/${file}`, `./${file}`);
16 | })
17 | rimraf.sync("./dist");
18 | rimraf.sync("./lib");
19 | } catch(e){
20 | console.error('Walletpack prepare.js error', e);
21 | }
22 |
--------------------------------------------------------------------------------
/packages/core/README.md:
--------------------------------------------------------------------------------
1 | # `core`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const core = require('core');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/core/__tests__/core.test.js:
--------------------------------------------------------------------------------
1 | // 'use strict';
2 | //
3 | // const core = require('../lib/core');
4 | //
5 | // describe('core', () => {
6 | // it('needs tests');
7 | // console.log('core', core);
8 | // });
9 |
10 |
--------------------------------------------------------------------------------
/packages/core/lib/core.js:
--------------------------------------------------------------------------------
1 | import migrations from './migrations'
2 | import models from './models'
3 | import plugins from './plugins'
4 | import services from './services'
5 | import store from './store'
6 | import util from './util'
7 |
8 | const ScatterCore = {
9 |
10 | initialize(
11 | {
12 | blockchains,
13 | plugins:_plugins,
14 | nameParser = null
15 | },
16 | store,
17 | security,
18 | framework,
19 | eventListener,
20 | {
21 | socketService = null,
22 | hardwareService = null,
23 | publicToPrivate = null,
24 | signer = null,
25 | },
26 | ){
27 | models.Blockchains.setBlockchains(blockchains, nameParser);
28 | plugins.PluginRepository.loadPlugins(_plugins);
29 |
30 | services.utility.StoreService.init(store);
31 | services.secure.Seeder.init(security);
32 | services.utility.Framework.init(framework);
33 | services.utility.EventService.init(eventListener);
34 |
35 | // Some wallets don't require dapp integration.
36 | if(socketService) services.utility.SocketService.init(socketService);
37 |
38 | // Some wallets aren't targeting hardware wallets.
39 | if(hardwareService) services.secure.HardwareService.init(hardwareService);
40 |
41 | // Optional method for providing extra ways to create private keys
42 | // from public keys. If only used for certain keys, return `false` on normal keys.
43 | // If it returns `null` or `PRIV_KEY` it will resolve that instead of falling back to internals.
44 | if(publicToPrivate) services.secure.KeyPairService.init(publicToPrivate);
45 | if(signer) services.secure.SigningService.init(signer);
46 |
47 | return true;
48 | },
49 |
50 | migrations,
51 | models,
52 | plugins,
53 | services,
54 | store,
55 | util,
56 | }
57 |
58 | export default ScatterCore;
--------------------------------------------------------------------------------
/packages/core/lib/migrations/index.js:
--------------------------------------------------------------------------------
1 | import * as migrator from './migrator';
2 | export default migrator;
--------------------------------------------------------------------------------
/packages/core/lib/migrations/migrator.js:
--------------------------------------------------------------------------------
1 | export const mathematicalVersion = version => {
2 | if(!version || version === '0') return 0;
3 | const parts = version.replace(/[.]/g,'_').replace(/[m]/g, '').split('_');
4 | if(parts.length !== 3) throw new Error("Migration error, invalid version");
5 |
6 | let total = 0;
7 | parts.map((v, i) => {
8 | const multiplier = i === 0 ? 100 : i === 1 ? 10 : 1;
9 | total += parseFloat(v) * multiplier;
10 | });
11 |
12 | return total;
13 | };
14 |
15 | const fnToVersion = fnName => fnName.replace(/[m]/g, '').replace(/[_]/g,'.');
16 |
17 | export default async (scatter, migrators) => {
18 | scatter.meta.regenerateVersion();
19 | if(scatter.isEncrypted()) return false;
20 | if(!scatter.meta.needsUpdating()) return false;
21 |
22 | const lastVersion = mathematicalVersion(scatter.meta.lastVersion);
23 | const nextVersions = Object.keys(migrators).filter(v => mathematicalVersion(v) > lastVersion)
24 | .sort((a,b) => mathematicalVersion(a) - mathematicalVersion(b));
25 |
26 | if(nextVersions.length) {
27 | for(let i = 0; i < nextVersions.length; i++){
28 | await migrators[nextVersions[i]](scatter)
29 | }
30 | scatter.meta.lastVersion = fnToVersion(nextVersions[nextVersions.length-1]);
31 | }
32 |
33 | return nextVersions.length > 0;
34 | }
--------------------------------------------------------------------------------
/packages/core/lib/models/Account.js:
--------------------------------------------------------------------------------
1 | import PluginRepository from '../plugins/PluginRepository';
2 | import {Blockchains} from "./Blockchains";
3 | import Token from "./Token";
4 | import StoreService from "../services/utility/StoreService";
5 |
6 | export default class Account {
7 | constructor(){
8 | this.keypairUnique = '';
9 | this.networkUnique = '';
10 | this.publicKey = '';
11 | this.name = '';
12 | this.authority = '';
13 |
14 | this.logins = 0;
15 |
16 | this.createdAt = +new Date();
17 | this.fromOrigin = null;
18 | }
19 |
20 | sendable(){
21 | return PluginRepository.plugin(this.blockchain()).accountsAreImported() ? this.name : this.publicKey;
22 | }
23 |
24 | formatted(){
25 | return PluginRepository.plugin(this.blockchain()).accountFormatter(this);
26 | }
27 |
28 | network(){
29 | return StoreService.get().state.scatter.settings.networks.find(x => x.unique() === this.networkUnique);
30 | }
31 |
32 | keypair(){
33 | return StoreService.get().state.scatter.keychain.keypairs.find(x => x.unique() === this.keypairUnique);
34 | }
35 |
36 | blockchain(){
37 | if(!this.keypair()) return console.error('account.blockchain() error');
38 | return this.keypair().publicKeys.find(x => x.key === this.publicKey).blockchain;
39 | }
40 |
41 | authorities(thisKeyOnly = true){
42 | if(!this.authority.length) return [];
43 | return StoreService.get().state.scatter.keychain.accounts
44 | .filter(x => x.identifiable() === this.identifiable() && (!thisKeyOnly || x.keypairUnique === this.keypairUnique))
45 | .sort((a,b) => a.authority.localeCompare(b.authority));
46 | }
47 |
48 | hasDangerousAuthority(){
49 | return this.authorities().find(x => x.authority === 'owner');
50 | }
51 |
52 | static placeholder(){ return new Account(); }
53 | static fromJson(json){ return Object.assign(this.placeholder(), json); }
54 | unique(){ return this.keypairUnique + this.networkUnique + this.name + this.authority + this.publicKey; }
55 | identifiable(){ return this.networkUnique + this.sendable(); }
56 | clone(){ return Account.fromJson(JSON.parse(JSON.stringify(this))) }
57 |
58 | asReturnable(){
59 | const returnable = PluginRepository.plugin(this.blockchain()).returnableAccount(this);
60 | returnable.chainId = this.network().chainId;
61 | returnable.isHardware = !!this.keypair().external;
62 | return returnable;
63 | }
64 |
65 | tokenCount(systemToken = null){
66 | if(!StoreService.get().state.balances) return 0;
67 | if(!StoreService.get().state.balances.hasOwnProperty(this.identifiable())) return 0;
68 | if(!StoreService.get().state.balances[this.identifiable()]) return 0;
69 | return StoreService.get().state.balances[this.identifiable()].filter(x => !systemToken ? true : x.identifiable() !== systemToken.identifiable()).length;
70 | }
71 |
72 | tokens(){
73 | let base = [this.network().systemToken()];
74 | if(!StoreService.get().state.balances) return base;
75 | if(!StoreService.get().state.balances.hasOwnProperty(this.identifiable())) return base;
76 | if(!StoreService.get().state.balances[this.identifiable()]) return base;
77 | return StoreService.get().state.balances[this.identifiable()];
78 | }
79 |
80 | balanceFor(token){
81 | return this.tokens().find(x => x.uniqueWithChain() === token.uniqueWithChain());
82 | }
83 |
84 | systemBalance(withSymbol = false){
85 | if(!StoreService.get().state.balances) return 0;
86 | if(!StoreService.get().state.balances.hasOwnProperty(this.identifiable())) return 0;
87 | if(!StoreService.get().state.balances[this.identifiable()]) return 0;
88 | const systemBalance = StoreService.get().state.balances[this.identifiable()].find(x => Token.fromJson(x).identifiable() === this.network().systemToken().identifiable());
89 | if(!systemBalance) return 0;
90 | return `${systemBalance.amount} ${withSymbol ? systemBalance.symbol : ''}`;
91 | }
92 |
93 | totalFiatBalance(){
94 | return this.tokens().reduce((acc, x) => {
95 | acc += x.fiatBalance(false) ? parseFloat(x.fiatBalance(false)) : 0;
96 | return acc;
97 | }, 0).toFixed(2)
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/packages/core/lib/models/AccountAction.js:
--------------------------------------------------------------------------------
1 | export default class AccountAction {
2 | constructor(type, onclick = () => {}, danger = false){
3 | this.type = type;
4 | this.onclick = onclick;
5 | this.isDangerous = danger;
6 | }
7 |
8 | static placeholder(){ return new AccountAction(); }
9 | static fromJson(json){ return Object.assign(this.placeholder(), json); }
10 | }
11 |
--------------------------------------------------------------------------------
/packages/core/lib/models/AuthorizedApp.js:
--------------------------------------------------------------------------------
1 | import Hasher from '../util/Hasher';
2 |
3 | export default class AuthorizedApp {
4 |
5 | constructor(_origin, _appkey){
6 | this.origin = _origin;
7 | this.appkey = _appkey;
8 | this.nextNonce = '';
9 | this.createdAt = +new Date();
10 | }
11 |
12 | static placeholder(){ return new AuthorizedApp(); }
13 | static fromJson(json){ return Object.assign(this.placeholder(), json); }
14 | checkKey(hashed){ return hashed === this.hashed(); }
15 | hashed(){ return Hasher.unsaltedQuickHash(this.appkey); }
16 | checkNonce(nonce){ return this.nextNonce === Hasher.unsaltedQuickHash(nonce) }
17 | }
--------------------------------------------------------------------------------
/packages/core/lib/models/Blockchains.js:
--------------------------------------------------------------------------------
1 |
2 | export let Blockchains = {
3 | EOSIO:'eos',
4 | ETH:'eth',
5 | TRX:'trx',
6 | BTC:'btc',
7 | FIO:'fio',
8 | };
9 |
10 | export let BlockchainsArray = Object.keys(Blockchains).map(key => ({key, value:Blockchains[key]}));
11 |
12 | export let blockchainName = x => {
13 | switch(x){
14 | case 'btc': return 'Bitcoin';
15 | case Blockchains.EOSIO: return 'EOSIO';
16 | case Blockchains.ETH: return 'Ethereum';
17 | case Blockchains.TRX: return 'Tron';
18 | case Blockchains.BTC: return 'Bitcoin';
19 | case Blockchains.FIO: return 'FIO';
20 | default: return x;
21 | }
22 | };
23 |
24 | export const setBlockchains = (_Blockchains, _blockchainNameParser = null) => {
25 | Blockchains = _Blockchains;
26 | BlockchainsArray = Object.keys(Blockchains).map(key => ({key, value:Blockchains[key]}));
27 | if(_blockchainNameParser) blockchainName = _blockchainNameParser;
28 | }
29 |
--------------------------------------------------------------------------------
/packages/core/lib/models/Contact.js:
--------------------------------------------------------------------------------
1 | import IdGenerator from '../util/IdGenerator'
2 | import {Blockchains} from './Blockchains';
3 |
4 | export default class Contact {
5 |
6 | constructor(_name = '', _recipient = '', _blockchain = null, _chainId = null){
7 | this.id = IdGenerator.text(24);
8 | this.name = _name;
9 | this.recipient = _recipient;
10 | this.blockchain = _blockchain;
11 | this.chainId = _chainId;
12 | }
13 |
14 | static placeholder(){ return new Contact(); }
15 | static fromJson(json){ return Object.assign(this.placeholder(), json); }
16 |
17 | unique(){ return `${this.blockchain}::${this.recipient}::${this.name}${/* LEGACY SUPPORT */ this.chainId ? `::${this.chainId}` : ''}`.toLowerCase().trim(); }
18 | clone(){ return Contact.fromJson(JSON.parse(JSON.stringify(this))) }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/packages/core/lib/models/CreditCard.js:
--------------------------------------------------------------------------------
1 | import AES from 'aes-oop';
2 | import IdGenerator from '../util/IdGenerator';
3 | import Crypto from '../util/Crypto';
4 | import Keychain from "./Keychain";
5 |
6 | export class CreditCardPersonalInformation {
7 | constructor() {
8 | this.name = '';
9 | this.email = '';
10 | this.birthdate = '';
11 | this.address = '';
12 | this.city = '';
13 | this.state = '';
14 | this.country = '';
15 | this.zipcode = '';
16 | }
17 |
18 | static placeholder(){ return new CreditCardPersonalInformation(); }
19 | static fromJson(json){ return Object.assign(this.placeholder(), json); }
20 | clone(){ return CreditCardPersonalInformation.fromJson(JSON.parse(JSON.stringify(this))) }
21 |
22 | }
23 |
24 | export class CreditCardSecureProperties {
25 |
26 | constructor(){
27 | this.number = '';
28 | this.authTokens = {};
29 | this.cardHash = '';
30 |
31 | this.personalInformation = CreditCardPersonalInformation.placeholder();
32 | }
33 |
34 | static placeholder(){ return new CreditCardSecureProperties(); }
35 | static fromJson(json){
36 | let p = Object.assign(this.placeholder(), json);
37 | if(json.hasOwnProperty('personalInformation'))
38 | p.personalInformation = CreditCardPersonalInformation.fromJson(json.personalInformation);
39 | return p;
40 | }
41 | clone(){ return CreditCardSecureProperties.fromJson(JSON.parse(JSON.stringify(this))) }
42 |
43 | }
44 |
45 | export default class CreditCard {
46 |
47 | constructor(){
48 | this.id = IdGenerator.text(24);
49 | this.name = '';
50 | this.lastFour = '';
51 | this.expiration = '';
52 | this.secure = CreditCardSecureProperties.placeholder();
53 | this.createdAt = +new Date();
54 | }
55 |
56 | static placeholder(){ return new CreditCard(); }
57 | static fromJson(json){
58 | let p = Object.assign(this.placeholder(), json);
59 | if(json.hasOwnProperty('secure'))
60 | p.secure = (typeof json.secure === 'string')
61 | ? json.secure : CreditCardSecureProperties.fromJson(json.secure);
62 | return p;
63 | }
64 | unique(){ return this.id; }
65 | clone(){ return CreditCard.fromJson(JSON.parse(JSON.stringify(this))) }
66 | hash(){ this.cardHash = Crypto.bufferToHash(this.secure.number); }
67 |
68 | isEncrypted(){
69 | return typeof this.secure === 'string';
70 | }
71 |
72 | encrypt(seed){
73 | if(!this.isEncrypted()) this.secure = AES.encrypt(this.secure, seed);
74 | }
75 |
76 | decrypt(seed){
77 | if(this.isEncrypted()) this.secure = AES.decrypt(this.secure, seed);
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/packages/core/lib/models/Explorer.js:
--------------------------------------------------------------------------------
1 | export default class Explorer {
2 |
3 | constructor(){
4 | this.raw = null;
5 | this.name = null;
6 | this.account = null;
7 | this.transaction = null;
8 | this.block = null;
9 | }
10 |
11 | static placeholder(){ return new Explorer(); }
12 | static fromJson(json){ return Object.assign(this.placeholder(), json); }
13 | static fromRaw(rawExplorer){
14 | if(!rawExplorer) return this.placeholder();
15 | return this.fromJson({
16 | raw:rawExplorer,
17 | name:rawExplorer.name,
18 | account:x => rawExplorer.account.replace('{x}', x),
19 | transaction:x => rawExplorer.transaction.replace('{x}', x),
20 | block:x => rawExplorer.block.replace('{x}', x),
21 | });
22 | }
23 |
24 | parsed(){
25 | return Explorer.fromRaw(this.raw);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/core/lib/models/Keychain.js:
--------------------------------------------------------------------------------
1 | import Identity, {LocationInformation} from './Identity';
2 | import Permission from './Permission';
3 | import Keypair from './Keypair';
4 | import Account from './Account';
5 | import AuthorizedApp from './AuthorizedApp';
6 | import CreditCard from "./CreditCard";
7 | import StoreService from "../services/utility/StoreService";
8 | import * as Actions from "../store/constants";
9 |
10 | export default class Keychain {
11 |
12 | constructor(){
13 | this.keypairs = [];
14 | this.accounts = [];
15 | this.identities = [];
16 | this.locations = [];
17 | this.permissions = [];
18 | this.cards = [];
19 | this.apps = [];
20 | this.avatars = {};
21 |
22 | this.lastUsedIdentity = null;
23 | }
24 |
25 | static placeholder(){ return new Keychain(); }
26 | static fromJson(json){
27 | let p = Object.assign(this.placeholder(), json);
28 | if(json.hasOwnProperty('keypairs')) p.keypairs = json.keypairs.map(x => Keypair.fromJson(x));
29 | if(json.hasOwnProperty('accounts')) p.accounts = json.accounts.map(x => Account.fromJson(x));
30 | if(json.hasOwnProperty('identities')) p.identities = json.identities.map(x => Identity.fromJson(x));
31 | if(json.hasOwnProperty('locations')) p.locations = json.locations.map(x => LocationInformation.fromJson(x));
32 | if(json.hasOwnProperty('permissions')) p.permissions = json.permissions.map(x => Permission.fromJson(x));
33 | if(json.hasOwnProperty('cards')) p.cards = json.cards.map(x => CreditCard.fromJson(x));
34 | if(json.hasOwnProperty('apps')) p.apps = json.apps.map(x => AuthorizedApp.fromJson(x));
35 | return p;
36 | }
37 |
38 | clone(){ return Keychain.fromJson(JSON.parse(JSON.stringify(this))) }
39 |
40 | findIdentity(id){
41 | return this.identities.find(identity => identity.id === id);
42 | }
43 |
44 | updateOrPushApp(app){
45 | this.apps.find(x => x.origin === app.origin)
46 | ? this.apps = this.apps.map(x => x.origin === app.origin ? app : x)
47 | : this.apps.unshift(app);
48 | }
49 |
50 | removeApp(app){
51 | this.apps = this.apps.filter(x => x.origin !== app.origin);
52 | }
53 |
54 | findApp(origin){
55 | return this.apps.find(x => x.origin === origin);
56 | }
57 |
58 | updateOrPushIdentity(identity){
59 | this.identities.find(id => id.id === identity.id)
60 | ? this.identities = this.identities.map(id => id.id === identity.id ? identity : id)
61 | : this.identities.unshift(identity);
62 | }
63 |
64 | removeIdentity(identity){
65 | this.identities = this.identities.filter(id => id.id !== identity.id);
66 | this.permissions = this.permissions.filter(perm => perm.identity !== identity.id);
67 | delete this.avatars[identity.id];
68 | }
69 |
70 | updateOrPushLocation(location){
71 | this.locations.find(id => id.id === location.id)
72 | ? this.locations = this.locations.map(id => id.id === location.id ? location : id)
73 | : this.locations.unshift(location);
74 | }
75 |
76 | removeLocation(location){
77 | this.locations = this.locations.filter(x => x.id !== location.id);
78 | this.identities.map(identity => {
79 | if(identity.location === location.id){
80 | identity.location = null;
81 | }
82 | })
83 | }
84 |
85 | getKeyPairByName(name){
86 | return this.keypairs.find(key => key.name.toLowerCase() === name.toLowerCase())
87 | }
88 |
89 | getKeyPairByPublicKey(publicKey){
90 | if(!publicKey) return;
91 | return this.keypairs.find(key => key.publicKeys.find(x => x.key.toLowerCase() === publicKey.toLowerCase()))
92 | }
93 |
94 | removeKeyPair(keypair){
95 | const accountsToRemove = this.accounts.filter(x => x.keypairUnique === keypair.unique()).map(x => x.unique());
96 | this.permissions = this.permissions.filter(x => !x.accounts.some(a => accountsToRemove.includes(a)));
97 | this.accounts = this.accounts.filter(x => x.keypairUnique !== keypair.unique());
98 | this.keypairs = this.keypairs.filter(key => key.unique() !== keypair.unique());
99 | this.correctHistories();
100 | this.correctAppLinks();
101 | }
102 |
103 | addAccount(account){
104 | if(!this.accounts.find(a => a.unique() === account.unique()))
105 | this.accounts.push(account);
106 | }
107 |
108 | removeAccount(account){
109 | const accountsToRemove = this.accounts.filter(x => x.unique() === account.unique()).map(x => x.unique());
110 | this.permissions = this.permissions.filter(x => !x.accounts.some(a => accountsToRemove.includes(a)));
111 | this.accounts = this.accounts.filter(a => a.unique() !== account.unique());
112 | this.correctHistories();
113 | this.correctAppLinks();
114 | }
115 |
116 | correctHistories(){
117 | const keypairUniques = this.keypairs.map(x => x.unique());
118 | const accountUniques = this.accounts.map(x => x.unique());
119 | StoreService.get().state.history.map(x => {
120 | const acc = Account.fromJson(x.type === 'action' ? x.account : x.from);
121 | if(!keypairUniques.includes(acc.keypairUnique) || !accountUniques.includes(acc.unique())) {
122 | StoreService.get().dispatch(Actions.DELTA_HISTORY, x);
123 | }
124 | });
125 | }
126 |
127 | correctAppLinks(){
128 | const origins = this.permissions.map(x => x.origin);
129 | this.apps = this.apps.filter(x => origins.includes(x => x.origin));
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/packages/core/lib/models/Keypair.js:
--------------------------------------------------------------------------------
1 | import AES from 'aes-oop';
2 | import {blockchainName, Blockchains} from './Blockchains';
3 | import IdGenerator from '../util/IdGenerator';
4 | import ExternalWallet from './hardware/ExternalWallet';
5 | import StoreService from "../services/utility/StoreService";
6 |
7 | export default class Keypair {
8 |
9 | constructor(blockchains = []){
10 | this.id = IdGenerator.text(24);
11 | this.name = '';
12 | this.privateKey = '';
13 |
14 | this.external = null;
15 | this.fork = null;
16 |
17 | this.publicKeys = [];
18 | this.blockchains = blockchains;
19 |
20 | this.createdAt = +new Date();
21 | }
22 |
23 | static placeholder(blockchains){ return new Keypair(blockchains); }
24 | static fromJson(json){
25 | let p = Object.assign(this.placeholder(), json);
26 | if(json.hasOwnProperty('external') && !!json.external) p.external = ExternalWallet.fromJson(json.external);
27 | return p;
28 | }
29 |
30 | resetExternal(){
31 | this.external.interface.close();
32 | this.external.interface.open();
33 | // this.external = ExternalWallet.fromJson(this.external);
34 | }
35 |
36 | accounts(unique = false){
37 | const accounts = StoreService.get().state.scatter.keychain.accounts.filter(x => x.keypairUnique === this.unique());
38 | if(!unique) return accounts;
39 | return accounts.reduce((acc, account) => {
40 | if(!acc.find(x => account.network().unique() === x.network().unique()
41 | && account.sendable() === x.sendable())) acc.push(account);
42 | return acc;
43 | }, [])
44 |
45 | }
46 |
47 | enabledKey(){
48 | return this.publicKeys.find(x => x.blockchain === this.blockchains[0]);
49 | }
50 |
51 | isUnique(){
52 | return !StoreService.get().state.scatter.keychain.keypairs.find(x => {
53 | return x.enabledKey().key === this.enabledKey().key
54 | })
55 | }
56 |
57 | setName(){
58 | this.name = `${blockchainName(this.enabledKey().blockchain)} Key - ${new Date().toDateString()} - ${IdGenerator.text(4)}`
59 | }
60 |
61 | unique(){ return this.id; }
62 | clone(){ return Keypair.fromJson(JSON.parse(JSON.stringify(this))) }
63 |
64 | /***
65 | * Checks whether a private key is encrypted
66 | * @returns {boolean}
67 | */
68 | isEncrypted(){
69 | return typeof this.privateKey === 'string' && this.privateKey.length > 100;
70 | }
71 |
72 | /***
73 | * Encrypts this Keypair's Private Key
74 | * @param seed - The seed to encrypt with
75 | */
76 | encrypt(seed){
77 | if(!this.isEncrypted())
78 | this.privateKey = AES.encrypt(this.privateKey, seed);
79 | }
80 |
81 | /***
82 | * Decrypts this Keypair's Private Key
83 | * @param seed - The seed to decrypt with
84 | */
85 | decrypt(seed){
86 | if(this.isEncrypted()) {
87 | this.privateKey = AES.decrypt(this.privateKey, seed);
88 | if(typeof this.privateKey === 'object' && this.privateKey.hasOwnProperty('data')) this.privateKey = this.privateKey.data;
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/packages/core/lib/models/Locale.js:
--------------------------------------------------------------------------------
1 | export default class Locale {
2 |
3 | constructor(){
4 | this.raw = null;
5 | this.name = null;
6 | this.methods = {};
7 | this.locales = {};
8 | }
9 | static placeholder(){ return new Locale(); }
10 | static fromJson(json){ return Object.assign(this.placeholder(), json); }
11 |
12 | static fromRaw(raw){
13 | if(!raw) return this.placeholder();
14 |
15 | const p = this.placeholder();
16 | p.raw = raw;
17 | p.name = raw.name;
18 |
19 | raw.methods.map(x => {
20 | p.methods[x.name] = new Function(x.args, x.body);
21 | });
22 |
23 | Object.keys(raw.locales).map(key => {
24 | p.locales[key] = (x) => {
25 | const parseString = s => {
26 | s = s.replace('{x}', x);
27 | Object.keys(p.methods).map(method => s = s.replace(`{${method}}`, p.methods[method](x)));
28 | return s;
29 | };
30 |
31 | if(typeof raw.locales[key] === 'string') return parseString(raw.locales[key]);
32 | else return raw.locales[key].map(x => parseString(x));
33 | }
34 | });
35 |
36 | return p;
37 | }
38 |
39 | parsed(){
40 | return Locale.fromRaw(JSON.parse(this.raw));
41 | }
42 |
43 | }
--------------------------------------------------------------------------------
/packages/core/lib/models/Meta.js:
--------------------------------------------------------------------------------
1 | import Framework from "../services/utility/Framework";
2 |
3 | export default class Meta {
4 |
5 | constructor(){
6 | this.version = Framework.getVersion();
7 | this.lastVersion = Framework.getVersion();
8 | this.acceptedTerms = false;
9 | this.lastSuggestedVersion = null;
10 | }
11 |
12 | getVersion(){
13 | return Framework.getVersion()
14 | }
15 |
16 | regenerateVersion(){
17 | this.version = Framework.getVersion();
18 | }
19 |
20 | needsUpdating(){
21 | return this.version !== this.lastVersion;
22 | }
23 |
24 | static placeholder(){ return new Meta(); }
25 | static fromJson(json){ return Object.assign(this.placeholder(), json); }
26 | }
--------------------------------------------------------------------------------
/packages/core/lib/models/Network.js:
--------------------------------------------------------------------------------
1 | import {Blockchains, BlockchainsArray} from './Blockchains';
2 | import IdGenerator from '../util/IdGenerator';
3 | import Token from "./Token";
4 | import PluginRepository from "../plugins/PluginRepository";
5 | import StoreService from "../services/utility/StoreService";
6 |
7 | export default class Network {
8 | constructor(_name = '', _protocol = 'https', _host = '', _port = 0, blockchain = null, chainId = '', _path = ''){
9 | this.id = IdGenerator.numeric(12);
10 | this.name = _name;
11 | this.protocol = _protocol;
12 | this.host = _host;
13 | this.port = _port;
14 | this.path = _path;
15 | this.blockchain = blockchain;
16 | this.chainId = chainId.toString();
17 |
18 | this.fromOrigin = null;
19 | this.createdAt = +new Date();
20 |
21 | this.token = null;
22 | }
23 |
24 | static placeholder(){ return new Network(); }
25 |
26 | static fromJson(json){
27 | const p = Object.assign(Network.placeholder(), json);
28 | p.chainId = p.chainId ? p.chainId.toString() : '';
29 | p.token = json.hasOwnProperty('token') && json.token ? Token.fromJson(json.token) : null;
30 | return p;
31 | }
32 |
33 | static fromUnique(netString){
34 | const blockchain = netString.split(':')[0];
35 | if(netString.indexOf(':chain:') > -1)
36 | return new Network('', '', '','',blockchain, netString.replace(`${blockchain}:chain:`,''));
37 |
38 | const splits = netString.replace(`${blockchain}:`, '').split(':');
39 | return new Network('', '', splits[0], parseInt(splits[1] || 80), blockchain)
40 | }
41 |
42 | unique(){ return (`${this.blockchain}:` + (this.chainId.length ? `chain:${this.chainId}` : `${this.host}:${this.port}`)).toLowerCase(); }
43 | fullhost(){ return `${this.protocol}://${this.host}${this.port ? ':' : ''}${this.port}${this.path ? this.path : ''}` }
44 | clone(){ return Network.fromJson(JSON.parse(JSON.stringify(this))) }
45 |
46 | isValid(){
47 | if(!BlockchainsArray.map(x => x.value).includes(this.blockchain)) return false;
48 | return this.host.length && this.port.toString().length && this.chainId.length
49 | }
50 |
51 | setPort(){
52 | if(!this.port) this.port = 80;
53 | if(![80,443].includes(parseInt(this.port))) return;
54 | this.port = this.protocol === 'http' ? 80 : 443;
55 | }
56 |
57 | systemToken(){
58 | if(this.token) return this.token;
59 | const token = PluginRepository.plugin(this.blockchain).defaultToken();
60 | token.chainId = this.chainId;
61 | return token;
62 | }
63 |
64 | accounts(unique = false){
65 | const accounts = StoreService.get().state.scatter.keychain.accounts.filter(x => x.networkUnique === this.unique());
66 | if(!unique) return accounts;
67 | return accounts.reduce((acc, account) => {
68 | if(!acc.find(x => account.network().unique() === x.network().unique()
69 | && account.sendable() === x.sendable())) acc.push(account);
70 | return acc;
71 | }, [])
72 |
73 | }
74 | }
--------------------------------------------------------------------------------
/packages/core/lib/models/Permission.js:
--------------------------------------------------------------------------------
1 | import Hasher from '../util/Hasher';
2 | import IdGenerator from '../util/IdGenerator';
3 | import StoreService from "../services/utility/StoreService";
4 | import {IdentityRequiredFields} from "./Identity";
5 |
6 | export default class Permission {
7 |
8 | constructor(){
9 | this.id = IdGenerator.numeric(24);
10 | // Mandatory
11 | this.origin = '';
12 |
13 | this.identity = '';
14 | this.accounts = [];
15 |
16 | // Optional
17 | this.contract = null;
18 | this.contractHash = null;
19 | this.action = null;
20 | this.mutableActionFields = [];
21 | this.immutableActionFields = [];
22 |
23 | this.timestamp = 0;
24 |
25 | this.identityRequirements = [];
26 |
27 | this.isIdentity = false;
28 | this.isIdentityRequirements = false;
29 | this.isContractAction = false;
30 | }
31 |
32 | static placeholder(){ return new Permission(); }
33 | static fromJson(json){ return Object.assign(this.placeholder(), json); }
34 | clone(){ return Permission.fromJson(JSON.parse(JSON.stringify(this))) }
35 |
36 | static fromAction(origin, identity, accounts, added){
37 | const base = Permission.fromJson({
38 | origin,
39 | identity:identity.id,
40 | accounts:accounts.map(x => x.unique())
41 | });
42 | return Object.assign(base, added);
43 | }
44 |
45 | checksum(){
46 | return Hasher.unsaltedQuickHash(
47 | this.origin+
48 | this.identity+
49 | (this.accounts||[]).join(',')+
50 | this.contract+
51 | this.contractHash+
52 | this.action+
53 | (this.identityRequirements||[]).join(',')
54 | )
55 | }
56 |
57 | getIdentity(){
58 | return StoreService.get().state.scatter.keychain.findIdentity(this.identity);
59 | }
60 |
61 | getAccounts(){
62 | const accounts = StoreService.get().state.scatter.keychain.accounts;
63 | return this.accounts.map(unique => accounts.find(x => x.unique() === unique));
64 | }
65 |
66 | isIdentityPermissionFor(origin){
67 | return this.isIdentity && this.origin === origin;
68 | }
69 |
70 | static createImmutableFieldsHash(allFields, mutableFields){
71 | return Hasher.unsaltedQuickHash(Object.keys(allFields).map(key => {
72 | if(!mutableFields.includes(key)) return allFields[key];
73 | else return null;
74 | }).filter(x => x).sort().join(','));
75 | }
76 |
77 | asIdentityRequirements(){
78 | return IdentityRequiredFields.fromPermission(this.identityRequirements);
79 | }
80 | }
--------------------------------------------------------------------------------
/packages/core/lib/models/Scatter.js:
--------------------------------------------------------------------------------
1 | import Meta from './Meta';
2 | import Keychain from './Keychain';
3 | import Settings from './Settings';
4 | import AES from 'aes-oop';
5 | import Hasher from '../util/Hasher'
6 | import IdGenerator from '../util/IdGenerator'
7 | import PluginRepository from "../plugins/PluginRepository";
8 | import Identity, {LocationInformation} from "./Identity";
9 | import Contact from "./Contact";
10 |
11 | export default class Scatter {
12 |
13 | constructor(){
14 | this.meta = Meta.placeholder();
15 | this.keychain = Keychain.placeholder();
16 | this.settings = Settings.placeholder();
17 | this.contacts = [];
18 | this.hash = Hasher.unsaltedQuickHash(IdGenerator.text(2048));
19 |
20 | this.onboarded = false;
21 |
22 | this.pin = null;
23 | this.pinForAll = false;
24 | }
25 |
26 | static async create(){
27 | const scatter = new Scatter();
28 | await Promise.all(PluginRepository.signatureProviders().map(async plugin => {
29 | const network = plugin.getEndorsedNetwork();
30 | scatter.settings.networks.push(network);
31 | }));
32 |
33 | const firstIdentity = Identity.placeholder();
34 | await firstIdentity.initialize(scatter.hash);
35 |
36 | firstIdentity.name = 'MyFirstIdentity';
37 | scatter.keychain.locations = [LocationInformation.fromJson({name:'Home'})];
38 | firstIdentity.location = scatter.keychain.locations[0];
39 |
40 | scatter.keychain.updateOrPushIdentity(firstIdentity);
41 |
42 | return scatter;
43 | }
44 | static placeholder(){ return new Scatter(); }
45 | static fromJson(json){
46 | let p = Object.assign(this.placeholder(), json);
47 | if(json.hasOwnProperty('meta')) p.meta = Meta.fromJson(json.meta);
48 | if(json.hasOwnProperty('settings')) p.settings = Settings.fromJson(json.settings);
49 | if(json.hasOwnProperty('keychain'))
50 | p.keychain = (typeof json.keychain === 'string')
51 | ? json.keychain : Keychain.fromJson(json.keychain);
52 |
53 | if(json.hasOwnProperty('contacts')) p.contacts = json.contacts.map(x => Contact.fromJson(x));
54 |
55 | return p;
56 | }
57 |
58 | clone(){ return Scatter.fromJson(JSON.parse(JSON.stringify(this))) }
59 |
60 | isEncrypted(){
61 | return typeof this.keychain !== 'object'
62 | }
63 |
64 | /***
65 | * Encrypts the entire keychain
66 | * @param seed - The seed to encrypt with
67 | */
68 | decrypt(seed){
69 | if(this.isEncrypted()) this.keychain = Keychain.fromJson(AES.decrypt(this.keychain, seed));
70 | }
71 |
72 | /***
73 | * Decrypts the entire keychain
74 | * @param seed - The seed to decrypt with
75 | */
76 | encrypt(seed){
77 | if(!this.isEncrypted()) this.keychain = AES.encrypt(this.keychain, seed);
78 | }
79 |
80 | savable(seed){
81 | // Encrypting in-place.
82 | this.keychain.cards.map(card => card.encrypt(seed));
83 | this.keychain.keypairs.map(keypair => keypair.encrypt(seed));
84 | this.keychain.identities.map(id => id.encrypt(seed));
85 |
86 | // Encrypting clone
87 | const clone = this.clone();
88 | clone.encrypt(seed);
89 | return clone;
90 | }
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | /*************************************/
100 | /************ HELPERS *************/
101 | /*************************************/
102 |
103 | networkTokens(){
104 | return this.settings.networks.map(x => {
105 | const token = x.systemToken();
106 | token.chainId = x.chainId;
107 | return token;
108 | }).reduce((acc, token) => {
109 | const exists = acc.find(x => x.unique() === token.unique() && x.chainId === token.chainId);
110 | if(!exists) acc.push(token);
111 | return acc;
112 | }, [])
113 | }
114 |
115 | allTokens(){
116 | return this.networkTokens().concat(this.settings.tokens);
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/packages/core/lib/models/Settings.js:
--------------------------------------------------------------------------------
1 | import Network from './Network';
2 | import PluginRepository from '../plugins/PluginRepository';
3 | import Token from "./Token";
4 | import Explorer from "./Explorer";
5 |
6 | export const BACKUP_STRATEGIES = {
7 | MANUAL:'manual',
8 | AUTOMATIC:'auto'
9 | }
10 |
11 | export const SETTINGS_OPTIONS = {
12 | GENERAL:{ locked:false, name:'General' },
13 | TOKENS:{ locked:false, name:'Tokens' },
14 | EXPLORER:{ locked:false, name:'Explorers' },
15 | BACKUP:{ locked:false, name:'Backup' },
16 | FIREWALL:{ locked:true, name:'Firewall' },
17 | PASSWORD:{ locked:true, name:'Password' },
18 | DESTROY:{ locked:true, name:'Destroy' },
19 | };
20 |
21 | export default class Settings {
22 |
23 | constructor(){
24 | this.networks = [];
25 | this.language = 'English';
26 | this.autoBackup = BACKUP_STRATEGIES.AUTOMATIC;
27 | this.backupLocation = '';
28 | this.explorers = PluginRepository.defaultExplorers();
29 | this.showNotifications = true;
30 |
31 | // Tokens
32 | this.showMainnetsOnly = true;
33 | this.displayToken = null;
34 | this.displayCurrency = 'USD';
35 | this.tokens = [];
36 | this.blacklistTokens = [];
37 |
38 | // {contract:[actions]}
39 | this.blacklistActions = {
40 | 'eos::eosio':['updateauth', 'linkauth'],
41 | 'eos::eosio.msig':['approve'],
42 | };
43 |
44 | this.balanceFilters = {};
45 | this.hideMainBalance = false;
46 |
47 | this.simpleMode = false;
48 | }
49 |
50 | static placeholder(){ return new Settings(); }
51 | static fromJson(json){
52 | let p = Object.assign(this.placeholder(), json);
53 | if(json.hasOwnProperty('networks')) p.networks = json.networks.map(x => Network.fromJson(x));
54 | if(json.hasOwnProperty('tokens')) p.tokens = json.tokens.map(x => Token.fromJson(x));
55 | if(json.hasOwnProperty('blacklistTokens')) p.blacklistTokens = json.blacklistTokens.map(x => Token.fromJson(x));
56 | if(json.hasOwnProperty('explorers')) p.explorers = Object.keys(json.explorers).reduce((acc, blockchain) => {
57 | acc[blockchain] = Explorer.fromRaw(json.explorers[blockchain].raw);
58 | return acc;
59 | }, {});
60 | return p;
61 | }
62 |
63 | updateOrPushNetwork(network){
64 | this.networks.find(n => n.id === network.id)
65 | ? this.networks = this.networks.map(n => n.id === network.id ? network : n)
66 | : this.networks.unshift(network);
67 | }
68 |
69 | removeNetwork(network){
70 | this.networks = this.networks.filter(n => n.id !== network.id);
71 | }
72 |
73 | blacklistAction(blockchain, contract, action){
74 | if(!contract.length || !action.length) return;
75 | if(!this.blacklistActions.hasOwnProperty(`${blockchain}::${contract}`)){
76 | this.blacklistActions[`${blockchain}::${contract}`] = []
77 | }
78 |
79 | this.blacklistActions[`${blockchain}::${contract}`].push(action);
80 | }
81 |
82 | removeBlacklistedAction(blockchain, contract, action){
83 | if(!this.blacklistActions.hasOwnProperty(`${blockchain}::${contract}`)) return;
84 | if(!this.blacklistActions[`${blockchain}::${contract}`].includes(action)) return;
85 | if(this.blacklistActions[`${blockchain}::${contract}`].length === 1) return delete this.blacklistActions[`${blockchain}::${contract}`];
86 | this.blacklistActions[`${blockchain}::${contract}`] = this.blacklistActions[`${blockchain}::${contract}`].filter(x => x !== action);
87 | }
88 |
89 | isActionBlacklisted(actionTag){
90 | const [blockchain, contract, action] = actionTag.split('::');
91 | return this.blacklistActions.hasOwnProperty(`${blockchain}::${contract}`)
92 | && this.blacklistActions[`${blockchain}::${contract}`].includes(action);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/packages/core/lib/models/Token.js:
--------------------------------------------------------------------------------
1 | import IdGenerator from "../util/IdGenerator";
2 | import PluginRepository from "../plugins/PluginRepository";
3 | import {Blockchains, BlockchainsArray} from "./Blockchains";
4 | import StoreService from "../services/utility/StoreService";
5 | import BalanceService from "../services/blockchain/BalanceService";
6 |
7 | export default class Token {
8 |
9 | constructor(blockchain = null, contract = '', symbol = '', name = null, decimals = null, chainId = ''){
10 | this.id = IdGenerator.text(24);
11 | this.blockchain = blockchain;
12 | this.contract = contract;
13 | this.symbol = symbol;
14 | this.name = name ? name : symbol;
15 | this.decimals = decimals ? decimals : 2;
16 |
17 | this.amount = 0;
18 |
19 | this.chainId = chainId;
20 | this.unusable = null;
21 |
22 | this.fromOrigin = '';
23 | this.createdAt = +new Date();
24 | }
25 |
26 | isValid(){
27 | if(Object.keys(this).length !== 11) return false;
28 | return BlockchainsArray.map(x => x.value).includes(this.blockchain) &&
29 | this.contract.length &&
30 | this.symbol.length &&
31 | this.name.length &&
32 | this.decimals.toString().length &&
33 | this.chainId.length
34 | }
35 |
36 | static placeholder(){ return new Token(); }
37 | static fromJson(json){
38 | const p = Object.assign(this.placeholder(), json);
39 | if(!json.hasOwnProperty('name') || !json.name.length) p.name = json.symbol;
40 | return p;
41 | }
42 | static fromUnique(unique){
43 | const p = this.placeholder();
44 | const [blockchain, contract, symbol, chainId] = unique.split(':');
45 | p.blockchain = blockchain;
46 | p.contract = contract;
47 | p.symbol = symbol ? symbol.toUpperCase() : 'INVALID_TOKEN';
48 | p.chainId = chainId;
49 | p.decimals = PluginRepository.plugin(blockchain).defaultDecimals();
50 | return p;
51 | }
52 |
53 | clone(){ return Token.fromJson(JSON.parse(JSON.stringify(this))) }
54 |
55 | unique(){ return `${this.blockchain}:${this.contract.toLowerCase()}:${this.symbol.toLowerCase()}` }
56 | uniqueWithChain(includeUnusable = true){ return `${this.blockchain}:${this.contract.toLowerCase()}:${this.symbol.toLowerCase()}:${this.chainId}${includeUnusable && this.unusable ? `:${this.unusable}` : ''}` }
57 | identifiable(){ return `${this.blockchain}:${this.contract.toLowerCase()}` }
58 |
59 | add(quantity){
60 | this.amount = (parseFloat(this.amount) + parseFloat(quantity)).toFixed(this.decimals);
61 | }
62 |
63 | network(){
64 | const networks = StoreService.get().state.scatter.settings.networks;
65 | const endorsed = networks.find(x => x.unique() === PluginRepository.plugin(this.blockchain).getEndorsedNetwork().unique());
66 | if(!this.chainId || !this.chainId.length) return endorsed;
67 | return networks.find(x => x.blockchain === this.blockchain && x.chainId === this.chainId) || endorsed;
68 | }
69 |
70 | formatted(){
71 | return `${this.amount} ${this.symbol}`;
72 | }
73 |
74 | fiatBalance(withSymbol = true, price = null){
75 | const unusableReplacement = this.uniqueWithChain().replace(`:${this.unusable}`, '');
76 | if(StoreService.get().state.prices.hasOwnProperty(this.uniqueWithChain())){
77 | price = price ? price : parseFloat(StoreService.get().state.prices[this.uniqueWithChain()][StoreService.get().state.scatter.settings.displayCurrency]);
78 | return `${parseFloat(price * parseFloat(this.amount)).toFixed(4)} ${withSymbol ? StoreService.get().state.scatter.settings.displayCurrency : ''}`;
79 | }
80 | else if(this.unusable && StoreService.get().state.prices.hasOwnProperty(unusableReplacement)){
81 | price = price ? price : parseFloat(StoreService.get().state.prices[unusableReplacement][StoreService.get().state.scatter.settings.displayCurrency]);
82 | return `${parseFloat(price * parseFloat(this.amount)).toFixed(4)} ${withSymbol ? StoreService.get().state.scatter.settings.displayCurrency : ''}`;
83 | }
84 |
85 | else {
86 | return null;
87 | }
88 | }
89 |
90 | fiatPrice(withSymbol = true){
91 | if(StoreService.get().state.prices.hasOwnProperty(this.uniqueWithChain())){
92 | const price = parseFloat(StoreService.get().state.prices[this.uniqueWithChain()][StoreService.get().state.scatter.settings.displayCurrency]);
93 | return `${parseFloat(price).toFixed(4)} ${withSymbol ? StoreService.get().state.scatter.settings.displayCurrency : ''}`
94 | } else {
95 | return null;
96 | }
97 | }
98 |
99 | baseTokenPrice(withSymbol = true){
100 | if(StoreService.get().state.prices.hasOwnProperty(this.uniqueWithChain())){
101 | const systemToken = this.network().systemToken();
102 | if(this.uniqueWithChain(false) === systemToken.uniqueWithChain(false)) return null;
103 | const baseTokenPrice = parseFloat(StoreService.get().state.prices[systemToken.uniqueWithChain()][StoreService.get().state.scatter.settings.displayCurrency]);
104 | const price = parseFloat(StoreService.get().state.prices[this.uniqueWithChain()][StoreService.get().state.scatter.settings.displayCurrency]);
105 | return `${parseFloat(price/baseTokenPrice).toFixed(10)} ${withSymbol ? systemToken.symbol : ''}`
106 | } else {
107 | return null;
108 | }
109 | }
110 |
111 | totalBalance(){
112 | if(BalanceService.totalBalances().totals.hasOwnProperty(this.uniqueWithChain())){
113 | return BalanceService.totalBalances().totals[this.uniqueWithChain()];
114 | } else {
115 | return null;
116 | }
117 | }
118 |
119 | symbolClass(){
120 | const iconSearch = `${this.blockchain}-${this.symbol}`.toLowerCase();
121 | const icons = ['eth-tusd', 'btc-btc', 'eos-eos', 'eth-dai', 'trx-trx', 'eth-eth', 'fio-fio'];
122 | return icons.includes(iconSearch) ? `token-icon token-${iconSearch}` : null;
123 | }
124 |
125 | truncatedSymbol(){
126 | return this.symbol.length > 4 ? this.symbol.substr(0,4) : this.symbol
127 | }
128 |
129 | accounts(){
130 | return StoreService.get().state.scatter.keychain.accounts.filter(x => x.blockchain() === this.blockchain && x.network().chainId === this.chainId).reduce((acc,x) => {
131 | if(!acc.find(y => y.sendable() === x.sendable())) acc.push(x);
132 | return acc;
133 | }, []);
134 |
135 | // Problem with doing this is that if the balance checks fail then accounts never show up.
136 | // const state = StoreService.get().state;
137 | // if(!state.balances) return [];
138 | // return Object.keys(state.balances).reduce((acc,accountUnique) => {
139 | // if(state.balances[accountUnique].find(token => token.uniqueWithChain() === this.uniqueWithChain())){
140 | // if(!acc.find(x => x.identifiable() === accountUnique)){
141 | // acc.push(state.scatter.keychain.accounts.find(x => x.identifiable() === accountUnique));
142 | // }
143 | // }
144 | // return acc;
145 | // }, []);
146 | }
147 |
148 | static sorter(a,b){
149 | const untouchable = !!b.unusable ? 1 : !!a.unusable ? -1 : 0;
150 | const systemTokenUniques = StoreService.get().state.scatter.networkTokens().map(x => x.uniqueWithChain(false));
151 | const isSelfSystem = systemTokenUniques.includes(b.uniqueWithChain(false)) ? 1 : systemTokenUniques.includes(a.uniqueWithChain(false)) ? -1 : 0;
152 | return isSelfSystem || untouchable || (b.fiatBalance(false) || 0) - (a.fiatBalance(false) || 0);
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/packages/core/lib/models/api/ApiActions.js:
--------------------------------------------------------------------------------
1 | export const GET_VERSION = 'getVersion';
2 | export const GET_PUBLIC_KEY = 'getPublicKey';
3 | export const LINK_ACCOUNT = 'linkAccount';
4 | export const HAS_ACCOUNT_FOR = 'hasAccountFor';
5 | export const LOGIN = 'getOrRequestIdentity';
6 | export const LOGIN_ALL = 'getAllAccountsFor';
7 | export const GET_AVATAR = 'getAvatar';
8 | export const IDENTITY_FROM_PERMISSIONS = 'identityFromPermissions';
9 | export const LOGOUT = 'forgetIdentity';
10 | export const TRANSFER = 'requestTransfer';
11 | export const SIGN = 'requestSignature';
12 | export const ADD_TOKEN = 'addToken';
13 | export const SIGN_ARBITRARY = 'requestArbitrarySignature';
14 | export const SUGGEST_NETWORK = 'requestAddNetwork';
15 | export const AUTHENTICATE = 'authenticate';
16 | export const UPDATE_IDENTITY = 'updateIdentity';
17 | export const CREATE_ENCRYPTION_KEY = 'createEncryptionKey';
18 |
--------------------------------------------------------------------------------
/packages/core/lib/models/api/index.js:
--------------------------------------------------------------------------------
1 | import * as ApiActions from './ApiActions';
2 |
3 | export default {ApiActions};
--------------------------------------------------------------------------------
/packages/core/lib/models/errors/Error.js:
--------------------------------------------------------------------------------
1 | import * as ErrorTypes from './ErrorTypes'
2 |
3 | export const ErrorCodes = {
4 | NO_SIGNATURE:402,
5 | FORBIDDEN:403,
6 | TIMED_OUT:408,
7 | LOCKED:423,
8 | UPGRADE_REQUIRED:426,
9 | TOO_MANY_REQUESTS:429
10 | };
11 |
12 | export default class Error {
13 |
14 | constructor(_type, _message, _code = ErrorCodes.LOCKED){
15 | this.type = _type;
16 | this.message = _message;
17 | this.code = _code;
18 | this.isError = true;
19 | }
20 |
21 | static locked(){
22 | return new Error(ErrorTypes.LOCKED, "The user's Scatter is locked. They have been notified and should unlock before continuing.")
23 | }
24 |
25 | static signatureError(_type, _message){
26 | return new Error(_type, _message, ErrorCodes.NO_SIGNATURE)
27 | }
28 |
29 | static malicious(_message){
30 | return new Error(ErrorTypes.MALICIOUS, _message, ErrorCodes.FORBIDDEN)
31 | }
32 |
33 | static rejected(){
34 | return new Error(ErrorTypes.REJECTED, 'The user rejected the request.', ErrorCodes.NO_SIGNATURE)
35 | }
36 |
37 | static identityMissing(){
38 | return this.signatureError("identity_missing", "Identity no longer exists on the user's keychain or user is not logged in.");
39 | }
40 |
41 | static badNetwork(){
42 | return this.signatureError("bad_network", "The network you provided is malformed.");
43 | }
44 |
45 | static noKeypair(){
46 | return this.signatureError("no_keypair", "The public key you provided does not exist on the user's keychain.");
47 | }
48 |
49 | static signatureAccountMissing(){
50 | return this.signatureError("account_missing", "You are trying to sign a request with an account that isn't currently linked or doesn't exist in the user's Scatter");
51 | }
52 |
53 | static sharedSecretNotAvailable(){
54 | return this.signatureError("no_shared_secret", "The blockchain you want to use to create a shared secret does not support creating them.");
55 | }
56 |
57 | static cantParseTransaction(){
58 | return this.signatureError("parsing_error", "Something happened while trying to parse the transaction internally.");
59 | }
60 |
61 | static noNetwork(){
62 | return this.signatureError("no_network", "This user does not have this network in their Scatter.");
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/packages/core/lib/models/errors/ErrorTypes.js:
--------------------------------------------------------------------------------
1 | export const MALICIOUS = 'malicious';
2 | export const LOCKED = 'locked';
3 | export const PROMPT_CLOSED = 'prompt_closed';
4 | export const UPGRADE_REQUIRED = 'upgrade_required';
5 | export const REJECTED = 'rejected';
6 |
--------------------------------------------------------------------------------
/packages/core/lib/models/errors/index.js:
--------------------------------------------------------------------------------
1 | import Error from './Error';
2 | import * as ErrorTypes from './ErrorTypes';
3 |
4 | export default {Error, ErrorTypes};
--------------------------------------------------------------------------------
/packages/core/lib/models/hardware/ExternalWallet.js:
--------------------------------------------------------------------------------
1 | import IdGenerator from '../../util/IdGenerator';
2 |
3 | export let EXT_WALLET_TYPES = {};
4 |
5 | // Format [ {type,name,wallet}, {type,name,wallet} ]
6 | let WALLETS = [];
7 |
8 | export default class ExternalWallet {
9 |
10 | static loadWallets(_wallets){
11 | EXT_WALLET_TYPES = _wallets.map(x => ({[x.type]:x.name}));
12 | WALLETS = _wallets;
13 | }
14 |
15 | constructor(_type = null, _blockchain = null){
16 | this.id = IdGenerator.text(64);
17 | this.type = _type;
18 | this.blockchain = _blockchain;
19 | this.addressIndex = 0;
20 | }
21 |
22 | static placeholder(){ return new ExternalWallet(); }
23 | static fromJson(json){
24 | return Object.assign(this.placeholder(), json);
25 | }
26 |
27 | setup(){
28 | this.interface = getInterface(this.type, this.blockchain);
29 | }
30 | }
31 |
32 | const getInterface = (type, blockchain) => {
33 | if(EXT_WALLET_TYPES.hasOwnProperty(type)) return WALLETS[type].wallet.typeToInterface(blockchain);
34 | return console.error('Type not defined in hardware wallets');
35 | }
36 |
37 | export class ExternalWalletInterface {
38 |
39 | constructor(handler){
40 | this.handler = handler;
41 | }
42 |
43 | async open(){
44 | return await this.handler.open();
45 | }
46 |
47 | async close(){
48 | return await this.handler.close();
49 | }
50 |
51 | async canConnect(){
52 | return await this.handler.canConnect();
53 | }
54 |
55 | async sign(publicKey, transaction, abi, network){
56 | return await this.handler.sign(publicKey, transaction, abi, network);
57 | }
58 |
59 | async getPublicKey(){
60 | return await this.handler.getPublicKey();
61 | }
62 |
63 | setAddressIndex(path){
64 | return this.handler.setAddressIndex(path);
65 | }
66 |
67 | availableBlockchains(){
68 | return this.handler.availableBlockchains();
69 | }
70 |
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/packages/core/lib/models/hardware/index.js:
--------------------------------------------------------------------------------
1 | import ExternalWallet from './ExternalWallet';
2 |
3 | export default {ExternalWallet};
--------------------------------------------------------------------------------
/packages/core/lib/models/histories/HistoricAction.js:
--------------------------------------------------------------------------------
1 | import History, {HISTORY_TYPES} from "./History";
2 | import Account from "../Account";
3 |
4 | export default class HistoricAction extends History {
5 |
6 |
7 | constructor(account, action, txid = ''){
8 | super(HISTORY_TYPES.Action, txid);
9 | this.account = account instanceof Account ? account.unique() : account;
10 | this.action = action;
11 | }
12 |
13 | clone(){ return HistoricAction.fromJson(JSON.parse(JSON.stringify(this))) }
14 | static placeholder(){ return new HistoricAction(); }
15 | static fromJson(json){ return Object.assign(this.placeholder(), json); }
16 |
17 | }
--------------------------------------------------------------------------------
/packages/core/lib/models/histories/HistoricExchange.js:
--------------------------------------------------------------------------------
1 | import History, {HISTORY_TYPES} from "./History";
2 | import Account from "../Account";
3 | import Token from "../Token";
4 |
5 | export default class HistoricExchange extends History {
6 |
7 |
8 | constructor(from, to, fromToken, toToken, orderDetails, txid = ''){
9 | super(HISTORY_TYPES.Exchange, txid);
10 | this.from = from;
11 | this.to = to;
12 | this.fromToken = fromToken;
13 | this.toToken = toToken;
14 | this.orderDetails = orderDetails;
15 | this.status = 'pending';
16 | }
17 |
18 | static placeholder(){ return new HistoricExchange(); }
19 | static fromJson(json){
20 | let p = Object.assign(this.placeholder(), json);
21 | p.from = Account.fromJson(json.from);
22 | p.fromToken = Token.fromJson(json.fromToken);
23 | p.toToken = Token.fromJson(json.toToken);
24 | return p;
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/packages/core/lib/models/histories/HistoricTransfer.js:
--------------------------------------------------------------------------------
1 | import History, {HISTORY_TYPES} from "./History";
2 | import Account from "../Account";
3 | import Token from "../Token";
4 |
5 | export default class HistoricTransfer extends History {
6 |
7 |
8 | constructor(from, to, token, amount, memo = null, txid = ''){
9 | super(HISTORY_TYPES.Transfer, txid);
10 | this.from = from;
11 | this.to = to;
12 | this.token = token;
13 | this.amount = amount;
14 | this.memo = memo;
15 | }
16 |
17 | static placeholder(){ return new HistoricTransfer(); }
18 | static fromJson(json){
19 | let p = Object.assign(this.placeholder(), json);
20 | p.from = Account.fromJson(json.from);
21 | p.token = Token.fromJson(json.token);
22 | return p;
23 | }
24 |
25 | }
--------------------------------------------------------------------------------
/packages/core/lib/models/histories/History.js:
--------------------------------------------------------------------------------
1 | import IdGenerator from "../../util/IdGenerator";
2 |
3 | export const HISTORY_TYPES = {
4 | Transfer:'transfer',
5 | Exchange:'exchange',
6 | Action:'action'
7 | };
8 |
9 | export default class History {
10 |
11 | constructor(type, txid = ''){
12 | this.id = IdGenerator.text(24);
13 | this.type = type;
14 | this.timestamp = +new Date();
15 | this.txid = typeof txid === 'string' ? txid : '';
16 | }
17 | }
--------------------------------------------------------------------------------
/packages/core/lib/models/histories/index.js:
--------------------------------------------------------------------------------
1 | import HistoricAction from './HistoricAction';
2 | import HistoricExchange from './HistoricExchange';
3 | import HistoricTransfer from './HistoricTransfer';
4 | import History from './History';
5 |
6 | export default {HistoricAction, HistoricExchange, HistoricTransfer, History};
--------------------------------------------------------------------------------
/packages/core/lib/models/index.js:
--------------------------------------------------------------------------------
1 | import api from './api/index';
2 | import hardware from './hardware/index';
3 | import histories from './histories/index';
4 |
5 | import Account from './Account';
6 | import AccountAction from './AccountAction';
7 | import AuthorizedApp from './AuthorizedApp';
8 | import * as Blockchains from './Blockchains';
9 | import Contact from './Contact';
10 | import CreditCard from './CreditCard';
11 | import Explorer from './Explorer';
12 | import Identity from './Identity';
13 | import Keychain from './Keychain';
14 | import Keypair from './Keypair';
15 | import Locale from './Locale';
16 | import Meta from './Meta';
17 | import Network from './Network';
18 | import Permission from './Permission';
19 | import Scatter from './Scatter';
20 | import Settings from './Settings';
21 | import Token from './Token';
22 |
23 | export default {
24 | api,
25 | hardware,
26 | histories,
27 | Account,
28 | AccountAction,
29 | AuthorizedApp,
30 | Blockchains,
31 | Contact,
32 | CreditCard,
33 | Explorer,
34 | Identity,
35 | Keychain,
36 | Keypair,
37 | Locale,
38 | Meta,
39 | Network,
40 | Permission,
41 | Scatter,
42 | Settings,
43 | Token,
44 | }
--------------------------------------------------------------------------------
/packages/core/lib/plugins/Plugin.js:
--------------------------------------------------------------------------------
1 |
2 | export default class Plugin {
3 |
4 | constructor(_name = '', _type = ''){
5 | this.name = _name;
6 | this.type = _type;
7 | }
8 |
9 | }
--------------------------------------------------------------------------------
/packages/core/lib/plugins/PluginRepository.js:
--------------------------------------------------------------------------------
1 | import * as PluginTypes from './PluginTypes';
2 | import {Blockchains, BlockchainsArray} from '../models/Blockchains';
3 | import Explorer from "../models/Explorer";
4 |
5 | class PluginRepositorySingleton {
6 |
7 | constructor(){
8 | this.plugins = [];
9 | }
10 |
11 | loadPlugins(plugins){
12 | plugins.map(plugin => this.plugins.push(new plugin));
13 | }
14 |
15 | signatureProviders(){
16 | return this.plugins.filter(plugin => plugin.type === PluginTypes.BLOCKCHAIN_SUPPORT);
17 | }
18 |
19 | plugin(name){
20 | return this.plugins.find(plugin => plugin.name === name);
21 | }
22 |
23 | defaultExplorers(){
24 | return BlockchainsArray.reduce((acc,x) => {
25 | if(this.plugin(x.value)) {
26 | acc[x.value] = Explorer.fromJson({
27 | raw:this.plugin(x.value).defaultExplorer()
28 | });
29 | }
30 | return acc;
31 | }, {})
32 | }
33 |
34 | bustCaches(){
35 | this.signatureProviders().map(sp => sp.bustCache())
36 | }
37 | }
38 |
39 | const PluginRepository = new PluginRepositorySingleton();
40 | export default PluginRepository;
41 |
--------------------------------------------------------------------------------
/packages/core/lib/plugins/PluginTypes.js:
--------------------------------------------------------------------------------
1 | export const BLOCKCHAIN_SUPPORT = 'blockchain_support';
--------------------------------------------------------------------------------
/packages/core/lib/plugins/defaults/index.js:
--------------------------------------------------------------------------------
1 | import pluginInterface from './interface';
2 |
3 | export default {
4 | pluginInterface,
5 | }
--------------------------------------------------------------------------------
/packages/core/lib/plugins/defaults/interface.js:
--------------------------------------------------------------------------------
1 | import {Blockchains} from "../../models/Blockchains";
2 | import * as PluginTypes from "../PluginTypes";
3 | import Plugin from "../Plugin";
4 |
5 | /***
6 | * DO NOT INCLUDE
7 | * This is just an interface for quickly raising
8 | * new Scatter blockchain plugins
9 | */
10 | export default class PluginInterface extends Plugin {
11 | constructor(){ super('blockchain_type', PluginTypes.BLOCKCHAIN_SUPPORT) }
12 |
13 | /***
14 | * BIP is the path on a device such as a ledger (HD wallet).
15 | * EXAMPLE: return `44'/0'/0'/0/`
16 | */
17 | bip(){}
18 |
19 | /***
20 | * If there is an internal cache, this method being called should clear it.
21 | */
22 | bustCache(){}
23 |
24 | /***
25 | * Explorer is an object that points to a web-interface block explorer.
26 | * {x} is used as a placeholder for an account/txid/block_id.
27 | * EXAMPLE:
28 | {
29 | "name":"EXPLORER NAME",
30 | "account":"https://explorer.com/account/{x}",
31 | "transaction":"https://explorer.com/transaction/{x}",
32 | "block":"https://explorer.com/block/{x}"
33 | }
34 | */
35 | defaultExplorer(){}
36 |
37 | /***
38 | * Account formatter turns an `Account` model into a string which is used as the recipient of transactions.
39 | * For instance in EOSIO blockchains the formatter would return `account.name`, but in Ethereum blockchains it would return
40 | * `account.publicKey` which denotes the address instead of a name.
41 | */
42 | accountFormatter(account){}
43 |
44 | /***
45 | * Returnable account is a POJO that is returned to interacting applications.
46 | * For instance, in EOSIO blockchains a name is required, however in Ethereum blockchains only a publicKey/address is required.
47 | */
48 | returnableAccount(account){}
49 |
50 | /***
51 | * This is a UI helper which defines what a placeholder value for a contract might be.
52 | * For instance in Ethereum blockchains it might be `0x...`, while in EOSIO blockchains it might be `eosio.token`
53 | */
54 | contractPlaceholder(){}
55 |
56 | /***
57 | * Check network simply checks the availability/connectivity of a `Network`.
58 | * This should either resolve or timeout after 2-4 seconds.
59 | */
60 | checkNetwork(network){}
61 |
62 | /***
63 | * An endorsed network is simply a "default" network hardcoded into the plugin, providing an absolute fallback
64 | * for a node connection.
65 | * THIS MUST RETURN A NETWORK CLASS
66 | * EXAMPLE:
67 | return new Network('EOS Mainnet', 'https', 'nodes.get-scatter.com', 443, Blockchains.EOSIO, MAINNET_CHAIN_ID)
68 | */
69 | getEndorsedNetwork(){}
70 |
71 | /***
72 | * Checks if a given network is the endorsed network (or a network matching the chainID)
73 | * EXAMPLE:
74 | return network.blockchain === Blockchains.EOSIO && network.chainId === MAINNET_CHAIN_ID;
75 | */
76 | isEndorsedNetwork(network){}
77 |
78 | /***
79 | * Fetches the chainID from the network (live) if available.
80 | */
81 | async getChainId(network){}
82 |
83 | /***
84 | * If specialized actions are required by the blockchain (like key management) this
85 | * should return true, otherwise false.
86 | * In the case of `true`, the plugin must also include an `accountActions` method
87 | */
88 | hasAccountActions(){}
89 |
90 | // OPTIONAL, this is only required if `hasAccountActions` is `true`.
91 | // accountActions(account, callback){
92 | // return [
93 | // new AccountAction("unlink_account", () => callback(account)),
94 | // new AccountAction("change_permissions", () => callback(account), true),
95 | // ];
96 | // }
97 |
98 | /***
99 | * This might need to be re-designed to be dynamic, however this designates a
100 | * blockchain as using resources like CPU/NET/RAM on EOSIO blockchains.
101 | * If this is true, then `getResourcesFor(account)`, `needsResources(account)` and `addResources(account)` must also be included.
102 | * For examples check the EOSIO plugin.
103 | */
104 | usesResources(){ return false; }
105 |
106 | /***
107 | * Accounts are sometimes required to be created or imported before being available (such is the case in EOSIO blockchains).
108 | * If this is set to false, a dummy account will always be created using the publicKey/address. If not, then an account creation
109 | * process will need to be created on the front-end which will require UI work.
110 | */
111 | accountsAreImported(){ return false; }
112 |
113 | /***
114 | * Should check whether a string account_name/address is a valid recipient.
115 | */
116 | isValidRecipient(name){}
117 |
118 | /***
119 | * Converts a private key to a public key
120 | */
121 | privateToPublic(privateKey){}
122 |
123 | /***
124 | * Checks whether a private key is valid
125 | */
126 | validPrivateKey(privateKey){}
127 |
128 | /***
129 | * Checks whether a public key is valid
130 | */
131 | validPublicKey(publicKey){}
132 |
133 | /***
134 | * Generates a random private key
135 | * NOTE: This isn't used in Scatter, as we use buffer keys generated elsewhere.
136 | */
137 | randomPrivateKey(){}
138 |
139 | /***
140 | * Converts a byte buffer into a hex private key.
141 | */
142 | bufferToHexPrivate(buffer){}
143 |
144 | /***
145 | * Converts a hex private key into a byte buffer.
146 | */
147 | hexPrivateToBuffer(privateKey){}
148 |
149 | /***
150 | * Takes a transaction payload and returns a flat array of participants.
151 | * EXAMPLES:
152 | * EOSIO: ['testaccount@active']
153 | * ETHEREUM: ['0x....']
154 | */
155 | actionParticipants(payload){}
156 |
157 | /***
158 | * Untouchable tokens are things like staked tokens on the system level.
159 | * If a blockchain has untouchable (un-usable/un-transferable) tokens, then a `async untouchableBalance(account)` method
160 | * must also be provided which returns an array of `Token` class.
161 | */
162 | hasUntouchableTokens(){ return false; }
163 |
164 | /***
165 | * Gets a single token's balance.
166 | * Returns a Token class where `token.amount` is the balance.
167 | */
168 | async balanceFor(account, token){}
169 |
170 | /***
171 | * Gets an array of token's values.
172 | * The `tokens` param might also be omitted which would mean to grab "all available tokens for an account".
173 | * Returns an array of Token class.
174 | */
175 | async balancesFor(account, tokens = null){}
176 |
177 | /***
178 | * The default decimal count for tokens on this blockchain.
179 | * Returns an integer.
180 | */
181 | defaultDecimals(){}
182 |
183 | /***
184 | * The default token for this blockchain.
185 | * Returns a Token class.
186 | */
187 | defaultToken(){}
188 |
189 | /***
190 | * This is usually used internally inside of your walletpack plugin.
191 | * Simply takes a payload and converts it into a request that signing popups understand.
192 | * Check one of the existing plugins for the structures.
193 | * EXAMPLE:
194 | payload.messages = [...];
195 | payload.identityKey = StoreService.get().state.scatter.keychain.identities[0].publicKey;
196 | payload.participants = [account];
197 | payload.network = account.network();
198 | payload.origin = 'Scatter';
199 | const request = {
200 | payload,
201 | origin:payload.origin,
202 | blockchain:'YOUR BLOCKCHAIN',
203 | requiredFields:{},
204 | type:Actions.SIGN,
205 | id:1,
206 | };
207 | */
208 | async signerWithPopup(payload, account, rejector){}
209 |
210 | /***
211 | * Creates a token transfer. Should use the signerWithPopup above to
212 | * create a popup which the users has to sign.
213 | * This might also be requested to be bypassed sometimes with the `prompForSignature = false` flag param.
214 | */
215 | async transfer({account, to, amount, contract, symbol, memo, promptForSignature = true}){}
216 |
217 | /***
218 | * Does the actual signing of a transaction.
219 | * The `payload` will vary based on blockchain as it comes directly from their own libraries.
220 | * The goal of this method is to turn a payload into a signed transaction though, so if that works in tests then
221 | * it will work perfectly elsewhere.
222 | * Notes:
223 | * `arbitrary` and `isHash`: Sometimes blockchains change signing types based on whether a signature is signing a string (arbitrary) or a hash.
224 | */
225 | async signer(payload, publicKey, arbitrary = false, isHash = false, privateKey = null){
226 | // IMPORTANT: This method should always start with these calls.
227 | // The `privateKey` is not ensured to be passed into this method.
228 | //if(!privateKey) privateKey = await KeyPairService.publicToPrivate(publicKey);
229 | //if (!privateKey) return;
230 | }
231 |
232 | /***
233 | * The goal of this method is to parse a `payload` into something which UI's understand.
234 | * Payload will differ based on blockchain, as it depends on the blockchain's actual javascript library.
235 | * The `abi` parameter is needed for blockchains which don't have on-chain ABI stores (which is bad).
236 | *
237 | * The results of this method should be an array structured as follows:
238 | [
239 | // Many actions can be in the result as some blockchain support multiple actions within a transaction (batch).
240 | {
241 | // This is a key-value pair of the details of the transaction.
242 | // These details will be displayed for the user in the signature prompt.
243 | data:{
244 | hello:'world',
245 | value:1,
246 | object:{...}
247 | }
248 | // The contract name, or in the case of a system token transfer then the account_name/address being sent to
249 | code,
250 | // Either the method name for a smart contract, or `transfer` for a system token transfer.
251 | type,
252 | // The authorizor of this transaction (account_name/address STRING)
253 | authorization
254 | }
255 | ]
256 | */
257 | async requestParser(payload, network, abi = null){}
258 | }
259 |
--------------------------------------------------------------------------------
/packages/core/lib/plugins/index.js:
--------------------------------------------------------------------------------
1 | import Plugin from './Plugin';
2 | import PluginRepository from './PluginRepository';
3 | import * as PluginTypes from './PluginTypes';
4 |
5 | export default {
6 | Plugin,
7 | PluginRepository,
8 | PluginTypes,
9 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/apis/BackendApiService.js:
--------------------------------------------------------------------------------
1 | import IdGenerator from "../../util/IdGenerator";
2 | import ecc from 'eosjs-ecc';
3 |
4 | // const baseUrl = `http://localhost:6547/v1/`;
5 | const baseUrl = `https://api.get-scatter.com/v1/`;
6 | const PROOF_KEY = 'EOS62b3WxfuRyP7JYaDbF3gr49joLWYpsF3kPmo2HPxPuGRDiRUwj';
7 |
8 | const getHeaders = () => {
9 | const proof = IdGenerator.text(64);
10 | return [proof, {
11 | 'Accept': 'application/json',
12 | 'Content-Type': 'application/json',
13 | proof,
14 | }]
15 | };
16 |
17 | // All API requests must come back signed with the known
18 | // public key associated with the Scatter API
19 | const validate = (proof, res) => {
20 | try {
21 | const signedProof = res.headers.get('proof');
22 | if(!signedProof) throw 'Invalid API Request';
23 | if(ecc.recover(signedProof, proof) !== PROOF_KEY) throw 'Invalid API Request';
24 | return res.json();
25 | } catch(e){
26 | throw "Invalid API Request";
27 | }
28 | };
29 |
30 | export const GET = route => {
31 | const [proof, headers] = getHeaders();
32 | return fetch(`${baseUrl}${route}`, {
33 | method:'GET',
34 | mode:'cors',
35 | headers
36 | }).then(res => validate(proof,res))
37 | };
38 |
39 | export const POST = (route, data) => {
40 | const [proof, headers] = getHeaders();
41 | return fetch(`${baseUrl}${route}`, {
42 | method:'POST',
43 | mode:'cors',
44 | headers,
45 | body:JSON.stringify(data),
46 | }).then(res => validate(proof,res))
47 | };
48 |
49 | export default class BackendApiService {
50 |
51 | // Add an array of applinks to filter only those results.
52 | static async apps(apps = []){
53 | return POST(`apps`, {apps});
54 | }
55 |
56 | // ACCOUNT CREATION
57 | static async checkMachineId(id){ return GET(`machine/${id}`); }
58 | static async createAccount(payload){ return POST(`create_bridge`, payload); }
59 |
60 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/apis/ExchangeService.js:
--------------------------------------------------------------------------------
1 | import * as Actions from '../../store/constants';
2 | import BalanceService from "../blockchain/BalanceService";
3 | import {GET, POST} from './BackendApiService';
4 | import Token from "../../models/Token";
5 | import StoreService from "../utility/StoreService";
6 | import Framework from "../utility/Framework";
7 |
8 |
9 | const timeout = (rq, caughtValue = null) => Promise.race([
10 | new Promise(resolve => setTimeout(() => resolve(caughtValue), 10000)),
11 | rq.catch(() => caughtValue)
12 | ]);
13 |
14 | let watchers = [];
15 | let watchTimeout;
16 |
17 | export default class ExchangeService {
18 |
19 | static async pairs(token){
20 | return timeout(POST('exchange/pairs', {token})).then(pairs => {
21 | if(!pairs) return [];
22 | Object.keys(pairs).map(key => pairs[key].map(x => x.token = Token.fromJson(x.token)));
23 | return pairs;
24 | });
25 | }
26 |
27 | static async rate(token, other, service){
28 | return timeout(POST('exchange/rate', {token, other, service}));
29 | }
30 |
31 | static async order(service, token, other, amount, from, to, returnsErrors = true){
32 | return timeout(POST('exchange/order', {service, token, other, amount, from, to, returnsErrors}));
33 | }
34 |
35 | static async accepted(id){
36 | return timeout(GET(`exchange/accepted/${id}`));
37 | }
38 |
39 | static async cancelled(id){
40 | return timeout(GET(`exchange/cancelled/${id}`));
41 | }
42 |
43 | static async orderStatus(id){
44 | return timeout(GET(`exchange/order/${id}`).then(res => res.updated.status));
45 | }
46 |
47 | static async stablePaths(){
48 | return timeout(GET(`exchange/stabilize/paths`), []);
49 | }
50 |
51 | static async pairable(){
52 | return timeout(GET(`exchange/pairable`), []);
53 | }
54 |
55 | static watch(history){
56 | watchers.push(history);
57 | this.checkExchanges();
58 | return true;
59 | }
60 |
61 | static async checkExchanges(){
62 | clearTimeout(watchTimeout);
63 | if(!watchers.length) return;
64 |
65 | for(let i = 0; i < watchers.length; i++){
66 | const history = watchers[i];
67 | const status = await this.orderStatus(history.orderDetails.id);
68 | if(status !== history.status){
69 | history.status = status;
70 | await StoreService.get().dispatch(Actions.UPDATE_HISTORY, history);
71 |
72 | if(status === 'complete'){
73 | // TODO: need to solve this with an injected sound service
74 | //SoundService.ding();
75 |
76 | Framework.pushNotification('Exchange Complete', `Your token exchange has just completed.`);
77 |
78 | watchers = watchers.filter(x => x.id !== history.id);
79 |
80 | const accounts = StoreService.get().state.scatter.keychain.accounts.filter(x => x.sendable() === history.to);
81 | if(accounts.length){
82 | for(let n = 0; n < accounts.length; n++){
83 | await BalanceService.loadBalancesFor(accounts[n]);
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
90 | setTimeout(() => this.checkExchanges(), 1000*30);
91 | }
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/packages/core/lib/services/apis/PriceService.js:
--------------------------------------------------------------------------------
1 | import * as Actions from '../../store/constants';
2 | import Token from "../../models/Token";
3 | import {GET} from "./BackendApiService";
4 | import StoreService from "../utility/StoreService";
5 | import {dateId} from "../../util/DateHelpers";
6 |
7 |
8 | // Once every 30 minutes.
9 | const intervalTime = 60000 * 30;
10 | let priceInterval;
11 |
12 |
13 | export default class PriceService {
14 |
15 | static async watchPrices(enable = true){
16 | clearInterval(priceInterval);
17 | if(!enable) return;
18 | return new Promise(async resolve => {
19 |
20 | const setPrices = async () => {
21 | await PriceService.setPrices();
22 | resolve(true);
23 | }
24 |
25 | await setPrices();
26 | priceInterval = setInterval(async () => {
27 | await setPrices();
28 | }, intervalTime);
29 | })
30 | }
31 |
32 | static async setPrices(){
33 | const prices = await PriceService.getAll();
34 | if(prices && Object.keys(prices).length) {
35 | await StoreService.get().dispatch(Actions.SET_PRICES, prices);
36 | }
37 | }
38 |
39 | static getAll(){
40 | return Promise.race([
41 | new Promise(resolve => setTimeout(() => resolve(false), 10000)),
42 | GET(`prices?v2=true`).catch(() => {
43 | return {error:"Problem connecting to Prices API"};
44 | })
45 | ])
46 | }
47 |
48 | static async getCurrencies(){
49 | return Promise.race([
50 | new Promise(resolve => setTimeout(() => resolve(false), 10000)),
51 | GET('currencies').catch(() => ['USD'])
52 | ])
53 | }
54 |
55 | static async getCurrencyPrices(){
56 | return Promise.race([
57 | new Promise(resolve => setTimeout(() => resolve(false), 10000)),
58 | GET('currencies/prices').catch(() => null)
59 | ])
60 | }
61 |
62 | static async loadPriceTimelineData(){
63 | const prices = await PriceService.getCurrencyPrices();
64 | const yesterday = await PriceService.getTimeline(dateId(1));
65 | const today = await PriceService.getTimeline();
66 | await StoreService.get().dispatch(Actions.SET_PRICE_DATA, {prices, yesterday, today});
67 | return {prices, yesterday, today};
68 | }
69 |
70 | static async getTimeline(date = null){
71 | const query = date ? `?date=${date}` : '';
72 | return Promise.race([
73 | new Promise(resolve => setTimeout(() => resolve(false), 10000)),
74 | GET('prices/timeline'+query).catch(() => {})
75 | ])
76 | }
77 |
78 | static getTotal(totals, displayCurrency, bypassDisplayToken, displayToken){
79 | if(!displayCurrency) displayCurrency = StoreService.get().state.scatter.settings.displayCurrency;
80 |
81 | if(!bypassDisplayToken && displayToken){
82 | if(totals.hasOwnProperty(displayToken)) return totals[displayToken]
83 | else {
84 | const token = (displayToken instanceof Token ? displayToken : Token.fromUnique(displayToken)).clone();
85 | token.amount = parseFloat(0).toFixed(token.decimals);
86 | return token;
87 | }
88 | } else {
89 | let total = 0;
90 | Object.keys(StoreService.get().state.prices).map(tokenUnique => {
91 | const balance = totals[tokenUnique];
92 | if(balance){
93 | const price = StoreService.get().state.prices[tokenUnique][displayCurrency];
94 | const value = parseFloat(parseFloat(balance.amount) * parseFloat(price));
95 | if(isNaN(value)) return;
96 | total += value;
97 | }
98 | });
99 |
100 | return Token.fromJson({
101 | symbol:this.fiatSymbol(displayCurrency),
102 | amount:total.toFixed(2),
103 | decimals:2,
104 | })
105 | }
106 | }
107 |
108 | static fiatSymbol(currency) {
109 | if(!currency) currency = StoreService.get().state.scatter.settings.displayCurrency;
110 | switch(currency){
111 | case 'USD':
112 | case 'AUD':
113 | case 'CAD':
114 | return '$';
115 | case 'CNY':
116 | case 'JPY':
117 | return '¥';
118 | case 'EUR': return '€';
119 | case 'GBP': return '£';
120 |
121 |
122 | default: return currency;
123 | }
124 | }
125 |
126 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/apis/index.js:
--------------------------------------------------------------------------------
1 | import ApiService from './ApiService';
2 | import BackendApiService from './BackendApiService';
3 | import ExchangeService from './ExchangeService';
4 | import PriceService from './PriceService';
5 |
6 | export default {
7 | ApiService,
8 | BackendApiService,
9 | ExchangeService,
10 | PriceService,
11 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/apps/AppsService.js:
--------------------------------------------------------------------------------
1 | import {BlockchainsArray, blockchainName} from "../../models/Blockchains";
2 | import * as Actions from '../../store/constants';
3 | import BackendApiService, {GET} from "../apis/BackendApiService";
4 | import StoreService from "../utility/StoreService";
5 | import ObjectHelpers from "../../util/ObjectHelpers";
6 |
7 | const storeApps = res => {
8 |
9 | }
10 |
11 | export default class AppsService {
12 |
13 | /***
14 | * Gets apps and binds them to state,
15 | * falls back to github if API is failing.
16 | * @returns {Promise}
17 | */
18 | static async getApps(include = [], store = true, imageBackend = 'https://rawgit.com/GetScatter/ScatterApps/master/logos', filetype = 'svg'){
19 | const apps = await BackendApiService.apps(include);
20 | const formattedApps = apps.reduce((acc,x) => {
21 | if(x.hasOwnProperty('hasimage')) x.img = `${imageBackend}/${x.applink}.${filetype}`;
22 |
23 | acc[x.applink] = x;
24 | return acc;
25 | }, {});
26 |
27 | if(store && StoreService.get()) {
28 | StoreService.get().dispatch(Actions.SET_DAPP_DATA, formattedApps);
29 | }
30 |
31 | return formattedApps;
32 | }
33 |
34 | static async getFeaturedApps(){
35 | return await GET('apps/featured');
36 | }
37 |
38 | static async getAppDataFromServer(origin){
39 | return GET(`app/${origin}`);
40 | }
41 |
42 | static appIsInLocalData(origin){
43 | const dappData = StoreService.get().state.dappData || {};
44 | let found = dappData[origin];
45 |
46 | if(!found){
47 | (() => {
48 | // Checking subdomains
49 | if(origin.split('.').length < 2) return;
50 | const [subdomain, domain, suffix] = origin.split('.');
51 | Object.keys(dappData).map(applink => {
52 | if(origin.indexOf(applink) === -1) return;
53 | const dapp = dappData[applink];
54 | if(!dapp.hasOwnProperty('subdomains') || !dapp.subdomains.length) return;
55 | // Checking wildcards
56 | if(dapp.subdomains.find(x => x === '*')){
57 | if(`*.${applink}` === `*.${domain}.${suffix}`) return found = dapp;
58 | }
59 | // Checking hardcoded domains
60 | else {
61 | dapp.subdomains.map(sub => {
62 | if(`${sub}.${applink}` === origin) return found = dapp;
63 | })
64 | }
65 | })
66 | })();
67 | }
68 |
69 | return found;
70 | }
71 |
72 | static getAppData(origin){
73 | const emptyResult = {
74 | applink:origin,
75 | type:'',
76 | name:origin,
77 | description:'',
78 | logo:'',
79 | url:'',
80 | };
81 |
82 | const found = AppsService.appIsInLocalData(origin);
83 | if(!found) return emptyResult;
84 |
85 | const maxDescriptionLength = 70;
86 | if(found.description.length > maxDescriptionLength){
87 | found.description = `${found.description.substr(0,70)}${found.description.length > 70 ? '...':''}`
88 | }
89 |
90 | return found;
91 | }
92 |
93 |
94 | static categories(selectedCategory = null){
95 | return AppsService.appsByCategory(selectedCategory).map(x => x.type)
96 | }
97 |
98 | static appsByCategory(selectedCategory = null){
99 | const dappData = StoreService.get().state.dappData;
100 | if(!dappData) return {};
101 | return Object.keys(dappData).reduce((acc, key) => {
102 | const item = dappData[key];
103 | if(!acc.find(x => x.type === item.type)) acc.push({type:item.type, apps:[]});
104 | acc.find(x => x.type === item.type).apps.push(item);
105 | return acc;
106 | }, []).map(cat => {
107 | ObjectHelpers.shuffle(cat.apps);
108 | if(selectedCategory) return cat;
109 | cat.apps = cat.apps.filter(({applink}) => AppsService.getAppData(applink).hasOwnProperty('img'));
110 | return cat;
111 | }).sort((a,b) => {
112 | return b.apps.length - a.apps.length
113 | });
114 | }
115 |
116 | static appsByTerm(searchTerm){
117 | const dappData = StoreService.get().state.dappData;
118 | if(!dappData) return {};
119 | return Object.keys(dappData).reduce((acc, key) => {
120 | const item = dappData[key];
121 | const found = prop => prop.toLowerCase().trim().indexOf(searchTerm.toLowerCase().trim()) > -1;
122 | if(found(item.applink) || found(item.name) || found(item.description)) acc.push(item);
123 | return acc;
124 | }, []);
125 | }
126 |
127 | static linkedApps(terms = '', typeFilter = null){
128 | return StoreService.get().state.scatter.keychain.permissions.filter(x => x.isIdentity).map(({origin:applink}) => {
129 | return AppsService.getAppData(applink);
130 | }).filter(app => {
131 | return app.type === typeFilter || !typeFilter
132 | }).filter(app => {
133 | return app.name.toLowerCase().indexOf(terms) > -1
134 | });
135 | }
136 |
137 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/apps/PermissionService.js:
--------------------------------------------------------------------------------
1 | import * as Actions from '../../store/constants';
2 |
3 | import Permission from '../../models/Permission'
4 | import {IdentityRequiredFields} from '../../models/Identity'
5 | import SocketService from "../utility/SocketService";
6 | import StoreService from "../utility/StoreService";
7 |
8 | export default class PermissionService {
9 |
10 | static identityFromPermissions(origin, formatForResult = true){
11 |
12 | const permissions = StoreService.get().state.scatter.keychain.permissions;
13 | const possibleId = permissions.find(x => x.isIdentityPermissionFor(origin));
14 | if(possibleId){
15 | let identityRequirements = IdentityRequiredFields.fromPermission(possibleId.identityRequirements);
16 | let identity = formatForResult ? possibleId.getIdentity().asOnlyRequiredFields(identityRequirements) : possibleId.getIdentity();
17 | if(!identity) return null;
18 | identity.accounts = possibleId.getAccounts().map(x => formatForResult ? x.asReturnable() : x);
19 | return identity;
20 | }
21 | return null;
22 | }
23 |
24 | static async addIdentityOriginPermission(identity, accounts, identityRequirements, origin){
25 | identityRequirements = IdentityRequiredFields.fromJson(identityRequirements);
26 | identityRequirements = identityRequirements.forPermission();
27 |
28 | await this.removeIdentityPermission(origin);
29 | const scatter = StoreService.get().state.scatter.clone();
30 |
31 |
32 | const permission = Permission.fromAction(origin, identity, accounts, {
33 | identityRequirements,
34 | isIdentity:true
35 | });
36 |
37 | scatter.keychain.permissions.push(permission);
38 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
39 | }
40 |
41 | static async removeIdentityPermission(origin){
42 | const scatter = StoreService.get().state.scatter.clone();
43 | // const idPermissions = scatter.keychain.permissions.find(x => x.isIdentity && x.origin === origin);
44 | // if(!idPermissions) return true;
45 | scatter.keychain.permissions = scatter.keychain.permissions.filter(x => !x.isIdentity || (x.isIdentity && x.origin !== origin));
46 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
47 | }
48 |
49 | static async addIdentityRequirementsPermission(origin, identity, identityRequirements){
50 | identityRequirements = IdentityRequiredFields.fromJson(identityRequirements);
51 |
52 | // No need for a permission.
53 | if(identityRequirements.isEmpty()) return;
54 |
55 | identityRequirements = identityRequirements.forPermission();
56 |
57 |
58 | const scatter = StoreService.get().state.scatter.clone();
59 |
60 | const permission = Permission.fromJson({
61 | origin, identity:identity.id, identityRequirements, isIdentityRequirements:true
62 | });
63 |
64 | // Don't duplicate requirements.
65 | if(scatter.keychain.permissions.find(x => x.checksum() === permission.checksum())) return;
66 |
67 | scatter.keychain.permissions.push(permission);
68 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
69 | }
70 |
71 | static hasIdentityRequirementsPermission(origin, identity, identityRequirements){
72 | identityRequirements = IdentityRequiredFields.fromJson(identityRequirements);
73 | identityRequirements = identityRequirements.forPermission();
74 |
75 | const permission = Permission.fromJson({
76 | origin, identity:identity.id, identityRequirements, isIdentityRequirements:true
77 | });
78 |
79 | return StoreService.get().state.scatter.keychain.permissions.find(x => x.checksum() === permission.checksum());
80 | }
81 |
82 | static createActionPermission(origin, identity, accounts, whitelistData){
83 |
84 | const immutableActionFields = Permission.createImmutableFieldsHash(whitelistData.fields, whitelistData.props);
85 |
86 | const permission = Permission.fromAction(origin, identity, accounts, {
87 | contract:whitelistData.code,
88 | contractHash:whitelistData.hash || null,
89 | action:whitelistData.type,
90 | immutableActionFields,
91 | mutableActionFields:whitelistData.props,
92 | timestamp:+new Date(),
93 | isContractAction:true
94 | });
95 |
96 |
97 | const scatter = StoreService.get().state.scatter.clone();
98 | return permission;
99 | }
100 |
101 | static async addActionPermissions(origin, identity, accounts, whitelists){
102 | if(!whitelists || !whitelists.length) return;
103 |
104 | const permissions = whitelists.map(whitelist =>
105 | PermissionService.createActionPermission(origin, identity, accounts, whitelist)
106 | ).filter(x => x);
107 |
108 | if(permissions.length){
109 | const scatter = StoreService.get().state.scatter.clone();
110 | permissions.map(perm => {
111 | // Removing all similar permissions for this action
112 | const similar = scatter.keychain.permissions.filter(x =>
113 | x.origin === origin
114 | && x.isContractAction
115 | && x.contract === perm.contract
116 | && x.action === perm.action
117 | ).map(x => x.id);
118 |
119 | scatter.keychain.permissions = scatter.keychain.permissions.filter(x => !similar.includes(x.id));
120 | scatter.keychain.permissions.push(perm)
121 | });
122 | await StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
123 | }
124 | }
125 |
126 | static hasActionPermission(origin, identity, accounts, message){
127 |
128 | const contract = message.code;
129 | const action = message.type;
130 | const contractHash = null;
131 |
132 | const permission = Permission.fromAction(origin, identity, accounts, {
133 | contract,
134 | contractHash,
135 | action,
136 | isContractAction:true
137 | });
138 |
139 | const matchingPermissions = StoreService.get().state.scatter.keychain.permissions.filter(x => x.checksum() === permission.checksum());
140 |
141 | if(!matchingPermissions.length) return false;
142 |
143 | return matchingPermissions.some(perm => {
144 | const immutableActionFields = Permission.createImmutableFieldsHash(message.data, perm.mutableActionFields);
145 | return perm.immutableActionFields === immutableActionFields;
146 | });
147 | }
148 |
149 | static isWhitelistedTransaction(origin, identity, accounts, messages, requiredFields){
150 | requiredFields = IdentityRequiredFields.fromJson(requiredFields);
151 |
152 | // Checking for permissions
153 | const whitelistedActions = messages.every(message =>
154 | PermissionService.hasActionPermission(origin, identity, accounts, message)
155 | );
156 |
157 |
158 | // Not all actions are whitelisted
159 | if(!whitelistedActions) return false;
160 |
161 | // Dont need to check for required fields
162 | if(requiredFields.isEmpty()) return true;
163 |
164 | return PermissionService.hasIdentityRequirementsPermission(origin, identity, requiredFields);
165 | }
166 |
167 | static checkAppLinkPermissions(origin){
168 | const permissions = StoreService.get().state.scatter.keychain.permissions.filter(x => x.origin === origin);
169 | if(!permissions.length) SocketService.sendEvent('logout', {}, origin);
170 | }
171 |
172 | static async removeAllPermissions(){
173 | const scatter = StoreService.get().state.scatter.clone();
174 |
175 | scatter.keychain.permissions = [];
176 | scatter.keychain.apps.map(app => {
177 | scatter.keychain.removeApp(app);
178 | SocketService.sendEvent('logout', {}, app.origin);
179 | })
180 |
181 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
182 | }
183 |
184 | static async removeAllPermissionsFor(origin){
185 | const scatter = StoreService.get().state.scatter.clone();
186 |
187 | const app = scatter.keychain.apps.find(x => x.origin === origin);
188 | if(app) scatter.keychain.removeApp(app);
189 |
190 | scatter.keychain.permissions = scatter.keychain.permissions.filter(x => x.origin !== origin);
191 | await StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
192 | this.checkAppLinkPermissions(origin);
193 | return true;
194 | }
195 |
196 | static async removePermission(permission){
197 | const scatter = StoreService.get().state.scatter.clone();
198 | scatter.keychain.permissions = scatter.keychain.permissions.filter(x => x.id !== permission.id);
199 | await StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
200 | this.checkAppLinkPermissions(permission.origin);
201 | return true;
202 | }
203 |
204 | static async removeDanglingPermissions(){
205 | const scatter = StoreService.get().state.scatter.clone();
206 | const origins = scatter.keychain.permissions.map(x => x.origin);
207 | scatter.keychain.apps = scatter.keychain.apps.filter(x => origins.includes(x.origin));
208 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
209 | }
210 |
211 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/apps/index.js:
--------------------------------------------------------------------------------
1 | import AppsService from './AppsService'
2 | import PermissionService from './PermissionService'
3 |
4 | export default {AppsService, PermissionService}
--------------------------------------------------------------------------------
/packages/core/lib/services/blockchain/AccountService.js:
--------------------------------------------------------------------------------
1 | import Account from '../../models/Account'
2 | import PluginRepository from '../../plugins/PluginRepository'
3 | import * as Actions from '../../store/constants'
4 | import {BlockchainsArray} from '../../models/Blockchains'
5 | import StoreService from "../utility/StoreService";
6 | import BalanceService from "./BalanceService";
7 |
8 | let checkedOrphanedAccounts = false;
9 |
10 | export default class AccountService {
11 |
12 | static async addAccount(account){
13 | const scatter = StoreService.get().state.scatter.clone();
14 | scatter.keychain.addAccount(account);
15 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
16 | }
17 |
18 | static async removeAccounts(accounts){
19 | const scatter = StoreService.get().state.scatter.clone();
20 | accounts.map(account => scatter.keychain.removeAccount(account));
21 | await StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
22 | return BalanceService.removeStaleBalances();
23 | }
24 |
25 | static async getAccountsFor(keypair, network){
26 | const publicKey = keypair.publicKeys.find(x => x.blockchain === network.blockchain).key;
27 | if(!publicKey) return null;
28 |
29 | let accounts = [];
30 |
31 | const plugin = PluginRepository.plugin(network.blockchain);
32 |
33 | if(!plugin.accountsAreImported()) accounts.push(Account.fromJson({
34 | keypairUnique:keypair.unique(),
35 | networkUnique:network.unique(),
36 | publicKey
37 | }));
38 |
39 | else {
40 | await AccountService.accountsFrom(plugin, [network], accounts, keypair);
41 | }
42 |
43 | return accounts;
44 | }
45 |
46 | static async importAllAccounts(keypair, isNewKeypair = false, blockchains = null, networks = null, addOnly = false){
47 | return new Promise(async resolve => {
48 | let scatter = StoreService.get().state.scatter.clone();
49 | let accounts = [];
50 |
51 | if(!networks) networks = scatter.settings.networks;
52 | if(!blockchains) blockchains = keypair.blockchains;
53 |
54 | await Promise.all(blockchains.map(async blockchain => {
55 | const plugin = PluginRepository.plugin(blockchain);
56 | const filteredNetworks = networks.filter(x => x.blockchain === blockchain);
57 | if(isNewKeypair && plugin.accountsAreImported()) return true;
58 | return AccountService.accountsFrom(plugin, filteredNetworks, accounts, keypair);
59 | }));
60 |
61 | const uniques = accounts.map(x => x.unique());
62 | const accountsToRemove = scatter.keychain.accounts.filter(x => x.keypairUnique === keypair.unique() && !uniques.includes(x.unique()) && blockchains.includes(x.blockchain));
63 |
64 |
65 | // This method takes a while, re-cloning to make sure we're
66 | // always up to date before committing the data to storage.
67 | scatter = StoreService.get().state.scatter.clone();
68 | if(!addOnly) accountsToRemove.map(account => scatter.keychain.removeAccount(account));
69 | accounts.map(account => scatter.keychain.addAccount(account));
70 |
71 | await BalanceService.removeStaleBalances();
72 |
73 | await StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
74 | setTimeout(() => {
75 | resolve(accounts);
76 | }, 100);
77 | })
78 | }
79 |
80 | static async importAllAccountsForNetwork(network){
81 | return new Promise(async resolve => {
82 | let scatter = StoreService.get().state.scatter.clone();
83 | const blockchain = network.blockchain;
84 | const keypairs = scatter.keychain.keypairs.filter(x => x.blockchains.includes(blockchain));
85 | let accounts = [];
86 |
87 | const plugin = PluginRepository.plugin(network.blockchain);
88 |
89 | await Promise.all(keypairs.map(async keypair => {
90 | return AccountService.accountsFrom(plugin, [network], accounts, keypair);
91 | }));
92 |
93 | // This method takes a while, re-cloning to make sure we're
94 | // always up to date before committing the data to storage.
95 | scatter = StoreService.get().state.scatter.clone();
96 | accounts.map(account => scatter.keychain.addAccount(account));
97 | await StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
98 | resolve(accounts);
99 | })
100 | }
101 |
102 | /***
103 | * Gets accounts from networks
104 | * @param plugin - Blockchain plugin
105 | * @param networks - Networks to fetch from
106 | * @param accounts - (OUT) accounts array to append to
107 | * @param keypair - Associated keypair
108 | * @returns {Promise<*>}
109 | */
110 | static async accountsFrom(plugin, networks, accounts, keypair){
111 | return new Promise(async resolve => {
112 | if(plugin.accountsAreImported()){
113 | (await Promise.all(networks.map(async network => {
114 | return await plugin.getImportableAccounts(keypair, network);
115 | }))).reduce((acc, arr) => {
116 | arr.map(account => {
117 | accounts.push(account)
118 | });
119 | return acc;
120 | }, []);
121 | resolve(true);
122 | } else {
123 | networks.map(network => {
124 | const key = keypair.publicKeys.find(x => x.blockchain === network.blockchain);
125 | if(key){
126 | accounts.push(Account.fromJson({
127 | keypairUnique:keypair.unique(),
128 | networkUnique:network.unique(),
129 | publicKey:key.key
130 | }));
131 | }
132 | });
133 | resolve(true);
134 | }
135 | })
136 | }
137 |
138 | static async incrementAccountLogins(accounts){
139 | const ids = accounts.map(x => x.unique());
140 | const scatter = StoreService.get().state.scatter.clone();
141 | scatter.keychain.accounts.filter(x => ids.includes(x.unique())).map(x => x.logins++);
142 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
143 | }
144 |
145 | static async fixOrphanedAccounts(){
146 | if(checkedOrphanedAccounts) return true;
147 | checkedOrphanedAccounts = true;
148 |
149 | const scatter = StoreService.get().state.scatter.clone();
150 | const keypairs = scatter.keychain.keypairs.map(x => x.unique());
151 | const orphaned = scatter.keychain.accounts.filter(x => !keypairs.includes(x.keypairUnique));
152 | if(!orphaned.length) return true;
153 |
154 | orphaned.map(x => scatter.keychain.removeAccount(x));
155 | await BalanceService.removeStaleBalances();
156 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/packages/core/lib/services/blockchain/BalanceService.js:
--------------------------------------------------------------------------------
1 | import PluginRepository from "../../plugins/PluginRepository";
2 | import * as Actions from "../../store/constants";
3 | import StoreService from "../utility/StoreService";
4 |
5 | let lastBalanceTime;
6 |
7 | export default class BalanceService {
8 |
9 | static async loadBalancesFor(account, returnOnly = false){
10 | try {
11 | const blockchain = account.blockchain();
12 | const plugin = PluginRepository.plugin(blockchain);
13 | const tokens = StoreService.get().state.scatter.allTokens().filter(x => x.blockchain === blockchain)
14 | .filter(x => x.chainId === account.network().chainId);
15 | const balances = await plugin.balancesFor(account, tokens);
16 |
17 | // We are now considering this "locked up balances", which should be fetched individually on-demand
18 | // (await this.loadUntouchables(account)).map(x => balances.push(x));
19 |
20 | if(returnOnly) return {account:account.identifiable(), balances};
21 | return StoreService.get().dispatch(Actions.SET_BALANCES, {account:account.identifiable(), balances});
22 | } catch(e){
23 | return null;
24 | }
25 | }
26 |
27 | static async loadAllBalances(force = false, returnOnly = false){
28 | if(!force && lastBalanceTime < (+new Date()+1000*60*5)) return;
29 | lastBalanceTime = +new Date();
30 | const accounts = StoreService.get().state.scatter.keychain.accounts.reduce((acc, account) => {
31 | // Filtering out permission based accounts
32 | if(!acc.find(x => x.identifiable() === account.identifiable())) acc.push(account);
33 | return acc;
34 | }, []).sort(async account => {
35 | // Sorting mainnets first.
36 | const isMainnet = PluginRepository.plugin(account.blockchain()).isEndorsedNetwork(account.network());
37 | return isMainnet ? -1 : 1;
38 | });
39 |
40 | let results = [];
41 | for(let i = 0; i < accounts.length; i++){
42 | await Promise.race([
43 | new Promise(resolve => setTimeout(() => resolve(), 20000)),
44 | this.loadBalancesFor(accounts[i]).then(x => results.push(x))
45 | ]);
46 | }
47 |
48 | if(returnOnly) return results;
49 |
50 | return true;
51 | }
52 |
53 | static removeStaleBalances(){
54 | const accountKeys = StoreService.get().state.scatter.keychain.accounts.map(x => x.identifiable());
55 | const keysToRemove = Object.keys(StoreService.get().state.balances).filter(key => !accountKeys.includes(key));
56 | return StoreService.get().dispatch(Actions.REMOVE_BALANCES, keysToRemove);
57 | }
58 |
59 | static async loadUntouchables(account){
60 | const plugin = PluginRepository.plugin(account.blockchain());
61 | return plugin.hasUntouchableTokens() ? plugin.untouchableBalance(account) : [];
62 | }
63 |
64 | static totalBalances(){
65 | const tokens = {};
66 | tokens['totals'] = {};
67 |
68 | const balances = StoreService.get().state.balances;
69 |
70 | Object.keys(balances).map(async accountUnique => {
71 | const account = StoreService.get().state.scatter.keychain.accounts.find(x => x.identifiable() === accountUnique);
72 | if(!account) return;
73 |
74 | if(!tokens.hasOwnProperty(account.networkUnique)){
75 | tokens[account.networkUnique] = {};
76 | }
77 |
78 | if(!balances.hasOwnProperty(accountUnique) || !balances[accountUnique]) return;
79 | balances[accountUnique].map(token => {
80 | if(!tokens[account.networkUnique].hasOwnProperty(token.uniqueWithChain())) {
81 | tokens[account.networkUnique][token.uniqueWithChain()] = token.clone();
82 | tokens['totals'][token.uniqueWithChain()] = token.clone();
83 | } else {
84 | tokens[account.networkUnique][token.uniqueWithChain()].add(token.amount);
85 | tokens['totals'][token.uniqueWithChain()].add(token.amount);
86 | }
87 | });
88 | });
89 |
90 | return tokens;
91 | }
92 |
93 | }
94 |
--------------------------------------------------------------------------------
/packages/core/lib/services/blockchain/ExplorerService.js:
--------------------------------------------------------------------------------
1 | import {blockchainName, Blockchains, BlockchainsArray} from "../../models/Blockchains";
2 | import PluginRepository from "../../plugins/PluginRepository";
3 | import Explorer from "../../models/Explorer";
4 | import {GET} from "../apis/BackendApiService";
5 |
6 | export default class ExplorerService {
7 |
8 | static getExplorers(){
9 | let explorers = {};
10 | BlockchainsArray.map(({value:blockchain}) => explorers[blockchain] = []);
11 |
12 | const setDefaultExplorers = () => {
13 | explorers = PluginRepository.defaultExplorers();
14 | };
15 |
16 | return Promise.race([
17 | new Promise((resolve) => setTimeout(() => {
18 | setDefaultExplorers();
19 | resolve(explorers);
20 | }, 3000)),
21 | GET(`explorers`)
22 | .then(res => {
23 | BlockchainsArray.map(({value:blockchain}) => {
24 | res[blockchainName(blockchain)].map(rawExplorer => {
25 | explorers[blockchain].push(Explorer.fromRaw(rawExplorer));
26 | });
27 | });
28 |
29 | return explorers;
30 | }).catch(err => {
31 | setDefaultExplorers();
32 | return explorers;
33 | })
34 | ])
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/blockchain/NetworkService.js:
--------------------------------------------------------------------------------
1 | import * as Actions from '../../store/constants';
2 |
3 | import AccountService from './AccountService';
4 | import BalanceService from "./BalanceService";
5 | import PluginRepository from "../../plugins/PluginRepository";
6 | import StoreService from "../utility/StoreService";
7 |
8 |
9 | export default class NetworkService {
10 |
11 | static async addNetwork(network){
12 | // Can't modify existing networks.
13 | const scatter = StoreService.get().state.scatter.clone();
14 | const networks = scatter.settings.networks;
15 | if(networks.find(x => x.id === network.id)) return;
16 |
17 | if(!network.name.length) return {error:"Missing Name"};
18 | if(!network.host.length) return {error:"Missing Host"};
19 | if(!network.port) return {error:"Missing Port"};
20 | if(!network.chainId) return {error:"Missing Chain"};
21 |
22 | network.setPort();
23 |
24 | if(networks.find(x => x.blockchain === network.blockchain && x.chainId === network.chainId))
25 | return {error:"Chain Exists"}
26 |
27 | if(networks.find(x => x.name.toLowerCase() === network.name.toLowerCase()))
28 | return {error:"Name Exists"};
29 |
30 | scatter.settings.updateOrPushNetwork(network);
31 | await StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
32 | const accounts = await AccountService.importAllAccountsForNetwork(network);
33 | if(accounts.length){
34 | for(let i = 0; i < accounts.length; i++){
35 | await BalanceService.loadBalancesFor(accounts[i]);
36 | }
37 | }
38 | PluginRepository.bustCaches();
39 | return true;
40 | }
41 |
42 | static async removeNetwork(network){
43 | PluginRepository.bustCaches();
44 | const scatter = StoreService.get().state.scatter.clone();
45 |
46 | // Removing accounts and permissions for this network
47 | const accounts = scatter.keychain.accounts.filter(x => x.networkUnique === network.unique());
48 | accounts.map(account => scatter.keychain.removeAccount(account));
49 | scatter.settings.removeNetwork(network);
50 | StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
51 | BalanceService.removeStaleBalances();
52 | return true;
53 | }
54 |
55 | static async updateNetwork(network){
56 | const scatter = StoreService.get().state.scatter.clone();
57 | scatter.settings.updateOrPushNetwork(network);
58 | await StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
59 | PluginRepository.bustCaches();
60 | return true;
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/packages/core/lib/services/blockchain/ResourceService.js:
--------------------------------------------------------------------------------
1 | import PluginRepository from '../../plugins/PluginRepository';
2 | import {Blockchains} from '../../models/Blockchains';
3 | import Account from '../../models/Account';
4 | import * as Actions from '../../store/constants';
5 | import StoreService from "../utility/StoreService";
6 |
7 | export default class ResourceService {
8 |
9 | static usesResources(account){
10 | account = Account.fromJson(account);
11 | const plugin = PluginRepository.plugin(account.blockchain());
12 | return plugin.usesResources();
13 | }
14 |
15 | static async needsResources(account){
16 | account = Account.fromJson(account);
17 | const plugin = PluginRepository.plugin(account.blockchain());
18 | if(!plugin.usesResources()) return false;
19 | return plugin.needsResources(account);
20 | }
21 |
22 | static async addResources(account){
23 | account = Account.fromJson(account);
24 | const plugin = PluginRepository.plugin(account.blockchain());
25 | if(!plugin.usesResources()) return false;
26 | return plugin.addResources(account);
27 | }
28 |
29 | static async getResourcesFor(account){
30 | account = Account.fromJson(account);
31 | const plugin = PluginRepository.plugin(account.blockchain());
32 | if(!plugin.usesResources()) return [];
33 | return plugin.getResourcesFor(account);
34 | }
35 |
36 | static async cacheResourceFor(account){
37 | if(!account) return;
38 | const resources = await ResourceService.getResourcesFor(account);
39 | StoreService.get().dispatch(Actions.ADD_RESOURCES, {acc:account.identifiable(), res:resources});
40 | }
41 |
42 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/blockchain/TransferService.js:
--------------------------------------------------------------------------------
1 | import {Blockchains, BlockchainsArray} from '../../models/Blockchains'
2 | import PluginRepository from '../../plugins/PluginRepository';
3 | import HistoricTransfer from "../../models/histories/HistoricTransfer";
4 | import * as Actions from '../../store/constants'
5 | import StoreService from "../utility/StoreService";
6 |
7 | export default class TransferService {
8 |
9 | static async [Blockchains.BTC](params){
10 | return this.baseTransfer(params);
11 | }
12 |
13 | static async [Blockchains.ETH](params){
14 | return this.baseTransfer(params);
15 | }
16 |
17 | static async [Blockchains.TRX](params){
18 | return this.baseTransfer(params);
19 | }
20 |
21 | static async [Blockchains.FIO](params){
22 | return this.baseTransfer(params);
23 | }
24 |
25 | static async [Blockchains.EOSIO](params){
26 | params.recipient = params.recipient.toLowerCase();
27 | return this.baseTransfer(params);
28 | }
29 |
30 | static async baseTransfer(params){
31 | let {account, recipient, amount, memo, token } = params;
32 | const plugin = PluginRepository.plugin(account.blockchain());
33 |
34 | const transfer = await PluginRepository.plugin(account.blockchain())
35 | .transfer({
36 | account,
37 | to:recipient,
38 | amount,
39 | token,
40 | memo,
41 | }).catch(x => x);
42 |
43 | if(transfer !== null) {
44 | if (transfer.hasOwnProperty('error')) return transfer;
45 | else {
46 | if(!params.bypassHistory){
47 | const history = new HistoricTransfer(account, recipient, token, amount, memo, this.getTransferId(transfer, token.blockchain));
48 | StoreService.get().dispatch(Actions.DELTA_HISTORY, history);
49 | }
50 |
51 | return transfer;
52 | }
53 | }
54 | return null;
55 | }
56 |
57 | static getTransferId(transfer, blockchain){
58 | switch(blockchain){
59 | case Blockchains.EOSIO: return transfer.transaction_id;
60 | case Blockchains.TRX: return transfer.txID;
61 | case Blockchains.ETH: return transfer.transactionHash;
62 | case Blockchains.BTC: return transfer.txid;
63 | case Blockchains.FIO: return transfer.transaction_id;
64 | }
65 | return null;
66 | }
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/packages/core/lib/services/blockchain/index.js:
--------------------------------------------------------------------------------
1 | import AccountService from './AccountService';
2 | import BalanceService from './BalanceService';
3 | import ExplorerService from './ExplorerService';
4 | import NetworkService from './NetworkService';
5 | import ResourceService from './ResourceService';
6 | import TransferService from './TransferService';
7 |
8 | export default {
9 | AccountService,
10 | BalanceService,
11 | ExplorerService,
12 | NetworkService,
13 | ResourceService,
14 | TransferService,
15 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/index.js:
--------------------------------------------------------------------------------
1 | import apis from './apis/index';
2 | import apps from './apps/index';
3 | import blockchain from './blockchain/index';
4 | import secure from './secure/index';
5 | import utility from './utility/index';
6 |
7 | export default {
8 | apis,
9 | apps,
10 | blockchain,
11 | secure,
12 | utility,
13 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/secure/HardwareService.js:
--------------------------------------------------------------------------------
1 | let hardwareService;
2 |
3 | const NO_INIT = "You must initialize the hardware service first.";
4 |
5 | export default class HardwareService {
6 |
7 | static init(_service){
8 | hardwareService = _service;
9 | }
10 |
11 | static async openConnections(onlyIfDisconnected = false){
12 | if(!hardwareService) return console.error(NO_INIT);
13 | return this.openConnections(onlyIfDisconnected);
14 | }
15 |
16 | static async checkHardware(account){
17 | if(!hardwareService) return console.error(NO_INIT);
18 | return hardwareService.checkHardware(account);
19 | }
20 |
21 | static async sign(network, publicKey, payload){
22 | if(!hardwareService) return console.error(NO_INIT);
23 | return hardwareService.sign(network, publicKey, payload);
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/secure/KeyPairService.js:
--------------------------------------------------------------------------------
1 | import {BlockchainsArray, Blockchains, blockchainName} from '../../models/Blockchains';
2 | import PluginRepository from '../../plugins/PluginRepository'
3 | import * as Actions from '../../store/constants';
4 |
5 | import Crypto from '../../util/Crypto';
6 | import Keypair from '../../models/Keypair';
7 | import HardwareService from "./HardwareService";
8 | import StoreService from "../utility/StoreService";
9 | import IdGenerator from "../../util/IdGenerator";
10 | import Seeder from "./Seeder";
11 |
12 | let publicToPrivate;
13 | export default class KeyPairService {
14 |
15 | static init(_publicToPrivate){
16 | publicToPrivate = _publicToPrivate;
17 | }
18 |
19 | static getImportedKeyBlockchains(privateKey){
20 | let blockchains = [];
21 | BlockchainsArray.map(blockchainKV => {
22 | try {
23 | const plugin = PluginRepository.plugin(blockchainKV.value);
24 | if(plugin.validPrivateKey(privateKey)) blockchains.push(blockchainKV.value);
25 | } catch(e){}
26 | });
27 | return blockchains;
28 | }
29 |
30 | static isValidPrivateKey(keypair){
31 | return !!this.getImportedKeyBlockchains(keypair.privateKey).length;
32 | }
33 |
34 | static convertHexPrivateToBuffer(keypair){
35 | if(typeof keypair.privateKey !== 'string') return false;
36 | let buffered = false;
37 | BlockchainsArray.map(blockchainKV => {
38 | if(buffered) return;
39 | try {
40 | const plugin = PluginRepository.plugin(blockchainKV.value);
41 | if(plugin.validPrivateKey(keypair.privateKey)){
42 | keypair.privateKey = plugin.hexPrivateToBuffer(keypair.privateKey);
43 | buffered = true;
44 | }
45 | } catch(e){}
46 | });
47 | }
48 |
49 | /***
50 | * Tries to make a keypair in place from a private key
51 | * @param keypair
52 | */
53 | static async makePublicKeys(keypair){
54 | keypair.publicKeys = [];
55 |
56 | return Promise.all(BlockchainsArray.map(blockchainKV => {
57 | return this.addPublicKey(keypair, blockchainKV.value);
58 | }));
59 | }
60 |
61 | static async addPublicKey(keypair, blockchain, allowDecryption = false){
62 | if(keypair.publicKeys.find(x => x.blockchain === blockchain)) return true;
63 |
64 | if(keypair.isEncrypted() && !allowDecryption) return false;
65 | else if(keypair.isEncrypted() && allowDecryption){
66 | const seed = await Seeder.getSeed();
67 | keypair.decrypt(seed);
68 | }
69 |
70 | try {
71 | const plugin = PluginRepository.plugin(blockchain);
72 | let p = keypair.privateKey;
73 | if(typeof p !== 'string') p = plugin.bufferToHexPrivate(p);
74 | keypair.publicKeys.push({blockchain, key:plugin.privateToPublic(p, keypair.fork)});
75 | } catch(e){
76 | console.log('err', e);
77 | return false;
78 | }
79 |
80 | return true;
81 | }
82 |
83 | static async generateKeyPair(keypair){
84 | keypair.privateKey = await Crypto.generatePrivateKey();
85 | return true;
86 | }
87 |
88 | static convertKey(keypair, blockchain){
89 | const clone = keypair.clone();
90 | clone.id = IdGenerator.text(24);
91 | clone.name = `${blockchainName(blockchain)} copy of ${keypair.name}`;
92 | clone.blockchains = [blockchain];
93 | clone.createdAt = +new Date();
94 | return clone;
95 | }
96 |
97 | static async saveKeyPair(keypair){
98 | if(!keypair.name.length) keypair.name = `Key-${IdGenerator.text(8)}`;
99 | if(!keypair.isUnique()) return {error:"Keypair already exists."};
100 | const scatter = StoreService.get().state.scatter.clone();
101 | scatter.keychain.keypairs.push(Keypair.fromJson(keypair));
102 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
103 | }
104 |
105 | static async updateKeyPair(keypair){
106 | if(!keypair.name.length) return;
107 | const scatter = StoreService.get().state.scatter.clone();
108 | scatter.keychain.keypairs.find(x => x.unique() === keypair.unique()).name = keypair.name;
109 | scatter.keychain.keypairs.find(x => x.unique() === keypair.unique()).blockchains = keypair.blockchains;
110 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
111 | }
112 |
113 | static async removeKeyPair(keypair){
114 | const scatter = StoreService.get().state.scatter.clone();
115 | scatter.keychain.removeKeyPair(keypair);
116 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
117 | }
118 |
119 | static getKeyPairFromPublicKey(publicKey){
120 | const keypair = StoreService.get().state.scatter.keychain.keypairs.find(x => x.publicKeys.find(k => k.key === publicKey));
121 | if(keypair) return keypair.clone();
122 |
123 |
124 | const identity = StoreService.get().state.scatter.keychain.identities.find(x => x.publicKey === publicKey);
125 | if(identity) {
126 | return Keypair.fromJson({
127 | name:identity.name,
128 | publicKeys:[{blockchain:'eos', key:publicKey}],
129 | privateKey:identity.privateKey
130 | });
131 | }
132 |
133 | return null;
134 | }
135 |
136 | static async publicToPrivate(publicKey){
137 | if(publicToPrivate){
138 | const p = await publicToPrivate(publicKey);
139 | if(p !== false) return p;
140 | }
141 |
142 | const keypair = this.getKeyPairFromPublicKey(publicKey, true);
143 | keypair.decrypt(await Seeder.getSeed());
144 | if(keypair) return keypair.privateKey;
145 | return null;
146 | }
147 |
148 | static async getHardwareKeyList(external, delta = 0, tries = 0){
149 | if(typeof external.interface.getAddress !== 'function') return false;
150 | if(tries >= 5) return false;
151 |
152 | return external.interface.getAddress(delta).catch(async err => {
153 | if(err.toString().match('CLA_NOT_SUPPORTED') || err.toString().match('Cannot write to HID device')){
154 | await HardwareService.openConnections();
155 | return this.getHardwareKeyList(external, delta, tries++);
156 | }
157 | return false;
158 | })
159 | }
160 |
161 |
162 | static async loadFromHardware(keypair, tries = 0){
163 | if(typeof keypair.external.interface.getPublicKey !== 'function') return false;
164 | if(tries >= 5) return false;
165 |
166 | return keypair.external.interface.getPublicKey().then(key => {
167 | if(PluginRepository.plugin(keypair.external.blockchain).validPublicKey(key)){
168 | keypair.external.publicKey = key;
169 | keypair.publicKeys.push({blockchain:keypair.external.blockchain, key});
170 | return true;
171 | } else return false;
172 | }).catch(async err => {
173 | if(err.toString().match('Cannot write to HID device')){
174 | await HardwareService.openConnections();
175 | return this.loadFromHardware(keypair, tries++);
176 | }
177 | return false;
178 | })
179 | }
180 |
181 | static isHardware(publicKey){
182 | const keypair = this.getKeyPairFromPublicKey(publicKey);
183 | if(!keypair) throw new Error('Keypair doesnt exist on keychain');
184 | return keypair.external !== null;
185 | }
186 |
187 | }
188 |
--------------------------------------------------------------------------------
/packages/core/lib/services/secure/PasswordService.js:
--------------------------------------------------------------------------------
1 | export default class PasswordService {
2 |
3 | static isLongEnough(password, suggested = 8){
4 | return password.length >= suggested;
5 | }
6 |
7 | static uppercaseCount(password){
8 | return password.split('').filter(x => x === x.toUpperCase()).length;
9 | }
10 |
11 | static lowercaseCount(password){
12 | return password.split('').filter(x => x !== x.toUpperCase()).length;
13 | }
14 |
15 | static specialCharCount(password){
16 | return password.replace(/[0-9a-zA-Z]/gi, '').length;
17 | }
18 |
19 | static hasError(password){
20 | if(!this.isLongEnough(password)) return 'Your password is not long enough (8 characters)';
21 | if(this.uppercaseCount(password) < 2) return `Passwords must have at least two uppercase letters`;
22 | if(this.lowercaseCount(password) < 2) return `Passwords must have at least two lowercase letters`;
23 | if(this.specialCharCount(password) < 2) return `Passwords must have at least two special characters (like # or @)`;
24 | return false;
25 | }
26 |
27 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/secure/QRService.js:
--------------------------------------------------------------------------------
1 | import QRCode from 'qrcode';
2 | import AES from 'aes-oop';
3 | import Mnemonic from '../../util/Mnemonic'
4 | import Seeder from "./Seeder";
5 |
6 | export default class QRService {
7 |
8 | static createQR(data, pass = null){
9 | return new Promise(async resolve => {
10 | if(!pass || !pass.length) {
11 | resolve(QRCode.toDataURL(JSON.stringify({data, salt: Seeder.getSalt()}), {errorCorrectionLevel: 'L'}));
12 | } else {
13 | const oldSeed = await Seeder.getSeed();
14 | const newSeed = (await Mnemonic.generateMnemonic(pass, Seeder.getSalt()))[1];
15 | const dData = AES.encrypt(AES.decrypt(data, oldSeed), newSeed);
16 | resolve(QRCode.toDataURL(JSON.stringify({data:dData, salt: Seeder.getSalt()}), {errorCorrectionLevel: 'L'}));
17 | }
18 | })
19 | }
20 |
21 | static async createUnEncryptedQR(data){
22 | return QRCode.toDataURL(JSON.stringify(data), {errorCorrectionLevel: 'L'});
23 | }
24 |
25 | static async decryptQR(data, salt, password){
26 | const [mnemonic, seed] = await Mnemonic.generateMnemonic(password, salt);
27 | try {
28 | return AES.decrypt(data, seed)
29 | } catch(e){
30 | console.error('Error decrypting QR: ', e);
31 | return null;
32 | }
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/secure/Seeder.js:
--------------------------------------------------------------------------------
1 | let seeder;
2 | export default class Seeder {
3 |
4 | static init(_seeder){
5 | seeder = _seeder;
6 | }
7 |
8 | static async getSalt(){
9 | return seeder.getSalt();
10 | }
11 |
12 | static async getSeed(){
13 | return seeder.get();
14 | }
15 |
16 | static async setSeed(seed){
17 | return seeder.set(seed);
18 | }
19 |
20 | static async clear(){
21 | return seeder.clear();
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/secure/SigningService.js:
--------------------------------------------------------------------------------
1 | import PluginRepository from "../../plugins/PluginRepository";
2 | import KeyPairService from "./KeyPairService";
3 | import HardwareService from "./HardwareService";
4 |
5 | let signer;
6 | export default class SigningService {
7 |
8 | static init(_signer){
9 | signer = _signer;
10 | }
11 |
12 | static sign(network, payload, publicKey, arbitrary = false, isHash = false){
13 | // payload, publicKey, arbitrary = false, isHash = false, account = null
14 | if(!signer){
15 | if(KeyPairService.isHardware(publicKey)){
16 | return HardwareService.sign(network, publicKey, payload);
17 | } else return PluginRepository.plugin(network.blockchain).signer(payload, publicKey, arbitrary, isHash);
18 | } else return signer(network, publicKey, payload, arbitrary, isHash);
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/secure/index.js:
--------------------------------------------------------------------------------
1 | import HardwareService from './HardwareService';
2 | import KeyPairService from './KeyPairService';
3 | import QRService from './QRService';
4 | import Seeder from './Seeder';
5 | import SigningService from './SigningService';
6 |
7 | export default {
8 | HardwareService,
9 | KeyPairService,
10 | QRService,
11 | Seeder,
12 | SigningService,
13 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/utility/ContactService.js:
--------------------------------------------------------------------------------
1 | import * as Actions from '../../store/constants'
2 | import StoreService from "./StoreService";
3 | import {BlockchainsArray} from "../../models/Blockchains";
4 | import PluginRepository from "../../plugins/PluginRepository";
5 |
6 | export default class ContactService {
7 |
8 | constructor(){}
9 |
10 | static async addOrUpdate(contact){
11 | contact.recipient = contact.recipient.trim();
12 | contact.name = contact.name.trim();
13 | const scatter = StoreService.get().state.scatter.clone();
14 |
15 | if(!contact.name.length) return {error:'Invalid contact name'};
16 | if(!contact.recipient.length) return {error:'Invalid contact account / address'};
17 |
18 | if(scatter.contacts.find(x => x.id !== contact.id && x.recipient.toLowerCase() === contact.recipient.toLowerCase()))
19 | return {error:"Contact Exists"};
20 |
21 | if(scatter.contacts.find(x => x.id !== contact.id && x.name.toLowerCase() === contact.name.toLowerCase()))
22 | return {error:"Contact Name Exists"};
23 |
24 |
25 | const c = scatter.contacts.find(x => x.id === contact.id);
26 | if(c){
27 | c.recipient = contact.recipient;
28 | c.name = contact.name;
29 | c.blockchain = contact.blockchain;
30 | } else {
31 | scatter.contacts.push(contact);
32 | }
33 |
34 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
35 | }
36 |
37 | static async remove(contact){
38 | const scatter = StoreService.get().state.scatter.clone();
39 | scatter.contacts = scatter.contacts.filter(x => x.id !== contact.id);
40 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
41 | }
42 |
43 | static validate(blockchain, contact){
44 | // You can add unsupported blockchains which we have no logic for,
45 | // so we will always default to true for those.
46 | if(!BlockchainsArray.map(x => x.value).includes(blockchain)) return true;
47 |
48 | return PluginRepository.plugin(blockchain).isValidRecipient(contact);
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/utility/EventService.js:
--------------------------------------------------------------------------------
1 |
2 | let eventListener;
3 |
4 | export default class EventService {
5 |
6 | static init(_service){
7 | eventListener = _service;
8 | }
9 |
10 | static emit(type, data){
11 | return eventListener(type, data);
12 | }
13 |
14 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/utility/Framework.js:
--------------------------------------------------------------------------------
1 | let framework;
2 | export default class Framework {
3 |
4 | static init(_framework){
5 | framework = _framework;
6 | }
7 |
8 | static getVersion(){
9 | return framework.getVersion();
10 | }
11 |
12 | static pushNotification(title, description){
13 | return framework.pushNotification(title, description);
14 | }
15 |
16 | static triggerDeepLink(deepLink){
17 |
18 | }
19 |
20 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/utility/IdentityService.js:
--------------------------------------------------------------------------------
1 | import StoreService from "./StoreService";
2 | import * as Actions from '../../store/constants';
3 |
4 | export default class IdentityService {
5 |
6 | static async addIdentity(identity){
7 | const clone = StoreService.get().state.scatter.clone();
8 | clone.keychain.updateOrPushIdentity(identity);
9 | return StoreService.get().dispatch(Actions.SET_SCATTER, clone);
10 | }
11 |
12 | static async updateIdentity(identity){
13 | return this.addIdentity(identity);
14 | }
15 |
16 | static async removeIdentity(identity){
17 | const clone = StoreService.get().state.scatter.clone();
18 | clone.keychain.removeIdentity(identity);
19 | return StoreService.get().dispatch(Actions.SET_SCATTER, clone);
20 | }
21 |
22 | static async addLocation(location){
23 | const clone = StoreService.get().state.scatter.clone();
24 | clone.keychain.updateOrPushLocation(location);
25 | return StoreService.get().dispatch(Actions.SET_SCATTER, clone);
26 | }
27 |
28 | static async updateLocation(location){
29 | return this.addLocation(location);
30 | }
31 |
32 | static async removeLocation(location){
33 | const clone = StoreService.get().state.scatter.clone();
34 | clone.keychain.removeLocation(location);
35 | return StoreService.get().dispatch(Actions.SET_SCATTER, clone);
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/utility/SingletonService.js:
--------------------------------------------------------------------------------
1 | import * as Actions from '../../store/constants';
2 | import AccountService from "../blockchain/AccountService";
3 | import PriceService from "../apis/PriceService";
4 | import PermissionService from "../apps/PermissionService";
5 | import StoreService from "./StoreService";
6 | import SocketService from "./SocketService";
7 | import AppsService from "../apps/AppsService";
8 | import PluginRepository from "../../plugins/PluginRepository";
9 | import {Blockchains} from "../../models/Blockchains";
10 |
11 | let initialized = false;
12 |
13 | export default class SingletonService {
14 |
15 | static async init(){
16 | if(initialized) return true;
17 | initialized = true;
18 | PluginRepository.plugin(Blockchains.TRX).init();
19 | SocketService.initialize();
20 | AppsService.getApps();
21 | PriceService.watchPrices();
22 |
23 | PermissionService.removeDanglingPermissions();
24 | AccountService.fixOrphanedAccounts();
25 | return true;
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/utility/SocketService.js:
--------------------------------------------------------------------------------
1 | import ApiService from '../apis/ApiService';
2 | import AuthorizedApp from '../../models/AuthorizedApp';
3 | import * as Actions from '../../store/constants';
4 | import StoreService from "./StoreService";
5 | import EventService from "./EventService";
6 |
7 |
8 |
9 | let service;
10 |
11 | const emit = (origin, id, path, data) => service.emit(origin, id, path, data);
12 | const getNewKey = (origin, id) => service.getNewKey(origin, id);
13 |
14 | export const handleApiResponse = async (request, id) => {
15 |
16 | // 2 way authentication
17 | const existingApp = StoreService.get().state.scatter.keychain.findApp(request.data.payload.origin);
18 |
19 | const updateNonce = async () => {
20 | const clone = StoreService.get().state.scatter.clone();
21 | existingApp.nextNonce = request.data.nextNonce;
22 | clone.keychain.updateOrPushApp(existingApp);
23 | return StoreService.get().dispatch(Actions.SET_SCATTER, clone);
24 | };
25 |
26 | const removeAppPermissions = async () => {
27 | const clone = StoreService.get().state.scatter.clone();
28 | clone.keychain.removeApp(existingApp);
29 | return StoreService.get().dispatch(Actions.SET_SCATTER, clone);
30 | };
31 |
32 | if(!existingApp) return;
33 | if(!existingApp.checkKey(request.data.appkey)) return;
34 | if(existingApp.nextNonce.length && !existingApp.checkNonce(request.data.nonce)) await removeAppPermissions();
35 | else await updateNonce();
36 |
37 | ApiService.handler(Object.assign(request.data, {plugin:request.plugin})).then(result => {
38 | emit(existingApp.origin, id, 'api', result);
39 | })
40 | };
41 |
42 | export const handlePairedResponse = async (request, id) => {
43 | const scatter = StoreService.get().state.scatter;
44 | const existingApp = scatter.keychain.findApp(request.data.origin);
45 | const linkApp = {
46 | type:'linkApp',
47 | payload:request.data
48 | };
49 |
50 | if(request.data.passthrough)
51 | return emit(request.data.origin, id, 'paired', existingApp && existingApp.checkKey(request.data.appkey));
52 |
53 | const addAuthorizedApp = async (newKey = null) => {
54 | const authedApp = new AuthorizedApp(request.data.origin, newKey ? newKey : request.data.appkey);
55 | const clone = scatter.clone();
56 | clone.keychain.updateOrPushApp(authedApp);
57 | await StoreService.get().dispatch(Actions.SET_SCATTER, clone);
58 | emit(request.data.origin, id, 'paired', true);
59 | };
60 |
61 | const repair = async () => {
62 | const newKey = await getNewKey(request.data.origin, id);
63 | if(newKey.data.origin !== request.data.origin || newKey.data.appkey.indexOf('appkey:') === -1) return emit(request.data.origin, id, 'paired', false);
64 | return addAuthorizedApp(newKey.data.appkey)
65 | }
66 |
67 | if(existingApp){
68 | if(existingApp.checkKey(request.data.appkey)) return emit(request.data.origin, id, 'paired', true);
69 | else EventService.emit('popout', linkApp).then( async ({result}) => {
70 | if(result) return repair();
71 | else emit(request.data.origin, id, 'paired', false);
72 | });
73 | }
74 | else return repair();
75 | };
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | /***
85 | * Gets certs that allow for `wss` local connections.
86 | * @returns {Promise}
87 | */
88 | export const getCerts = async () => {
89 | return fetch('https://certs.get-scatter.com?rand='+Math.round(Math.random()*100 + 1))
90 | .then(res => res.json())
91 | .then(res => {
92 | if(res.hasOwnProperty('key') && res.hasOwnProperty('cert')) return res;
93 | EventService.emit('no_certs');
94 | return null;
95 | })
96 | .catch(() => console.error('Could not fetch certs. Probably due to a proxy, vpn, or firewall.'));
97 | };
98 |
99 | export default class SocketService {
100 |
101 | static init(_service){
102 | service = _service;
103 | }
104 |
105 | static async initialize(){
106 | return service.initialize();
107 | }
108 |
109 | static async close(){
110 | return service.close();
111 | }
112 |
113 | static async sendEvent(event, payload, origin){
114 | return service.sendEvent(event, payload, origin);
115 | }
116 |
117 | static async broadcastEvent(event, payload){
118 | return service.broadcastEvent(event, payload);
119 | }
120 |
121 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/utility/StoreService.js:
--------------------------------------------------------------------------------
1 | import * as Actions from '../../store/constants'
2 |
3 | let store;
4 |
5 | /***
6 | * This is a helper service which returns the store
7 | * but allows for testing suites to be run without vuex
8 | */
9 | export default class StoreService {
10 |
11 | static init(_store){
12 | store = _store;
13 | }
14 |
15 | static get(){
16 | return store;
17 | }
18 |
19 | }
--------------------------------------------------------------------------------
/packages/core/lib/services/utility/TokenService.js:
--------------------------------------------------------------------------------
1 | import * as Actions from '../../store/constants';
2 | import BigNumber from "bignumber.js";
3 | import Token from "../../models/Token";
4 | import StoreService from "./StoreService";
5 | import BalanceService from "../blockchain/BalanceService";
6 |
7 | const filterOutToken = (scatter, token) => {
8 | scatter.settings.tokens = scatter.settings.tokens.filter(x => x.unique() !== token.unique());
9 | scatter.settings.blacklistTokens = scatter.settings.blacklistTokens.filter(x => x.unique() !== token.unique());
10 | if(scatter.settings.displayToken === token.unique()) scatter.settings.displayToken = null;
11 | }
12 |
13 | export default class TokenService {
14 |
15 | static async addToken(token, blacklist = false){
16 | const scatter = StoreService.get().state.scatter.clone();
17 |
18 | // Never adding system tokens.
19 | if(StoreService.get().state.scatter.networkTokens().find(x => x.unique() === token.unique())) return true;
20 |
21 | if(!token.symbol.length) return {error:"Symbol Missing"};
22 | if(!token.contract.length) return {error:"Contract missing"};
23 |
24 | if(!blacklist && scatter.settings.tokens.find(x => x.unique() === token.unique()))
25 | return {error:"Token exists already (whitelist)"};
26 |
27 | if(blacklist && scatter.settings.blacklistTokens.find(x => x.unique() === token.unique()))
28 | return {error:"Token exists already (blacklist)"};
29 |
30 | if(!token.name.trim().length) token.name = token.symbol;
31 |
32 | filterOutToken(scatter, token);
33 |
34 | if(!blacklist) scatter.settings.tokens.unshift(token);
35 | else scatter.settings.blacklistTokens.unshift(token);
36 |
37 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
38 | }
39 |
40 | static removeToken(token){
41 | const scatter = StoreService.get().state.scatter.clone();
42 |
43 | // Never removing system tokens.
44 | if(StoreService.get().state.scatter.networkTokens().find(x => x.unique() === token.unique())) return true;
45 |
46 | filterOutToken(scatter, token);
47 | StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
48 | }
49 |
50 | static hasToken(token){
51 | const scatter = StoreService.get().state.scatter.clone();
52 |
53 | return !!BalanceService.totalBalances().totals[token.unique()] ||
54 | !!scatter.settings.tokens.find(x => x.unique() === token.unique()) ||
55 | !!scatter.settings.blacklistTokens.find(x => x.unique() === token.unique());
56 | }
57 |
58 | static async setDisplayCurrency(ticker){
59 | const scatter = StoreService.get().state.scatter.clone();
60 | scatter.settings.displayCurrency = ticker;
61 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
62 | }
63 |
64 | static async setDisplayToken(token){
65 | const scatter = StoreService.get().state.scatter.clone();
66 | scatter.settings.displayToken = token instanceof Token ? token.uniqueWithChain() : token;
67 | return StoreService.get().dispatch(Actions.SET_SCATTER, scatter);
68 | }
69 |
70 |
71 | static formatAmount(amount, token, div = false){
72 | const operator = div ? 'div' : 'times';
73 | let decimalString = '';
74 | for(let i = 0; i < token.decimals; i++){ decimalString += '0'; }
75 | return new BigNumber(amount.toString(10), 10)[operator](`1${decimalString}`).toString(10);
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/packages/core/lib/services/utility/index.js:
--------------------------------------------------------------------------------
1 | import ContactService from './ContactService';
2 | import Framework from './Framework';
3 | import SingletonService from './SingletonService';
4 | import SocketService from './SocketService';
5 | import StoreService from './StoreService';
6 | import TokenService from './TokenService';
7 | import EventService from './EventService';
8 |
9 | export default {
10 | ContactService,
11 | Framework,
12 | SingletonService,
13 | SocketService,
14 | StoreService,
15 | TokenService,
16 | EventService,
17 | }
18 |
--------------------------------------------------------------------------------
/packages/core/lib/store/constants.js:
--------------------------------------------------------------------------------
1 | export const SET_DAPP_DATA = 'setDappData';
2 | export const SET_DAPP_LOGO = 'setDappLogo';
3 | export const SET_RESOURCES = 'setResources';
4 | export const ADD_RESOURCES = 'addResources';
5 | export const SET_SCATTER = 'setScatter';
6 | export const LOAD_SCATTER = 'loadScatter';
7 | export const HOLD_SCATTER = 'holdScatter';
8 | export const SET_BALANCES = 'setBalances';
9 | export const REMOVE_BALANCES = 'removeBalances';
10 | export const SET_PRICES = 'setPrices';
11 | export const UPDATE_HISTORY = 'updateHistory';
12 | export const DELTA_HISTORY = 'deltaHistory';
13 | export const LOAD_HISTORY = 'loadHistory';
14 | export const SET_PRICE_DATA = 'setPriceData';
--------------------------------------------------------------------------------
/packages/core/lib/store/index.js:
--------------------------------------------------------------------------------
1 | import * as constants from './constants';
2 | export default {constants};
--------------------------------------------------------------------------------
/packages/core/lib/util/Crypto.js:
--------------------------------------------------------------------------------
1 | import ecc from 'eosjs-ecc';
2 | const {PrivateKey} = ecc;
3 |
4 | import PluginRepository from '../plugins/PluginRepository';
5 |
6 | export default class Crypto {
7 |
8 | static async generatePrivateKey(){
9 | return (await PrivateKey.randomKey()).toBuffer();
10 | }
11 |
12 | static bufferToPrivateKey(buffer, blockchain){
13 | return PluginRepository.plugin(blockchain).bufferToHexPrivate(buffer);
14 | }
15 |
16 | static privateKeyToBuffer(privateKey, blockchain){
17 | return PluginRepository.plugin(blockchain).hexPrivateToBuffer(privateKey);
18 | }
19 |
20 | static bufferToHash(buffer){
21 | return ecc.sha256(buffer);
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/packages/core/lib/util/DateHelpers.js:
--------------------------------------------------------------------------------
1 | export const dateId = (minusDays = 0) => {
2 | const d = new Date();
3 | d.setDate(d.getDate()-minusDays);
4 | const date = d.getUTCDate();
5 | const month = d.getUTCMonth()+1;
6 | const year = d.getUTCFullYear();
7 | return `${date}-${month}-${year}`;
8 | };
9 |
10 | export const hourNow = () => {
11 | const d = new Date();
12 | return d.getHours();
13 | };
14 |
15 | export const daysOld = (id, days) => {
16 | const [d2,m2,y2] = id.split('-');
17 |
18 | const d = new Date();
19 | const ago = new Date(d.getTime() - (days * 24 * 60 * 60 * 1000));
20 | const then = new Date(y2, m2-1, d2, 0, 0, 0, 0);
21 |
22 | return then < ago;
23 | };
24 |
25 | export const utcToLocal = (id, hour = 0) => {
26 | const [d2,m2,y2] = id.split('-');
27 |
28 | const d = new Date();
29 | d.setUTCDate(d2);
30 | d.setUTCMonth(m2);
31 | d.setUTCFullYear(y2);
32 | d.setUTCHours(hour);
33 | const date = d.getDate();
34 | const month = d.getMonth();
35 | const year = d.getFullYear();
36 | return [`${date}-${month}-${year}`, d.getHours()];
37 | };
--------------------------------------------------------------------------------
/packages/core/lib/util/Hasher.js:
--------------------------------------------------------------------------------
1 | import Seeder from "../services/secure/Seeder";
2 |
3 | const ecc = require('eosjs-ecc');
4 | const scrypt = require('scrypt-async');
5 |
6 | export default class Hasher {
7 |
8 | /***
9 | * Hashes a cleartext using the SHA-256 algorithm.
10 | * This is INSECURE and should only be used for fingerprinting.
11 | * @param cleartext
12 | */
13 | static unsaltedQuickHash(cleartext) {
14 | return ecc.sha256(cleartext);
15 | }
16 |
17 | /***
18 | * Hashes a cleartext using scrypt.
19 | * @param cleartext
20 | * @param salt
21 | */
22 | static async secureHash(cleartext, salt = null) {
23 | return new Promise(async resolve => {
24 | if(!salt) salt = await Seeder.getSalt() || 'SALT_ME';
25 | scrypt(cleartext, salt, {
26 | N: 16384,
27 | r: 8,
28 | p: 1,
29 | dkLen: 16,
30 | encoding: 'hex'
31 | }, (derivedKey) => {
32 | resolve(derivedKey);
33 | })
34 | });
35 | }
36 | }
--------------------------------------------------------------------------------
/packages/core/lib/util/Http.js:
--------------------------------------------------------------------------------
1 | /***
2 | * THIS HTTP SERVICE IS ONLY USED FOR HARDWARE WALLET CONNECTIONS
3 | *
4 | */
5 |
6 | export const get = async route => {
7 | return Promise.race([
8 | fetch(route).then(res => res.json()).catch(() => null),
9 | new Promise(resolve => setTimeout(() => resolve(null), 60000))
10 | ])
11 | }
12 |
13 | export const post = async (route, data) => {
14 | return Promise.race([
15 | fetch(route, {
16 | method: "POST",
17 | headers: {
18 | 'Accept': 'application/json, text/plain, */*',
19 | 'Content-Type': 'application/json'
20 | },
21 | body: JSON.stringify(data)
22 | }).then(res => res.json()).catch(() => null),
23 | new Promise(resolve => setTimeout(() => resolve(null), 120000))
24 | ])
25 | };
--------------------------------------------------------------------------------
/packages/core/lib/util/IdGenerator.js:
--------------------------------------------------------------------------------
1 | const nodeCrypto = typeof window === 'undefined' ? require('crypto') : null;
2 |
3 | const getRandomNumber = () => {
4 | const nodeJsEnv = () => parseInt(nodeCrypto.randomBytes(8).toString('hex'), 16) / 0xffffffffffffffff;
5 | const browserEnv = () => {
6 | const arr = new Uint32Array(1);
7 | window.crypto.getRandomValues(arr);
8 | return arr[0]/(0xffffffff + 1);
9 | }
10 |
11 | return nodeCrypto ? nodeJsEnv() : browserEnv();
12 | }
13 |
14 | export default class IdGenerator {
15 |
16 | static rand(){
17 | return getRandomNumber();
18 | }
19 |
20 | /***
21 | * Generates a random string of specified size
22 | * @param size - The length of the string to generate
23 | * @returns {string} - The generated random string
24 | */
25 | static text(size){
26 | let text = "";
27 | const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
28 | for(let i=0; i max ) return IdGenerator.numeric(max) + IdGenerator.numeric(size - max);
42 |
43 | max = Math.pow(10, size+add);
44 | const min = max / 10,
45 | number = Math.floor(IdGenerator.rand() * (max - min + 1)) + min;
46 |
47 | return ("" + number).substring(add);
48 | }
49 |
50 | }
--------------------------------------------------------------------------------
/packages/core/lib/util/Mnemonic.js:
--------------------------------------------------------------------------------
1 | import Hasher from "./Hasher";
2 | import bip39 from 'bip39'
3 |
4 | export class Mnemonic {
5 |
6 | /***
7 | * Generates a mnemonic from a password
8 | * @param password
9 | * @param salt
10 | * @returns {[string,string]}
11 | */
12 | static async generateMnemonic(password, salt = null) {
13 | const hash = await Hasher.secureHash(password, salt);
14 | let mnemonic = bip39.entropyToMnemonic(hash);
15 | return [mnemonic, bip39.mnemonicToSeedHex(mnemonic)];
16 | }
17 |
18 | static async mnemonicToSeed(mnemonic){
19 | return bip39.mnemonicToSeedHex(mnemonic);
20 | }
21 | }
22 |
23 | export default Mnemonic;
--------------------------------------------------------------------------------
/packages/core/lib/util/ObjectHelpers.js:
--------------------------------------------------------------------------------
1 | /***
2 | * A set of helpers for Objects/Arrays
3 | */
4 | export default class ObjectHelpers {
5 |
6 | /***
7 | * Makes a single level array distinct
8 | * @param array
9 | * @returns {*}
10 | */
11 | static distinct(array){
12 | return array.reduce((a,b) => (a.includes(b)) ? a : a.concat(b), []);
13 | }
14 |
15 | /***
16 | * Flattens an array into a single dimension
17 | * @param array
18 | * @returns {*}
19 | */
20 | static flatten(array){
21 | if(!Array.isArray(array)) return array;
22 | return array.reduce(
23 | (a, b) => a.concat(Array.isArray(b) ? this.flatten(b) : b), []
24 | );
25 | }
26 |
27 | /***
28 | * Flattens an array into a single dimension
29 | * @param val
30 | * @returns {*}
31 | */
32 | static flattenObject(val){
33 | if(typeof val !== 'object') return this.flatten(val);
34 | return this.flatten(Object.keys(val).map(key => {
35 | return this.flattenObject(val[key]);
36 | }));
37 | }
38 |
39 | static shuffle(a) {
40 | for (let i = a.length - 1; i > 0; i--) {
41 | const j = Math.floor(Math.random() * (i + 1));
42 | [a[i], a[j]] = [a[j], a[i]];
43 | }
44 | return a;
45 | }
46 |
47 | static objectTake(obj, limit){
48 | let limited = {};
49 | if(Object.keys(obj).length < limit) return obj;
50 | Object.keys(obj).map(key => {
51 | if(Object.keys(limited).length >= limit) return;
52 | limited[key] = obj[key];
53 | });
54 | return limited;
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/packages/core/lib/util/TestingHelper.js:
--------------------------------------------------------------------------------
1 | export const RUNNING_TESTS = process.env['NODE_ENV'] === 'testing';
2 | export let SHOW_POPUPS_AS_CONSOLE = false;
3 |
4 | export const setPopupsAsConsole = bool => SHOW_POPUPS_AS_CONSOLE = bool;
5 |
--------------------------------------------------------------------------------
/packages/core/lib/util/index.js:
--------------------------------------------------------------------------------
1 | import Crypto from './Crypto'
2 | import * as DateHelpers from './DateHelpers'
3 | import Hasher from './Hasher'
4 | import * as Http from './Http'
5 | import IdGenerator from './IdGenerator'
6 | import Mnemonic from './Mnemonic'
7 | import ObjectHelpers from './ObjectHelpers'
8 | import * as TestingHelper from './TestingHelper'
9 |
10 | export default {
11 | Crypto,
12 | DateHelpers,
13 | Hasher,
14 | Http,
15 | IdGenerator,
16 | Mnemonic,
17 | ObjectHelpers,
18 | TestingHelper,
19 | }
--------------------------------------------------------------------------------
/packages/core/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@walletpack/core",
3 | "version": "1.0.51",
4 | "description": "> TODO: description",
5 | "author": "GetScatter Ltd. 2019",
6 | "homepage": "",
7 | "license": "ISC",
8 | "main": "core.js",
9 | "files": [
10 | "dist",
11 | "prepare.js"
12 | ],
13 | "scripts": {
14 | "install": "node prepare.js",
15 | "test": "echo \"Error: run tests from root\" && exit 1"
16 | },
17 | "dependencies": {
18 | "aes-oop": "^1.0.4",
19 | "bignumber.js": "^9.0.0",
20 | "bip39": "^2.6.0",
21 | "eosjs-ecc": "^4.0.4",
22 | "qrcode": "^1.4.1",
23 | "scrypt-async": "^2.0.1"
24 | },
25 | "gitHead": "70bd19f93b503618a79eb519eef082bfc40b16d7",
26 | "publishConfig": {
27 | "access": "public"
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/packages/core/prepare.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const rimraf = require("rimraf");
4 |
5 | const paths = __dirname.split(path.sep);
6 | const parent = paths[paths.length-2];
7 |
8 | if(parent === 'packages') return;
9 |
10 | try {
11 | rimraf.sync("./__tests__");
12 | const files = fs.readdirSync(`./dist`);
13 | files.map(file => {
14 | if(fs.existsSync(`./${file}`)) rimraf.sync(`./${file}`);
15 | fs.renameSync(`./dist/${file}`, `./${file}`);
16 | })
17 | rimraf.sync("./dist");
18 | rimraf.sync("./lib");
19 | } catch(e){
20 | console.error('Walletpack prepare.js error', e);
21 | }
22 |
--------------------------------------------------------------------------------
/packages/eosio/README.md:
--------------------------------------------------------------------------------
1 | # `eosio`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const eosio = require('eosio');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/eosio/__tests__/api.test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const {assert} = require('chai');
3 |
4 | require('isomorphic-fetch');
5 | const LightAPI = require('../lib/api').default;
6 | const Account = require('../../core/lib/models/Account').default;
7 | const Network = require('../../core/lib/models/Network').default;
8 |
9 | const network = Network.fromJson({
10 | blockchain:'eos',
11 | name:'EOS Mainnet',
12 | host:'nodes.get-scatter.com',
13 | port:443,
14 | protocol:'https',
15 | chainId:'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906',
16 | });
17 |
18 | const account = Account.fromJson({
19 | name:'ramdeathtest',
20 | authority:'active',
21 | publicKey:'',
22 | keypairUnique:'abcd',
23 | networkUnique:network.unique(),
24 | });
25 |
26 | describe('eosio', () => {
27 |
28 | it('should be able to fetch balances', done => {
29 | new Promise(async () => {
30 |
31 | done();
32 | })
33 | });
34 | });
35 |
--------------------------------------------------------------------------------
/packages/eosio/lib/api.js:
--------------------------------------------------------------------------------
1 | import Token from "@walletpack/core/models/Token";
2 | import {Blockchains} from "@walletpack/core/models/Blockchains";
3 |
4 |
5 | let networks, hosts;
6 | let cache = {};
7 |
8 | export default class LightAPI {
9 |
10 | static async cacheEndpointsAndNetworks(){
11 | return fetch(`https://endpoints.light.xeos.me/endpoints.json`).catch(() => null).then(x => x.json()).then(x => {
12 | hosts = Object.keys(x['api-endpoints']).reduce((acc, host) => {
13 | x['api-endpoints'][host].networks.map(network => {
14 | if(!acc[network]) acc[network] = host;
15 | });
16 | return acc;
17 | }, {});
18 | networks = Object.keys(x['networks']).reduce((acc, network) => {
19 | acc[x['networks'][network].chainid] = network;
20 | return acc;
21 | }, {});
22 | return true;
23 | });
24 | }
25 |
26 | static async getNetworks(){
27 | if(!networks) await LightAPI.cacheEndpointsAndNetworks();
28 | return networks;
29 | }
30 |
31 | static async networkString(network){
32 | const networks = await this.getNetworks();
33 | if(!networks) return null;
34 | return networks[network.chainId];
35 | }
36 |
37 | static async fetchBalances(account, network, parseResults){
38 | const networkString = await this.networkString(network);
39 | if(!networkString) return null;
40 | if(!hosts[networkString]) return null;
41 |
42 | if(cache[account.unique()]) return parseResults(cache[account.unique()]);
43 |
44 | return await Promise.race([
45 | // Maximum timeout for this request
46 | new Promise(resolve => setTimeout(() => resolve(null), 8000)),
47 |
48 | fetch(`${hosts[networkString]}/api/balances/${networkString}/${account.name}`).then(r => r.json()).then(res => {
49 |
50 | // Caching this response, and then removing it after 5 seconds.
51 | // cache[account.unique()] = res;
52 | // setTimeout(() => delete cache[account.unique()], 5000);
53 |
54 | return parseResults(res);
55 | }).catch(err => {
56 | console.log('err', err);
57 | return null;
58 | })
59 | ])
60 | }
61 |
62 | static async balancesFor(account, network){
63 | const parseResults = res => {
64 | return res.balances.map(balance => {
65 | return Token.fromJson({
66 | blockchain:Blockchains.EOSIO,
67 | contract:balance.contract,
68 | symbol:balance.currency,
69 | name:balance.currency,
70 | amount:balance.amount,
71 | decimals:balance.decimals,
72 | chainId:network.chainId
73 | })
74 | });
75 | };
76 |
77 | return this.fetchBalances(account, network, parseResults);
78 | }
79 |
80 | static async getAccountsFromPublicKey(publicKey, network){
81 | const networkString = await this.networkString(network);
82 | if(!networkString) return null;
83 | if(!hosts[networkString]) return null;
84 |
85 | return await Promise.race([
86 | // Maximum timeout for this request
87 | new Promise(resolve => setTimeout(() => resolve(null), 5000)),
88 |
89 | fetch(`${hosts[networkString]}/api/key/${publicKey}`).then(r => r.json()).then(res => {
90 | if(!res[networkString]) return null;
91 | const rawAccounts = res[networkString].accounts;
92 | let accounts = [];
93 | Object.keys(rawAccounts).map(name => {
94 | rawAccounts[name]
95 | .filter(acc => acc.auth.keys.some(({pubkey}) => pubkey === publicKey))
96 | .map(acc => accounts.push({name, authority: acc.perm}))
97 | });
98 |
99 | return accounts;
100 | }).catch(err => {
101 | console.error('err', err);
102 | return null;
103 | })
104 | ])
105 | }
106 |
107 | }
108 |
--------------------------------------------------------------------------------
/packages/eosio/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@walletpack/eosio",
3 | "version": "0.0.61",
4 | "lockfileVersion": 1,
5 | "requires": true,
6 | "dependencies": {
7 | "@babel/runtime": {
8 | "version": "7.6.0",
9 | "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.6.0.tgz",
10 | "integrity": "sha512-89eSBLJsxNxOERC0Op4vd+0Bqm6wRMqMbFtV3i0/fbaWw/mJ8Q3eBvgX0G4SyrOOLCtbu98HspF8o09MRT+KzQ==",
11 | "requires": {
12 | "regenerator-runtime": "^0.13.2"
13 | },
14 | "dependencies": {
15 | "regenerator-runtime": {
16 | "version": "0.13.7",
17 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
18 | "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew=="
19 | }
20 | }
21 | },
22 | "babel-runtime": {
23 | "version": "6.26.0",
24 | "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
25 | "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
26 | "requires": {
27 | "core-js": "^2.4.0",
28 | "regenerator-runtime": "^0.11.0"
29 | }
30 | },
31 | "base-x": {
32 | "version": "3.0.8",
33 | "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz",
34 | "integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==",
35 | "requires": {
36 | "safe-buffer": "^5.0.1"
37 | }
38 | },
39 | "bigi": {
40 | "version": "1.4.2",
41 | "resolved": "https://registry.npmjs.org/bigi/-/bigi-1.4.2.tgz",
42 | "integrity": "sha1-nGZalfiLiwj8Bc/XMfVhhZ1yWCU="
43 | },
44 | "browserify-aes": {
45 | "version": "1.0.6",
46 | "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.0.6.tgz",
47 | "integrity": "sha1-Xncl297x/Vkw1OurSFZ85FHEigo=",
48 | "requires": {
49 | "buffer-xor": "^1.0.2",
50 | "cipher-base": "^1.0.0",
51 | "create-hash": "^1.1.0",
52 | "evp_bytestokey": "^1.0.0",
53 | "inherits": "^2.0.1"
54 | }
55 | },
56 | "bs58": {
57 | "version": "4.0.1",
58 | "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
59 | "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=",
60 | "requires": {
61 | "base-x": "^3.0.2"
62 | }
63 | },
64 | "buffer-xor": {
65 | "version": "1.0.3",
66 | "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
67 | "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk="
68 | },
69 | "bytebuffer": {
70 | "version": "5.0.1",
71 | "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz",
72 | "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=",
73 | "requires": {
74 | "long": "~3"
75 | }
76 | },
77 | "cipher-base": {
78 | "version": "1.0.4",
79 | "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
80 | "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
81 | "requires": {
82 | "inherits": "^2.0.1",
83 | "safe-buffer": "^5.0.1"
84 | }
85 | },
86 | "core-js": {
87 | "version": "2.6.11",
88 | "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz",
89 | "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg=="
90 | },
91 | "create-hash": {
92 | "version": "1.1.3",
93 | "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.1.3.tgz",
94 | "integrity": "sha1-YGBCrIuSYnUPSDyt2rD1gZFy2P0=",
95 | "requires": {
96 | "cipher-base": "^1.0.1",
97 | "inherits": "^2.0.1",
98 | "ripemd160": "^2.0.0",
99 | "sha.js": "^2.4.0"
100 | }
101 | },
102 | "create-hmac": {
103 | "version": "1.1.6",
104 | "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.6.tgz",
105 | "integrity": "sha1-rLniIaThe9sHbpBlfEK5PjcmzwY=",
106 | "requires": {
107 | "cipher-base": "^1.0.3",
108 | "create-hash": "^1.1.0",
109 | "inherits": "^2.0.1",
110 | "ripemd160": "^2.0.0",
111 | "safe-buffer": "^5.0.1",
112 | "sha.js": "^2.4.8"
113 | }
114 | },
115 | "ecurve": {
116 | "version": "1.0.5",
117 | "resolved": "https://registry.npmjs.org/ecurve/-/ecurve-1.0.5.tgz",
118 | "integrity": "sha1-0Ujo/lCmdPmDu1uuCdoOoj4QU14=",
119 | "requires": {
120 | "bigi": "^1.1.0"
121 | }
122 | },
123 | "eosjs": {
124 | "version": "20.0.3",
125 | "resolved": "https://registry.npmjs.org/eosjs/-/eosjs-20.0.3.tgz",
126 | "integrity": "sha512-h0WSxsDo7AHz5IzpbQDrzyT/CYmbBDtFiiolTdtGd2FdKXEa6LDER4WbNp4qxCY7kD65KeG3knkGmOOFGbPJxw==",
127 | "requires": {
128 | "babel-runtime": "6.26.0",
129 | "eosjs-ecc": "4.0.7",
130 | "text-encoding": "0.7.0"
131 | },
132 | "dependencies": {
133 | "eosjs-ecc": {
134 | "version": "4.0.7",
135 | "resolved": "https://registry.npmjs.org/eosjs-ecc/-/eosjs-ecc-4.0.7.tgz",
136 | "integrity": "sha512-uuqhqnrDy9XTpKfkhiZqRDUTCCI9oWBalVK5IosL7kpYwA9I3lm68INYFLyWsHpF2xwHqPql8MrMYJ3zfOn5Qg==",
137 | "requires": {
138 | "@babel/runtime": "7.6.0",
139 | "bigi": "1.4.2",
140 | "browserify-aes": "1.0.6",
141 | "bs58": "4.0.1",
142 | "bytebuffer": "5.0.1",
143 | "create-hash": "1.1.3",
144 | "create-hmac": "1.1.6",
145 | "ecurve": "1.0.5",
146 | "randombytes": "2.0.5"
147 | }
148 | }
149 | }
150 | },
151 | "evp_bytestokey": {
152 | "version": "1.0.3",
153 | "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
154 | "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
155 | "requires": {
156 | "md5.js": "^1.3.4",
157 | "safe-buffer": "^5.1.1"
158 | }
159 | },
160 | "hash-base": {
161 | "version": "3.1.0",
162 | "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
163 | "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
164 | "requires": {
165 | "inherits": "^2.0.4",
166 | "readable-stream": "^3.6.0",
167 | "safe-buffer": "^5.2.0"
168 | }
169 | },
170 | "inherits": {
171 | "version": "2.0.4",
172 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
173 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
174 | },
175 | "long": {
176 | "version": "3.2.0",
177 | "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
178 | "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s="
179 | },
180 | "md5.js": {
181 | "version": "1.3.5",
182 | "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
183 | "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
184 | "requires": {
185 | "hash-base": "^3.0.0",
186 | "inherits": "^2.0.1",
187 | "safe-buffer": "^5.1.2"
188 | }
189 | },
190 | "randombytes": {
191 | "version": "2.0.5",
192 | "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.0.5.tgz",
193 | "integrity": "sha512-8T7Zn1AhMsQ/HI1SjcCfT/t4ii3eAqco3yOcSzS4mozsOz69lHLsoMXmF9nZgnFanYscnSlUSgs8uZyKzpE6kg==",
194 | "requires": {
195 | "safe-buffer": "^5.1.0"
196 | }
197 | },
198 | "readable-stream": {
199 | "version": "3.6.0",
200 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
201 | "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
202 | "requires": {
203 | "inherits": "^2.0.3",
204 | "string_decoder": "^1.1.1",
205 | "util-deprecate": "^1.0.1"
206 | }
207 | },
208 | "regenerator-runtime": {
209 | "version": "0.11.1",
210 | "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
211 | "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
212 | },
213 | "ripemd160": {
214 | "version": "2.0.2",
215 | "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
216 | "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
217 | "requires": {
218 | "hash-base": "^3.0.0",
219 | "inherits": "^2.0.1"
220 | }
221 | },
222 | "safe-buffer": {
223 | "version": "5.2.1",
224 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
225 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
226 | },
227 | "sha.js": {
228 | "version": "2.4.11",
229 | "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
230 | "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
231 | "requires": {
232 | "inherits": "^2.0.1",
233 | "safe-buffer": "^5.0.1"
234 | }
235 | },
236 | "string_decoder": {
237 | "version": "1.3.0",
238 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
239 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
240 | "requires": {
241 | "safe-buffer": "~5.2.0"
242 | }
243 | },
244 | "text-encoding": {
245 | "version": "0.7.0",
246 | "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.7.0.tgz",
247 | "integrity": "sha512-oJQ3f1hrOnbRLOcwKz0Liq2IcrvDeZRHXhd9RgLrsT+DjWY/nty1Hi7v3dtkaEYbPYe0mUoOfzRrMwfXXwgPUA=="
248 | },
249 | "util-deprecate": {
250 | "version": "1.0.2",
251 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
252 | "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
253 | }
254 | }
255 | }
256 |
--------------------------------------------------------------------------------
/packages/eosio/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@walletpack/eosio",
3 | "version": "0.0.67",
4 | "description": "> TODO: description",
5 | "author": "GetScatter Ltd. 2019",
6 | "homepage": "",
7 | "license": "ISC",
8 | "main": "eosio.js",
9 | "files": [
10 | "dist",
11 | "prepare.js"
12 | ],
13 | "scripts": {
14 | "install": "node prepare.js",
15 | "test": "echo \"Error: run tests from root\" && exit 1"
16 | },
17 | "dependencies": {
18 | "@walletpack/core": "^1.0.51",
19 | "eosjs": "^21.0.3",
20 | "eosjs-ecc": "^4.0.7"
21 | },
22 | "gitHead": "70bd19f93b503618a79eb519eef082bfc40b16d7",
23 | "publishConfig": {
24 | "access": "public"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/packages/eosio/prepare.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const rimraf = require("rimraf");
4 |
5 | const paths = __dirname.split(path.sep);
6 | const parent = paths[paths.length-2];
7 |
8 | if(parent === 'packages') return;
9 |
10 | try {
11 | rimraf.sync("./__tests__");
12 | const files = fs.readdirSync(`./dist`);
13 | files.map(file => {
14 | if(fs.existsSync(`./${file}`)) rimraf.sync(`./${file}`);
15 | fs.renameSync(`./dist/${file}`, `./${file}`);
16 | })
17 | rimraf.sync("./dist");
18 | rimraf.sync("./lib");
19 | } catch(e){
20 | console.error('Walletpack prepare.js error', e);
21 | }
22 |
--------------------------------------------------------------------------------
/packages/ethereum/README.md:
--------------------------------------------------------------------------------
1 | # `ethereum`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const ethereum = require('ethereum');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/ethereum/__tests__/ethereum.test.js:
--------------------------------------------------------------------------------
1 | // 'use strict';
2 | //
3 | // const ethereum = require('..');
4 | //
5 | // describe('ethereum', () => {
6 | // it('needs tests');
7 | // });
8 |
--------------------------------------------------------------------------------
/packages/ethereum/lib/erc20.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "constant": true,
4 | "inputs": [],
5 | "name": "name",
6 | "outputs": [
7 | {
8 | "name": "",
9 | "type": "string"
10 | }
11 | ],
12 | "payable": false,
13 | "stateMutability": "view",
14 | "type": "function"
15 | },
16 | {
17 | "constant": false,
18 | "inputs": [
19 | {
20 | "name": "_spender",
21 | "type": "address"
22 | },
23 | {
24 | "name": "_value",
25 | "type": "uint256"
26 | }
27 | ],
28 | "name": "approve",
29 | "outputs": [
30 | {
31 | "name": "",
32 | "type": "bool"
33 | }
34 | ],
35 | "payable": false,
36 | "stateMutability": "nonpayable",
37 | "type": "function"
38 | },
39 | {
40 | "constant": true,
41 | "inputs": [],
42 | "name": "totalSupply",
43 | "outputs": [
44 | {
45 | "name": "",
46 | "type": "uint256"
47 | }
48 | ],
49 | "payable": false,
50 | "stateMutability": "view",
51 | "type": "function"
52 | },
53 | {
54 | "constant": false,
55 | "inputs": [
56 | {
57 | "name": "_from",
58 | "type": "address"
59 | },
60 | {
61 | "name": "_to",
62 | "type": "address"
63 | },
64 | {
65 | "name": "_value",
66 | "type": "uint256"
67 | }
68 | ],
69 | "name": "transferFrom",
70 | "outputs": [
71 | {
72 | "name": "",
73 | "type": "bool"
74 | }
75 | ],
76 | "payable": false,
77 | "stateMutability": "nonpayable",
78 | "type": "function"
79 | },
80 | {
81 | "constant": true,
82 | "inputs": [],
83 | "name": "decimals",
84 | "outputs": [
85 | {
86 | "name": "",
87 | "type": "uint8"
88 | }
89 | ],
90 | "payable": false,
91 | "stateMutability": "view",
92 | "type": "function"
93 | },
94 | {
95 | "constant": true,
96 | "inputs": [
97 | {
98 | "name": "_owner",
99 | "type": "address"
100 | }
101 | ],
102 | "name": "balanceOf",
103 | "outputs": [
104 | {
105 | "name": "balance",
106 | "type": "uint256"
107 | }
108 | ],
109 | "payable": false,
110 | "stateMutability": "view",
111 | "type": "function"
112 | },
113 | {
114 | "constant": true,
115 | "inputs": [],
116 | "name": "symbol",
117 | "outputs": [
118 | {
119 | "name": "",
120 | "type": "string"
121 | }
122 | ],
123 | "payable": false,
124 | "stateMutability": "view",
125 | "type": "function"
126 | },
127 | {
128 | "constant": false,
129 | "inputs": [
130 | {
131 | "name": "_to",
132 | "type": "address"
133 | },
134 | {
135 | "name": "_value",
136 | "type": "uint256"
137 | }
138 | ],
139 | "name": "transfer",
140 | "outputs": [
141 | {
142 | "name": "",
143 | "type": "bool"
144 | }
145 | ],
146 | "payable": false,
147 | "stateMutability": "nonpayable",
148 | "type": "function"
149 | },
150 | {
151 | "constant": true,
152 | "inputs": [
153 | {
154 | "name": "_owner",
155 | "type": "address"
156 | },
157 | {
158 | "name": "_spender",
159 | "type": "address"
160 | }
161 | ],
162 | "name": "allowance",
163 | "outputs": [
164 | {
165 | "name": "",
166 | "type": "uint256"
167 | }
168 | ],
169 | "payable": false,
170 | "stateMutability": "view",
171 | "type": "function"
172 | },
173 | {
174 | "payable": true,
175 | "stateMutability": "payable",
176 | "type": "fallback"
177 | },
178 | {
179 | "anonymous": false,
180 | "inputs": [
181 | {
182 | "indexed": true,
183 | "name": "owner",
184 | "type": "address"
185 | },
186 | {
187 | "indexed": true,
188 | "name": "spender",
189 | "type": "address"
190 | },
191 | {
192 | "indexed": false,
193 | "name": "value",
194 | "type": "uint256"
195 | }
196 | ],
197 | "name": "Approval",
198 | "type": "event"
199 | },
200 | {
201 | "anonymous": false,
202 | "inputs": [
203 | {
204 | "indexed": true,
205 | "name": "from",
206 | "type": "address"
207 | },
208 | {
209 | "indexed": true,
210 | "name": "to",
211 | "type": "address"
212 | },
213 | {
214 | "indexed": false,
215 | "name": "value",
216 | "type": "uint256"
217 | }
218 | ],
219 | "name": "Transfer",
220 | "type": "event"
221 | }
222 | ]
--------------------------------------------------------------------------------
/packages/ethereum/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@walletpack/ethereum",
3 | "version": "0.0.61",
4 | "description": "> TODO: description",
5 | "author": "GetScatter Ltd. 2019",
6 | "homepage": "",
7 | "license": "ISC",
8 | "main": "ethereum.js",
9 | "files": [
10 | "dist",
11 | "prepare.js"
12 | ],
13 | "scripts": {
14 | "install": "node prepare.js",
15 | "test": "echo \"Error: run tests from root\" && exit 1"
16 | },
17 | "dependencies": {
18 | "@walletpack/core": "^1.0.51",
19 | "ethereumjs-tx": "^2.1.0",
20 | "ethereumjs-util": "^6.1.0",
21 | "web3": "^1.2.0",
22 | "web3-provider-engine": "^15.0.4"
23 | },
24 | "gitHead": "70bd19f93b503618a79eb519eef082bfc40b16d7",
25 | "publishConfig": {
26 | "access": "public"
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/packages/ethereum/prepare.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const rimraf = require("rimraf");
4 |
5 | const paths = __dirname.split(path.sep);
6 | const parent = paths[paths.length-2];
7 |
8 | if(parent === 'packages') return;
9 |
10 | try {
11 | rimraf.sync("./__tests__");
12 | const files = fs.readdirSync(`./dist`);
13 | files.map(file => {
14 | if(fs.existsSync(`./${file}`)) rimraf.sync(`./${file}`);
15 | fs.renameSync(`./dist/${file}`, `./${file}`);
16 | })
17 | rimraf.sync("./dist");
18 | rimraf.sync("./lib");
19 | } catch(e){
20 | console.error('Walletpack prepare.js error', e);
21 | }
22 |
--------------------------------------------------------------------------------
/packages/fio/README.md:
--------------------------------------------------------------------------------
1 | # `fio`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const fio = require('fio');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/fio/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@walletpack/fio",
3 | "version": "0.0.29",
4 | "description": "> TODO: description",
5 | "author": "GetScatter Ltd. 2019",
6 | "homepage": "",
7 | "license": "ISC",
8 | "main": "fio.js",
9 | "files": [
10 | "dist",
11 | "prepare.js"
12 | ],
13 | "scripts": {
14 | "install": "node prepare.js",
15 | "test": "echo \"Error: run tests from root\" && exit 1"
16 | },
17 | "dependencies": {
18 | "@fioprotocol/fiojs": "^1.0.1",
19 | "@walletpack/core": "^1.0.51"
20 | },
21 | "devDependencies": {
22 | "@fioprotocol/fiosdk": "latest"
23 | },
24 | "publishConfig": {
25 | "access": "public"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/fio/prepare.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const rimraf = require("rimraf");
4 |
5 | const paths = __dirname.split(path.sep);
6 | const parent = paths[paths.length-2];
7 |
8 | if(parent === 'packages') return;
9 |
10 | try {
11 | rimraf.sync("./__tests__");
12 | const files = fs.readdirSync(`./dist`);
13 | files.map(file => {
14 | if(fs.existsSync(`./${file}`)) rimraf.sync(`./${file}`);
15 | fs.renameSync(`./dist/${file}`, `./${file}`);
16 | })
17 | rimraf.sync("./dist");
18 | rimraf.sync("./lib");
19 | } catch(e){
20 | console.error('Walletpack prepare.js error', e);
21 | }
22 |
--------------------------------------------------------------------------------
/packages/fio/yarn-error.log:
--------------------------------------------------------------------------------
1 | Arguments:
2 | C:\Program Files\nodejs\node.exe C:\Program Files (x86)\Yarn\bin\yarn.js add @fioprotocol/fiojs
3 |
4 | PATH:
5 | C:\Python27\;C:\Python27\Scripts;C:\Program Files (x86)\Common Files\Intel\Shared Libraries\redist\intel64\compiler;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\Git\cmd;C:\WINDOWS\System32\OpenSSH\;C:\Program Files (x86)\Yarn\bin\;C:\Users\nsjames\AppData\Roaming\nvm;C:\Program Files\nodejs;C:\Program Files\nodejs\;C:\ProgramData\chocolatey\bin;C:\Program Files\Java\jdk1.8.0_221\bin;C:\Program Files\SafeNet\Authentication\SAC\x64;C:\Program Files\SafeNet\Authentication\SAC\x32;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Users\nsjames\.windows-build-tools\python27\;C:\Program Files\nodejs\node_modules\npm\bin\node-gyp-bin;C:\Users\nsjames\AppData\Roaming\npm\node_modules\windows-build-tools\node_modules\.bin;C:\Users\nsjames\AppData\Roaming\npm\node_modules\.bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Git\cmd;C:\Program Files\Git\mingw64\bin;C:\Program Files\Git\usr\bin;C:\Program Files\nodejs\;C:\Program Files (x86)\Yarn\bin\;C:\Users\nsjames\AppData\Local\Microsoft\WindowsApps;C:\Users\nsjames\AppData\Local\Microsoft\WindowsApps;C:\Users\nsjames\AppData\Local\Yarn\bin;C:\Users\nsjames\AppData\Roaming\nvm;C:\Program Files\nodejs;C:\Users\nsjames\AppData\Roaming\npm;C:\kubernetes;;C:\Program Files\Docker Toolbox
6 |
7 | Yarn version:
8 | 1.12.3
9 |
10 | Node version:
11 | 10.15.3
12 |
13 | Platform:
14 | win32 x64
15 |
16 | Trace:
17 | SyntaxError: C:\Work\Libraries\walletpack\packages\fio\package.json: Unexpected token } in JSON at position 405
18 | at JSON.parse ()
19 | at C:\Program Files (x86)\Yarn\lib\cli.js:1631:59
20 | at Generator.next ()
21 | at step (C:\Program Files (x86)\Yarn\lib\cli.js:304:30)
22 | at C:\Program Files (x86)\Yarn\lib\cli.js:315:13
23 |
24 | npm manifest:
25 | {
26 | "name": "@walletpack/fio",
27 | "version": "0.0.1",
28 | "description": "> TODO: description",
29 | "author": "GetScatter Ltd. 2019",
30 | "homepage": "",
31 | "license": "ISC",
32 | "main": "fio.js",
33 | "files": [
34 | "dist",
35 | "prepare.js"
36 | ],
37 | "scripts": {
38 | "install": "node prepare.js",
39 | "test": "echo \"Error: run tests from root\" && exit 1"
40 | },
41 | "dependencies": {
42 | "@walletpack/core": "^1.0.41",
43 | },
44 | "publishConfig": {
45 | "access": "public"
46 | }
47 | }
48 |
49 | yarn manifest:
50 | No manifest
51 |
52 | Lockfile:
53 | No lockfile
54 |
--------------------------------------------------------------------------------
/packages/tron/README.md:
--------------------------------------------------------------------------------
1 | # `tron`
2 |
3 | > TODO: description
4 |
5 | ## Usage
6 |
7 | ```
8 | const tron = require('tron');
9 |
10 | // TODO: DEMONSTRATE API
11 | ```
12 |
--------------------------------------------------------------------------------
/packages/tron/__tests__/tron.test.js:
--------------------------------------------------------------------------------
1 | // 'use strict';
2 | //
3 | // import Account from "@walletpack/core/lib/models/Account";
4 | // import Network from "@walletpack/core/lib/models/Network";
5 | // import Keypair from "@walletpack/core/lib/models/Keypair";
6 | // import {Blockchains} from "@walletpack/core/lib/models/Blockchains";
7 | // import Token from "@walletpack/core/lib/models/Token";
8 | //
9 | // const tron = new (require('../lib/tron').default)();
10 | //
11 | // const keypair = Keypair.fromJson({
12 | // name:'Testing key',
13 | // // Just a known address with ANTE tokens.
14 | // // publicKeys:[{blockchain:Blockchains.TRX, key:'TFKSq1F1RBhqmLjqktcRk74YpMDGCDQAeX'}],
15 | // publicKeys:[{blockchain:Blockchains.TRX, key:'TF2quv1hTipcZ8FJ8FRsXXLSiJ1C15dqkW'}],
16 | // privateKey:'...'
17 | // })
18 | //
19 | // const network = Network.fromJson({
20 | // "name":"Tron Mainnet",
21 | // "host":"api.trongrid.io",
22 | // "port":443,
23 | // "protocol":"https",
24 | // "chainId":"1"
25 | // })
26 | //
27 | // const account = Account.fromJson({
28 | // keypairUnique:keypair.unique(),
29 | // networkUnique:network.unique(),
30 | // publicKey:keypair.publicKeys[0].key,
31 | // });
32 | //
33 | // // Testing with TRONbet's ANTE token:
34 | // // https://www.trontokens.org/token/trc20/TRONbet/TCN77KWWyUyi2A4Cu7vrh5dnmRyvUuME1E
35 | // const token = Token.fromJson({
36 | // contract:'TCN77KWWyUyi2A4Cu7vrh5dnmRyvUuME1E',
37 | // blockchain:Blockchains.TRX,
38 | // symbol:'ANTE',
39 | // decimals:6,
40 | // chainId:network.chainId
41 | // })
42 | //
43 | // // Removing need for StoreService's state
44 | // account.network = () => network;
45 | // account.sendable = () => account.publicKey;
46 | //
47 | // describe('tron', () => {
48 | // it('should be able to init', done => {
49 | // new Promise(async() => {
50 | // tron.init();
51 | // done();
52 | // })
53 | // });
54 | //
55 | // it('should be able to get trc20 balances', done => {
56 | // new Promise(async() => {
57 | // const balances = await tron.balancesFor(account, [token]);
58 | // console.log('balances', balances);
59 | // done();
60 | // })
61 | // });
62 | //
63 | // // it('should be able to parse trc20 transactions', done => {
64 | // // new Promise(async() => {
65 | // //
66 | // // const json = `transaction {"transaction":{"transaction":{},"participants":["TF2quv1hTipcZ8FJ8FRsXXLSiJ1C15dqkW"]},"blockchain":"trx","network":{"id":"216730975559","name":"Tron Mainnet","protocol":"https","host":"api.trongrid.io","port":443,"path":"","blockchain":"trx","chainId":"1","fromOrigin":null,"createdAt":1571601826773,"token":null},"requiredFields":{}}`;
67 | // //
68 | // // const transfer = await tron.transfer({
69 | // // account,
70 | // // // Random address
71 | // // to:'TU9Rpk8YqTea5oYx1h26a2P6vsGn8faRBt',
72 | // // amount:'100',
73 | // // token,
74 | // // promptForSignature:false
75 | // // });
76 | // // console.log('transfer', transfer);
77 | // // done();
78 | // // })
79 | // // });
80 | //
81 | // // it('should be able to send trc20 tokens', done => {
82 | // // new Promise(async() => {
83 | // // const transfer = await tron.transfer({
84 | // // account,
85 | // // // Random address
86 | // // to:'TU9Rpk8YqTea5oYx1h26a2P6vsGn8faRBt',
87 | // // amount:'1',
88 | // // token,
89 | // // promptForSignature:false
90 | // // });
91 | // // console.log('transfer', transfer);
92 | // // done();
93 | // // })
94 | // // });
95 | // });
96 |
--------------------------------------------------------------------------------
/packages/tron/lib/trc20.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "constant": false,
4 | "inputs": [
5 | {
6 | "name": "spender",
7 | "type": "address"
8 | },
9 | {
10 | "name": "value",
11 | "type": "uint256"
12 | }
13 | ],
14 | "name": "approve",
15 | "outputs": [
16 | {
17 | "name": "",
18 | "type": "bool"
19 | }
20 | ],
21 | "payable": false,
22 | "stateMutability": "nonpayable",
23 | "type": "function",
24 | "signature": "0x095ea7b3"
25 | },
26 | {
27 | "constant": true,
28 | "inputs": [],
29 | "name": "totalSupply",
30 | "outputs": [
31 | {
32 | "name": "",
33 | "type": "uint256"
34 | }
35 | ],
36 | "payable": false,
37 | "stateMutability": "view",
38 | "type": "function",
39 | "signature": "0x18160ddd"
40 | },
41 | {
42 | "constant": false,
43 | "inputs": [
44 | {
45 | "name": "from",
46 | "type": "address"
47 | },
48 | {
49 | "name": "to",
50 | "type": "address"
51 | },
52 | {
53 | "name": "value",
54 | "type": "uint256"
55 | }
56 | ],
57 | "name": "transferFrom",
58 | "outputs": [
59 | {
60 | "name": "",
61 | "type": "bool"
62 | }
63 | ],
64 | "payable": false,
65 | "stateMutability": "nonpayable",
66 | "type": "function",
67 | "signature": "0x23b872dd"
68 | },
69 | {
70 | "constant": false,
71 | "inputs": [
72 | {
73 | "name": "spender",
74 | "type": "address"
75 | },
76 | {
77 | "name": "addedValue",
78 | "type": "uint256"
79 | }
80 | ],
81 | "name": "increaseAllowance",
82 | "outputs": [
83 | {
84 | "name": "",
85 | "type": "bool"
86 | }
87 | ],
88 | "payable": false,
89 | "stateMutability": "nonpayable",
90 | "type": "function",
91 | "signature": "0x39509351"
92 | },
93 | {
94 | "constant": true,
95 | "inputs": [
96 | {
97 | "name": "owner",
98 | "type": "address"
99 | }
100 | ],
101 | "name": "balanceOf",
102 | "outputs": [
103 | {
104 | "name": "",
105 | "type": "uint256"
106 | }
107 | ],
108 | "payable": false,
109 | "stateMutability": "view",
110 | "type": "function",
111 | "signature": "0x70a08231"
112 | },
113 | {
114 | "constant": false,
115 | "inputs": [
116 | {
117 | "name": "spender",
118 | "type": "address"
119 | },
120 | {
121 | "name": "subtractedValue",
122 | "type": "uint256"
123 | }
124 | ],
125 | "name": "decreaseAllowance",
126 | "outputs": [
127 | {
128 | "name": "",
129 | "type": "bool"
130 | }
131 | ],
132 | "payable": false,
133 | "stateMutability": "nonpayable",
134 | "type": "function",
135 | "signature": "0xa457c2d7"
136 | },
137 | {
138 | "constant": false,
139 | "inputs": [
140 | {
141 | "name": "to",
142 | "type": "address"
143 | },
144 | {
145 | "name": "value",
146 | "type": "uint256"
147 | }
148 | ],
149 | "name": "transfer",
150 | "outputs": [
151 | {
152 | "name": "",
153 | "type": "bool"
154 | }
155 | ],
156 | "payable": false,
157 | "stateMutability": "nonpayable",
158 | "type": "function",
159 | "signature": "0xa9059cbb"
160 | },
161 | {
162 | "constant": true,
163 | "inputs": [
164 | {
165 | "name": "owner",
166 | "type": "address"
167 | },
168 | {
169 | "name": "spender",
170 | "type": "address"
171 | }
172 | ],
173 | "name": "allowance",
174 | "outputs": [
175 | {
176 | "name": "",
177 | "type": "uint256"
178 | }
179 | ],
180 | "payable": false,
181 | "stateMutability": "view",
182 | "type": "function",
183 | "signature": "0xdd62ed3e"
184 | },
185 | {
186 | "inputs": [
187 | {
188 | "name": "name",
189 | "type": "string"
190 | },
191 | {
192 | "name": "symbol",
193 | "type": "string"
194 | },
195 | {
196 | "name": "decimals",
197 | "type": "uint8"
198 | },
199 | {
200 | "name": "cap",
201 | "type": "uint256"
202 | }
203 | ],
204 | "payable": false,
205 | "stateMutability": "nonpayable",
206 | "type": "constructor",
207 | "signature": "constructor"
208 | },
209 | {
210 | "anonymous": false,
211 | "inputs": [
212 | {
213 | "indexed": true,
214 | "name": "from",
215 | "type": "address"
216 | },
217 | {
218 | "indexed": true,
219 | "name": "to",
220 | "type": "address"
221 | },
222 | {
223 | "indexed": false,
224 | "name": "value",
225 | "type": "uint256"
226 | }
227 | ],
228 | "name": "Transfer",
229 | "type": "event",
230 | "signature": "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
231 | },
232 | {
233 | "anonymous": false,
234 | "inputs": [
235 | {
236 | "indexed": true,
237 | "name": "owner",
238 | "type": "address"
239 | },
240 | {
241 | "indexed": true,
242 | "name": "spender",
243 | "type": "address"
244 | },
245 | {
246 | "indexed": false,
247 | "name": "value",
248 | "type": "uint256"
249 | }
250 | ],
251 | "name": "Approval",
252 | "type": "event",
253 | "signature": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925"
254 | },
255 | {
256 | "constant": true,
257 | "inputs": [],
258 | "name": "name",
259 | "outputs": [
260 | {
261 | "name": "",
262 | "type": "string"
263 | }
264 | ],
265 | "payable": false,
266 | "stateMutability": "view",
267 | "type": "function",
268 | "signature": "0x06fdde03"
269 | },
270 | {
271 | "constant": true,
272 | "inputs": [],
273 | "name": "symbol",
274 | "outputs": [
275 | {
276 | "name": "",
277 | "type": "string"
278 | }
279 | ],
280 | "payable": false,
281 | "stateMutability": "view",
282 | "type": "function",
283 | "signature": "0x95d89b41"
284 | },
285 | {
286 | "constant": true,
287 | "inputs": [],
288 | "name": "decimals",
289 | "outputs": [
290 | {
291 | "name": "",
292 | "type": "uint8"
293 | }
294 | ],
295 | "payable": false,
296 | "stateMutability": "view",
297 | "type": "function",
298 | "signature": "0x313ce567"
299 | }
300 | ]
301 |
--------------------------------------------------------------------------------
/packages/tron/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@walletpack/tron",
3 | "version": "0.0.63",
4 | "description": "> TODO: description",
5 | "author": "GetScatter Ltd. 2019",
6 | "homepage": "",
7 | "license": "ISC",
8 | "main": "tron.js",
9 | "files": [
10 | "dist",
11 | "prepare.js"
12 | ],
13 | "scripts": {
14 | "install": "node prepare.js",
15 | "test": "echo \"Error: run tests from root\" && exit 1"
16 | },
17 | "dependencies": {
18 | "@walletpack/core": "^1.0.51",
19 | "elliptic": "^6.5.2",
20 | "ethereumjs-util": "^6.1.0",
21 | "tronweb": "^2.8.0"
22 | },
23 | "gitHead": "70bd19f93b503618a79eb519eef082bfc40b16d7",
24 | "publishConfig": {
25 | "access": "public"
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/packages/tron/prepare.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const rimraf = require("rimraf");
4 |
5 | const paths = __dirname.split(path.sep);
6 | const parent = paths[paths.length-2];
7 |
8 | if(parent === 'packages') return;
9 |
10 | try {
11 | rimraf.sync("./__tests__");
12 | const files = fs.readdirSync(`./dist`);
13 | files.map(file => {
14 | if(fs.existsSync(`./${file}`)) rimraf.sync(`./${file}`);
15 | fs.renameSync(`./dist/${file}`, `./${file}`);
16 | })
17 | rimraf.sync("./dist");
18 | rimraf.sync("./lib");
19 | } catch(e){
20 | console.error('Walletpack prepare.js error', e);
21 | }
22 |
--------------------------------------------------------------------------------
/scripts/copy-files.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 |
4 | const packages = fs.readdirSync(`./packages`);
5 | packages.map(pack => {
6 | const jsons = fs.readdirSync(`./packages/${pack}/lib`).filter(x => x.indexOf('.json') > -1);
7 | jsons.map(json => {
8 | fs.copyFileSync(`./packages/${pack}/lib/${json}`, `./packages/${pack}/dist/${json}`)
9 | })
10 | })
11 |
--------------------------------------------------------------------------------
/scripts/prepare.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const rimraf = require("rimraf");
4 |
5 | const paths = __dirname.split(path.sep);
6 | const parent = paths[paths.length-2];
7 |
8 | if(parent === 'packages') return;
9 |
10 | try {
11 | rimraf.sync("./__tests__");
12 | const files = fs.readdirSync(`./dist`);
13 | files.map(file => {
14 | if(fs.existsSync(`./${file}`)) rimraf.sync(`./${file}`);
15 | fs.renameSync(`./dist/${file}`, `./${file}`);
16 | })
17 | rimraf.sync("./dist");
18 | rimraf.sync("./lib");
19 | } catch(e){
20 | console.error('Walletpack prepare.js error', e);
21 | }
22 |
--------------------------------------------------------------------------------
/scripts/prepublish.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const packages = fs.readdirSync('./packages');
3 | packages.map(pdir => fs.copyFileSync('./scripts/prepare.js', `./packages/${pdir}/prepare.js`));
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack')
3 | const fs = require('fs');
4 |
5 | const getPackagePath = x => `./packages/${x}/src/index.js`;
6 | const packageFiles = fs.readdirSync('./packages');
7 | const entry = packageFiles.reduce((o, file) => Object.assign(o, {[`${file}.min.js`]: getPackagePath(file)}), {});
8 |
9 | module.exports = {
10 | entry,
11 | output: {
12 | path: path.resolve(__dirname, './bundles'),
13 | filename: 'walletpack-[name]'
14 | },
15 | resolve: {
16 | modules:[
17 | "node_modules"
18 | ]
19 | },
20 | module: {
21 | rules: [
22 | {
23 | test: /\.js$/,
24 | use: {
25 | loader: 'babel-loader',
26 | options: {
27 | presets: [
28 | '@babel/preset-env'
29 | ],
30 | plugins:[
31 | '@babel/plugin-transform-runtime'
32 | ]
33 | }
34 | },
35 | exclude: /node_modules/
36 | }
37 | ],
38 | },
39 | plugins: [
40 |
41 | ],
42 | stats: { colors: true },
43 | // devtool: false,
44 | devtool: 'inline-source-map',
45 | externals: {
46 | '@walletpack/core': 'WalletPack'
47 | }
48 | }
49 |
--------------------------------------------------------------------------------