Skip to content

Commit 25c1053

Browse files
committed
Merge branch 'main' of github.com:AmbireTech/ambire-common into release/v2.100
2 parents 951f28b + b1f984a commit 25c1053

4 files changed

Lines changed: 94 additions & 2 deletions

File tree

src/controllers/portfolio/portfolio.test.ts

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { getAccountState } from '../../libs/accountState/accountState'
1919
import * as defiPricesLib from '../../libs/defiPositions/defiPrices'
2020
import { getProviderId } from '../../libs/defiPositions/helpers'
2121
import * as defiProviders from '../../libs/defiPositions/providers'
22-
import { DeFiPositionsError } from '../../libs/defiPositions/types'
22+
import { AssetType, DeFiPositionsError } from '../../libs/defiPositions/types'
2323
import {
2424
erc721CollectionToLearnedAssetKeys,
2525
learnedErc721sToHints
@@ -2067,6 +2067,85 @@ describe('Portfolio Controller ', () => {
20672067
restore()
20682068
})
20692069

2070+
it("Uniswap V3 shouldn't lose its API enhancement (e.g. rewards) when the discovery call is skipped", async () => {
2071+
const { controller } = await prepareTest()
2072+
2073+
// A formatted discovery response carrying a Debank Uniswap V3 entry. It's mocked rather than
2074+
// relying on the live API, since Debank's coverage of a given account varies over time.
2075+
const discoveryWithDebankUniV3 = {
2076+
data: {
2077+
defi: {
2078+
updatedAt: Date.now(),
2079+
isForceUpdate: false,
2080+
positions: [
2081+
{
2082+
providerName: 'Uniswap V3',
2083+
chainId: 1n,
2084+
source: 'debank' as const,
2085+
iconUrl: '',
2086+
siteUrl: 'https://app.uniswap.org',
2087+
type: 'common' as const,
2088+
positions: [
2089+
{
2090+
id: 'debank-uni-v3-eth',
2091+
assets: [
2092+
{
2093+
address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
2094+
symbol: 'WETH',
2095+
name: 'Wrapped Ether',
2096+
decimals: 18,
2097+
amount: 1000000000000000000n,
2098+
priceIn: { price: 3000, baseCurrency: 'usd' },
2099+
value: 3000,
2100+
type: AssetType.Liquidity,
2101+
iconUrl: ''
2102+
}
2103+
],
2104+
additionalData: { positionIndex: 'debank-uni-v3-eth', name: 'Liquidity Pool' }
2105+
}
2106+
],
2107+
positionInUSD: 3000
2108+
}
2109+
]
2110+
},
2111+
hints: null,
2112+
otherNetworksDefiCounts: {}
2113+
},
2114+
discoveryTime: 1,
2115+
errors: []
2116+
}
2117+
2118+
// @ts-expect-error - getPortfolioFromApiDiscovery is private, spied on at runtime
2119+
const discoverySpy: any = jest.spyOn(controller, 'getPortfolioFromApiDiscovery')
2120+
// First update: the custom (deployless) position is enhanced with the Debank entry (source: 'mixed')
2121+
discoverySpy.mockResolvedValueOnce(discoveryWithDebankUniV3)
2122+
// Second update: a skipped discovery returns null. This is a routine occurrence (the data is
2123+
// still fresh) and is NOT a failure.
2124+
discoverySpy.mockResolvedValueOnce(null)
2125+
2126+
await controller.updateSelectedAccount(DEFI_TEST_ACCOUNT.addr, [ethereum])
2127+
const firstResult = controller.getAccountPortfolioState(DEFI_TEST_ACCOUNT.addr)['1']!.result
2128+
const uniBeforeSkip = firstResult?.defiPositions.positionsByProvider.find(
2129+
(p) => getProviderId(p.providerName) === 'uniswap v3'
2130+
)
2131+
2132+
// Check before updating again to ensure it's mixed
2133+
expect(uniBeforeSkip?.source).toBe('mixed')
2134+
2135+
await controller.updateSelectedAccount(DEFI_TEST_ACCOUNT.addr, [ethereum], undefined, {
2136+
defiMaxDataAgeMs: 0,
2137+
isManualUpdate: true
2138+
})
2139+
2140+
const secondResult = controller.getAccountPortfolioState(DEFI_TEST_ACCOUNT.addr)['1']!.result
2141+
const uniAfterSkip = secondResult?.defiPositions.positionsByProvider.find(
2142+
(p) => getProviderId(p.providerName) === 'uniswap v3'
2143+
)
2144+
2145+
// MUST BE MIXED!!!!!
2146+
expect(uniAfterSkip?.source).toBe('mixed')
2147+
})
2148+
20702149
describe('Defi apps', () => {
20712150
it('should skip update if canSkipUpdate=true', async () => {
20722151
const { controller } = await prepareTest()

src/controllers/portfolio/portfolio.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1340,7 +1340,7 @@ export class PortfolioController extends EventEmitter implements IPortfolioContr
13401340
network,
13411341
this.#fetch,
13421342
state.result?.defiPositions.positionsByProvider || [],
1343-
discoveryData?.data?.defi?.positions || [],
1343+
discoveryData?.data?.defi?.positions,
13441344
getIsExternalApiDefiPositionsCallSuccessful(discoveryData)
13451345
)
13461346
])

src/libs/portfolio/helpers.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,13 @@ describe('Portfolio helpers', () => {
276276

277277
expect(total.usd).toBe(150.05)
278278
})
279+
it('Returns the defi total when there are no tokens', () => {
280+
const defiState = structuredClone(PORTFOLIO_STATE['1']?.result?.defiPositions!)
281+
282+
const total = getTotal([], defiState)
283+
284+
expect(total.usd).toBeGreaterThan(0)
285+
})
279286
})
280287
describe('getHintsError', () => {
281288
it('NoApiHintsError is returned if there are no previous hints', () => {

src/libs/portfolio/helpers.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -569,6 +569,12 @@ export const getTotal = (
569569
)
570570
}
571571

572+
// In case the user doesn't have any tokens or the function is calculating for the custom
573+
// network `defiApps` that doesn't have any tokens
574+
if (!Object.keys(tokensTotal).length && Object.keys(defiTotal).length > 0) {
575+
return defiTotal
576+
}
577+
572578
return Object.keys(tokensTotal).reduce((cur, key) => {
573579
cur[key] = (tokensTotal[key] || 0) + (defiTotal[key] || 0)
574580

0 commit comments

Comments
 (0)