Skip to content

Commit

Permalink
Merge pull request #572 from EdgeApp/william/auto-tokens
Browse files Browse the repository at this point in the history
Auto tokens
  • Loading branch information
swansontec authored Nov 30, 2023
2 parents 1515f34 + 1c12c67 commit cb15414
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 105 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

- added: Accept an `onNewTokens` callback from `EdgeCurrencyEngine`.
- added: Emit an `enabledDetectedTokens` event when auto-enabling tokens.
- added: Expose auto-detected tokens as `EdgeCurrencyWallet.detectedTokenIds`.
- changed: Save enabled tokens by their tokenId, not by their currency code.
- fixed: Add missing `export` to the `EdgeCorePluginFactory` type definition.

## 1.11.0 (2023-10-18)
Expand Down
16 changes: 7 additions & 9 deletions src/core/account/plugin-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,15 @@ export class CurrencyConfig
payload: { accountId, pluginId, tokenId: newTokenId, token }
})

// Do we need to tweak enabled tokens?
if (oldToken.currencyCode !== token.currencyCode) {
if (newTokenId !== tokenId) {
// Enable the new token if the tokenId changed:
const { wallets } = ai.props.state.currency
for (const walletId of Object.keys(wallets)) {
const walletState = wallets[walletId]
if (
walletState.accountId !== accountId ||
walletState.pluginId !== pluginId ||
!walletState.enabledTokens.includes(oldToken.currencyCode)
!walletState.enabledTokenIds.includes(tokenId)
) {
continue
}
Expand All @@ -112,17 +112,15 @@ export class CurrencyConfig
type: 'CURRENCY_WALLET_ENABLED_TOKENS_CHANGED',
payload: {
walletId,
currencyCodes: uniqueStrings(
[...walletState.enabledTokens, token.currencyCode],
[oldToken.currencyCode]
enabledTokenIds: uniqueStrings(
[...walletState.enabledTokenIds, newTokenId],
[tokenId]
)
}
})
}
}

// Remove the old token if the tokenId changed:
if (newTokenId !== tokenId) {
// Remove the old token if the tokenId changed:
ai.props.dispatch({
type: 'ACCOUNT_CUSTOM_TOKEN_REMOVED',
payload: { accountId, pluginId, tokenId }
Expand Down
35 changes: 26 additions & 9 deletions src/core/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,23 @@ export type RootAction =
txidHashes: TxidHashes
}
}
| {
// Called when a currency engine fires the onAddressChecked callback.
type: 'CURRENCY_ENGINE_CHANGED_UNACTIVATED_TOKEN_IDS'
payload: {
unactivatedTokenIds: string[]
walletId: string
}
}
| {
// Called when a currency engine fires the onNewTokens callback.
type: 'CURRENCY_ENGINE_DETECTED_TOKENS'
payload: {
detectedTokenIds: string[]
enablingTokenIds: string[]
walletId: string
}
}
| {
type: 'CURRENCY_ENGINE_GOT_TXS'
payload: {
Expand Down Expand Up @@ -247,7 +264,7 @@ export type RootAction =
| {
type: 'CURRENCY_WALLET_ENABLED_TOKENS_CHANGED'
payload: {
currencyCodes: string[]
enabledTokenIds: string[]
walletId: string
}
}
Expand Down Expand Up @@ -287,6 +304,14 @@ export type RootAction =
walletId: string
}
}
| {
type: 'CURRENCY_WALLET_LOADED_TOKEN_FILE'
payload: {
detectedTokenIds: string[]
enabledTokenIds: string[]
walletId: string
}
}
| {
// Called when a currency wallet receives a new name.
type: 'CURRENCY_WALLET_NAME_CHANGED'
Expand All @@ -303,14 +328,6 @@ export type RootAction =
walletInfo: EdgeWalletInfo
}
}
| {
// Called when a currency engine fires the onAddressChecked callback.
type: 'CURRENCY_ENGINE_CHANGED_UNACTIVATED_TOKEN_IDS'
payload: {
unactivatedTokenIds: string[]
walletId: string
}
}
| {
// Fired when we fetch exchange pairs from some server.
type: 'EXCHANGE_PAIRS_FETCHED'
Expand Down
28 changes: 13 additions & 15 deletions src/core/currency/wallet/currency-wallet-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ import {
} from './currency-wallet-files'
import { CurrencyWalletInput } from './currency-wallet-pixie'
import { MergedTransaction } from './currency-wallet-reducer'
import { tokenIdsToCurrencyCodes, uniqueStrings } from './enabled-tokens'
import { upgradeMemos } from './upgrade-memos'

const fakeMetadata = {
Expand Down Expand Up @@ -197,26 +196,25 @@ export function makeCurrencyWalletApi(

// Tokens:
async changeEnabledTokenIds(tokenIds: string[]): Promise<void> {
const { dispatch, state, walletId, walletState } = input.props
const { builtinTokens, customTokens } = state.accounts[accountId]
const { currencyInfo } = walletState
const { dispatch, walletId, walletState } = input.props
const { accountId, pluginId } = walletState
const accountState = input.props.state.accounts[accountId]
const allTokens = accountState.allTokens[pluginId] ?? {}

const enabledTokenIds = tokenIds.filter(
tokenId => allTokens[tokenId] != null
)

dispatch({
type: 'CURRENCY_WALLET_ENABLED_TOKENS_CHANGED',
payload: {
walletId,
currencyCodes: uniqueStrings(
tokenIdsToCurrencyCodes(
builtinTokens[pluginId],
customTokens[pluginId],
currencyInfo,
tokenIds
)
)
}
payload: { walletId, enabledTokenIds }
})
},

get detectedTokenIds(): string[] {
return input.props.walletState.detectedTokenIds
},

get enabledTokenIds(): string[] {
return input.props.walletState.enabledTokenIds
},
Expand Down
38 changes: 36 additions & 2 deletions src/core/currency/wallet/currency-wallet-callbacks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import { combineTxWithFile } from './currency-wallet-api'
import { asIntegerString } from './currency-wallet-cleaners'
import {
loadAddressFiles,
loadEnabledTokensFile,
loadFiatFile,
loadNameFile,
loadTokensFile,
loadTxFileNames,
setupNewTxMetadata
} from './currency-wallet-files'
Expand All @@ -34,6 +34,7 @@ import {
mergeTx,
TxidHashes
} from './currency-wallet-reducer'
import { uniqueStrings } from './enabled-tokens'

let throttleRateLimitMs = 5000

Expand Down Expand Up @@ -122,6 +123,39 @@ export function makeCurrencyWalletCallbacks(
})
},

onNewTokens(tokenIds: string[]) {
pushUpdate({
id: walletId,
action: 'onNewTokens',
updateFunc: () => {
// Before we update redux, figure out what's truly new:
const { detectedTokenIds, enabledTokenIds } = input.props.walletState
const enablingTokenIds = uniqueStrings(tokenIds, [
...detectedTokenIds,
...enabledTokenIds
])

// Update redux:
input.props.dispatch({
type: 'CURRENCY_ENGINE_DETECTED_TOKENS',
payload: {
detectedTokenIds: tokenIds,
enablingTokenIds,
walletId
}
})

// Fire an event to the GUI:
if (enablingTokenIds.length > 0) {
const walletApi = input.props?.walletOutput?.walletApi
if (walletApi != null) {
emit(walletApi, 'enabledDetectedTokens', enablingTokenIds)
}
}
}
})
},

onUnactivatedTokenIdsChanged(unactivatedTokenIds: string[]) {
pushUpdate({
id: walletId,
Expand Down Expand Up @@ -333,7 +367,7 @@ export function watchCurrencyWallet(input: CurrencyWalletInput): void {
const changes = getStorageWalletLastChanges(props.state, walletId)
if (changes !== lastChanges) {
lastChanges = changes
await loadEnabledTokensFile(input)
await loadTokensFile(input)
await loadFiatFile(input)
await loadNameFile(input)
await loadTxFileNames(input)
Expand Down
20 changes: 15 additions & 5 deletions src/core/currency/wallet/currency-wallet-cleaners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,22 @@ export function asIntegerString(raw: unknown): string {
// ---------------------------------------------------------------------

/**
* This uses currency codes, since we cannot break the data on disk.
* To fix this one day, we can either migrate to a new file name,
* or we can use `asEither` to switch between this format
* and some new format based on token ID's.
* Old core versions used currency codes instead of tokenId's.
*/
export const asEnabledTokensFile = asArray<string>(asString)
export const asLegacyTokensFile = asArray<string>(asString)

/**
* Stores enabled tokenId's on disk.
*/
export const asTokensFile = asObject({
// All the tokens that the engine should check.
// This includes both manually-enabled tokens and auto-enabled tokens:
enabledTokenIds: asArray(asString),

// These tokenId's have been detected on-chain at least once.
// The user can still remove them from the enabled tokens list.
detectedTokenIds: asArray(asString)
})

export const asTransactionFile = asObject<TransactionFile>({
txid: asString,
Expand Down
78 changes: 55 additions & 23 deletions src/core/currency/wallet/currency-wallet-files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ import {
} from '../../storage/storage-selectors'
import { combineTxWithFile } from './currency-wallet-api'
import {
asEnabledTokensFile,
asLegacyAddressFile,
asLegacyMapFile,
asLegacyTokensFile,
asLegacyTransactionFile,
asTokensFile,
asTransactionFile,
asWalletFiatFile,
asWalletNameFile,
Expand All @@ -31,31 +32,38 @@ import {
} from './currency-wallet-cleaners'
import { CurrencyWalletInput } from './currency-wallet-pixie'
import { TxFileNames } from './currency-wallet-reducer'
import { currencyCodesToTokenIds } from './enabled-tokens'

const CURRENCY_FILE = 'Currency.json'
const ENABLED_TOKENS_FILE = 'EnabledTokens.json'
const LEGACY_MAP_FILE = 'fixedLegacyFileNames.json'
const LEGACY_TOKENS_FILE = 'EnabledTokens.json'
const TOKENS_FILE = 'Tokens.json'
const WALLET_NAME_FILE = 'WalletName.json'

const enabledTokensFile = makeJsonFile(asEnabledTokensFile)
const legacyAddressFile = makeJsonFile(asLegacyAddressFile)
const legacyMapFile = makeJsonFile(asLegacyMapFile)
const legacyTokensFile = makeJsonFile(asLegacyTokensFile)
const legacyTransactionFile = makeJsonFile(asLegacyTransactionFile)
const tokensFile = makeJsonFile(asTokensFile)
const transactionFile = makeJsonFile(asTransactionFile)
const walletFiatFile = makeJsonFile(asWalletFiatFile)
const walletNameFile = makeJsonFile(asWalletNameFile)

/**
* Updates the enabled tokens on a wallet.
*/
export async function changeEnabledTokens(
export async function writeTokensFile(
input: CurrencyWalletInput,
currencyCodes: string[]
detectedTokenIds: string[],
enabledTokenIds: string[]
): Promise<void> {
const { state, walletId } = input.props
const disklet = getStorageWalletDisklet(state, walletId)

await enabledTokensFile.save(disklet, ENABLED_TOKENS_FILE, currencyCodes)
await tokensFile.save(disklet, TOKENS_FILE, {
detectedTokenIds,
enabledTokenIds
})
}

/**
Expand Down Expand Up @@ -146,23 +154,6 @@ export async function setCurrencyWalletFiat(
})
}

export async function loadEnabledTokensFile(
input: CurrencyWalletInput
): Promise<void> {
const { dispatch, state, walletId } = input.props
const disklet = getStorageWalletDisklet(state, walletId)

const clean = await enabledTokensFile.load(disklet, ENABLED_TOKENS_FILE)
if (clean == null) return

// Future currencyCode to tokenId logic will live here.

dispatch({
type: 'CURRENCY_WALLET_ENABLED_TOKENS_CHANGED',
payload: { walletId: input.props.walletId, currencyCodes: clean }
})
}

/**
* Loads the wallet fiat currency file.
*/
Expand Down Expand Up @@ -219,6 +210,47 @@ export async function loadNameFile(input: CurrencyWalletInput): Promise<void> {
})
}

/**
* Load the enabled tokens file, with fallback to the legacy file.
*/
export async function loadTokensFile(
input: CurrencyWalletInput
): Promise<void> {
const { dispatch, state, walletId } = input.props
const disklet = getStorageWalletDisklet(state, walletId)

const clean = await tokensFile.load(disklet, TOKENS_FILE)
if (clean != null) {
dispatch({
type: 'CURRENCY_WALLET_LOADED_TOKEN_FILE',
payload: { walletId: input.props.walletId, ...clean }
})
return
}

const legacyCurrencyCodes = await legacyTokensFile.load(
disklet,
LEGACY_TOKENS_FILE
)
const { accountId, currencyInfo, pluginId } = input.props.walletState
const accountState = input.props.state.accounts[accountId]
const tokenIds = currencyCodesToTokenIds(
accountState.builtinTokens[pluginId],
accountState.customTokens[pluginId],
currencyInfo,
legacyCurrencyCodes ?? []
)

dispatch({
type: 'CURRENCY_WALLET_LOADED_TOKEN_FILE',
payload: {
walletId: input.props.walletId,
detectedTokenIds: [],
enabledTokenIds: tokenIds
}
})
}

/**
* Loads transaction metadata files.
*/
Expand Down
Loading

0 comments on commit cb15414

Please sign in to comment.