edge-core-js
Version:
Edge account & wallet management library
214 lines (190 loc) • 5.43 kB
JavaScript
const _jsxFileName = "src/react-native.tsx";import { asObject, asString } from 'cleaners'
import { makeReactNativeDisklet } from 'disklet'
import * as React from 'react'
import { base64 } from 'rfc4648'
import { bridgifyObject } from 'yaob'
import { defaultOnLog, } from './core/log/log'
import { parseReply } from './core/login/login-fetch'
import { EdgeCoreBridge } from './io/react-native/react-native-webview'
import { asMessagesPayload } from './types/server-cleaners'
import {
NetworkError
} from './types/types'
import { hmacSha256 } from './util/crypto/hashes'
import { utf8 } from './util/encoding'
import { timeout } from './util/promise'
export { makeFakeIo } from './core/fake/fake-io'
export * from './types/types'
function defaultOnError(error) {
console.error(error)
}
export function MakeEdgeContext(props) {
const {
allowDebugging,
crashReporter,
debug,
nativeIo,
pluginUris = [],
onError = defaultOnError,
onLoad,
onLog = defaultOnLog,
// Inner context options:
airbitzSupport = false,
apiKey,
apiSecret,
appId = '',
authServer,
changeServer,
deviceDescription,
hideKeys,
infoServer,
loginServer,
logSettings,
plugins,
skipBlockHeight,
syncServer
} = props
if (onLoad == null) {
throw new TypeError('No onLoad passed to MakeEdgeContext')
}
return (
React.createElement(EdgeCoreBridge, {
allowDebugging: allowDebugging,
debug: debug,
onError: onError,
onLoad: async root => {
const context = await root.makeEdgeContext(
bridgifyNativeIo(nativeIo),
bridgifyLogBackend({ crashReporter, onLog }),
pluginUris,
{
airbitzSupport,
apiKey,
apiSecret,
appId,
authServer,
changeServer,
deviceDescription,
hideKeys,
infoServer,
loginServer,
logSettings,
plugins,
skipBlockHeight,
syncServer
}
)
await onLoad(context)
}, __self: this, __source: {fileName: _jsxFileName, lineNumber: 61}}
)
)
}
export function MakeFakeEdgeWorld(props) {
const {
allowDebugging,
crashReporter,
debug,
nativeIo,
pluginUris = [],
onError = defaultOnError,
onLoad,
onLog = defaultOnLog
} = props
if (onLoad == null) {
throw new TypeError('No onLoad passed to MakeFakeEdgeWorld')
}
return (
React.createElement(EdgeCoreBridge, {
allowDebugging: allowDebugging,
debug: debug,
onError: onError,
onLoad: async root => {
const fakeWorld = await root.makeFakeEdgeWorld(
bridgifyNativeIo(nativeIo),
bridgifyLogBackend({ crashReporter, onLog }),
pluginUris,
props.users
)
await onLoad(fakeWorld)
}, __self: this, __source: {fileName: _jsxFileName, lineNumber: 110}}
)
)
}
function bridgifyNativeIo(nativeIo = {}) {
const out = {}
for (const key of Object.keys(nativeIo)) {
out[key] = bridgifyObject(nativeIo[key])
}
return out
}
function bridgifyLogBackend(backend) {
if (backend.crashReporter != null) bridgifyObject(backend.crashReporter)
return bridgifyObject(backend)
}
/** Just the parts of LoginStash that `fetchLoginMessages` needs. */
const asUsernameStash = asObject({
loginId: asString,
username: asString
})
/**
* Fetches any login-related messages for all the users on this device.
*/
export async function fetchLoginMessages(
apiKey,
apiSecret
) {
const disklet = makeReactNativeDisklet()
// Load the login stashes from disk:
const loginMap = {} // loginId -> username
const listing = await disklet.list('logins')
const files = await Promise.all(
Object.keys(listing)
.filter(path => listing[path] === 'file')
.map(path => disklet.getText(path).catch(() => '{}'))
)
for (const text of files) {
try {
const { username, loginId } = asUsernameStash(JSON.parse(text))
loginMap[loginId] = username
} catch (error) {}
}
const bodyText = JSON.stringify({ loginIds: Object.keys(loginMap) })
// API key:
let authorization = `Token ${apiKey}`
if (apiSecret != null) {
const requestText = `POST\n/api/v2/messages\n${bodyText}`
const hash = hmacSha256(utf8.parse(requestText), apiSecret)
authorization = `HMAC ${apiKey} ${base64.stringify(hash)}`
}
const uri = 'https://login.edge.app/api/v2/messages'
const opts = {
method: 'POST',
headers: {
'content-type': 'application/json',
accept: 'application/json',
authorization
},
body: JSON.stringify({ loginIds: Object.keys(loginMap) })
}
return await timeout(
window.fetch(uri, opts),
30000,
new NetworkError('Could not reach the auth server: timeout')
).then(response => {
if (!response.ok) {
throw new Error(`${uri} return status code ${response.status}`)
}
return response.json().then(json => {
const clean = asMessagesPayload(parseReply(json))
const out = []
for (const message of clean) {
const { loginId, ...rest } = message
const id = base64.stringify(loginId)
const username = loginMap[id]
if (username == null) continue
out.push({ ...rest, loginId: id, username })
}
return out
})
})
}