From fb7fb24621c704b8885a4d6e1432cfed4e1a4823 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 10 Mar 2026 19:41:54 +0000 Subject: [PATCH] Fix Coinflow withdrawal tx compatibility and ATA fee payer Co-authored-by: Ray Jacobson --- package-lock.json | 87 ++++++++----------- .../common/src/hooks/useCoinflowAdapter.ts | 5 -- .../src/services/audius-backend/solana.ts | 38 +++++--- .../src/store/ui/withdraw-usdc/sagas.ts | 11 ++- packages/web/package.json | 2 +- 5 files changed, 74 insertions(+), 69 deletions(-) diff --git a/package-lock.json b/package-lock.json index 5b17801938b..91760776f6c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5798,56 +5798,6 @@ "version": "4.0.0", "license": "ISC" }, - "node_modules/@coinflowlabs/react": { - "version": "5.5.1", - "resolved": "https://registry.npmjs.org/@coinflowlabs/react/-/react-5.5.1.tgz", - "integrity": "sha512-3KKjEUqlzTJGO9KNKfkelWnoY3ksGmRPc8rb9zoaa5ofN4BKKciNiVGRK0I3vo76+Lgh6fyZ5/W+WEtZEECutQ==", - "license": "Apache-2.0", - "dependencies": { - "@nsure-ai/web-client-sdk": "^1.1.90", - "bn.js": "^5.2.2", - "bs58": "~5.0.0", - "lz-string": "^1.5.0" - }, - "peerDependencies": { - "@coinflowlabs/lib-common": "*", - "@solana/web3.js": ">=1.54.0", - "bs58": "~5.0.0", - "react": ">=16" - }, - "peerDependenciesMeta": { - "@coinflowlabs/lib-common": { - "optional": true - }, - "@solana/web3.js": { - "optional": true - }, - "bs58": { - "optional": true - } - } - }, - "node_modules/@coinflowlabs/react/node_modules/base-x": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.1.tgz", - "integrity": "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==", - "license": "MIT" - }, - "node_modules/@coinflowlabs/react/node_modules/bn.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.2.tgz", - "integrity": "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw==", - "license": "MIT" - }, - "node_modules/@coinflowlabs/react/node_modules/bs58": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz", - "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==", - "license": "MIT", - "dependencies": { - "base-x": "^4.0.0" - } - }, "node_modules/@commander-js/extra-typings": { "version": "12.1.0", "license": "MIT", @@ -133888,7 +133838,7 @@ "@audius/harmony": "*", "@audius/sdk": "*", "@cloudflare/kv-asset-handler": "0.2.0", - "@coinflowlabs/react": "5.5.1", + "@coinflowlabs/react": "^5.8.1", "@emotion/css": "11.13.5", "@emotion/react": "11.14.0", "@emotion/server": "11.11.0", @@ -134151,6 +134101,35 @@ "url": "https://paulmillr.com/funding/" } }, + "packages/web/node_modules/@coinflowlabs/react": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/@coinflowlabs/react/-/react-5.8.1.tgz", + "integrity": "sha512-S2GgnJLltxd7Pa6Vh1LdpVwS/apT10wJ22rAIOrhP/H1jGZ+mCBnEJKs8725Ec5GEX3Gol+oAhvz+f6ESeVIGA==", + "license": "Apache-2.0", + "dependencies": { + "@nsure-ai/web-client-sdk": "^1.1.90", + "bn.js": "^5.2.3", + "bs58": "~5.0.0", + "lz-string": "^1.5.0" + }, + "peerDependencies": { + "@coinflowlabs/lib-common": "*", + "@solana/web3.js": ">=1.54.0", + "bs58": "~5.0.0", + "react": ">=16" + }, + "peerDependenciesMeta": { + "@coinflowlabs/lib-common": { + "optional": true + }, + "@solana/web3.js": { + "optional": true + }, + "bs58": { + "optional": true + } + } + }, "packages/web/node_modules/@electron/notarize": { "version": "2.2.0", "dev": true, @@ -136013,6 +135992,12 @@ "version": "5.0.0", "license": "MIT" }, + "packages/web/node_modules/bn.js": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.3.tgz", + "integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==", + "license": "MIT" + }, "packages/web/node_modules/body-scroll-lock": { "version": "4.0.0-beta.0", "license": "MIT" diff --git a/packages/common/src/hooks/useCoinflowAdapter.ts b/packages/common/src/hooks/useCoinflowAdapter.ts index 976f546fbc3..ee4f97c4c42 100644 --- a/packages/common/src/hooks/useCoinflowAdapter.ts +++ b/packages/common/src/hooks/useCoinflowAdapter.ts @@ -64,11 +64,6 @@ export const useCoinflowWithdrawalAdapter = () => { sendTransaction: async ( transaction: Transaction | VersionedTransaction ) => { - if (transaction instanceof VersionedTransaction) { - throw new Error( - 'VersionedTransaction not supported in withdrawal adapter' - ) - } if (!currentUser) { throw new Error('Missing current user') } diff --git a/packages/common/src/services/audius-backend/solana.ts b/packages/common/src/services/audius-backend/solana.ts index b9a0c12ff8b..34866c86d4a 100644 --- a/packages/common/src/services/audius-backend/solana.ts +++ b/packages/common/src/services/audius-backend/solana.ts @@ -17,7 +17,8 @@ import { Keypair, PublicKey, Transaction, - TransactionInstruction + TransactionInstruction, + VersionedTransaction } from '@solana/web3.js' import { CommonStoreContext } from '~/store/storeContext' @@ -261,7 +262,7 @@ export const decorateCoinflowWithdrawalTransaction = async ( ethAddress, wallet }: { - transaction: Transaction + transaction: Transaction | VersionedTransaction ethAddress: string wallet: Keypair } @@ -278,7 +279,9 @@ export const decorateCoinflowWithdrawalTransaction = async ( // Filter any compute budget instructions since the budget will // definitely change - const instructions = transaction.instructions.filter( + const originalInstructions = + await sdk.services.solanaClient.getInstructions(transaction) + const instructions = originalInstructions.filter( (instruction) => !instruction.programId.equals(ComputeBudgetProgram.programId) ) @@ -447,6 +450,8 @@ type TransferFromUserBankParams = { analyticsFields: any /** If included, will attach a signed memo indicating a recovery transaction. */ signer?: Keypair + /** If included, this keypair pays tx/ATA fees instead of relay. */ + feePayerSigner?: Keypair } export const transferFromUserBank = async ({ @@ -459,11 +464,15 @@ export const transferFromUserBank = async ({ track, make, analyticsFields, - signer + signer, + feePayerSigner }: TransferFromUserBankParams) => { let isCreatingTokenAccount = false try { const instructions: TransactionInstruction[] = [] + const feePayer = feePayerSigner?.publicKey + ? feePayerSigner.publicKey + : await sdk.services.solanaRelay.getFeePayer() // Check if destinationWallet is already an associated token account let destination = destinationWallet @@ -520,10 +529,9 @@ export const transferFromUserBank = async ({ ...analyticsFields }) ) - const payerKey = await sdk.services.solanaRelay.getFeePayer() const createAtaInstruction = createAssociatedTokenAccountIdempotentInstruction( - payerKey, + feePayer, destination, destinationWallet, mint @@ -546,7 +554,8 @@ export const transferFromUserBank = async ({ await sdk.services.claimableTokensClient.createTransferInstruction({ ethWallet, mint, - destination + destination, + feePayer }) instructions.push(transferInstruction) @@ -566,11 +575,20 @@ export const transferFromUserBank = async ({ } const transaction = await sdk.services.solanaClient.buildTransaction({ - instructions + instructions, + feePayer: feePayerSigner?.publicKey }) - if (signer) { - transaction.sign([signer]) + const signers = [signer, feePayerSigner].filter( + (keypair): keypair is Keypair => !!keypair + ) + if (signers.length) { + const uniqueSigners = signers.filter( + (keypair, index) => + signers.findIndex((s) => s.publicKey.equals(keypair.publicKey)) === + index + ) + transaction.sign(uniqueSigners) } const signature = diff --git a/packages/common/src/store/ui/withdraw-usdc/sagas.ts b/packages/common/src/store/ui/withdraw-usdc/sagas.ts index 89141736f20..337f2a25173 100644 --- a/packages/common/src/store/ui/withdraw-usdc/sagas.ts +++ b/packages/common/src/store/ui/withdraw-usdc/sagas.ts @@ -86,7 +86,8 @@ function* doWithdrawUSDCCoinflow({ track, make, analyticsFields, - signer: rootSolanaAccount + signer: rootSolanaAccount, + feePayerSigner: rootSolanaAccount }) console.debug( @@ -209,6 +210,7 @@ function* doWithdrawUSDCManualTransfer({ const withdrawalAmountDollars = amount / 100 const queryClient = yield* getContext('queryClient') const sdk = yield* getSDK() + const solanaWalletService = yield* getContext('solanaWalletService') const connection = sdk.services.solanaClient.connection const env = yield* getContext('env') const mint = new PublicKey(env.USDC_MINT_ADDRESS) @@ -234,6 +236,10 @@ function* doWithdrawUSDCManualTransfer({ } const ethWallet = user.wallet const destinationWallet = new PublicKey(destinationAddress) + const rootSolanaAccount = yield* call([solanaWalletService, 'getKeypair']) + if (!rootSolanaAccount) { + throw new Error('Missing solana root wallet') + } const signature = yield* call(transferFromUserBank, { connection, @@ -244,7 +250,8 @@ function* doWithdrawUSDCManualTransfer({ destinationWallet, track, make, - analyticsFields + analyticsFields, + feePayerSigner: rootSolanaAccount }) console.debug('Withdraw USDC - successfully transferred USDC.', { diff --git a/packages/web/package.json b/packages/web/package.json index 82775c916df..42682eb2b82 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -74,7 +74,7 @@ "@audius/harmony": "*", "@audius/sdk": "*", "@cloudflare/kv-asset-handler": "0.2.0", - "@coinflowlabs/react": "5.5.1", + "@coinflowlabs/react": "^5.8.1", "@emotion/css": "11.13.5", "@emotion/react": "11.14.0", "@emotion/server": "11.11.0",