Skip to content

Commit da26b51

Browse files
committed
Add EdgeMemoryWallet
Intended to be used briefly and without any local state saved. It returns once the underlying engine is fully synced.
1 parent b15b94a commit da26b51

File tree

4 files changed

+232
-0
lines changed

4 files changed

+232
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
- added: Add `makeMemoryWallet` method to ephemeral wallet objects that can query balances and spend funds
6+
57
## 2.8.1 (2024-07-11)
68

79
- fixed: Filter transactions with empty (zero) nativeAmount and networkFee

src/core/account/account-api.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
EdgeGetActivationAssetsOptions,
1818
EdgeGetActivationAssetsResults,
1919
EdgeLobby,
20+
EdgeMemoryWallet,
2021
EdgePendingVoucher,
2122
EdgePluginMap,
2223
EdgeResult,
@@ -59,6 +60,7 @@ import { changeWalletStates } from './account-files'
5960
import { AccountState } from './account-reducer'
6061
import { makeDataStoreApi } from './data-store-api'
6162
import { makeLobbyApi } from './lobby-api'
63+
import { makeMemoryWalletInner } from './memory-wallet'
6264
import { CurrencyConfig, SwapConfig } from './plugin-api'
6365

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

503+
async makeMemoryWallet(
504+
walletType: string,
505+
opts: EdgeCreateCurrencyWalletOptions = {}
506+
): Promise<EdgeMemoryWallet> {
507+
const config = Object.values(currencyConfigs).find(
508+
plugin => plugin.currencyInfo.walletType === walletType
509+
)
510+
if (config == null) throw new Error('Invalid walletType')
511+
512+
return await makeMemoryWalletInner(ai, config, walletType, opts)
513+
},
514+
501515
async createCurrencyWallets(
502516
createWallets: EdgeCreateCurrencyWallet[]
503517
): Promise<Array<EdgeResult<EdgeCurrencyWallet>>> {

src/core/account/memory-wallet.ts

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
import { makeMemoryDisklet } from 'disklet'
2+
import { bridgifyObject, close, update, watchMethod } from 'yaob'
3+
4+
import {
5+
EdgeBalanceMap,
6+
EdgeCreateCurrencyWalletOptions,
7+
EdgeCurrencyConfig,
8+
EdgeMemoryWallet,
9+
EdgeSpendInfo,
10+
EdgeTokenId,
11+
EdgeTransaction,
12+
EdgeWalletInfo
13+
} from '../../browser'
14+
import { makePeriodicTask, PeriodicTask } from '../../util/periodic-task'
15+
import { snooze } from '../../util/snooze'
16+
import { getMaxSpendableInner } from '../currency/wallet/max-spend'
17+
import { makeLog } from '../log/log'
18+
import { getCurrencyTools } from '../plugins/plugins-selectors'
19+
import { ApiInput } from '../root-pixie'
20+
21+
let memoryWalletCount = 0
22+
23+
export const makeMemoryWalletInner = async (
24+
ai: ApiInput,
25+
config: EdgeCurrencyConfig,
26+
walletType: string,
27+
opts: EdgeCreateCurrencyWalletOptions = {}
28+
): Promise<EdgeMemoryWallet> => {
29+
const { keys } = opts
30+
if (keys == null) throw new Error('No keys provided')
31+
32+
const walletId = `memorywallet-${memoryWalletCount++}`
33+
const walletInfo: EdgeWalletInfo = {
34+
id: walletId,
35+
type: walletType,
36+
keys
37+
}
38+
39+
const tools = await getCurrencyTools(ai, config.currencyInfo.pluginId)
40+
const publicKeys = await tools.derivePublicKey(walletInfo)
41+
walletInfo.keys = { ...publicKeys, ...walletInfo.keys }
42+
43+
const log = makeLog(ai.props.logBackend, `${walletId}-${walletType}`)
44+
let balanceMap: EdgeBalanceMap = new Map()
45+
let detectedTokenIds: string[] = []
46+
let syncRatio: number = 0
47+
48+
let needsUpdate = false
49+
const updateWallet = (): void => {
50+
if (needsUpdate) {
51+
update(out)
52+
needsUpdate = false
53+
}
54+
}
55+
const updater = makePeriodicTask(async () => {
56+
await snooze(1000) // one second
57+
updateWallet()
58+
}, 0)
59+
60+
const plugin = ai.props.state.plugins.currency[config.currencyInfo.pluginId]
61+
const engine = await plugin.makeCurrencyEngine(walletInfo, {
62+
callbacks: {
63+
onAddressChanged: () => {},
64+
onAddressesChecked: (progressRatio: number) => {
65+
if (out.syncRatio === 1) return
66+
67+
if (progressRatio === 1) {
68+
syncRatio = progressRatio
69+
needsUpdate = true
70+
}
71+
},
72+
onNewTokens: (tokenIds: string[]) => {
73+
const sortedTokenIds = tokenIds.sort((a, b) => a.localeCompare(b))
74+
75+
if (detectedTokenIds.length !== sortedTokenIds.length) {
76+
detectedTokenIds = sortedTokenIds
77+
needsUpdate = true
78+
return
79+
}
80+
for (let i = 0; i < sortedTokenIds.length; i++) {
81+
if (detectedTokenIds[i] !== sortedTokenIds[i]) {
82+
detectedTokenIds = sortedTokenIds
83+
needsUpdate = true
84+
return
85+
}
86+
}
87+
},
88+
onStakingStatusChanged: () => {},
89+
onTokenBalanceChanged: (tokenId: EdgeTokenId, balance: string) => {
90+
if (balanceMap.get(tokenId) === balance) return
91+
92+
balanceMap = new Map(balanceMap)
93+
balanceMap.set(tokenId, balance)
94+
needsUpdate = true
95+
},
96+
onTransactionsChanged: () => {},
97+
onTxidsChanged: () => {},
98+
onUnactivatedTokenIdsChanged: () => {},
99+
onWcNewContractCall: () => {},
100+
onBlockHeightChanged: () => {},
101+
onBalanceChanged: () => {}
102+
},
103+
customTokens: { ...config.customTokens },
104+
enabledTokenIds: [...Object.keys(config.allTokens)],
105+
log,
106+
userSettings: { ...(config.userSettings ?? {}) },
107+
walletLocalDisklet: makeMemoryDisklet(),
108+
walletLocalEncryptedDisklet: makeMemoryDisklet()
109+
})
110+
111+
const {
112+
unsafeBroadcastTx = false,
113+
unsafeMakeSpend = false,
114+
unsafeSyncNetwork = false
115+
} = plugin.currencyInfo
116+
117+
const privateKeys = { ...keys }
118+
119+
let syncNetworkTask: PeriodicTask
120+
// Setup syncNetwork routine if defined by the currency engine:
121+
if (engine.syncNetwork != null) {
122+
// Get the private keys if required by the engine:
123+
const doNetworkSync = async (): Promise<void> => {
124+
if (engine.syncNetwork != null) {
125+
const delay = await engine.syncNetwork({
126+
privateKeys: unsafeSyncNetwork ? { privateKeys: keys } : undefined
127+
})
128+
syncNetworkTask.setDelay(delay)
129+
} else {
130+
syncNetworkTask.stop()
131+
}
132+
}
133+
syncNetworkTask = makePeriodicTask(doNetworkSync, 10000, {
134+
onError: error => {
135+
ai.props.log.error(error)
136+
}
137+
})
138+
syncNetworkTask.start({ wait: false })
139+
}
140+
141+
const out = bridgifyObject<EdgeMemoryWallet>({
142+
watch: watchMethod,
143+
get balanceMap() {
144+
return balanceMap
145+
},
146+
get detectedTokenIds() {
147+
return detectedTokenIds
148+
},
149+
get syncRatio() {
150+
return syncRatio
151+
},
152+
async changeEnabledTokenIds(tokenIds: string[]) {
153+
if (engine.changeEnabledTokenIds != null) {
154+
await engine.changeEnabledTokenIds(tokenIds)
155+
}
156+
},
157+
async startEngine() {
158+
await engine.startEngine()
159+
syncNetworkTask?.start({ wait: false })
160+
},
161+
async getMaxSpendable(spendInfo: EdgeSpendInfo) {
162+
return await getMaxSpendableInner(
163+
spendInfo,
164+
plugin,
165+
engine,
166+
config.allTokens,
167+
walletInfo
168+
)
169+
},
170+
async makeSpend(spendInfo: EdgeSpendInfo) {
171+
return await engine.makeSpend(
172+
spendInfo,
173+
unsafeMakeSpend ? privateKeys : undefined
174+
)
175+
},
176+
async signTx(tx: EdgeTransaction) {
177+
return await engine.signTx(tx, privateKeys)
178+
},
179+
async broadcastTx(tx: EdgeTransaction) {
180+
return await engine.broadcastTx(
181+
tx,
182+
unsafeBroadcastTx ? privateKeys : undefined
183+
)
184+
},
185+
async saveTx() {},
186+
187+
async close() {
188+
log.warn('killing memory wallet')
189+
syncNetworkTask?.stop()
190+
close(out)
191+
await engine.killEngine()
192+
}
193+
})
194+
195+
updater.start({ wait: false })
196+
return out
197+
}

src/types/types.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1239,6 +1239,21 @@ export interface EdgeCurrencyWallet {
12391239
readonly otherMethods: EdgeOtherMethods
12401240
}
12411241

1242+
export interface EdgeMemoryWallet {
1243+
readonly watch: Subscriber<EdgeMemoryWallet>
1244+
readonly balanceMap: EdgeBalanceMap
1245+
readonly detectedTokenIds: string[]
1246+
readonly syncRatio: number
1247+
readonly changeEnabledTokenIds: (tokenIds: string[]) => Promise<void>
1248+
readonly startEngine: () => Promise<void>
1249+
readonly getMaxSpendable: (spendInfo: EdgeSpendInfo) => Promise<string>
1250+
readonly makeSpend: (spendInfo: EdgeSpendInfo) => Promise<EdgeTransaction>
1251+
readonly signTx: (tx: EdgeTransaction) => Promise<EdgeTransaction>
1252+
readonly broadcastTx: (tx: EdgeTransaction) => Promise<EdgeTransaction>
1253+
readonly saveTx: (tx: EdgeTransaction) => Promise<void>
1254+
readonly close: () => Promise<void>
1255+
}
1256+
12421257
// ---------------------------------------------------------------------
12431258
// swap plugin
12441259
// ---------------------------------------------------------------------
@@ -1636,6 +1651,10 @@ export interface EdgeAccount {
16361651
walletId: string
16371652
) => Promise<EdgeCurrencyWallet>
16381653
readonly waitForAllWallets: () => Promise<void>
1654+
readonly makeMemoryWallet: (
1655+
walletType: string,
1656+
opts?: EdgeCreateCurrencyWalletOptions
1657+
) => Promise<EdgeMemoryWallet>
16391658

16401659
// Token & wallet activation:
16411660
readonly getActivationAssets: (

0 commit comments

Comments
 (0)