Skip to content

Commit

Permalink
Add EdgeMemoryWallet
Browse files Browse the repository at this point in the history
Intended to be used briefly and without any local state saved. It returns once the underlying engine is fully synced.
  • Loading branch information
peachbits committed Jul 22, 2024
1 parent b15b94a commit da26b51
Show file tree
Hide file tree
Showing 4 changed files with 232 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- added: Add `makeMemoryWallet` method to ephemeral wallet objects that can query balances and spend funds

## 2.8.1 (2024-07-11)

- fixed: Filter transactions with empty (zero) nativeAmount and networkFee
Expand Down
14 changes: 14 additions & 0 deletions src/core/account/account-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
EdgeGetActivationAssetsOptions,
EdgeGetActivationAssetsResults,
EdgeLobby,
EdgeMemoryWallet,
EdgePendingVoucher,
EdgePluginMap,
EdgeResult,
Expand Down Expand Up @@ -59,6 +60,7 @@ import { changeWalletStates } from './account-files'
import { AccountState } from './account-reducer'
import { makeDataStoreApi } from './data-store-api'
import { makeLobbyApi } from './lobby-api'
import { makeMemoryWalletInner } from './memory-wallet'
import { CurrencyConfig, SwapConfig } from './plugin-api'

/**
Expand Down Expand Up @@ -498,6 +500,18 @@ export function makeAccountApi(ai: ApiInput, accountId: string): EdgeAccount {
return await finishWalletCreation(ai, accountId, walletInfo.id, opts)
},

async makeMemoryWallet(
walletType: string,
opts: EdgeCreateCurrencyWalletOptions = {}
): Promise<EdgeMemoryWallet> {
const config = Object.values(currencyConfigs).find(
plugin => plugin.currencyInfo.walletType === walletType
)
if (config == null) throw new Error('Invalid walletType')

return await makeMemoryWalletInner(ai, config, walletType, opts)
},

async createCurrencyWallets(
createWallets: EdgeCreateCurrencyWallet[]
): Promise<Array<EdgeResult<EdgeCurrencyWallet>>> {
Expand Down
197 changes: 197 additions & 0 deletions src/core/account/memory-wallet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import { makeMemoryDisklet } from 'disklet'
import { bridgifyObject, close, update, watchMethod } from 'yaob'

import {
EdgeBalanceMap,
EdgeCreateCurrencyWalletOptions,
EdgeCurrencyConfig,
EdgeMemoryWallet,
EdgeSpendInfo,
EdgeTokenId,
EdgeTransaction,
EdgeWalletInfo
} from '../../browser'
import { makePeriodicTask, PeriodicTask } from '../../util/periodic-task'
import { snooze } from '../../util/snooze'
import { getMaxSpendableInner } from '../currency/wallet/max-spend'
import { makeLog } from '../log/log'
import { getCurrencyTools } from '../plugins/plugins-selectors'
import { ApiInput } from '../root-pixie'

let memoryWalletCount = 0

export const makeMemoryWalletInner = async (
ai: ApiInput,
config: EdgeCurrencyConfig,
walletType: string,
opts: EdgeCreateCurrencyWalletOptions = {}
): Promise<EdgeMemoryWallet> => {
const { keys } = opts
if (keys == null) throw new Error('No keys provided')

const walletId = `memorywallet-${memoryWalletCount++}`
const walletInfo: EdgeWalletInfo = {
id: walletId,
type: walletType,
keys
}

const tools = await getCurrencyTools(ai, config.currencyInfo.pluginId)
const publicKeys = await tools.derivePublicKey(walletInfo)
walletInfo.keys = { ...publicKeys, ...walletInfo.keys }

const log = makeLog(ai.props.logBackend, `${walletId}-${walletType}`)
let balanceMap: EdgeBalanceMap = new Map()
let detectedTokenIds: string[] = []
let syncRatio: number = 0

let needsUpdate = false
const updateWallet = (): void => {
if (needsUpdate) {
update(out)
needsUpdate = false
}
}
const updater = makePeriodicTask(async () => {
await snooze(1000) // one second
updateWallet()
}, 0)

const plugin = ai.props.state.plugins.currency[config.currencyInfo.pluginId]
const engine = await plugin.makeCurrencyEngine(walletInfo, {
callbacks: {
onAddressChanged: () => {},
onAddressesChecked: (progressRatio: number) => {
if (out.syncRatio === 1) return

if (progressRatio === 1) {
syncRatio = progressRatio
needsUpdate = true
}
},
onNewTokens: (tokenIds: string[]) => {
const sortedTokenIds = tokenIds.sort((a, b) => a.localeCompare(b))

if (detectedTokenIds.length !== sortedTokenIds.length) {
detectedTokenIds = sortedTokenIds
needsUpdate = true
return
}
for (let i = 0; i < sortedTokenIds.length; i++) {
if (detectedTokenIds[i] !== sortedTokenIds[i]) {
detectedTokenIds = sortedTokenIds
needsUpdate = true
return
}
}
},
onStakingStatusChanged: () => {},
onTokenBalanceChanged: (tokenId: EdgeTokenId, balance: string) => {
if (balanceMap.get(tokenId) === balance) return

balanceMap = new Map(balanceMap)
balanceMap.set(tokenId, balance)
needsUpdate = true
},
onTransactionsChanged: () => {},
onTxidsChanged: () => {},
onUnactivatedTokenIdsChanged: () => {},
onWcNewContractCall: () => {},
onBlockHeightChanged: () => {},
onBalanceChanged: () => {}
},
customTokens: { ...config.customTokens },
enabledTokenIds: [...Object.keys(config.allTokens)],
log,
userSettings: { ...(config.userSettings ?? {}) },
walletLocalDisklet: makeMemoryDisklet(),
walletLocalEncryptedDisklet: makeMemoryDisklet()
})

const {
unsafeBroadcastTx = false,
unsafeMakeSpend = false,
unsafeSyncNetwork = false
} = plugin.currencyInfo

const privateKeys = { ...keys }

let syncNetworkTask: PeriodicTask
// Setup syncNetwork routine if defined by the currency engine:
if (engine.syncNetwork != null) {
// Get the private keys if required by the engine:
const doNetworkSync = async (): Promise<void> => {
if (engine.syncNetwork != null) {
const delay = await engine.syncNetwork({
privateKeys: unsafeSyncNetwork ? { privateKeys: keys } : undefined
})
syncNetworkTask.setDelay(delay)
} else {
syncNetworkTask.stop()
}
}
syncNetworkTask = makePeriodicTask(doNetworkSync, 10000, {
onError: error => {
ai.props.log.error(error)
}
})
syncNetworkTask.start({ wait: false })
}

const out = bridgifyObject<EdgeMemoryWallet>({
watch: watchMethod,
get balanceMap() {
return balanceMap
},
get detectedTokenIds() {
return detectedTokenIds
},
get syncRatio() {
return syncRatio
},
async changeEnabledTokenIds(tokenIds: string[]) {
if (engine.changeEnabledTokenIds != null) {
await engine.changeEnabledTokenIds(tokenIds)
}
},
async startEngine() {
await engine.startEngine()
syncNetworkTask?.start({ wait: false })
},
async getMaxSpendable(spendInfo: EdgeSpendInfo) {
return await getMaxSpendableInner(
spendInfo,
plugin,
engine,
config.allTokens,
walletInfo
)
},
async makeSpend(spendInfo: EdgeSpendInfo) {
return await engine.makeSpend(
spendInfo,
unsafeMakeSpend ? privateKeys : undefined
)
},
async signTx(tx: EdgeTransaction) {
return await engine.signTx(tx, privateKeys)
},
async broadcastTx(tx: EdgeTransaction) {
return await engine.broadcastTx(
tx,
unsafeBroadcastTx ? privateKeys : undefined
)
},
async saveTx() {},

async close() {
log.warn('killing memory wallet')
syncNetworkTask?.stop()
close(out)
await engine.killEngine()
}
})

updater.start({ wait: false })
return out
}
19 changes: 19 additions & 0 deletions src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1239,6 +1239,21 @@ export interface EdgeCurrencyWallet {
readonly otherMethods: EdgeOtherMethods
}

export interface EdgeMemoryWallet {
readonly watch: Subscriber<EdgeMemoryWallet>
readonly balanceMap: EdgeBalanceMap
readonly detectedTokenIds: string[]
readonly syncRatio: number
readonly changeEnabledTokenIds: (tokenIds: string[]) => Promise<void>
readonly startEngine: () => Promise<void>
readonly getMaxSpendable: (spendInfo: EdgeSpendInfo) => Promise<string>
readonly makeSpend: (spendInfo: EdgeSpendInfo) => Promise<EdgeTransaction>
readonly signTx: (tx: EdgeTransaction) => Promise<EdgeTransaction>
readonly broadcastTx: (tx: EdgeTransaction) => Promise<EdgeTransaction>
readonly saveTx: (tx: EdgeTransaction) => Promise<void>
readonly close: () => Promise<void>
}

// ---------------------------------------------------------------------
// swap plugin
// ---------------------------------------------------------------------
Expand Down Expand Up @@ -1636,6 +1651,10 @@ export interface EdgeAccount {
walletId: string
) => Promise<EdgeCurrencyWallet>
readonly waitForAllWallets: () => Promise<void>
readonly makeMemoryWallet: (
walletType: string,
opts?: EdgeCreateCurrencyWalletOptions
) => Promise<EdgeMemoryWallet>

// Token & wallet activation:
readonly getActivationAssets: (
Expand Down

0 comments on commit da26b51

Please sign in to comment.