Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PAY-3648] Migrate buy-usdc to sdk #10677

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
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
29 changes: 9 additions & 20 deletions packages/common/src/hooks/useCoinflowAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { useAppContext } from '~/context'
import { Name } from '~/models/Analytics'
import {
decorateCoinflowWithdrawalTransaction,
relayTransaction,
getRootSolanaAccount
} from '~/services/audius-backend'
import {
Expand Down Expand Up @@ -76,40 +75,30 @@ export const useCoinflowWithdrawalAdapter = () => {
feePayer
})
finalTransaction.partialSign(wallet)
const { res, error, errorCode } = await relayTransaction(
audiusBackend,
{
transaction: finalTransaction,
const { signature } = await sdk.services.solanaRelay.relay({
transaction: finalTransaction,
sendOptions: {
skipPreflight: true
}
)
if (!res) {
})
if (!signature) {
console.error('Relaying Coinflow transaction failed.', {
error,
errorCode,
finalTransaction
})
track(
make({
eventName:
Name.WITHDRAW_USDC_COINFLOW_SEND_TRANSACTION_FAILED,
error: error ?? undefined,
errorCode: errorCode ?? undefined
eventName: Name.WITHDRAW_USDC_COINFLOW_SEND_TRANSACTION_FAILED
})
)
throw new Error(
`Relaying Coinflow transaction failed: ${
error ?? 'Unknown error'
}`
)
throw new Error('Relaying Coinflow transaction failed')
}
track(
make({
eventName: Name.WITHDRAW_USDC_COINFLOW_SEND_TRANSACTION,
signature: res
signature
})
)
return res
return signature
}
}
})
Expand Down
124 changes: 45 additions & 79 deletions packages/common/src/services/audius-backend/solana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,15 +430,24 @@ export const decorateCoinflowWithdrawalTransaction = async (
data.decimals
)

const transferFromUserBankInstructions =
await solanaWeb3Manager.createTransferInstructionsFromCurrentUser({
amount: new BN(data.amount.toString()),
mint: 'usdc',
senderSolanaAddress: userBank,
recipientSolanaAddress: keys.destination.pubkey.toBase58(),
instructionIndex: transferInstructionIndex + 1,
feePayerKey: feePayer
const transferFromUserBankSecpInstruction =
await sdk.services.claimableTokensClient.createTransferSecpInstruction({
amount: data.amount,
mint: 'USDC',
destination: keys.destination.pubkey,
ethWallet: ethAddress,
instructionIndex: transferInstructionIndex + 1
})
const transferFromUserBankInstruction =
await sdk.services.claimableTokensClient.createTransferInstruction({
feePayer,
mint: 'USDC',
destination: keys.destination.pubkey,
ethWallet: ethAddress
})
const priorityFeeInstruction = ComputeBudgetProgram.setComputeUnitPrice({
microLamports: 100000
})

const withdrawalMemoInstruction = new TransactionInstruction({
keys: [
Expand All @@ -457,7 +466,9 @@ export const decorateCoinflowWithdrawalTransaction = async (
transferInstructionIndex,
1,
transferToUserBankInstruction,
...transferFromUserBankInstructions,
transferFromUserBankSecpInstruction,
transferFromUserBankInstruction,
priorityFeeInstruction,
withdrawalMemoInstruction
)

Expand All @@ -475,6 +486,7 @@ export const decorateCoinflowWithdrawalTransaction = async (
}

export const createTransferToUserBankTransaction = async (
sdk: AudiusSdk,
audiusBackendInstance: AudiusBackend,
{
userBank,
Expand All @@ -494,8 +506,6 @@ export const createTransferToUserBankTransaction = async (
recentBlockhash?: string
}
) => {
const libs = await audiusBackendInstance.getAudiusLibsTyped()
const mintPublicKey = libs.solanaWeb3Manager!.mints[mint]
const associatedTokenAccount = await findAssociatedTokenAddress(
audiusBackendInstance,
{
Expand Down Expand Up @@ -534,31 +544,30 @@ export const createTransferToUserBankTransaction = async (
* that doesn't add the purchase memo.
*/
export const createPaymentRouterRouteTransaction = async (
audiusBackendInstance: AudiusBackend,
sdk: AudiusSdk,
{
sender,
splits
splits,
total
}: {
sender: PublicKey
splits: Record<string, number | BN>
splits: { amount: bigint; wallet: PublicKey }[]
total: bigint
}
) => {
const solanaWeb3Manager = (await audiusBackendInstance.getAudiusLibsTyped())
.solanaWeb3Manager!
const { blockhash } = await solanaWeb3Manager
.getConnection()
.getLatestBlockhash()
const [transfer, route] =
// All the memo related parameters are ignored
await solanaWeb3Manager.getPurchaseContentWithPaymentRouterInstructions({
id: 0, // ignored
type: 'track', // ignored
blocknumber: 0, // ignored
splits,
purchaserUserId: 0, // ignored
senderAccount: sender,
purchaseAccess: PurchaseAccess.STREAM // ignored
const connection = sdk.services.solanaClient.connection
const { blockhash } = await connection.getLatestBlockhash()
const transfer =
await sdk.services.paymentRouterClient.createTransferInstruction({
total,
sourceWallet: sender,
mint: 'USDC'
})
const route = await sdk.services.paymentRouterClient.createRouteInstruction({
total,
splits,
mint: 'USDC'
})
return new Transaction({
recentBlockhash: blockhash,
feePayer: sender
Expand Down Expand Up @@ -600,55 +609,15 @@ export const relayTransaction = async (
})
}

/**
* Relays the given versioned transaction using the libs transaction handler
*/
export const relayVersionedTransaction = async (
audiusBackendInstance: AudiusBackend,
{
transaction,
addressLookupTableAccounts,
skipPreflight
}: {
transaction: VersionedTransaction
addressLookupTableAccounts: AddressLookupTableAccount[]
skipPreflight?: boolean
}
) => {
const placeholderSignature = Buffer.from(PLACEHOLDER_SIGNATURE)
const libs = await audiusBackendInstance.getAudiusLibsTyped()
const decompiledMessage = TransactionMessage.decompile(transaction.message, {
addressLookupTableAccounts
})
const signatures = transaction.message.staticAccountKeys
.slice(0, transaction.message.header.numRequiredSignatures)
.map((publicKey, index) => ({
publicKey: publicKey.toBase58(),
signature: Buffer.from(transaction.signatures[index])
}))
.filter((meta) => !meta.signature.equals(placeholderSignature))
return await libs.solanaWeb3Manager!.transactionHandler.handleTransaction({
instructions: decompiledMessage.instructions,
recentBlockhash: decompiledMessage.recentBlockhash,
signatures,
feePayerOverride: decompiledMessage.payerKey,
lookupTableAddresses: addressLookupTableAccounts.map((lut) =>
lut.key.toBase58()
),
skipPreflight
})
}

/**
* Helper that gets the lookup table accounts (that is, the account holding the lookup table,
* not the accounts _in_ the lookup table) from their addresses.
*/
export const getLookupTableAccounts = async (
audiusBackendInstance: AudiusBackend,
sdk: AudiusSdk,
{ lookupTableAddresses }: { lookupTableAddresses: string[] }
) => {
const libs = await audiusBackendInstance.getAudiusLibsTyped()
const connection = libs.solanaWeb3Manager!.getConnection()
const connection = sdk.services.solanaClient.connection
return await Promise.all(
lookupTableAddresses.map(async (address) => {
const account = await connection.getAddressLookupTable(
Expand All @@ -666,23 +635,20 @@ export const getLookupTableAccounts = async (
* Helper to create a versioned transaction with lookup tables
*/
export const createVersionedTransaction = async (
audiusBackendInstance: AudiusBackend,
sdk: AudiusSdk,
{
instructions,
lookupTableAddresses,
feePayer,
sdk
feePayer
}: {
instructions: TransactionInstruction[]
lookupTableAddresses: string[]
feePayer: PublicKey
sdk: AudiusSdk
}
) => {
const addressLookupTableAccounts = await getLookupTableAccounts(
audiusBackendInstance,
{ lookupTableAddresses }
)
const addressLookupTableAccounts = await getLookupTableAccounts(sdk, {
lookupTableAddresses
})
const recentBlockhash = await getRecentBlockhash({ sdk })

const message = new TransactionMessage({
Expand Down
31 changes: 13 additions & 18 deletions packages/common/src/store/buy-crypto/sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ import {
getRootSolanaAccount,
getTokenAccountInfo,
pollForBalanceChange,
pollForTokenBalanceChange,
relayVersionedTransaction
pollForTokenBalanceChange
} from '~/services/audius-backend/solana'
import { FeatureFlags } from '~/services/index'
import { IntKeys } from '~/services/remote-config/types'
Expand Down Expand Up @@ -143,7 +142,6 @@ function* swapSolForCrypto({
userbank: PublicKey
quoteResponse: Awaited<ReturnType<typeof jupiterInstance.quoteGet>>
}) {
const audiusBackendInstance = yield* getContext('audiusBackendInstance')
// Create a memo
// See: https://github.com/solana-labs/solana-program-library/blob/d6297495ea4dcc1bd48f3efdd6e3bbdaef25a495/memo/js/src/index.ts#L27
const memoInstruction = new TransactionInstruction({
Expand Down Expand Up @@ -213,23 +211,20 @@ function* swapSolForCrypto({
closeWSOLInstruction
]
const sdk = yield* getSDK()
const { transaction, addressLookupTableAccounts } = yield* call(
createVersionedTransaction,
audiusBackendInstance,
{
instructions,
lookupTableAddresses: addressLookupTableAddresses,
feePayer,
sdk
}
)
const { transaction } = yield* call(createVersionedTransaction, sdk, {
instructions,
lookupTableAddresses: addressLookupTableAddresses,
feePayer
})
transaction.sign([wallet])

return yield* call(relayVersionedTransaction, audiusBackendInstance, {
const { signature } = yield* call(sdk.services.solanaRelay.relay, {
transaction,
addressLookupTableAccounts,
skipPreflight: true
sendOptions: {
skipPreflight: true
}
})
return signature
}

function* assertRecoverySuccess({
Expand Down Expand Up @@ -529,7 +524,7 @@ function* doBuyCryptoViaSol({
const maxRetryCount = 3
const retryDelayMs = 3000
do {
const { res, error } = yield* call(swapSolForCrypto, {
const { signature } = yield* call(swapSolForCrypto, {
feePayer,
mint,
wallet,
Expand Down Expand Up @@ -806,7 +801,7 @@ function* recoverBuyCryptoViaSolIfNecessary() {

// Do the swap. Just do it once, slippage shouldn't be a
// concern since the quote is fresh and the tolerance is high.
const { res, error: recoveryError } = yield* call(swapSolForCrypto, {
const signature = yield* call(swapSolForCrypto, {
quoteResponse: exactInQuote,
mint,
wallet,
Expand Down
Loading