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

Fix Withdrawal flow errors due to slippage calculations #7525

Merged
merged 3 commits into from
Feb 8, 2024
Merged
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
4 changes: 3 additions & 1 deletion packages/common/src/services/remote-config/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const MAX_USDC_PURCHASE_AMOUNT_CENTS = 150000
export const MIN_CONTENT_PRICE_CENTS = 100
export const MAX_CONTENT_PRICE_CENTS = 150000
export const BUY_TOKEN_VIA_SOL_SLIPPAGE_BPS = 50
export const BUY_SOL_VIA_TOKEN_SLIPPAGE_BPS = 50

export const remoteConfigIntDefaults: { [key in IntKeys]: number | null } = {
[IntKeys.IMAGE_QUICK_FETCH_TIMEOUT_MS]: 5000,
Expand Down Expand Up @@ -49,7 +50,8 @@ export const remoteConfigIntDefaults: { [key in IntKeys]: number | null } = {
[IntKeys.HANDLE_VERIFICATION_TIMEOUT_MILLIS]:
DEFAULT_HANDLE_VERIFICATION_TIMEOUT_MILLIS,
[IntKeys.COINFLOW_MAXIMUM_CENTS]: 1000,
[IntKeys.MIN_USDC_WITHDRAW_BALANCE_CENTS]: 500
[IntKeys.MIN_USDC_WITHDRAW_BALANCE_CENTS]: 500,
[IntKeys.BUY_SOL_WITH_TOKEN_SLIPPAGE_BPS]: BUY_SOL_VIA_TOKEN_SLIPPAGE_BPS
}

export const remoteConfigStringDefaults: {
Expand Down
6 changes: 1 addition & 5 deletions packages/common/src/services/remote-config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,4 @@ export type { AllRemoteConfigKeys } from './types'
export { FeatureFlags } from './feature-flags'
export { remoteConfig } from './remote-config'
export type { RemoteConfigInstance } from './remote-config'
export {
remoteConfigIntDefaults,
remoteConfigDoubleDefaults,
remoteConfigBooleanDefaults
} from './defaults'
export * from './defaults'
7 changes: 7 additions & 0 deletions packages/common/src/services/remote-config/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,13 @@ export enum IntKeys {
*/
BUY_AUDIO_SLIPPAGE = 'BUY_AUDIO_SLIPPAGE',

/**
* The maximum amount the price of SOL is allowed to slip before
* the Jupiter swap from USDC to SOL
* Expressed in percentage basis points (1pt = 0.01%).
*/
BUY_SOL_WITH_TOKEN_SLIPPAGE_BPS = 'BUY_SOL_WITH_TOKEN_SLIPPAGE_BPS',

/**
* The interval in milliseconds between polls for gated tracks to check for access
*/
Expand Down
6 changes: 5 additions & 1 deletion packages/common/src/store/ui/withdraw-usdc/slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ const slice = createSlice({
_action: PayloadAction<{
/** Balance in cents. Used for analytics */
currentBalance: number
/** Transfer amount in cents */
method: WithdrawMethod
/** Transfer amount in cents */
amount: number
destinationAddress: string
}>
Expand Down Expand Up @@ -68,6 +68,9 @@ const slice = createSlice({
state.withdrawStatus = Status.ERROR
state.withdrawError = action.payload.error
},
updateAmount: (state, action: PayloadAction<{ amount: number }>) => {
state.amount = action.payload.amount
},
cleanup: () => initialState
}
})
Expand All @@ -80,6 +83,7 @@ export const {
coinflowWithdrawalCanceled,
withdrawUSDCSucceeded,
withdrawUSDCFailed,
updateAmount,
cleanup
} = slice.actions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ import { useCallback } from 'react'
import { useCoinflowWithdrawalAdapter } from '@audius/common/hooks'
import {
useCoinflowWithdrawModal,
withdrawUSDCActions
withdrawUSDCActions,
withdrawUSDCSelectors
} from '@audius/common/store'
import { CoinflowWithdraw } from '@coinflowlabs/react'
import { useDispatch } from 'react-redux'
import { useDispatch, useSelector } from 'react-redux'

import ModalDrawer from 'pages/audio-rewards-page/components/modals/ModalDrawer'
import { env } from 'services/env'
import zIndex from 'utils/zIndex'

import styles from './CoinflowWithdrawModal.module.css'

const { getWithdrawAmount } = withdrawUSDCSelectors
const { coinflowWithdrawalCanceled, coinflowWithdrawalSucceeded } =
withdrawUSDCActions

Expand All @@ -33,12 +35,8 @@ const MERCHANT_ID = env.COINFLOW_MERCHANT_ID
const IS_PRODUCTION = env.ENVIRONMENT === 'production'

export const CoinflowWithdrawModal = () => {
const {
data: { amount },
isOpen,
onClose,
onClosed
} = useCoinflowWithdrawModal()
const { isOpen, onClose, onClosed } = useCoinflowWithdrawModal()
const amount = useSelector(getWithdrawAmount)

const adapter = useCoinflowWithdrawalAdapter()
const dispatch = useDispatch()
Expand All @@ -57,7 +55,7 @@ export const CoinflowWithdrawModal = () => {
[dispatch, onClose]
)

const showContent = isOpen && adapter
const showContent = isOpen && adapter && amount !== undefined

return (
<ModalDrawer
Expand All @@ -71,7 +69,7 @@ export const CoinflowWithdrawModal = () => {
>
{showContent ? (
<CoinflowWithdraw
amount={amount}
amount={amount / 100}
lockAmount={true}
wallet={adapter.wallet}
connection={adapter.connection}
Expand Down
4 changes: 4 additions & 0 deletions packages/web/src/services/audius-backend/Jupiter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ const getQuote = async ({
BigInt(quote.outAmount),
outputToken.decimals
),
otherAmountThreshold: convertBigIntToAmountObject(
BigInt(quote.otherAmountThreshold),
swapMode === 'ExactIn' ? outputToken.decimals : inputToken.decimals
),
quote,
inputTokenSymbol,
outputTokenSymbol
Expand Down
28 changes: 18 additions & 10 deletions packages/web/src/services/solana/WithdrawUSDC.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import {
getRecentBlockhash,
getLookupTableAccounts
getLookupTableAccounts,
IntKeys,
BUY_SOL_VIA_TOKEN_SLIPPAGE_BPS
} from '@audius/common/services'
import { MintName } from '@audius/sdk'
import {
Expand All @@ -24,15 +26,12 @@ import {
} from 'services/audius-backend/Jupiter'
import { audiusBackendInstance } from 'services/audius-backend/audius-backend-instance'
import { getLibs } from 'services/audius-libs'
import { remoteConfigInstance } from 'services/remote-config/remote-config-instance'
import {
getAssociatedTokenAccountRent,
getTransferTransactionFee
} from 'services/solana/solana'

// TODO: Grab from remote config
// Allowable slippage amount for USDC jupiter swaps in %.
const USDC_SLIPPAGE = 3

export const getFundDestinationTokenAccountFees = async (
account: PublicKey
) => {
Expand All @@ -57,6 +56,10 @@ export const createSwapUserbankToSolInstructions = async ({
wallet: PublicKey
}) => {
const libs = await getLibs()
const slippageBps =
remoteConfigInstance.getRemoteVar(
IntKeys.BUY_SOL_WITH_TOKEN_SLIPPAGE_BPS
) ?? BUY_SOL_VIA_TOKEN_SLIPPAGE_BPS

const usdcUserBank = await libs.solanaWeb3Manager!.deriveUserBank({
mint
Expand Down Expand Up @@ -86,11 +89,13 @@ export const createSwapUserbankToSolInstructions = async ({
inputTokenSymbol: tokenSymbol,
outputTokenSymbol: 'SOL',
inputAmount: outSolAmount,
slippage: USDC_SLIPPAGE,
slippage: slippageBps,
swapMode: 'ExactOut',
onlyDirectRoutes: true
})
const usdcNeededAmount = quoteRoute.inputAmount.amount
// Use the otherAmountThreshold which will account for max slippage.
// We will end up with potentially extra SOL in the root account.
const usdcNeededAmount = quoteRoute.otherAmountThreshold.amount
const transferToTemporaryTokenAccountInstructions =
await libs.solanaWeb3Manager!.createTransferInstructionsFromCurrentUser({
amount: new BN(usdcNeededAmount),
Expand All @@ -111,12 +116,14 @@ export const createSwapUserbankToSolInstructions = async ({
)

// 5. Swap the tokens for wSOL
// Use ExactIn to ensure all the tokens get used in the swap
// Use ExactIn to ensure all the USDC tokens get used in the swap.
// We need to make sure to clear out all of the USDC tokens so that
// the relayer is happy that the account cannot be drained elsewhere.
const swapQuote = await JupiterSingleton.getQuote({
inputTokenSymbol: tokenSymbol,
outputTokenSymbol: 'SOL',
inputAmount: usdcNeededAmount / 10 ** 6,
slippage: USDC_SLIPPAGE,
slippage: slippageBps,
swapMode: 'ExactIn',
onlyDirectRoutes: true
})
Expand Down Expand Up @@ -173,7 +180,8 @@ export const createSwapUserbankToSolInstructions = async ({
closeWSOLInstructionAgain,
closeTemporaryTokenAccountInstruction
],
lookupTableAddresses
lookupTableAddresses,
usdcNeededAmount
}
}

Expand Down
Loading