Skip to content

Commit

Permalink
[PAY-2241][PAY-2259] Add Funds via Coinflow (#6915)
Browse files Browse the repository at this point in the history
  • Loading branch information
rickyrombo authored Dec 12, 2023
1 parent 0f571a1 commit dc91844
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 3 deletions.
33 changes: 33 additions & 0 deletions packages/common/src/services/audius-backend/solana.ts
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,39 @@ export const createTransferToUserBankTransaction = async (
return tx
}

/**
* A pared down version of {@link purchaseContentWithPaymentRouter}
* that doesn't add the purchase memo.
*/
export const createPaymentRouterRouteTransaction = async (
audiusBackendInstance: AudiusBackend,
{
sender,
splits
}: {
sender: PublicKey
splits: Record<string, number | BN>
}
) => {
const solanaWeb3Manager = (await audiusBackendInstance.getAudiusLibsTyped())
.solanaWeb3Manager!
const { blockhash } = await solanaWeb3Manager.connection.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
})
return new Transaction({
recentBlockhash: blockhash,
feePayer: sender
}).add(transfer, route)
}

/**
* Relays the given transaction using the libs transaction handler
*/
Expand Down
59 changes: 58 additions & 1 deletion packages/common/src/store/buy-usdc/sagas.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { USDC } from '@audius/fixed-decimal'
import { Keypair, PublicKey } from '@solana/web3.js'
import retry from 'async-retry'
import BN from 'bn.js'
import { takeLatest } from 'redux-saga/effects'
import { call, put, race, select, take, takeLeading } from 'typed-redux-saga'

import { Name } from 'models/Analytics'
import { ErrorLevel } from 'models/ErrorReporting'
import { PurchaseVendor } from 'models/PurchaseContent'
import {
createPaymentRouterRouteTransaction,
createTransferToUserBankTransaction,
findAssociatedTokenAddress,
getRecentBlockhash,
Expand All @@ -18,6 +21,12 @@ import {
import { getAccountUser } from 'store/account/selectors'
import { getContext } from 'store/effects'
import { getFeePayer } from 'store/solana/selectors'
import {
transactionCanceled as coinflowTransactionCanceled,
transactionFailed as coinflowTransactionFailed,
transactionSucceeded as coinflowTransactionSucceeded
} from 'store/ui/coinflow-modal/slice'
import { coinflowOnrampModalActions } from 'store/ui/modals/coinflow-onramp-modal'
import { setVisibility } from 'store/ui/modals/parentSlice'
import { initializeStripeModal } from 'store/ui/stripe-modal/slice'
import { waitForValue } from 'utils'
Expand Down Expand Up @@ -284,7 +293,55 @@ function* doBuyUSDC({
})
break
}
case PurchaseVendor.COINFLOW:
case PurchaseVendor.COINFLOW: {
const feePayerAddress = yield* select(getFeePayer)
if (!feePayerAddress) {
throw new Error('Missing feePayer unexpectedly')
}
const rootAccount = yield* call(
getRootSolanaAccount,
audiusBackendInstance
)

const amount = desiredAmount / 100.0
// Send the USDC through the payment router, sans purchase memo, and
// route everything to the user's user bank.
// Required as only the payment router program is allowed via coinflow.
const coinflowTransaction = yield* call(
createPaymentRouterRouteTransaction,
audiusBackendInstance,
{
sender: rootAccount.publicKey,
splits: {
[userBank.toBase58()]: new BN(USDC(amount).value.toString())
}
}
)
const serializedTransaction = coinflowTransaction
.serialize({ requireAllSignatures: false, verifySignatures: false })
.toString('base64')
yield* put(
coinflowOnrampModalActions.open({
amount,
serializedTransaction
})
)

const result = yield* race({
succeeded: take(coinflowTransactionSucceeded),
failed: take(coinflowTransactionFailed),
canceled: take(coinflowTransactionCanceled)
})

// Return early for failure or cancellation
if (result.canceled) {
throw new Error('Canceled Coinflow purchase')
}
if (result.failed) {
throw new Error('Coinflow transaction failed')
}
break
}
default:
throw new BuyUSDCError(
BuyUSDCErrorCode.OnrampError,
Expand Down
3 changes: 1 addition & 2 deletions packages/common/src/store/purchase-content/sagas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ function* doStartPurchaseContentFlow({
usdcConfig.minUSDCPurchaseAmountCents
)

if (balanceNeeded.lten(0)) {
if (purchaseMethod === PurchaseMethod.BALANCE && balanceNeeded.lten(0)) {
// No balance needed, perform the purchase right away
yield* call(purchaseContent, audiusBackendInstance, {
id: contentId,
Expand All @@ -393,7 +393,6 @@ function* doStartPurchaseContentFlow({
})
} else {
// We need to acquire USDC before the purchase can continue

// Invariant: The user must be checking out with a card
if (purchaseMethod !== PurchaseMethod.CARD) {
throw new PurchaseContentError(
Expand Down

0 comments on commit dc91844

Please sign in to comment.