Skip to content

Commit

Permalink
chore(jumpstart): support for reclaim from retired jumpstart contracts (
Browse files Browse the repository at this point in the history
#5200)

### Description

This PR adds a `retiredContractAddresses` to the jumpstart dynamic
config.

If we need to change the contract for whatever reason, we should still
allow users to reclaim their funds that were sent to previous versions
of the contract. The only place we need to do this is in the transaction
classification stage - we should mark transactions to _any_ jumpstart
contract appropriately, from there the jumpstart transaction details
screen will allow reclaim from that contract directly.

Of course this logic relies on the abi of the jumpstart contracts
staying the same, since a single abi is baked into the app.

### Test plan

Unit tests

### Related issues

n/a

### Backwards compatibility

Y

### Network scalability

Y
  • Loading branch information
kathaypacific authored Apr 3, 2024
1 parent 3315e1e commit 6cb5702
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 9 deletions.
40 changes: 38 additions & 2 deletions src/jumpstart/JumpstartTransactionDetailsScreen.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,19 @@ const mockReclaimTx: TransactionRequest = {
maxFeePerGas: BigInt(1),
maxPriorityFeePerGas: undefined,
}
const mockRetiredContractAddress = '0xretired'
const mockTransactionHash = '0x544367eaf2b01622dd1c7b75a6b19bf278d72127aecfb2e5106424c40c268e8b'

describe('JumpstartTransactionDetailsScreen', () => {
beforeEach(() => {
jest.clearAllMocks()

jest.mocked(getDynamicConfigParams).mockReturnValue({
jumpstartContracts: {
[NetworkId['celo-alfajores']]: { contractAddress: mockJumpstartAdddress },
[NetworkId['celo-alfajores']]: {
contractAddress: mockJumpstartAdddress,
retiredContractAddresses: [mockRetiredContractAddress],
},
},
})
jest.mocked(prepareTransactions).mockResolvedValue({
Expand Down Expand Up @@ -115,7 +120,7 @@ describe('JumpstartTransactionDetailsScreen', () => {
__typename: 'TokenTransferV3',
networkId: NetworkId['celo-alfajores'],
type,
transactionHash: '0x544367eaf2b01622dd1c7b75a6b19bf278d72127aecfb2e5106424c40c268e8b',
transactionHash: mockTransactionHash,
timestamp: 1542306118,
block: '8648978',
address,
Expand All @@ -139,6 +144,11 @@ describe('JumpstartTransactionDetailsScreen', () => {
)
)
expect(getByTestId('JumpstartContent/AmountValue')).toHaveTextContent('10.00 cUSD')
expect(fetchClaimStatus).toHaveBeenCalledWith(
mockJumpstartAdddress,
'celo-alfajores',
mockTransactionHash
)
})

it('shows the correct amount and no reclaim button for jumpstart received transactions', async () => {
Expand Down Expand Up @@ -234,6 +244,32 @@ describe('JumpstartTransactionDetailsScreen', () => {
])
})

it('uses the relevant jumpstart contract to prepare the reclaim transaction', async () => {
jest.mocked(fetchClaimStatus).mockResolvedValue({
beneficiary: mockAccount,
index: 0,
claimed: false,
})
renderScreen({
transaction: tokenTransfer({
type: TokenTransactionTypeV2.Sent,
address: mockRetiredContractAddress,
}),
})

await waitFor(() =>
expect(fetchClaimStatus).toHaveBeenCalledWith(
mockRetiredContractAddress,
'celo-alfajores',
mockTransactionHash
)
)
expect(prepareTransactions).toHaveBeenCalledWith({
baseTransactions: [expect.objectContaining({ to: mockRetiredContractAddress })],
feeCurrencies: expect.any(Array),
})
})

it('shows an error if the reclaim failed', async () => {
jest.mocked(fetchClaimStatus).mockResolvedValue({
beneficiary: mockAccount,
Expand Down
6 changes: 5 additions & 1 deletion src/statsig/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,11 @@ export const DynamicConfigs = {
configName: StatsigDynamicConfigs.WALLET_JUMPSTART_CONFIG,
defaultValues: {
jumpstartContracts: {} as {
[key in NetworkId]?: { contractAddress?: string; depositERC20GasEstimate: string }
[key in NetworkId]?: {
contractAddress?: string
depositERC20GasEstimate: string
retiredContractAddresses?: string[]
}
},
maxAllowedSendAmountUsd: 100,
},
Expand Down
13 changes: 10 additions & 3 deletions src/transactions/feed/TransferFeedItem.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const MOCK_CONTACT = {
contactId: 'contactId',
address: MOCK_ADDRESS,
}
const mockRetiredJumpstartAdddress = '0xabc'

jest.mock('src/statsig')

Expand All @@ -68,7 +69,10 @@ describe('TransferFeedItem', () => {
jest.mocked(getFeatureGate).mockReturnValue(true)
jest.mocked(getDynamicConfigParams).mockReturnValue({
jumpstartContracts: {
[NetworkId['celo-alfajores']]: { contractAddress: mockJumpstartAdddress },
[NetworkId['celo-alfajores']]: {
contractAddress: mockJumpstartAdddress,
retiredContractAddresses: [mockRetiredJumpstartAdddress],
},
},
})
})
Expand Down Expand Up @@ -654,10 +658,13 @@ describe('TransferFeedItem', () => {
expect(queryByTestId('TransferFeedItem/tokenAmount')).toBeNull()
})

it('renders correctly for jumpstart deposit', async () => {
it.each([
{ address: mockJumpstartAdddress, addressType: 'current' },
{ address: mockRetiredJumpstartAdddress, addressType: 'retired' },
])('renders correctly for jumpstart deposit to $addressType contract', async ({ address }) => {
const { getByTestId } = renderScreen({
type: TokenTransactionTypeV2.Sent,
address: mockJumpstartAdddress,
address,
amount: {
tokenAddress: mockCusdAddress,
tokenId: mockCusdTokenId,
Expand Down
12 changes: 9 additions & 3 deletions src/transactions/feed/TransferFeedItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { useTokenInfo } from 'src/tokens/hooks'
import TransactionFeedItemImage from 'src/transactions/feed/TransactionFeedItemImage'
import { useTransferFeedDetails } from 'src/transactions/transferFeedUtils'
import { TokenTransfer } from 'src/transactions/types'
import { isPresent } from 'src/utils/typescript'
interface Props {
transfer: TokenTransfer
}
Expand Down Expand Up @@ -99,10 +100,15 @@ function TransferFeedItem({ transfer }: Props) {
}

function isJumpstartTransaction(tx: TokenTransfer) {
const jumpstartAddress = getDynamicConfigParams(
const jumpstartConfig = getDynamicConfigParams(
DynamicConfigs[StatsigDynamicConfigs.WALLET_JUMPSTART_CONFIG]
).jumpstartContracts[tx.networkId]?.contractAddress
return tx.address === jumpstartAddress
).jumpstartContracts[tx.networkId]
const jumpstartAddresses = [
jumpstartConfig?.contractAddress,
...(jumpstartConfig?.retiredContractAddresses ?? []),
].filter(isPresent)

return jumpstartAddresses.includes(tx.address)
}

const styles = StyleSheet.create({
Expand Down

0 comments on commit 6cb5702

Please sign in to comment.