@@ -15,6 +15,12 @@ import { chainIdToBebopChain } from '../types'
1515import { bebopServiceFactory } from './bebopService'
1616import { assetIdToBebopSolanaToken , assetIdToBebopToken } from './helpers'
1717
18+ // PMM charges the fee set against our api key, which nets exactly this bps
19+ const BEBOP_PMM_AFFILIATE_BPS = '60'
20+
21+ const isPmmEligible = ( affiliateBps : string | undefined ) =>
22+ bnOrZero ( affiliateBps ) . eq ( BEBOP_PMM_AFFILIATE_BPS )
23+
1824export const fetchBebopQuote = async ( {
1925 buyAsset,
2026 sellAsset,
@@ -53,31 +59,32 @@ export const fetchBebopQuote = async ({
5359 approval_type : 'Standard' ,
5460 skip_validation : 'true' ,
5561 gasless : 'false' ,
56- // PMM fees are configured against our api key on Bebop's side, not sent per quote
5762 source : 'shapeshift' ,
5863 }
5964
6065 const jamParams = new URLSearchParams ( baseParams )
61- const pmmParams = new URLSearchParams ( baseParams )
6266
63- // JAM settles the fee on-chain, so it still takes our bps and a recipient per quote
64- if ( affiliateBps && affiliateBps !== '0' ) {
65- jamParams . set ( 'fee' , affiliateBps )
66- jamParams . set ( 'fee_recipient' , getAddress ( getTreasuryAddressFromChainId ( buyAsset . chainId ) ) )
67- }
67+ // Without an explicit fee JAM charges our api key's flat fee, so 1 bps is the lowest it can go
68+ jamParams . set ( 'fee' , affiliateBps && bnOrZero ( affiliateBps ) . gt ( 0 ) ? affiliateBps : '1' )
69+ jamParams . set ( 'fee_recipient' , getAddress ( getTreasuryAddressFromChainId ( buyAsset . chainId ) ) )
6870
6971 const service = bebopServiceFactory ( { apiKey } )
7072
71- const [ maybeJam , maybePmm ] = await Promise . all ( [
73+ const requests = [
7274 service . get < BebopQuoteResponse > ( `https://api.bebop.xyz/jam/${ chainName } /v2/quote` , {
7375 params : jamParams ,
7476 } ) ,
75- service . get < BebopQuoteResponse > ( `https://api.bebop.xyz/pmm/${ chainName } /v3/quote` , {
76- params : pmmParams ,
77- } ) ,
78- ] )
77+ ]
7978
80- const maybeResponses = [ maybeJam , maybePmm ]
79+ if ( isPmmEligible ( affiliateBps ) ) {
80+ requests . push (
81+ service . get < BebopQuoteResponse > ( `https://api.bebop.xyz/pmm/${ chainName } /v3/quote` , {
82+ params : new URLSearchParams ( baseParams ) ,
83+ } ) ,
84+ )
85+ }
86+
87+ const maybeResponses = await Promise . all ( requests )
8188
8289 if ( maybeResponses . every ( maybeResponse => maybeResponse . isErr ( ) ) ) {
8390 return Err (
@@ -129,6 +136,7 @@ export const fetchBebopSolanaQuote = async ({
129136 takerAddress,
130137 receiverAddress,
131138 slippageTolerancePercentageDecimal,
139+ affiliateBps,
132140 apiKey,
133141} : {
134142 buyAsset : Asset
@@ -137,9 +145,19 @@ export const fetchBebopSolanaQuote = async ({
137145 takerAddress : string
138146 receiverAddress : string
139147 slippageTolerancePercentageDecimal : string
148+ affiliateBps ?: string
140149 apiKey : string
141150} ) : Promise < Result < BebopSolanaQuoteResponse , SwapErrorRight > > => {
142151 try {
152+ if ( ! isPmmEligible ( affiliateBps ) ) {
153+ return Err (
154+ makeSwapErrorRight ( {
155+ message : 'Bebop Solana is PMM only and cannot take the requested bps' ,
156+ code : TradeQuoteError . NoRouteFound ,
157+ } ) ,
158+ )
159+ }
160+
143161 // Bebop's Solana routes don't wrap native SOL on the input side, so selling native SOL
144162 // always fails on-chain. Reject up front instead of returning an unexecutable quote.
145163 if ( sellAsset . assetId === solAssetId ) {
@@ -166,7 +184,6 @@ export const fetchBebopSolanaQuote = async ({
166184 approval_type : 'Standard' ,
167185 skip_validation : 'false' ,
168186 gasless : 'true' ,
169- // Solana is PMM only, so its fees are configured against our api key on Bebop's side
170187 source : 'shapeshift' ,
171188 } )
172189
0 commit comments