edge-core-js 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/android/src/main/assets/edge-core-js/edge-core.js +1 -1
- package/lib/core/account/account-api.js +2 -2
- package/lib/core/account/account-pixie.js +3 -6
- package/lib/core/account/lobby-api.js +1 -3
- package/lib/core/actions.js +6 -0
- package/lib/core/context/context-pixie.js +47 -2
- package/lib/core/context/info-cache-file.js +18 -0
- package/lib/core/currency/currency-pixie.js +32 -2
- package/lib/core/currency/wallet/currency-wallet-callbacks.js +3 -7
- package/lib/core/currency/wallet/currency-wallet-files.js +18 -16
- package/lib/core/currency/wallet/metadata.js +2 -1
- package/lib/core/fake/fake-db.js +3 -3
- package/lib/core/fake/fake-server.js +6 -6
- package/lib/core/fake/fake-world.js +5 -2
- package/lib/core/plugins/plugins-actions.js +6 -3
- package/lib/core/plugins/plugins-selectors.js +1 -1
- package/lib/core/root-reducer.js +21 -2
- package/lib/core/root.js +33 -16
- package/lib/core/storage/storage-actions.js +3 -3
- package/lib/core/swap/swap-api.js +5 -4
- package/lib/flow/types.js +6 -2
- package/lib/io/react-native/native-bridge.js +1 -0
- package/lib/node/index.js +179 -82
- package/lib/types/types.js +4 -0
- package/lib/util/match-json.js +47 -0
- package/package.json +1 -1
- package/src/types/types.ts +6 -2
- package/lib/core/context/context-reducer.js +0 -31
|
@@ -621,8 +621,8 @@ export function makeAccountApi(ai, accountId) {
|
|
|
621
621
|
throw new Error(
|
|
622
622
|
`activateWallet unsupported by walletId ${activateWalletId}`
|
|
623
623
|
)
|
|
624
|
-
const walletId = _optionalChain([paymentInfo, 'optionalAccess', _ => _.walletId])
|
|
625
|
-
const wallet = currencyWallets[
|
|
624
|
+
const walletId = _nullishCoalesce(_optionalChain([paymentInfo, 'optionalAccess', _ => _.walletId]), () => ( ''))
|
|
625
|
+
const wallet = currencyWallets[walletId]
|
|
626
626
|
|
|
627
627
|
if (wallet == null) {
|
|
628
628
|
throw new Error(`No wallet for walletId ${walletId}`)
|
|
@@ -154,10 +154,7 @@ const accountPixie = combinePixies({
|
|
|
154
154
|
|
|
155
155
|
return {
|
|
156
156
|
update() {
|
|
157
|
-
|
|
158
|
-
if (accountOutput == null) return
|
|
159
|
-
const { accountApi } = accountOutput
|
|
160
|
-
if (accountApi == null) return
|
|
157
|
+
if (_optionalChain([input, 'access', _ => _.props, 'access', _2 => _2.accountOutput, 'optionalAccess', _3 => _3.accountApi]) == null) return
|
|
161
158
|
|
|
162
159
|
// Start once the EdgeAccount API exists:
|
|
163
160
|
dataTask.start({ wait: true })
|
|
@@ -244,14 +241,14 @@ const accountPixie = combinePixies({
|
|
|
244
241
|
lastActiveWalletIds = activeWalletIds
|
|
245
242
|
|
|
246
243
|
let lastOut = {}
|
|
247
|
-
if (_optionalChain([accountOutput, 'optionalAccess',
|
|
244
|
+
if (_optionalChain([accountOutput, 'optionalAccess', _4 => _4.currencyWallets]) != null) {
|
|
248
245
|
lastOut = accountOutput.currencyWallets
|
|
249
246
|
}
|
|
250
247
|
|
|
251
248
|
const out = {}
|
|
252
249
|
const { wallets } = input.props.output.currency
|
|
253
250
|
for (const walletId of activeWalletIds) {
|
|
254
|
-
const api = _optionalChain([wallets, 'access',
|
|
251
|
+
const api = _optionalChain([wallets, 'access', _5 => _5[walletId], 'optionalAccess', _6 => _6.walletApi])
|
|
255
252
|
if (api !== lastOut[walletId]) dirty = true
|
|
256
253
|
if (api != null) out[walletId] = api
|
|
257
254
|
}
|
|
@@ -37,9 +37,7 @@ export async function fetchAppIdInfo(
|
|
|
37
37
|
appId
|
|
38
38
|
) {
|
|
39
39
|
try {
|
|
40
|
-
const infoServerUri = shuffle(
|
|
41
|
-
ai.props.state.contextConfig.edgeServers.infoServers
|
|
42
|
-
)[0]
|
|
40
|
+
const [infoServerUri] = shuffle(ai.props.state.infoServers)
|
|
43
41
|
const url = `${infoServerUri}/v1/appIdInfo/${appId}`
|
|
44
42
|
const response = await ai.props.io.fetch(url)
|
|
45
43
|
if (response.status === 404) {
|
package/lib/core/actions.js
CHANGED
|
@@ -1,9 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
combinePixies,
|
|
3
|
+
filterPixie,
|
|
4
|
+
stopUpdates,
|
|
5
|
+
|
|
6
|
+
} from 'redux-pixies'
|
|
2
7
|
import { close, update } from 'yaob'
|
|
3
8
|
|
|
4
9
|
|
|
10
|
+
import { makePeriodicTask } from '../../util/periodic-task'
|
|
11
|
+
import { shuffle } from '../../util/shuffle'
|
|
5
12
|
|
|
6
13
|
import { makeContextApi } from './context-api'
|
|
14
|
+
import {
|
|
15
|
+
asInfoCacheFile,
|
|
16
|
+
INFO_CACHE_FILE_NAME,
|
|
17
|
+
infoCacheFile
|
|
18
|
+
} from './info-cache-file'
|
|
7
19
|
|
|
8
20
|
|
|
9
21
|
|
|
@@ -41,5 +53,38 @@ export const context = combinePixies({
|
|
|
41
53
|
}
|
|
42
54
|
}
|
|
43
55
|
}
|
|
44
|
-
}
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
infoFetcher: filterPixie(
|
|
59
|
+
(input) => {
|
|
60
|
+
async function doInfoSync() {
|
|
61
|
+
const { dispatch, io } = input.props
|
|
62
|
+
|
|
63
|
+
const [infoServerUri] = shuffle(input.props.state.infoServers)
|
|
64
|
+
const response = await fetch(`${infoServerUri}/v1/coreRollup`, {
|
|
65
|
+
headers: { accept: 'application/json' }
|
|
66
|
+
})
|
|
67
|
+
if (!response.ok) return
|
|
68
|
+
const json = await response.json()
|
|
69
|
+
|
|
70
|
+
const infoCache = asInfoCacheFile(json)
|
|
71
|
+
dispatch({
|
|
72
|
+
type: 'INFO_CACHE_FETCHED',
|
|
73
|
+
payload: infoCache
|
|
74
|
+
})
|
|
75
|
+
await infoCacheFile.save(io.disklet, INFO_CACHE_FILE_NAME, infoCache)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const infoTask = makePeriodicTask(doInfoSync, 10 * 60 * 1000)
|
|
79
|
+
infoTask.start()
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
update() {},
|
|
83
|
+
destroy() {
|
|
84
|
+
infoTask.stop()
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
props => (props.state.paused ? undefined : props)
|
|
89
|
+
)
|
|
45
90
|
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { asArray, asObject, asString } from 'cleaners'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
import { asJsonObject, makeJsonFile } from '../../util/file-helpers'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
export const INFO_CACHE_FILE_NAME = 'infoCache.json'
|
|
12
|
+
|
|
13
|
+
export const asInfoCacheFile = asObject({
|
|
14
|
+
corePlugins: asObject(asJsonObject),
|
|
15
|
+
syncServers: asArray(asString)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
export const infoCacheFile = makeJsonFile(asInfoCacheFile)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { combinePixies, mapPixie, } from 'redux-pixies'
|
|
1
|
+
function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }import { combinePixies, mapPixie, } from 'redux-pixies'
|
|
2
|
+
|
|
3
|
+
import { matchJson } from '../../util/match-json'
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
import {
|
|
@@ -21,5 +23,33 @@ export const currency = combinePixies({
|
|
|
21
23
|
walletState: props.state.currency.wallets[walletId],
|
|
22
24
|
walletOutput: props.output.currency.wallets[walletId]
|
|
23
25
|
})
|
|
24
|
-
)
|
|
26
|
+
),
|
|
27
|
+
|
|
28
|
+
pluginUpdater(input) {
|
|
29
|
+
let lastInfo
|
|
30
|
+
|
|
31
|
+
return async () => {
|
|
32
|
+
const { infoCache, plugins } = input.props.state
|
|
33
|
+
|
|
34
|
+
// Bail out quickly if nothing has changed:
|
|
35
|
+
if (lastInfo === infoCache) return
|
|
36
|
+
|
|
37
|
+
// Update plugins after the first run:
|
|
38
|
+
if (lastInfo != null) {
|
|
39
|
+
for (const pluginId of Object.keys(plugins.currency)) {
|
|
40
|
+
const plugin = plugins.currency[pluginId]
|
|
41
|
+
const newPayload = _nullishCoalesce(_optionalChain([infoCache, 'access', _ => _.corePlugins, 'optionalAccess', _2 => _2[pluginId]]), () => ( {}))
|
|
42
|
+
const oldPayload = _nullishCoalesce(_optionalChain([lastInfo, 'access', _3 => _3.corePlugins, 'optionalAccess', _4 => _4[pluginId]]), () => ( {}))
|
|
43
|
+
|
|
44
|
+
if (
|
|
45
|
+
plugin.updateInfoPayload != null &&
|
|
46
|
+
!matchJson(oldPayload, newPayload)
|
|
47
|
+
) {
|
|
48
|
+
await plugin.updateInfoPayload(newPayload)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
lastInfo = infoCache
|
|
53
|
+
}
|
|
54
|
+
}
|
|
25
55
|
})
|
|
@@ -30,11 +30,7 @@ import {
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
import {
|
|
34
|
-
|
|
35
|
-
mergeTx,
|
|
36
|
-
|
|
37
|
-
} from './currency-wallet-reducer'
|
|
33
|
+
import { mergeTx, } from './currency-wallet-reducer'
|
|
38
34
|
import { uniqueStrings } from './enabled-tokens'
|
|
39
35
|
|
|
40
36
|
let throttleRateLimitMs = 5000
|
|
@@ -198,7 +194,7 @@ export function makeCurrencyWalletCallbacks(
|
|
|
198
194
|
return
|
|
199
195
|
}
|
|
200
196
|
pushUpdate({
|
|
201
|
-
id: `${walletId}==${tokenId}`,
|
|
197
|
+
id: `${walletId}==${String(tokenId)}`,
|
|
202
198
|
action: 'onTokenBalanceChanged',
|
|
203
199
|
updateFunc: () => {
|
|
204
200
|
input.props.dispatch({
|
|
@@ -418,7 +414,7 @@ export function watchCurrencyWallet(input) {
|
|
|
418
414
|
}
|
|
419
415
|
|
|
420
416
|
export const validateConfirmations = (
|
|
421
|
-
tx,
|
|
417
|
+
tx, // Either EdgeTransaction or MergedTransaction
|
|
422
418
|
blockHeight,
|
|
423
419
|
requiredConfirmations = 1 // Default confirmation rule is 1 block
|
|
424
420
|
) => {
|
|
@@ -235,23 +235,25 @@ export async function loadTokensFile(
|
|
|
235
235
|
disklet,
|
|
236
236
|
LEGACY_TOKENS_FILE
|
|
237
237
|
)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
238
|
+
if (legacyCurrencyCodes != null) {
|
|
239
|
+
const { accountId, currencyInfo, pluginId } = input.props.walletState
|
|
240
|
+
const accountState = input.props.state.accounts[accountId]
|
|
241
|
+
const tokenIds = currencyCodesToTokenIds(
|
|
242
|
+
accountState.builtinTokens[pluginId],
|
|
243
|
+
accountState.customTokens[pluginId],
|
|
244
|
+
currencyInfo,
|
|
245
|
+
legacyCurrencyCodes
|
|
246
|
+
)
|
|
246
247
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
248
|
+
dispatch({
|
|
249
|
+
type: 'CURRENCY_WALLET_LOADED_TOKEN_FILE',
|
|
250
|
+
payload: {
|
|
251
|
+
walletId: input.props.walletId,
|
|
252
|
+
detectedTokenIds: [],
|
|
253
|
+
enabledTokenIds: tokenIds
|
|
254
|
+
}
|
|
255
|
+
})
|
|
256
|
+
}
|
|
255
257
|
}
|
|
256
258
|
|
|
257
259
|
/**
|
|
@@ -7,8 +7,9 @@ export const asEdgeMetadata = raw => {
|
|
|
7
7
|
const { exchangeAmount = {} } = clean
|
|
8
8
|
|
|
9
9
|
// Delete corrupt amounts that exceed the Javascript number range:
|
|
10
|
-
for (const fiat of Object.keys(
|
|
10
|
+
for (const fiat of Object.keys(exchangeAmount)) {
|
|
11
11
|
if (String(exchangeAmount[fiat]).includes('e')) {
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
12
13
|
delete exchangeAmount[fiat]
|
|
13
14
|
}
|
|
14
15
|
}
|
package/lib/core/fake/fake-db.js
CHANGED
|
@@ -30,9 +30,9 @@ export class FakeDb {
|
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
constructor() {
|
|
33
|
-
this.lobbies =
|
|
33
|
+
this.lobbies = new Map()
|
|
34
34
|
this.logins = []
|
|
35
|
-
this.repos =
|
|
35
|
+
this.repos = new Map()
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
getLoginById(loginId) {
|
|
@@ -80,7 +80,7 @@ export class FakeDb {
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
setupRepo(syncKey, repo) {
|
|
83
|
-
this.repos
|
|
83
|
+
this.repos.set(syncKey, repo)
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
dumpLogin(login) {
|
|
@@ -279,7 +279,7 @@ function createLogin(request, login) {
|
|
|
279
279
|
keyBoxes: []
|
|
280
280
|
}))(body.data)
|
|
281
281
|
for (const syncKey of keys.newSyncKeys) {
|
|
282
|
-
db.repos
|
|
282
|
+
db.repos.set(syncKey, {})
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
// Start building the new database row:
|
|
@@ -326,7 +326,7 @@ const addKeysRoute = withLogin2(request => {
|
|
|
326
326
|
|
|
327
327
|
// Set up repos:
|
|
328
328
|
for (const syncKey of clean.newSyncKeys) {
|
|
329
|
-
db.repos
|
|
329
|
+
db.repos.set(syncKey, {})
|
|
330
330
|
}
|
|
331
331
|
login.keyBoxes = softCat(login.keyBoxes, clean.keyBoxes)
|
|
332
332
|
|
|
@@ -596,7 +596,7 @@ const withLobby =
|
|
|
596
596
|
request => {
|
|
597
597
|
const { db, path } = request
|
|
598
598
|
const lobbyId = path.split('/')[4]
|
|
599
|
-
const lobby = db.lobbies
|
|
599
|
+
const lobby = db.lobbies.get(lobbyId)
|
|
600
600
|
return lobby != null
|
|
601
601
|
? server({ ...request, lobby, lobbyId })
|
|
602
602
|
: fallback({ ...request, lobbyId })
|
|
@@ -619,7 +619,7 @@ const createLobbyRoute = withLobby(
|
|
|
619
619
|
const { timeout = 600 } = clean
|
|
620
620
|
const expires = new Date(Date.now() + 1000 * timeout).toISOString()
|
|
621
621
|
|
|
622
|
-
db.lobbies
|
|
622
|
+
db.lobbies.set(lobbyId, { request: clean, replies: [], expires })
|
|
623
623
|
return statusResponse()
|
|
624
624
|
}
|
|
625
625
|
)
|
|
@@ -643,7 +643,7 @@ const getLobbyRoute = withLobby(request => {
|
|
|
643
643
|
|
|
644
644
|
const deleteLobbyRoute = withLobby(request => {
|
|
645
645
|
const { db, lobbyId } = request
|
|
646
|
-
|
|
646
|
+
db.lobbies.delete(lobbyId)
|
|
647
647
|
return statusResponse()
|
|
648
648
|
})
|
|
649
649
|
|
|
@@ -682,7 +682,7 @@ const withRepo =
|
|
|
682
682
|
const syncKey = elements[4]
|
|
683
683
|
// const hash = elements[5]
|
|
684
684
|
|
|
685
|
-
const repo = db.repos
|
|
685
|
+
const repo = db.repos.get(syncKey)
|
|
686
686
|
if (repo == null) {
|
|
687
687
|
// This is not the auth server, so we have a different format:
|
|
688
688
|
return jsonResponse({ msg: 'Hash not found' }, { status: 404 })
|
|
@@ -158,7 +158,9 @@ export function makeFakeWorld(
|
|
|
158
158
|
|
|
159
159
|
// Find the data on the server:
|
|
160
160
|
const login = fakeDb.getLoginById(loginId)
|
|
161
|
-
if (login == null)
|
|
161
|
+
if (login == null) {
|
|
162
|
+
throw new Error(`Cannot find user ${account.rootLoginId}`)
|
|
163
|
+
}
|
|
162
164
|
|
|
163
165
|
// Figure out which repos to use:
|
|
164
166
|
const syncKeys = []
|
|
@@ -171,7 +173,8 @@ export function makeFakeWorld(
|
|
|
171
173
|
}
|
|
172
174
|
const repos = {}
|
|
173
175
|
for (const syncKey of syncKeys) {
|
|
174
|
-
|
|
176
|
+
const repo = fakeDb.repos.get(syncKey)
|
|
177
|
+
if (repo != null) repos[syncKey] = wasEdgeRepoDump(repo)
|
|
175
178
|
}
|
|
176
179
|
|
|
177
180
|
return {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { navigateDisklet } from 'disklet'
|
|
1
|
+
function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }import { navigateDisklet } from 'disklet'
|
|
2
|
+
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
|
|
@@ -35,7 +36,7 @@ export function addEdgeCorePlugins(plugins) {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
// Save the new plugins:
|
|
38
|
-
for (const pluginId
|
|
39
|
+
for (const pluginId of Object.keys(plugins)) {
|
|
39
40
|
allPlugins[pluginId] = plugins[pluginId]
|
|
40
41
|
}
|
|
41
42
|
|
|
@@ -56,6 +57,7 @@ export function lockEdgeCorePlugins() {
|
|
|
56
57
|
*/
|
|
57
58
|
export function watchPlugins(
|
|
58
59
|
ios,
|
|
60
|
+
infoCache,
|
|
59
61
|
logBackend,
|
|
60
62
|
pluginsInit,
|
|
61
63
|
dispatch
|
|
@@ -66,7 +68,7 @@ export function watchPlugins(
|
|
|
66
68
|
function pluginsAdded(plugins) {
|
|
67
69
|
const out = {}
|
|
68
70
|
|
|
69
|
-
for (const pluginId
|
|
71
|
+
for (const pluginId of Object.keys(plugins)) {
|
|
70
72
|
const plugin = plugins[pluginId]
|
|
71
73
|
const log = makeLog(logBackend, pluginId)
|
|
72
74
|
const initOptions = pluginsInit[pluginId]
|
|
@@ -76,6 +78,7 @@ export function watchPlugins(
|
|
|
76
78
|
try {
|
|
77
79
|
if (typeof plugin === 'function') {
|
|
78
80
|
const opts = {
|
|
81
|
+
infoPayload: _nullishCoalesce(_optionalChain([infoCache, 'access', _ => _.corePlugins, 'optionalAccess', _2 => _2[pluginId]]), () => ( {})),
|
|
79
82
|
initOptions: typeof initOptions === 'object' ? initOptions : {},
|
|
80
83
|
io: legacyIo,
|
|
81
84
|
log,
|
|
@@ -70,7 +70,7 @@ export async function waitForPlugins(ai) {
|
|
|
70
70
|
|
|
71
71
|
const { currency, swap } = props.state.plugins
|
|
72
72
|
const missingPlugins = []
|
|
73
|
-
for (const pluginId
|
|
73
|
+
for (const pluginId of Object.keys(init)) {
|
|
74
74
|
const shouldLoad = init[pluginId] !== false && init[pluginId] != null
|
|
75
75
|
if (shouldLoad && currency[pluginId] == null && swap[pluginId] == null) {
|
|
76
76
|
missingPlugins.push(pluginId)
|
package/lib/core/root-reducer.js
CHANGED
|
@@ -3,7 +3,7 @@ import { buildReducer, mapReducer } from 'redux-keto'
|
|
|
3
3
|
|
|
4
4
|
import { accountReducer, } from './account/account-reducer'
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
import { currency, } from './currency/currency-reducer'
|
|
8
8
|
import { login, } from './login/login-reducer'
|
|
9
9
|
import { plugins, } from './plugins/plugins-reducer'
|
|
@@ -26,6 +26,8 @@ import { storageWallets, } from './storage/storage-reducer'
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
|
|
30
|
+
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
export const defaultLogSettings = {
|
|
@@ -64,6 +66,20 @@ export const reducer = buildReducer({
|
|
|
64
66
|
return action.type === 'INIT' ? action.payload.hideKeys : state
|
|
65
67
|
},
|
|
66
68
|
|
|
69
|
+
infoCache(state = {}, action) {
|
|
70
|
+
switch (action.type) {
|
|
71
|
+
case 'INIT':
|
|
72
|
+
return action.payload.infoCache
|
|
73
|
+
case 'INFO_CACHE_FETCHED':
|
|
74
|
+
return action.payload
|
|
75
|
+
}
|
|
76
|
+
return state
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
infoServers(state = [], action) {
|
|
80
|
+
return action.type === 'INIT' ? action.payload.infoServers : state
|
|
81
|
+
},
|
|
82
|
+
|
|
67
83
|
lastAccountId(state, action, next) {
|
|
68
84
|
return `login${next.accountCount}`
|
|
69
85
|
},
|
|
@@ -90,7 +106,10 @@ export const reducer = buildReducer({
|
|
|
90
106
|
return action.type === 'INIT' ? action.payload.skipBlockHeight : state
|
|
91
107
|
},
|
|
92
108
|
|
|
93
|
-
|
|
109
|
+
syncServers(state = [], action) {
|
|
110
|
+
return action.type === 'INIT' ? action.payload.syncServers : state
|
|
111
|
+
},
|
|
112
|
+
|
|
94
113
|
currency,
|
|
95
114
|
login,
|
|
96
115
|
plugins,
|
package/lib/core/root.js
CHANGED
|
@@ -7,6 +7,7 @@ import { emit } from 'yaob'
|
|
|
7
7
|
import { validateServer } from '../util/validateServer'
|
|
8
8
|
|
|
9
9
|
import { CLIENT_FILE_NAME, clientFile } from './context/client-file'
|
|
10
|
+
import { INFO_CACHE_FILE_NAME, infoCacheFile } from './context/info-cache-file'
|
|
10
11
|
import { filterLogs, makeLog } from './log/log'
|
|
11
12
|
import { loadStashes } from './login/login-stash'
|
|
12
13
|
import { watchPlugins } from './plugins/plugins-actions'
|
|
@@ -35,23 +36,33 @@ export async function makeContext(
|
|
|
35
36
|
apiKey,
|
|
36
37
|
appId = '',
|
|
37
38
|
authServer = 'https://login.edge.app/api',
|
|
38
|
-
infoServer
|
|
39
|
-
syncServer
|
|
40
|
-
'https://sync-us1.edge.app',
|
|
41
|
-
'https://sync-us2.edge.app',
|
|
42
|
-
'https://sync-us3.edge.app',
|
|
43
|
-
'https://sync-us4.edge.app',
|
|
44
|
-
'https://sync-us5.edge.app',
|
|
45
|
-
'https://sync-us6.edge.app',
|
|
46
|
-
'https://sync-eu.edge.app'
|
|
47
|
-
],
|
|
39
|
+
infoServer,
|
|
40
|
+
syncServer,
|
|
48
41
|
deviceDescription = null,
|
|
49
42
|
hideKeys = false,
|
|
50
43
|
plugins: pluginsInit = {},
|
|
51
44
|
skipBlockHeight = false
|
|
52
45
|
} = opts
|
|
53
|
-
const infoServers =
|
|
54
|
-
|
|
46
|
+
const infoServers =
|
|
47
|
+
typeof infoServer === 'string'
|
|
48
|
+
? [infoServer]
|
|
49
|
+
: infoServer != null && infoServer.length > 0
|
|
50
|
+
? infoServer
|
|
51
|
+
: ['https://info-eu1.edge.app', 'https://info-us1.edge.app']
|
|
52
|
+
const syncServers =
|
|
53
|
+
typeof syncServer === 'string'
|
|
54
|
+
? [syncServer]
|
|
55
|
+
: syncServer != null && syncServer.length > 0
|
|
56
|
+
? syncServer
|
|
57
|
+
: [
|
|
58
|
+
'https://sync-us1.edge.app',
|
|
59
|
+
'https://sync-us2.edge.app',
|
|
60
|
+
'https://sync-us3.edge.app',
|
|
61
|
+
'https://sync-us4.edge.app',
|
|
62
|
+
'https://sync-us5.edge.app',
|
|
63
|
+
'https://sync-us6.edge.app',
|
|
64
|
+
'https://sync-eu.edge.app'
|
|
65
|
+
]
|
|
55
66
|
const logSettings = { ...defaultLogSettings, ...opts.logSettings }
|
|
56
67
|
if (apiKey == null) {
|
|
57
68
|
throw new Error('No API key provided')
|
|
@@ -71,21 +82,26 @@ export async function makeContext(
|
|
|
71
82
|
})
|
|
72
83
|
const log = makeLog(logBackend, 'edge-core')
|
|
73
84
|
|
|
74
|
-
|
|
75
|
-
|
|
85
|
+
let [clientInfo, infoCache = {}, stashes] = await Promise.all([
|
|
86
|
+
clientFile.load(io.disklet, CLIENT_FILE_NAME),
|
|
87
|
+
infoCacheFile.load(io.disklet, INFO_CACHE_FILE_NAME),
|
|
88
|
+
loadStashes(io.disklet, log)
|
|
89
|
+
])
|
|
90
|
+
|
|
91
|
+
// Save the clientId if we don't have one:
|
|
76
92
|
if (clientInfo == null) {
|
|
77
93
|
clientInfo = { clientId: io.random(16) }
|
|
78
94
|
await clientFile.save(io.disklet, CLIENT_FILE_NAME, clientInfo)
|
|
79
95
|
}
|
|
80
96
|
|
|
81
97
|
// Load the login stashes from disk:
|
|
82
|
-
const stashes = await loadStashes(io.disklet, log)
|
|
83
98
|
redux.dispatch({
|
|
84
99
|
type: 'INIT',
|
|
85
100
|
payload: {
|
|
86
101
|
apiKey,
|
|
87
102
|
appId,
|
|
88
103
|
authServer,
|
|
104
|
+
infoCache,
|
|
89
105
|
infoServers,
|
|
90
106
|
syncServers,
|
|
91
107
|
clientId: clientInfo.clientId,
|
|
@@ -101,13 +117,14 @@ export async function makeContext(
|
|
|
101
117
|
// Subscribe to new plugins:
|
|
102
118
|
const closePlugins = watchPlugins(
|
|
103
119
|
ios,
|
|
120
|
+
infoCache,
|
|
104
121
|
logBackend,
|
|
105
122
|
pluginsInit,
|
|
106
123
|
redux.dispatch
|
|
107
124
|
)
|
|
108
125
|
|
|
109
126
|
// Create sync client:
|
|
110
|
-
const syncClient =
|
|
127
|
+
const syncClient = makeSyncClient({
|
|
111
128
|
log,
|
|
112
129
|
fetch: io.fetch,
|
|
113
130
|
edgeServers: { infoServers, syncServers }
|
|
@@ -43,9 +43,9 @@ export async function addStorageWallet(
|
|
|
43
43
|
const { syncKey } = walletInfo.keys
|
|
44
44
|
const { lastHash } = status
|
|
45
45
|
ai.props.log.error(
|
|
46
|
-
`Could not sync ${syncKey} with last hash ${String(
|
|
47
|
-
|
|
48
|
-
)}`
|
|
46
|
+
`Could not sync ${String(syncKey)} with last hash ${String(
|
|
47
|
+
lastHash
|
|
48
|
+
)}: ${String(error)}`
|
|
49
49
|
)
|
|
50
50
|
onError(error)
|
|
51
51
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }import { gt, lt } from 'biggystring'
|
|
1
|
+
function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }import { gt, lt } from 'biggystring'
|
|
2
2
|
import { bridgifyObject, close } from 'yaob'
|
|
3
3
|
|
|
4
4
|
import {
|
|
@@ -33,11 +33,11 @@ export async function fetchSwapQuotes(
|
|
|
33
33
|
promoCodes = {},
|
|
34
34
|
slowResponseMs = 20000
|
|
35
35
|
} = opts
|
|
36
|
-
const { log } = ai.props
|
|
36
|
+
const { log, state } = ai.props
|
|
37
37
|
|
|
38
|
-
const account =
|
|
38
|
+
const account = state.accounts[accountId]
|
|
39
39
|
const { swapSettings, userSettings } = account
|
|
40
|
-
const swapPlugins =
|
|
40
|
+
const swapPlugins = state.plugins.swap
|
|
41
41
|
|
|
42
42
|
log.warn(
|
|
43
43
|
'Requesting swap quotes for: ',
|
|
@@ -62,6 +62,7 @@ export async function fetchSwapQuotes(
|
|
|
62
62
|
promises.push(
|
|
63
63
|
swapPlugins[pluginId]
|
|
64
64
|
.fetchSwapQuote(request, userSettings[pluginId], {
|
|
65
|
+
infoPayload: _nullishCoalesce(_optionalChain([state, 'access', _ => _.infoCache, 'access', _2 => _2.corePlugins, 'optionalAccess', _3 => _3[pluginId]]), () => ( {})),
|
|
65
66
|
promoCode: promoCodes[pluginId]
|
|
66
67
|
})
|
|
67
68
|
.then(
|
package/lib/flow/types.js
CHANGED
|
@@ -165,9 +165,12 @@ export type EdgeNativeIo = {
|
|
|
165
165
|
* All core plugins receive these options at creation time.
|
|
166
166
|
*/
|
|
167
167
|
export type EdgeCorePluginOptions = {
|
|
168
|
-
|
|
168
|
+
/** Load-time options (like API keys) passed into the context */
|
|
169
169
|
initOptions: JsonObject;
|
|
170
170
|
|
|
171
|
+
/** Data provided by the info server */
|
|
172
|
+
infoPayload: JsonObject;
|
|
173
|
+
|
|
171
174
|
// Access to the world outside the plugin:
|
|
172
175
|
io: EdgeIo;
|
|
173
176
|
log: EdgeLog; // Plugin-scoped logging
|
|
@@ -1040,6 +1043,7 @@ export type EdgeCurrencyPlugin = {
|
|
|
1040
1043
|
walletInfo: EdgeWalletInfo,
|
|
1041
1044
|
opts: EdgeCurrencyEngineOptions,
|
|
1042
1045
|
) => Promise<EdgeCurrencyEngine>;
|
|
1046
|
+
+updateInfoPayload?: (infoPayload: JsonObject) => Promise<void>;
|
|
1043
1047
|
|
|
1044
1048
|
// Escape hatch:
|
|
1045
1049
|
+otherMethods?: EdgeOtherMethods;
|
|
@@ -1317,7 +1321,7 @@ export type EdgeSwapPlugin = {
|
|
|
1317
1321
|
+fetchSwapQuote: (
|
|
1318
1322
|
request: EdgeSwapRequest,
|
|
1319
1323
|
userSettings: JsonObject | void,
|
|
1320
|
-
opts: { promoCode?: string },
|
|
1324
|
+
opts: { infoPayload: JsonObject; promoCode?: string },
|
|
1321
1325
|
) => Promise<EdgeSwapQuote>;
|
|
1322
1326
|
}
|
|
1323
1327
|
|