Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

},

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