diff --git a/src/controllers/portfolio/portfolio.ts b/src/controllers/portfolio/portfolio.ts index a0704746e..8877abef4 100644 --- a/src/controllers/portfolio/portfolio.ts +++ b/src/controllers/portfolio/portfolio.ts @@ -25,6 +25,7 @@ import { // eslint-disable-next-line import/no-cycle import { AccountState, + AdditionalPortfolioGetResult, GetOptions, NetworkState, PortfolioControllerState, @@ -427,7 +428,7 @@ export class PortfolioController extends EventEmitter { this.#setNetworkLoading(accountId, 'latest', 'rewards', true) this.emitUpdate() - let res: any + let res: AdditionalPortfolioGetResult try { res = await this.#callRelayer(`/v2/identity/${accountId}/portfolio-additional`) } catch (e: any) { @@ -445,7 +446,7 @@ export class PortfolioController extends EventEmitter { res.data.rewards.walletClaimableBalance || [] ] .flat() - .map((t: any) => ({ + .map((t) => ({ ...t, symbol: t.address === '0x47Cd7E91C3CBaAF266369fe8518345fc4FC12935' ? 'xWALLET' : t.symbol, flags: getFlags(res.data.rewards, 'rewards', t.networkId, t.address) @@ -464,7 +465,7 @@ export class PortfolioController extends EventEmitter { } } - const gasTankTokens = res.data.gasTank.balance.map((t: any) => ({ + const gasTankTokens = res.data.gasTank.balance.map((t) => ({ ...t, flags: getFlags(res.data, 'gasTank', t.networkId, t.address) })) diff --git a/src/libs/deployless/deployless.ts b/src/libs/deployless/deployless.ts index dd9df686b..8b9069994 100644 --- a/src/libs/deployless/deployless.ts +++ b/src/libs/deployless/deployless.ts @@ -2,6 +2,7 @@ import assert from 'assert' import { AbiCoder, concat, getBytes, Interface, JsonRpcProvider, Provider } from 'ethers' import DeploylessCompiled from '../../../contracts/compiled/Deployless.json' +import { TokenError } from '../portfolio/interfaces' // this is a magic contract that is constructed like `constructor(bytes memory contractBytecode, bytes memory data)` and returns the result from the call // compiled from relayer:a7ea373559d8c419577ac05527bd37fbee8856ae/src/velcro-v3/contracts/Deployless.sol with solc 0.8.17 @@ -35,6 +36,19 @@ export enum DeploylessMode { ProxyContract, StateOverride } + +export type DeploylessResult = { + nfts?: bigint[] + name?: string + amount: bigint + decimals: number + symbol: string + error: TokenError +} + +export interface SimulationResult extends DeploylessResult { + addr: string +} export type CallOptions = { mode: DeploylessMode // Note: some RPCs don't seem to like numbers, we can use hex strings for them diff --git a/src/libs/portfolio/gecko.ts b/src/libs/portfolio/gecko.ts index 5beb5307d..a7d62eaf2 100644 --- a/src/libs/portfolio/gecko.ts +++ b/src/libs/portfolio/gecko.ts @@ -15,7 +15,7 @@ export function geckoResponseIdentifier(tokenAddr: string, network: Network): st } export function geckoRequestBatcher(queue: QueueElement[]): Request[] { - const segments: { [key: string]: any[] } = {} + const segments: { [key: string]: QueueElement[] } = {} // eslint-disable-next-line no-restricted-syntax for (const queueItem of queue) { let segmentId: string = queueItem.data.baseCurrency diff --git a/src/libs/portfolio/getOnchainBalances.ts b/src/libs/portfolio/getOnchainBalances.ts index c471b3a75..a6d606c60 100644 --- a/src/libs/portfolio/getOnchainBalances.ts +++ b/src/libs/portfolio/getOnchainBalances.ts @@ -4,7 +4,13 @@ import { Network } from '../../interfaces/network' import { getEoaSimulationStateOverride } from '../../utils/simulationStateOverride' import { getAccountDeployParams, isSmartAccount } from '../account/account' import { callToTuple, toSingletonCall } from '../accountOp/accountOp' -import { Deployless, DeploylessMode, parseErr } from '../deployless/deployless' +import { + Deployless, + DeploylessMode, + DeploylessResult, + parseErr, + SimulationResult +} from '../deployless/deployless' import { getFlags, overrideSymbol } from './helpers' import { CollectionResult, @@ -94,18 +100,18 @@ export async function getNFTs( deployless: Deployless, opts: Partial, accountAddr: string, - tokenAddrs: [string, any][], + tokenAddrs: [string, { isKnown: boolean; tokens: string[]; enumerable: boolean }][], limits: LimitsOptions ): Promise<[[TokenError, CollectionResult][], {}][]> { const deploylessOpts = getDeploylessOpts(accountAddr, !network.rpcNoStateOverride, opts) - const mapToken = (token: any) => { + const mapToken = (token: DeploylessResult) => { return { name: token.name, networkId: network.id, symbol: token.symbol, - amount: BigInt(token.nfts.length), + amount: BigInt(token.nfts?.length ?? 0), decimals: 1, - collectibles: [...token.nfts] + collectibles: token.nfts ? [...token.nfts] : [] } as CollectionResult } @@ -125,7 +131,7 @@ export async function getNFTs( ) )[0] - return [collections.map((token: any) => [token.error, mapToken(token)]), {}] + return [collections.map((token: DeploylessResult) => [token.error, mapToken(token)]), {}] } const { accountOps, account } = opts.simulation @@ -158,18 +164,18 @@ export async function getNFTs( // simulation was performed if the nonce is changed const hasSimulation = afterNonce !== beforeNonce - const simulationTokens: (CollectionResult & { addr: any })[] | null = hasSimulation - ? after[0].map((simulationToken: any, tokenIndex: number) => ({ + const simulationTokens: (SimulationResult & { addr: string })[] | null = hasSimulation + ? after[0].map((simulationToken: SimulationResult, tokenIndex: number) => ({ ...mapToken(simulationToken), addr: deltaAddressesMapping[tokenIndex] })) : null return [ - before[0].map((beforeToken: any, i: number) => { + before[0].map((beforeToken: DeploylessResult, i: number) => { const simulationToken = simulationTokens ? simulationTokens.find( - (token: any) => token.addr.toLowerCase() === tokenAddrs[i][0].toLowerCase() + (token: SimulationResult) => token.addr.toLowerCase() === tokenAddrs[i][0].toLowerCase() ) : null @@ -211,7 +217,7 @@ export async function getTokens( accountAddr: string, tokenAddrs: string[] ): Promise<[[TokenError, TokenResult][], MetaData][]> { - const mapToken = (token: any, address: string) => { + const mapToken = (token: DeploylessResult, address: string) => { return { amount: token.amount, networkId: network.id, @@ -233,7 +239,10 @@ export async function getTokens( ) return [ - results.map((token: any, i: number) => [token.error, mapToken(token, tokenAddrs[i])]), + results.map((token: DeploylessResult, i: number) => [ + token.error, + mapToken(token, tokenAddrs[i]) + ]), { blockNumber } @@ -268,16 +277,18 @@ export async function getTokens( const hasSimulation = afterNonce !== beforeNonce const simulationTokens = hasSimulation - ? after[0].map((simulationToken: any, tokenIndex: number) => ({ + ? after[0].map((simulationToken: SimulationResult, tokenIndex: number) => ({ ...simulationToken, amount: simulationToken.amount, addr: deltaAddressesMapping[tokenIndex] })) : null return [ - before[0].map((token: any, i: number) => { + before[0].map((token: DeploylessResult, i: number) => { const simulation = simulationTokens - ? simulationTokens.find((simulationToken: any) => simulationToken.addr === tokenAddrs[i]) + ? simulationTokens.find( + (simulationToken: SimulationResult) => simulationToken.addr === tokenAddrs[i] + ) : null // Here's the math before `simulationAmount` and `amountPostSimulation`. diff --git a/src/libs/portfolio/helpers.ts b/src/libs/portfolio/helpers.ts index 71e785471..a966a61c7 100644 --- a/src/libs/portfolio/helpers.ts +++ b/src/libs/portfolio/helpers.ts @@ -42,7 +42,7 @@ export function getFlags( const isRewardsOrGasTank = ['gasTank', 'rewards'].includes(networkId) const onGasTank = networkId === 'gasTank' - let rewardsType = null + let rewardsType: 'wallet-vesting' | 'wallet-rewards' | null = null if (networkData?.xWalletClaimableBalance?.address.toLowerCase() === address.toLowerCase()) rewardsType = 'wallet-rewards' if (networkData?.walletClaimableBalance?.address.toLowerCase() === address.toLowerCase()) @@ -54,7 +54,8 @@ export function getFlags( (isRewardsOrGasTank ? t.networkId === tokenNetwork : t.networkId === networkId) ) - const canTopUpGasTank = foundFeeToken && !foundFeeToken?.disableGasTankDeposit && !rewardsType + const canTopUpGasTank = + (foundFeeToken && !foundFeeToken?.disableGasTankDeposit && !rewardsType) || false const isFeeToken = address === ZeroAddress || // disable if not in gas tank diff --git a/src/libs/portfolio/interfaces.ts b/src/libs/portfolio/interfaces.ts index 49eb71aa5..3ad7c5aa3 100644 --- a/src/libs/portfolio/interfaces.ts +++ b/src/libs/portfolio/interfaces.ts @@ -106,6 +106,28 @@ export interface PortfolioLibGetResult { afterNonce: bigint } +type GasTankOrRewardsToken = Omit +export interface AdditionalPortfolioGetResult { + data: { + rewards: { + supplyControllerAddr: string + claimableRewardsData: ClaimableRewardsData + walletClaimableBalance: GasTankOrRewardsToken + xWalletClaimableBalance: GasTankOrRewardsToken + } + gasTank: { + availableGasTankAssets: (GasTankOrRewardsToken & { + icon: string + })[] + balance: (GasTankOrRewardsToken & { + availableAmount: string + cashback: string + saved: string + })[] + } + } +} + interface Total { [currency: string]: number } diff --git a/src/libs/portfolio/pagination.ts b/src/libs/portfolio/pagination.ts index bc3102bfd..a59b00170 100644 --- a/src/libs/portfolio/pagination.ts +++ b/src/libs/portfolio/pagination.ts @@ -1,3 +1,4 @@ +import { QueueElement } from './batcher' import { CollectionResult, ERC721Enumerable, @@ -8,7 +9,7 @@ import { } from './interfaces' export function paginate( - input: string[] | [string, ERC721Enumerable | ERC721Innumerable][], + input: string[] | QueueElement[] | [string, ERC721Enumerable | ERC721Innumerable][], limit: number ): any[][] { const pages = [] diff --git a/src/libs/portfolio/portfolio.ts b/src/libs/portfolio/portfolio.ts index 91ff61fd8..198f70a15 100644 --- a/src/libs/portfolio/portfolio.ts +++ b/src/libs/portfolio/portfolio.ts @@ -266,7 +266,7 @@ export class Portfolio { .filter((_tokensWithErrResult: [TokenError, TokenResult]) => tokenFilter(_tokensWithErrResult) ) - .map(([, result]: [any, TokenResult]) => result) + .map(([, result]) => result) const unfilteredCollections = collectionsWithErrResult.map(([error, x], i) => { const address = collectionsHints[i][0] as unknown as string