Skip to content

Commit

Permalink
Fix USDC user bank creation (#10445)
Browse files Browse the repository at this point in the history
  • Loading branch information
rickyrombo committed Nov 15, 2024
1 parent 60a39fa commit 13f80fd
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 74 deletions.
5 changes: 5 additions & 0 deletions .changeset/twelve-cycles-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@audius/sdk': patch
---

Fix getOrCreateUserBank failures due to low priority fees
8 changes: 1 addition & 7 deletions packages/common/src/services/audius-backend/solana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,21 +110,15 @@ export const getTokenAccountInfo = async (
audiusBackendInstance: AudiusBackend,
{
tokenAccount,
mint = DEFAULT_MINT,
commitment = 'processed'
}: {
tokenAccount: PublicKey
mint?: MintName
commitment?: Commitment
}
): Promise<Account | null> => {
return (
await audiusBackendInstance.getAudiusLibs()
).solanaWeb3Manager!.getTokenAccountInfo(
tokenAccount.toString(),
mint,
commitment
)
).solanaWeb3Manager!.getTokenAccountInfo(tokenAccount.toString(), commitment)
}

export const deriveUserBankPubkey = async (
Expand Down
79 changes: 23 additions & 56 deletions packages/common/src/store/buy-usdc/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
import { PublicKey } from '@solana/web3.js'
import { channel, Channel } from 'redux-saga'
import { call, delay, select, take } from 'typed-redux-saga'
import { call, delay, select } from 'typed-redux-saga'

import {
createUserBankIfNeeded,
getTokenAccountInfo,
MintName
} from '~/services/audius-backend/solana'
import { getTokenAccountInfo, MintName } from '~/services/audius-backend/solana'
import { IntKeys } from '~/services/remote-config'
import {
MAX_CONTENT_PRICE_CENTS,
Expand All @@ -16,66 +11,38 @@ import {
BUY_TOKEN_VIA_SOL_SLIPPAGE_BPS
} from '~/services/remote-config/defaults'

import { getAccountUser } from '../account/selectors'
import { getContext } from '../effects'
import { getFeePayer } from '../solana/selectors'

const POLL_ACCOUNT_INFO_DELAY_MS = 1000
const POLL_ACCOUNT_INFO_RETRIES = 30

// Create a channel to manage concurrent requests
let pendingUserBankCreation: Channel<{
result?: Awaited<ReturnType<typeof createUserBankIfNeeded>>
error?: Error
}> | null = null

/**
* Derives a USDC user bank for a given eth address, creating it if necessary.
* Defaults to the wallet of the current user. Uses a channel to manage concurrent
* requests so there is only one creation attempt in flight at a time.
* Defaults to the wallet of the current user.
*/
export function* getOrCreateUSDCUserBank(ethAddress?: string) {
// If there's already a pending operation, wait for its result
if (pendingUserBankCreation) {
const { result, error } = yield* take(pendingUserBankCreation)
if (error) throw error
if (!result)
throw new Error('No user bank returned from createUserBankIfNeeded')
return result
}

// Create a new channel for this bank creation
pendingUserBankCreation = channel()
try {
const audiusBackendInstance = yield* getContext('audiusBackendInstance')
const { track } = yield* getContext('analytics')
const feePayerOverride = yield* select(getFeePayer)

if (!feePayerOverride) {
throw new Error(
'getOrCreateUSDCUserBank: unexpectedly no fee payer override'
)
const audiusSdk = yield* getContext('audiusSdk')
const sdk = yield* call(audiusSdk)
let ethWallet = ethAddress
if (!ethWallet) {
const user = yield* select(getAccountUser)
if (!user?.wallet) {
throw new Error('Failed to create USDC user bank: No user wallet found.')
}

// Perform the bank creation
const result = yield* call(createUserBankIfNeeded, audiusBackendInstance, {
ethAddress,
feePayerOverride,
mint: 'usdc',
recordAnalytics: track
})

// Put the successful result on the channel
pendingUserBankCreation.put({ result })
return result
} catch (error) {
// Put the error on the channel
pendingUserBankCreation.put({ error: error as Error })
throw error
} finally {
// Close and cleanup the channel
pendingUserBankCreation.close()
pendingUserBankCreation = null
ethWallet = user.wallet
}
const { userBank } = yield* call(
[
sdk.services.claimableTokensClient,
sdk.services.claimableTokensClient.getOrCreateUserBank
],
{
ethWallet,
mint: 'USDC'
}
)
return userBank
}

/** Polls for the given token account info up to a maximum retry count. Useful
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import {
} from '@audius/spl'
import { SendTransactionOptions } from '@solana/wallet-adapter-base'
import {
TransactionMessage,
VersionedTransaction,
Secp256k1Program,
PublicKey,
Transaction,
SendTransactionError
SendTransactionError,
ComputeBudgetProgram
} from '@solana/web3.js'

import { productionConfig } from '../../../../config/production'
Expand Down Expand Up @@ -152,18 +152,22 @@ export class ClaimableTokensClient {
userBank,
programId: this.programId
})
const confirmationStrategyArgs =
const computeBudgetLimitInstruction =
ComputeBudgetProgram.setComputeUnitLimit({
units: 50000
})
const { blockhash, lastValidBlockHeight } =
await this.client.connection.getLatestBlockhash()
const message = new TransactionMessage({
payerKey: feePayer,
recentBlockhash: confirmationStrategyArgs.blockhash,
instructions: [createUserBankInstruction]
}).compileToLegacyMessage()
const transaction = new VersionedTransaction(message)
const transaction = await this.client.buildTransaction({
instructions: [
createUserBankInstruction,
computeBudgetLimitInstruction
],
recentBlockhash: blockhash
})
const signature = await this.sendTransaction(transaction)
const confirmationStrategy = { ...confirmationStrategyArgs, signature }
await this.client.connection.confirmTransaction(
confirmationStrategy,
{ blockhash, lastValidBlockHeight, signature },
'finalized'
)
return { userBank, didExist: false }
Expand Down

0 comments on commit 13f80fd

Please sign in to comment.