Skip to content

Commit 1eef8ba

Browse files
authored
Merge pull request #5989 from EdgeApp/sam/migrate-fix-zano
fix: preserve imported token migrations
2 parents 051fb7f + 4bfced1 commit 1eef8ba

File tree

8 files changed

+373
-154
lines changed

8 files changed

+373
-154
lines changed

eslint.config.mjs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,6 @@ export default [
238238
'src/components/scenes/ConfirmScene.tsx',
239239
'src/components/scenes/CreateWalletAccountSelectScene.tsx',
240240
'src/components/scenes/CreateWalletAccountSetupScene.tsx',
241-
'src/components/scenes/CreateWalletCompletionScene.tsx',
242241

243242
'src/components/scenes/CurrencyNotificationScene.tsx',
244243
'src/components/scenes/DefaultFiatSettingScene.tsx',
@@ -288,8 +287,6 @@ export default [
288287

289288
'src/components/scenes/SwapSettingsScene.tsx',
290289
'src/components/scenes/SwapSuccessScene.tsx',
291-
'src/components/scenes/SweepPrivateKeyCalculateFeeScene.tsx',
292-
'src/components/scenes/SweepPrivateKeyCompletionScene.tsx',
293290

294291
'src/components/scenes/TransactionDetailsScene.tsx',
295292

src/components/scenes/CreateWalletCompletionScene.tsx

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -252,23 +252,44 @@ const CreateWalletCompletionComponent: React.FC<Props> = props => {
252252
})
253253

254254
const handleMigrate = useHandler(() => {
255-
// Transform filtered items into the structure expected by the migration component
256-
const migrateWalletList: MigrateWalletItem[] = newWalletItems.map(
257-
createWallet => {
258-
const { key, pluginId } = createWallet
259-
const wallet = wallets.find(
260-
wallet => wallet.currencyInfo.pluginId === pluginId
261-
)
255+
// Build migrate items from successfully created parent wallets, then attach
256+
// selected tokens for the same plugin to that same wallet id.
257+
const successfulNewWalletItems = newWalletItems.filter(
258+
item => itemStatus[item.key] === 'complete'
259+
)
260+
const addedTokenKeys = new Set<string>()
261+
const migrateWalletList: MigrateWalletItem[] = []
262+
263+
for (const createWallet of successfulNewWalletItems) {
264+
const { key, pluginId, walletType } = createWallet
265+
const wallet = wallets.find(
266+
wallet => wallet.currencyInfo.pluginId === pluginId
267+
)
268+
const createWalletId = wallet?.id ?? ''
269+
const displayName = walletNames[key]
270+
271+
migrateWalletList.push({
272+
...createWallet,
273+
createWalletId,
274+
displayName,
275+
key,
276+
type: 'create'
277+
})
278+
279+
for (const tokenItem of newTokenItems) {
280+
if (tokenItem.pluginId !== pluginId) continue
281+
if (addedTokenKeys.has(tokenItem.key)) continue
282+
addedTokenKeys.add(tokenItem.key)
262283

263-
return {
264-
...createWallet,
265-
createWalletId: wallet == null ? '' : wallet.id,
266-
displayName: walletNames[key],
267-
key,
284+
migrateWalletList.push({
285+
...tokenItem,
286+
createWalletId,
287+
displayName,
288+
walletType,
268289
type: 'create'
269-
}
290+
})
270291
}
271-
)
292+
}
272293

273294
// Navigate to the migration screen with the prepared list
274295
if (migrateWalletList.length > 0) {

src/components/scenes/MigrateWalletCalculateFeeScene.tsx

Lines changed: 115 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ export interface MigrateWalletCalculateFeeParams {
3939

4040
type Props = EdgeAppSceneProps<'migrateWalletCalculateFee'>
4141

42-
type AssetRowState = string | Error
42+
type AssetRowState = string | Error | 'included'
43+
type MakeMaxSpendMethod = (params: {
44+
tokenIds?: Array<string | null>
45+
spendTargets: Array<{ publicAddress: string }>
46+
}) => Promise<EdgeTransaction>
4347

4448
const MigrateWalletCalculateFeeComponent: React.FC<Props> = props => {
4549
const { navigation, route } = props
@@ -117,6 +121,14 @@ const MigrateWalletCalculateFeeComponent: React.FC<Props> = props => {
117121
/>
118122
)
119123
}
124+
} else if (fee === 'included') {
125+
rightSide = (
126+
<EdgeText
127+
style={{ color: theme.secondaryText, fontSize: theme.rem(0.75) }}
128+
>
129+
{lstrings.string_included}
130+
</EdgeText>
131+
)
120132
} else {
121133
const fakeEdgeTransaction: EdgeTransaction = {
122134
blockHeight: 0,
@@ -222,73 +234,123 @@ const MigrateWalletCalculateFeeComponent: React.FC<Props> = props => {
222234
}, [])
223235

224236
let successCount = 0
225-
const walletPromises = []
237+
const walletRoutines = []
226238
for (const bundle of bundledWalletAssets) {
227239
const wallet = currencyWallets[bundle[bundle.length - 1].createWalletId]
228240
const {
229241
currencyInfo: { pluginId }
230242
} = wallet
231-
232243
let feeTotal = '0'
233244
const bundlesFeeTotals = new Map<string, AssetRowState>(
234245
bundle.map(item => [item.key, '0'])
235246
)
236247

237-
const assetPromises = bundle.map((asset, i) => {
238-
return async () => {
239-
const publicAddress =
240-
SPECIAL_CURRENCY_INFO[pluginId].dummyPublicAddress ??
241-
(await wallet.getReceiveAddress({ tokenId: null })).publicAddress
242-
const spendInfo: EdgeSpendInfo = {
243-
tokenId: asset.tokenId,
244-
spendTargets: [{ publicAddress }],
245-
networkFeeOption: 'standard'
246-
}
247-
248-
try {
249-
const maxAmount = await wallet.getMaxSpendable(spendInfo)
250-
if (maxAmount === '0') {
251-
throw new InsufficientFundsError({ tokenId: asset.tokenId })
252-
}
253-
const maxSpendInfo = {
254-
...spendInfo,
255-
spendTargets: [{ publicAddress, nativeAmount: maxAmount }]
256-
}
257-
const edgeTransaction = await wallet.makeSpend(maxSpendInfo)
258-
const txFee =
259-
edgeTransaction.parentNetworkFee ?? edgeTransaction.networkFee
260-
bundlesFeeTotals.set(asset.key, txFee)
261-
feeTotal = add(feeTotal, txFee)
262-
263-
// While imperfect, sanity check that the total fee spent so far to send tokens + fee to send mainnet currency is under the total mainnet balance
264-
if (
265-
i === bundle.length - 1 &&
266-
lt(wallet.balanceMap.get(null) ?? '0', feeTotal)
267-
) {
268-
throw new InsufficientFundsError({
269-
tokenId: null,
270-
networkFee: feeTotal
248+
let assetSpendRoutines: Array<() => Promise<void>>
249+
if (
250+
(wallet.otherMethods.makeMaxSpend as
251+
| MakeMaxSpendMethod
252+
| undefined) != null
253+
) {
254+
assetSpendRoutines = [
255+
async () => {
256+
const publicAddress =
257+
SPECIAL_CURRENCY_INFO[pluginId].dummyPublicAddress ??
258+
(await wallet.getReceiveAddress({ tokenId: null }))
259+
.publicAddress
260+
261+
try {
262+
const tokenIds = bundle.map(item => item.tokenId)
263+
const edgeTransaction = await (
264+
wallet.otherMethods.makeMaxSpend as MakeMaxSpendMethod
265+
)({
266+
tokenIds,
267+
spendTargets: [{ publicAddress }]
271268
})
272-
}
273-
} catch (e: any) {
274-
for (const key of bundlesFeeTotals.keys()) {
275-
const insufficientFundsError = asMaybeInsufficientFundsError(e)
276-
if (insufficientFundsError != null) {
277-
bundlesFeeTotals.set(key, e)
278-
} else {
269+
const txFee =
270+
edgeTransaction.parentNetworkFee ?? edgeTransaction.networkFee
271+
for (const item of bundle) {
279272
bundlesFeeTotals.set(
280-
key,
281-
Error(lstrings.migrate_unknown_error_fragment)
273+
item.key,
274+
item.tokenId == null ? txFee : 'included'
282275
)
283276
}
277+
} catch (e: any) {
278+
for (const key of bundlesFeeTotals.keys()) {
279+
const insufficientFundsError =
280+
asMaybeInsufficientFundsError(e)
281+
if (insufficientFundsError != null) {
282+
bundlesFeeTotals.set(key, e)
283+
} else {
284+
bundlesFeeTotals.set(
285+
key,
286+
Error(lstrings.migrate_unknown_error_fragment)
287+
)
288+
}
289+
}
284290
}
285291
}
286-
}
287-
})
292+
]
293+
} else {
294+
// Create an array of async functions to call that will spend each
295+
// asset in the bundle.
296+
assetSpendRoutines = bundle.map((asset, i) => {
297+
return async () => {
298+
const publicAddress =
299+
SPECIAL_CURRENCY_INFO[pluginId].dummyPublicAddress ??
300+
(await wallet.getReceiveAddress({ tokenId: null }))
301+
.publicAddress
302+
const spendInfo: EdgeSpendInfo = {
303+
tokenId: asset.tokenId,
304+
spendTargets: [{ publicAddress }],
305+
networkFeeOption: 'standard'
306+
}
307+
308+
try {
309+
const maxAmount = await wallet.getMaxSpendable(spendInfo)
310+
if (maxAmount === '0') {
311+
throw new InsufficientFundsError({ tokenId: asset.tokenId })
312+
}
313+
const maxSpendInfo = {
314+
...spendInfo,
315+
spendTargets: [{ publicAddress, nativeAmount: maxAmount }]
316+
}
317+
const edgeTransaction = await wallet.makeSpend(maxSpendInfo)
318+
const txFee =
319+
edgeTransaction.parentNetworkFee ?? edgeTransaction.networkFee
320+
bundlesFeeTotals.set(asset.key, txFee)
321+
feeTotal = add(feeTotal, txFee)
322+
323+
// While imperfect, sanity check that the total fee spent so far to send tokens + fee to send mainnet currency is under the total mainnet balance
324+
if (
325+
i === bundle.length - 1 &&
326+
lt(wallet.balanceMap.get(null) ?? '0', feeTotal)
327+
) {
328+
throw new InsufficientFundsError({
329+
tokenId: null,
330+
networkFee: feeTotal
331+
})
332+
}
333+
} catch (e: any) {
334+
for (const key of bundlesFeeTotals.keys()) {
335+
const insufficientFundsError =
336+
asMaybeInsufficientFundsError(e)
337+
if (insufficientFundsError != null) {
338+
bundlesFeeTotals.set(key, e)
339+
} else {
340+
bundlesFeeTotals.set(
341+
key,
342+
Error(lstrings.migrate_unknown_error_fragment)
343+
)
344+
}
345+
}
346+
}
347+
}
348+
})
349+
}
288350

289-
walletPromises.push(async () => {
290-
for (const promise of assetPromises) {
291-
await promise()
351+
walletRoutines.push(async () => {
352+
for (const spendRoutine of assetSpendRoutines) {
353+
await spendRoutine()
292354
}
293355

294356
const success = [...bundlesFeeTotals.values()].some(
@@ -305,8 +367,8 @@ const MigrateWalletCalculateFeeComponent: React.FC<Props> = props => {
305367
}
306368

307369
await Promise.all(
308-
walletPromises.map(async promise => {
309-
await promise()
370+
walletRoutines.map(async routine => {
371+
await routine()
310372
})
311373
)
312374

src/components/scenes/MigrateWalletCompletionScene.tsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ interface Props extends EdgeAppSceneProps<'migrateWalletCompletion'> {}
4141
interface MigrateWalletTokenItem extends MigrateWalletItem {
4242
tokenId: string
4343
}
44+
type MakeMaxSpendMethod = (params: {
45+
tokenIds?: Array<string | null>
46+
spendTargets: Array<{ publicAddress: string }>
47+
metadata?: EdgeSpendInfo['metadata']
48+
}) => Promise<EdgeTransaction>
4449

4550
const MigrateWalletCompletionComponent: React.FC<Props> = props => {
4651
const { navigation, route } = props
@@ -181,6 +186,57 @@ const MigrateWalletCompletionComponent: React.FC<Props> = props => {
181186
]
182187
await newWallet.changeEnabledTokenIds(tokenIdsToEnable)
183188

189+
if (
190+
(oldWallet.otherMethods.makeMaxSpend as
191+
| MakeMaxSpendMethod
192+
| undefined) != null
193+
) {
194+
try {
195+
const tokenIds = bundle.map(item => item.tokenId)
196+
const unsignedTx = await (
197+
oldWallet.otherMethods.makeMaxSpend as MakeMaxSpendMethod
198+
)({
199+
tokenIds,
200+
spendTargets: [{ publicAddress: newPublicAddress }],
201+
metadata: {
202+
category: 'Transfer',
203+
name: newWalletName,
204+
notes: sprintf(
205+
lstrings.migrate_wallet_tx_notes,
206+
newWalletName
207+
)
208+
}
209+
})
210+
const signedTx = await oldWallet.signTx(unsignedTx)
211+
const broadcastedTx = await oldWallet.broadcastTx(signedTx)
212+
await oldWallet.saveTx(broadcastedTx)
213+
214+
for (const item of bundle) {
215+
handleItemStatus(item, 'complete')
216+
}
217+
const successfullyTransferredTokenIds = tokenIds.filter(
218+
(id): id is string => id != null
219+
)
220+
await oldWallet.changeEnabledTokenIds(
221+
tokenIdsToEnable.filter(
222+
tokenId => !successfullyTransferredTokenIds.includes(tokenId)
223+
)
224+
)
225+
226+
const { modalShown } = securityCheckedWallets[oldWalletId]
227+
securityCheckedWallets[oldWalletId] = {
228+
checked: true,
229+
modalShown
230+
}
231+
} catch (e) {
232+
showError(e)
233+
for (const item of bundle) {
234+
handleItemStatus(item, 'error')
235+
}
236+
}
237+
return
238+
}
239+
184240
// Send tokens
185241
let feeTotal = '0'
186242
const hasError = false

0 commit comments

Comments
 (0)