fix: remove version check in redux lists update + migrate outdated USDC saved tokens (#7544)
* fix: remove version check lists update * add migration and test * pr review
This commit is contained in:
parent
9eaa22f644
commit
443a00a777
@ -12,7 +12,6 @@ import { useAllLists } from 'state/lists/hooks'
|
|||||||
import { useFetchListCallback } from '../../hooks/useFetchListCallback'
|
import { useFetchListCallback } from '../../hooks/useFetchListCallback'
|
||||||
import useIsWindowVisible from '../../hooks/useIsWindowVisible'
|
import useIsWindowVisible from '../../hooks/useIsWindowVisible'
|
||||||
import { acceptListUpdate } from './actions'
|
import { acceptListUpdate } from './actions'
|
||||||
import { shouldAcceptVersionUpdate } from './utils'
|
|
||||||
|
|
||||||
export default function Updater(): null {
|
export default function Updater(): null {
|
||||||
const { provider } = useWeb3React()
|
const { provider } = useWeb3React()
|
||||||
@ -61,7 +60,7 @@ export default function Updater(): null {
|
|||||||
})
|
})
|
||||||
}, [dispatch, fetchList, lists, rehydrated])
|
}, [dispatch, fetchList, lists, rehydrated])
|
||||||
|
|
||||||
// automatically update lists if versions are minor/patch
|
// automatically update lists for every version update
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
Object.keys(lists).forEach((listUrl) => {
|
Object.keys(lists).forEach((listUrl) => {
|
||||||
const list = lists[listUrl]
|
const list = lists[listUrl]
|
||||||
@ -71,13 +70,7 @@ export default function Updater(): null {
|
|||||||
case VersionUpgrade.NONE:
|
case VersionUpgrade.NONE:
|
||||||
throw new Error('unexpected no version bump')
|
throw new Error('unexpected no version bump')
|
||||||
case VersionUpgrade.PATCH:
|
case VersionUpgrade.PATCH:
|
||||||
case VersionUpgrade.MINOR: {
|
case VersionUpgrade.MINOR:
|
||||||
if (shouldAcceptVersionUpdate(listUrl, list.current, list.pendingUpdate, bump)) {
|
|
||||||
dispatch(acceptListUpdate(listUrl))
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// update any active or inactive lists
|
|
||||||
case VersionUpgrade.MAJOR:
|
case VersionUpgrade.MAJOR:
|
||||||
dispatch(acceptListUpdate(listUrl))
|
dispatch(acceptListUpdate(listUrl))
|
||||||
}
|
}
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
import { TokenList, VersionUpgrade } from '@uniswap/token-lists'
|
|
||||||
|
|
||||||
import { shouldAcceptVersionUpdate } from './utils'
|
|
||||||
|
|
||||||
function buildTokenList(count: number): TokenList {
|
|
||||||
const tokens = []
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
tokens.push({
|
|
||||||
name: `Token ${i}`,
|
|
||||||
address: `0x${i.toString().padStart(40, '0')}`,
|
|
||||||
symbol: `T${i}`,
|
|
||||||
decimals: 18,
|
|
||||||
chainId: 1,
|
|
||||||
logoURI: `https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x${i
|
|
||||||
.toString()
|
|
||||||
.padStart(40, '0')}/logo.png`,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
name: 'Defi',
|
|
||||||
logoURI:
|
|
||||||
'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0x514910771AF9Ca656af840dff83E8264EcF986CA/logo.png',
|
|
||||||
keywords: ['defi', 'uniswap'],
|
|
||||||
timestamp: '2021-03-12T00:00:00.000Z',
|
|
||||||
version: {
|
|
||||||
major: 1,
|
|
||||||
minor: 0,
|
|
||||||
patch: 0,
|
|
||||||
},
|
|
||||||
tokens,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('shouldAcceptMinorVersionUpdate', () => {
|
|
||||||
it('returns false for patch when tokens have changed', () => {
|
|
||||||
jest.spyOn(console, 'debug').mockReturnValue(undefined)
|
|
||||||
expect(shouldAcceptVersionUpdate('test_list', buildTokenList(1), buildTokenList(2), VersionUpgrade.PATCH)).toEqual(
|
|
||||||
false
|
|
||||||
)
|
|
||||||
expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(/should have been MAJOR/))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns true for patch when tokens are the same', () => {
|
|
||||||
expect(shouldAcceptVersionUpdate('test_list', buildTokenList(1), buildTokenList(1), VersionUpgrade.PATCH)).toEqual(
|
|
||||||
true
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns true for minor version bump with tokens added', () => {
|
|
||||||
expect(shouldAcceptVersionUpdate('test_list', buildTokenList(1), buildTokenList(2), VersionUpgrade.MINOR)).toEqual(
|
|
||||||
true
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns true for no version bump', () => {
|
|
||||||
expect(shouldAcceptVersionUpdate('test_list', buildTokenList(1), buildTokenList(2), VersionUpgrade.MINOR)).toEqual(
|
|
||||||
true
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('returns false for minor version bump with tokens removed', () => {
|
|
||||||
jest.spyOn(console, 'debug').mockReturnValue(undefined)
|
|
||||||
expect(shouldAcceptVersionUpdate('test_list', buildTokenList(2), buildTokenList(1), VersionUpgrade.MINOR)).toEqual(
|
|
||||||
false
|
|
||||||
)
|
|
||||||
expect(console.debug).toHaveBeenCalledWith(expect.stringMatching(/should have been MAJOR/))
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,19 +0,0 @@
|
|||||||
import { minVersionBump, TokenList, VersionUpgrade } from '@uniswap/token-lists'
|
|
||||||
|
|
||||||
export function shouldAcceptVersionUpdate(
|
|
||||||
listUrl: string,
|
|
||||||
current: TokenList,
|
|
||||||
update: TokenList,
|
|
||||||
targetBump: VersionUpgrade.PATCH | VersionUpgrade.MINOR
|
|
||||||
): boolean {
|
|
||||||
const min = minVersionBump(current.tokens, update.tokens)
|
|
||||||
// Automatically update minor/patch as long as bump matches the min update.
|
|
||||||
if (targetBump >= min) {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
console.debug(
|
|
||||||
`List at url ${listUrl} could not automatically update because the version bump was only PATCH/MINOR while the update had breaking changes and should have been MAJOR`
|
|
||||||
)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
74
src/state/migrations/3.test.ts
Normal file
74
src/state/migrations/3.test.ts
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { ChainId, Token } from '@uniswap/sdk-core'
|
||||||
|
import { createMigrate } from 'redux-persist'
|
||||||
|
import { RouterPreference } from 'state/routing/types'
|
||||||
|
import { SlippageTolerance } from 'state/user/types'
|
||||||
|
|
||||||
|
import { migration1 } from './1'
|
||||||
|
import { migration2 } from './2'
|
||||||
|
import { migration3, PersistAppStateV3 } from './3'
|
||||||
|
|
||||||
|
const previousState: PersistAppStateV3 = {
|
||||||
|
user: {
|
||||||
|
userLocale: null,
|
||||||
|
userRouterPreference: RouterPreference.API,
|
||||||
|
userHideClosedPositions: false,
|
||||||
|
userSlippageTolerance: SlippageTolerance.Auto,
|
||||||
|
userSlippageToleranceHasBeenMigratedToAuto: true,
|
||||||
|
userDeadline: 1800,
|
||||||
|
tokens: {
|
||||||
|
// wrong tokens
|
||||||
|
[ChainId.OPTIMISM]: {
|
||||||
|
'0x7F5c764cBc14f9669B88837ca1490cCa17c31607': new Token(
|
||||||
|
ChainId.OPTIMISM,
|
||||||
|
'0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
|
||||||
|
6,
|
||||||
|
'USDC',
|
||||||
|
'USD Coin'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
[ChainId.BASE]: {
|
||||||
|
'0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA': new Token(
|
||||||
|
ChainId.BASE,
|
||||||
|
'0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA',
|
||||||
|
6,
|
||||||
|
'USDC',
|
||||||
|
'USD Coin'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pairs: {},
|
||||||
|
timestamp: Date.now(),
|
||||||
|
hideBaseWalletBanner: false,
|
||||||
|
},
|
||||||
|
_persist: {
|
||||||
|
version: 2,
|
||||||
|
rehydrated: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('migration to v3', () => {
|
||||||
|
it('should migrate users who currently have outdated USDC.e saved', async () => {
|
||||||
|
const migrator = createMigrate(
|
||||||
|
{
|
||||||
|
1: migration1,
|
||||||
|
2: migration2,
|
||||||
|
3: migration3,
|
||||||
|
},
|
||||||
|
{ debug: false }
|
||||||
|
)
|
||||||
|
const result: any = await migrator(previousState, 3)
|
||||||
|
expect(Object.keys(result?.user?.tokens).length).toEqual(2)
|
||||||
|
expect(result?.user?.tokens[ChainId.OPTIMISM]?.['0x7F5c764cBc14f9669B88837ca1490cCa17c31607'].symbol).toEqual(
|
||||||
|
'USDC.e'
|
||||||
|
)
|
||||||
|
expect(result?.user?.tokens[ChainId.OPTIMISM]?.['0x7F5c764cBc14f9669B88837ca1490cCa17c31607'].name).toEqual(
|
||||||
|
'Bridged USDC'
|
||||||
|
)
|
||||||
|
expect(result?.user?.tokens[ChainId.BASE]?.['0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA'].symbol).toEqual('USDbC')
|
||||||
|
expect(result?.user?.tokens[ChainId.BASE]?.['0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA'].name).toEqual(
|
||||||
|
'USD Base Coin'
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result?._persist.version).toEqual(3)
|
||||||
|
})
|
||||||
|
})
|
55
src/state/migrations/3.ts
Normal file
55
src/state/migrations/3.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { Token } from '@uniswap/sdk-core'
|
||||||
|
import { ChainId } from '@uniswap/sdk-core'
|
||||||
|
import { PersistState } from 'redux-persist'
|
||||||
|
import { serializeToken } from 'state/user/hooks'
|
||||||
|
import { UserState } from 'state/user/reducer'
|
||||||
|
|
||||||
|
export type PersistAppStateV3 = {
|
||||||
|
_persist: PersistState
|
||||||
|
} & { user?: UserState }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Migration to clear users' imported token lists, after
|
||||||
|
* breaking changes to token info for multichain native USDC.
|
||||||
|
*/
|
||||||
|
export const migration3 = (state: PersistAppStateV3 | undefined) => {
|
||||||
|
if (state?.user) {
|
||||||
|
// Update USDC.e tokens to use the the new USDC.e symbol (from USDC)
|
||||||
|
const USDCe_ADDRESSES: { [key in ChainId]?: string } = {
|
||||||
|
[ChainId.OPTIMISM]: '0x7F5c764cBc14f9669B88837ca1490cCa17c31607',
|
||||||
|
[ChainId.OPTIMISM_GOERLI]: '0x7E07E15D2a87A24492740D16f5bdF58c16db0c4E',
|
||||||
|
[ChainId.ARBITRUM_ONE]: '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8',
|
||||||
|
[ChainId.ARBITRUM_GOERLI]: '0x8FB1E3fC51F3b789dED7557E680551d93Ea9d892',
|
||||||
|
[ChainId.AVALANCHE]: '0xA7D7079b0FEaD91F3e65f86E8915Cb59c1a4C664',
|
||||||
|
[ChainId.POLYGON]: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
|
||||||
|
[ChainId.POLYGON_MUMBAI]: '0xe11a86849d99f524cac3e7a0ec1241828e332c62',
|
||||||
|
}
|
||||||
|
for (const [chainId, address] of Object.entries(USDCe_ADDRESSES)) {
|
||||||
|
const chainIdKey = Number(chainId) as ChainId
|
||||||
|
if (state.user.tokens[chainIdKey]?.[address]) {
|
||||||
|
state.user.tokens[chainIdKey][address] = serializeToken(
|
||||||
|
new Token(chainIdKey, address, 6, 'USDC.e', 'Bridged USDC')
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Update USDbC token to use the new USDbC symbol (from USDC)
|
||||||
|
const USDbC_BASE = new Token(
|
||||||
|
ChainId.BASE,
|
||||||
|
'0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA',
|
||||||
|
6,
|
||||||
|
'USDbC',
|
||||||
|
'USD Base Coin'
|
||||||
|
)
|
||||||
|
if (state.user.tokens[ChainId.BASE]?.[USDbC_BASE.address]) {
|
||||||
|
state.user.tokens[ChainId.BASE][USDbC_BASE.address] = serializeToken(USDbC_BASE)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
_persist: {
|
||||||
|
...state._persist,
|
||||||
|
version: 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user