Skip to content

Commit

Permalink
Merge pull request #5344 from EdgeApp/matthew/earn-scene-error
Browse files Browse the repository at this point in the history
Earn scene loop handling adjustments
  • Loading branch information
peachbits authored Nov 11, 2024
2 parents e765f43 + 2fa42a9 commit 4f69faa
Show file tree
Hide file tree
Showing 9 changed files with 105 additions and 87 deletions.
64 changes: 36 additions & 28 deletions src/components/scenes/Staking/EarnScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ import { cacheStyles, Theme, useTheme } from '../../services/ThemeContext'

interface Props extends EdgeAppSceneProps<'earnScene'> {}

let USERNAME: string | undefined
let STAKE_POLICY_MAP: StakePolicyMap = {}

export interface EarnSceneParams {}

interface WalletStakeInfo {
Expand All @@ -49,6 +52,11 @@ export const EarnScene = (props: Props) => {
const styles = getStyles(theme)

const account = useSelector(state => state.core.account)
if (USERNAME !== account.username) {
// Reset local variable if user changes
USERNAME = account.username
STAKE_POLICY_MAP = {}
}

const currencyConfigMap = useSelector(state => state.core.account.currencyConfig)

Expand All @@ -58,11 +66,7 @@ export const EarnScene = (props: Props) => {
const [isPortfolioSelected, setIsPortfolioSelected] = React.useState(false)
const [isLoading, setIsLoading] = React.useState(true)

// Store `stakePolicyMap` in a ref and manage re-renders manually to avoid
// re-initializing it every time we enter the scene.
const [updateCounter, setUpdateCounter] = React.useState(0)
const stakePolicyMapRef = React.useRef<StakePolicyMap>({})
const stakePolicyMap = stakePolicyMapRef.current

const handleSelectEarn = useHandler(() => setIsPortfolioSelected(false))
const handleSelectPortfolio = useHandler(() => setIsPortfolioSelected(true))
Expand All @@ -71,40 +75,40 @@ export const EarnScene = (props: Props) => {
async () => {
for (const pluginId of Object.keys(currencyConfigMap)) {
const isStakingSupported = SPECIAL_CURRENCY_INFO[pluginId]?.isStakingSupported === true && ENV.ENABLE_STAKING
if (stakePolicyMap[pluginId] != null || !isStakingSupported) continue
if (STAKE_POLICY_MAP[pluginId] != null || !isStakingSupported) continue

// Initialize stake policy
try {
const stakePlugins = await getStakePlugins(pluginId)
stakePolicyMap[pluginId] = []
const stakePlugins = await getStakePlugins(pluginId)
STAKE_POLICY_MAP[pluginId] = []

for (const stakePlugin of stakePlugins) {
const stakePolicies = stakePlugin.getPolicies({ currencyCode: currencyConfigMap[pluginId].currencyInfo.currencyCode })
const matchingWallets = wallets.filter((wallet: EdgeCurrencyWallet) => wallet.currencyInfo.pluginId === pluginId)
const matchingWallets = wallets.filter((wallet: EdgeCurrencyWallet) => wallet.currencyInfo.pluginId === pluginId)
for (const stakePlugin of stakePlugins) {
const stakePolicies = stakePlugin.getPolicies({ pluginId })

for (const stakePolicy of stakePolicies) {
const walletStakePositions = []
for (const wallet of matchingWallets) {
for (const stakePolicy of stakePolicies) {
const walletStakePositions = []
for (const wallet of matchingWallets) {
try {
// Determine if a wallet matching this policy has an open position
const stakePosition = await stakePlugin.fetchStakePosition({ stakePolicyId: stakePolicy.stakePolicyId, wallet, account })
const allocations = getPositionAllocations(stakePosition)
const { staked, earned, unstaked } = allocations
const isPositionOpen = [...staked, ...earned, ...unstaked].some(positionAllocation => !zeroString(positionAllocation.nativeAmount))

walletStakePositions.push({ wallet, isPositionOpen, stakePosition })
} catch (e) {
showDevError(e)
}

stakePolicyMap[pluginId].push({
stakePlugin,
stakePolicy,
walletStakeInfos: walletStakePositions
})
// Trigger re-render
setUpdateCounter(prevCounter => prevCounter + 1)
}

STAKE_POLICY_MAP[pluginId].push({
stakePlugin,
stakePolicy,
walletStakeInfos: walletStakePositions
})
// Trigger re-render
setUpdateCounter(prevCounter => prevCounter + 1)
}
} catch (e) {
showDevError(e)
}
}
setIsLoading(false)
Expand All @@ -116,12 +120,16 @@ export const EarnScene = (props: Props) => {
const renderStakeItems = (displayStakeInfo: DisplayStakeInfo, currencyInfo: EdgeCurrencyInfo) => {
const { stakePlugin, stakePolicy, walletStakeInfos } = displayStakeInfo

const openStakePositions = walletStakeInfos.filter(walletStakeInfo => walletStakeInfo.isPositionOpen)

if (isPortfolioSelected && openStakePositions.length === 0) {
return null
}

const handlePress = async () => {
let walletId: string | undefined
let stakePosition

const openStakePositions = walletStakeInfos.filter(walletStakeInfo => walletStakeInfo.isPositionOpen)

if (walletStakeInfos.length === 1 || (isPortfolioSelected && openStakePositions.length === 1)) {
// Only one compatible wallet if on "Discover", or only one open
// position on "Portfolio." Auto-select the wallet.
Expand Down Expand Up @@ -179,8 +187,8 @@ export const EarnScene = (props: Props) => {
<SceneWrapper scroll padding={theme.rem(0.5)}>
<EdgeSwitch labelA={lstrings.staking_discover} labelB={lstrings.staking_portfolio} onSelectA={handleSelectEarn} onSelectB={handleSelectPortfolio} />
<SectionHeader leftTitle={lstrings.staking_earning_pools} />
{Object.keys(stakePolicyMap).map(pluginId =>
stakePolicyMap[pluginId].map(displayStakeInfo => renderStakeItems(displayStakeInfo, currencyConfigMap[pluginId].currencyInfo))
{Object.keys(STAKE_POLICY_MAP).map(pluginId =>
STAKE_POLICY_MAP[pluginId].map(displayStakeInfo => renderStakeItems(displayStakeInfo, currencyConfigMap[pluginId].currencyInfo))
)}
{isLoading && <ActivityIndicator style={styles.loader} size="large" color={theme.primaryText} />}
</SceneWrapper>
Expand Down
2 changes: 1 addition & 1 deletion src/components/scenes/Staking/StakeOptionsScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const StakeOptionsSceneComponent = (props: Props) => {

const handleStakeOptionPress = (stakePolicy: StakePolicy) => {
const { stakePolicyId } = stakePolicy
const stakePlugin = getPluginFromPolicy(stakePlugins, stakePolicy)
const stakePlugin = getPluginFromPolicy(stakePlugins, stakePolicy, { pluginId })
// Transition to next scene immediately
const stakePosition = stakePositionMap[stakePolicyId]
if (stakePlugin != null) navigation.push('stakeOverview', { stakePlugin, walletId: wallet.id, stakePolicy: stakePolicy, stakePosition })
Expand Down
50 changes: 25 additions & 25 deletions src/components/themed/TransactionListTop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export class TransactionListTopComponent extends React.PureComponent<Props, Stat
const stakePlugins = await getStakePlugins(pluginId)
const stakePolicies: StakePolicy[] = []
for (const stakePlugin of stakePlugins) {
const policies = stakePlugin.getPolicies({ wallet, currencyCode })
const policies = stakePlugin.getPolicies({ pluginId, wallet, currencyCode })
stakePolicies.push(...policies)
}
const newState = { stakePolicies, stakePlugins }
Expand All @@ -175,31 +175,31 @@ export class TransactionListTopComponent extends React.PureComponent<Props, Stat
updatePositions = async ({ stakePlugins = [], stakePolicies = [] }: { stakePlugins?: StakePlugin[]; stakePolicies?: StakePolicy[] }) => {
let lockedNativeAmount = '0'
const stakePositionMap: StakePositionMap = {}
for (const stakePolicy of stakePolicies) {
// Don't show liquid staking positions as locked amount
if (stakePolicy.isLiquidStaking === true) continue

let total: string | undefined
const stakePlugin = getPluginFromPolicy(stakePlugins, stakePolicy)
if (stakePlugin == null) continue
try {
const stakePosition = await stakePlugin.fetchStakePosition({
stakePolicyId: stakePolicy.stakePolicyId,
wallet: this.props.wallet,
account: this.props.account
})
for (const stakePlugin of stakePlugins) {
for (const stakePolicy of stakePolicies) {
// Don't show liquid staking positions as locked amount
if (stakePolicy.isLiquidStaking === true) continue

let total: string | undefined
try {
const stakePosition = await stakePlugin.fetchStakePosition({
stakePolicyId: stakePolicy.stakePolicyId,
wallet: this.props.wallet,
account: this.props.account
})

stakePositionMap[stakePolicy.stakePolicyId] = stakePosition
const { staked, earned } = getPositionAllocations(stakePosition)
total = this.getTotalPosition(this.props.currencyCode, [...staked, ...earned])
} catch (err) {
console.error(err)
const { displayName } = stakePolicy.stakeProviderInfo
datelog(`${displayName}: ${lstrings.stake_unable_to_query_locked}`)
continue
}
stakePositionMap[stakePolicy.stakePolicyId] = stakePosition
const { staked, earned } = getPositionAllocations(stakePosition)
total = this.getTotalPosition(this.props.currencyCode, [...staked, ...earned])
} catch (err) {
console.error(err)
const { displayName } = stakePolicy.stakeProviderInfo
datelog(`${displayName}: ${lstrings.stake_unable_to_query_locked}`)
continue
}

lockedNativeAmount = add(lockedNativeAmount, total)
lockedNativeAmount = add(lockedNativeAmount, total)
}
}
this.setState({ stakePositionMap })
this.setState({ lockedNativeAmount })
Expand Down Expand Up @@ -641,7 +641,7 @@ export class TransactionListTopComponent extends React.PureComponent<Props, Stat
} else if (stakePolicies.length === 1) {
const [stakePolicy] = stakePolicies
const { stakePolicyId } = stakePolicy
const stakePlugin = getPluginFromPolicy(stakePlugins, stakePolicy)
const stakePlugin = getPluginFromPolicy(stakePlugins, stakePolicy, { pluginId: wallet.currencyInfo.pluginId })
// Transition to next scene immediately
const stakePosition = stakePositionMap[stakePolicyId]
if (stakePlugin != null) navigation.push('stakeOverview', { stakePlugin, walletId: wallet.id, stakePolicy: stakePolicy, stakePosition })
Expand Down
28 changes: 12 additions & 16 deletions src/plugins/stake-plugins/generic/GenericStakePlugin.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
import { ChangeQuote, ChangeQuoteRequest, StakePlugin, StakePluginFactory, StakePolicy, StakePolicyFilter, StakePosition, StakePositionRequest } from '../types'
import {
ChangeQuote,
ChangeQuoteRequest,
filterStakePolicies,
StakePlugin,
StakePluginFactory,
StakePolicy,
StakePolicyFilter,
StakePosition,
StakePositionRequest
} from '../types'
import { CardanoPooledKilnAdapterConfig, makeCardanoKilnAdapter } from './policyAdapters/CardanoKilnAdaptor'
import { CoreumNativeSkateKitAdapterConfig, makeSkateKitAdapter } from './policyAdapters/CoreumStakeKitAdaptor'
import { EthereumPooledKilnAdapterConfig, makeEthereumKilnAdapter } from './policyAdapters/EthereumKilnAdaptor'
Expand All @@ -22,21 +32,7 @@ export const makeGenericStakePlugin =

const instance: StakePlugin = {
getPolicies(filter?: StakePolicyFilter): StakePolicy[] {
const { currencyCode, wallet } = filter ?? {}
let filteredPolicies: StakePolicy[] = policies

if (wallet != null) {
filteredPolicies = filteredPolicies.filter(policy =>
[...policy.rewardAssets, ...policy.stakeAssets].some(asset => asset.pluginId === wallet.currencyInfo.pluginId)
)
}
if (currencyCode != null) {
filteredPolicies = filteredPolicies.filter(policy =>
[...policy.rewardAssets, ...policy.stakeAssets].some(asset => asset.currencyCode === currencyCode)
)
}

return filteredPolicies
return filterStakePolicies(policies, filter)
},

async fetchChangeQuote(request: ChangeQuoteRequest): Promise<ChangeQuote> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,13 @@ export const makeEthereumKilnAdapter = (policyConfig: StakePolicyConfig<Ethereum
const walletSigner = new EdgeWalletSigner(wallet, provider)
const walletAddress = await walletSigner.getAddress()

let txCount: number = await walletSigner.getTransactionCount('pending')
const nextNonce = (): number => txCount++
let txCount: number | undefined
const nextNonce = async (): Promise<number> => {
if (txCount == null) {
txCount = await walletSigner.getTransactionCount('pending')
}
return txCount++
}

const feeData = await provider.getFeeData()
const maxFeePerGas = feeData.maxFeePerGas !== null ? feeData.maxFeePerGas : undefined
Expand Down Expand Up @@ -113,7 +118,7 @@ export const makeEthereumKilnAdapter = (policyConfig: StakePolicyConfig<Ethereum
gasLimit: '500000',
maxFeePerGas,
maxPriorityFeePerGas,
nonce: nextNonce(),
nonce: await nextNonce(),
customData: {
metadata: {
name: metadataName,
Expand All @@ -133,7 +138,7 @@ export const makeEthereumKilnAdapter = (policyConfig: StakePolicyConfig<Ethereum
gasLimit: '250000', // Typically uses 190000-225000 gas
maxFeePerGas,
maxPriorityFeePerGas,
nonce: nextNonce(),
nonce: await nextNonce(),
value: requestNativeAmount,
customData: {
metadata: {
Expand Down Expand Up @@ -163,7 +168,7 @@ export const makeEthereumKilnAdapter = (policyConfig: StakePolicyConfig<Ethereum
gasLimit: '500000',
maxFeePerGas,
maxPriorityFeePerGas,
nonce: nextNonce(),
nonce: await nextNonce(),
customData: {
metadata: {
name: metadataName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,13 @@ export const makeGlifInfinityPoolAdapter = (policyConfig: StakePolicyConfig<Glif
const walletSigner = new EdgeWalletSigner(wallet, provider)
const walletAddress = await walletSigner.getAddress()

let txCount: number = await walletSigner.getTransactionCount('pending')
const nextNonce = (): number => txCount++
let txCount: number | undefined
const nextNonce = async (): Promise<number> => {
if (txCount == null) {
txCount = await walletSigner.getTransactionCount('pending')
}
return txCount++
}

const feeData = await provider.getFeeData()
const maxFeePerGas = feeData.maxFeePerGas !== null ? feeData.maxFeePerGas : undefined
Expand Down Expand Up @@ -109,7 +114,7 @@ export const makeGlifInfinityPoolAdapter = (policyConfig: StakePolicyConfig<Glif
value: requestNativeAmount,
maxFeePerGas,
maxPriorityFeePerGas,
nonce: nextNonce(),
nonce: await nextNonce(),
customData: {
metadata: {
name: metadataName,
Expand Down Expand Up @@ -152,7 +157,7 @@ export const makeGlifInfinityPoolAdapter = (policyConfig: StakePolicyConfig<Glif
await poolTokenContract.connect(walletSigner).populateTransaction.approve(simpleRampContract.address, expectedLiquidityAmount, {
maxFeePerGas,
maxPriorityFeePerGas,
nonce: nextNonce(),
nonce: await nextNonce(),
customData: {
metadata: {
name: metadataName,
Expand All @@ -170,7 +175,7 @@ export const makeGlifInfinityPoolAdapter = (policyConfig: StakePolicyConfig<Glif
gasLimit: 250000000,
maxFeePerGas,
maxPriorityFeePerGas,
nonce: nextNonce(),
nonce: await nextNonce(),
customData: {
metadata: {
name: metadataName,
Expand Down
6 changes: 5 additions & 1 deletion src/plugins/stake-plugins/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export interface StakePosition {
// -----------------------------------------------------------------------------

export interface StakePolicyFilter {
pluginId?: string
wallet?: EdgeCurrencyWallet
currencyCode?: string
}
Expand All @@ -191,14 +192,17 @@ export const filterStakePolicies = (policies: StakePolicy[], filter?: StakePolic
if (filter == null) return policies

let out: StakePolicy[] = [...policies]
const { currencyCode, wallet } = filter
const { currencyCode, pluginId, wallet } = filter

if (wallet != null) {
out = out.filter(policy => [...policy.rewardAssets, ...policy.stakeAssets].some(asset => asset.pluginId === wallet.currencyInfo.pluginId))
}
if (currencyCode != null) {
out = out.filter(policy => [...policy.rewardAssets, ...policy.stakeAssets].some(asset => asset.currencyCode === currencyCode))
}
if (pluginId != null) {
out = out.filter(policy => [...policy.rewardAssets, ...policy.stakeAssets].some(asset => asset.pluginId === pluginId))
}
return out
}

Expand Down
6 changes: 3 additions & 3 deletions src/plugins/stake-plugins/uniswapV2/uniV2Plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ export const makeUniV2StakePlugin = async (pluginId: string): Promise<StakePlugi
const instance: StakePlugin = {
getPolicies(filter?: StakePolicyFilter): StakePolicy[] {
let out: StakePolicyInfo[] = [...pluginInfo.policyInfo]
const { currencyCode, wallet } = filter ?? {}
const { currencyCode, pluginId } = filter ?? {}

if (wallet != null) {
out = out.filter(policy => [...policy.rewardAssets, ...policy.stakeAssets].some(asset => asset.pluginId === wallet.currencyInfo.pluginId))
if (pluginId != null) {
out = out.filter(policy => [...policy.rewardAssets, ...policy.stakeAssets].some(asset => asset.pluginId === pluginId))
}
if (currencyCode != null) {
out = out.filter(policy => [...policy.rewardAssets, ...policy.stakeAssets].some(asset => asset.currencyCode === currencyCode))
Expand Down
6 changes: 3 additions & 3 deletions src/util/stakeUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { sprintf } from 'sprintf-js'

import { formatTimeDate } from '../locales/intl'
import { lstrings } from '../locales/strings'
import { PositionAllocation, StakePlugin, StakePolicy, StakePosition } from '../plugins/stake-plugins/types'
import { PositionAllocation, StakePlugin, StakePolicy, StakePolicyFilter, StakePosition } from '../plugins/stake-plugins/types'
import { getCurrencyIconUris } from './CdnUris'
import { getUkCompliantString } from './ukComplianceUtils'

Expand Down Expand Up @@ -88,8 +88,8 @@ export const getPolicyIconUris = (
return { stakeAssetUris, rewardAssetUris }
}

export const getPluginFromPolicy = (stakePlugins: StakePlugin[], stakePolicy: StakePolicy): StakePlugin | undefined => {
return stakePlugins.find(plugin => plugin.getPolicies().find(policy => policy.stakePolicyId === stakePolicy.stakePolicyId))
export const getPluginFromPolicy = (stakePlugins: StakePlugin[], stakePolicy: StakePolicy, filter?: StakePolicyFilter): StakePlugin | undefined => {
return stakePlugins.find(plugin => plugin.getPolicies(filter).find(policy => policy.stakePolicyId === stakePolicy.stakePolicyId))
}

/**
Expand Down

0 comments on commit 4f69faa

Please sign in to comment.