diff --git a/src/components/Aggregator/adapters/0xV2.ts b/src/components/Aggregator/adapters/0xV2.ts index 33fbda65..7f6c62b1 100644 --- a/src/components/Aggregator/adapters/0xV2.ts +++ b/src/components/Aggregator/adapters/0xV2.ts @@ -1,4 +1,4 @@ -import { numberToHex, size, zeroAddress, concat} from 'viem'; +import { numberToHex, size, zeroAddress, concat } from 'viem'; import { sendTx } from '../utils/sendTx'; import { getTxs } from '../utils/getTxs'; @@ -25,10 +25,23 @@ export const chainToId = { const nativeToken = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; const feeCollectorAddress = '0x9Ab6164976514F1178E2BB4219DA8700c9D96E9A'; const permit2Address = '0x000000000022d473030f116ddee9f6b43ac78ba3'; +const allowanceHolderAddress = { + ethereum: '0x0000000000001ff3684f28c67538d4d072c22734', + bsc: '0x0000000000001ff3684f28c67538d4d072c22734', + polygon: '0x0000000000001ff3684f28c67538d4d072c22734', + optimism: '0x0000000000001ff3684f28c67538d4d072c22734', + arbitrum: '0x0000000000001ff3684f28c67538d4d072c22734', + avax: '0x0000000000001ff3684f28c67538d4d072c22734', + base: '0x0000000000001ff3684f28c67538d4d072c22734', + blast: '0x0000000000001ff3684f28c67538d4d072c22734', + scroll: '0x0000000000005e88410ccdfade4a5efae4b49562', + mantle: '0x0000000000005e88410ccdfade4a5efae4b49562', + linea: '0x000000000000175a8b9bc6d539b3708eed92ea6c' + +} export async function getQuote(chain: string, from: string, to: string, amount: string, extra) { // amount should include decimals - const tokenFrom = from === zeroAddress ? nativeToken : from; const tokenTo = to === zeroAddress ? nativeToken : to; @@ -43,6 +56,50 @@ export async function getQuote(chain: string, from: string, to: string, amount: // only expects integer const slippage = (extra.slippage * 100) | 0; + if (extra.isAtomicTxsSupported && allowanceHolderAddress[chain]) { + const data = await fetch( + `https://api.0x.org/swap/allowance-holder/quote?chainId=${chainToId[chain]}&buyToken=${tokenTo}&${amountParam}&sellToken=${tokenFrom}&slippageBps=${slippage}&taker=${taker}&tradeSurplusRecipient=${feeCollectorAddress}`, + { + headers: { + '0x-api-key': process.env.OX_API_KEY as string, + '0x-version': 'v2' + } + } + ).then(async (r) => { + if (r.status !== 200) { + // throw new Error('Failed to fetch'); + return null; + } + + const data = await r.json(); + + return data; + }).catch((e) => { + console.log(e); + return null; + }); + + if (data) { + if ( + data?.issues?.allowance?.spender && + data.issues.allowance.spender.toLowerCase() !== allowanceHolderAddress[chain].toLowerCase() + ) { + throw new Error(`Approval address does not match`); + } + + return { + amountReturned: data?.buyAmount || 0, + amountIn: data?.sellAmount || 0, + tokenApprovalAddress: allowanceHolderAddress[chain], + estimatedGas: data.transaction.gas, + rawQuote: { ...data, gasLimit: data.transaction.gas }, + isSignatureNeededForSwap: false, + logo: 'https://www.gitbook.com/cdn-cgi/image/width=40,height=40,fit=contain,dpr=2,format=auto/https%3A%2F%2F1690203644-files.gitbook.io%2F~%2Ffiles%2Fv0%2Fb%2Fgitbook-x-prod.appspot.com%2Fo%2Fspaces%252FKX9pG8rH3DbKDOvV7di7%252Ficon%252F1nKfBhLbPxd2KuXchHET%252F0x%2520logo.png%3Falt%3Dmedia%26token%3D25a85a3e-7f72-47ea-a8b2-e28c0d24074b' + }; + } + } + + // fetch permit2 quote if allowance holder quote is not available const data = await fetch( `https://api.0x.org/swap/permit2/quote?chainId=${chainToId[chain]}&buyToken=${tokenTo}&${amountParam}&sellToken=${tokenFrom}&slippageBps=${slippage}&taker=${taker}&tradeSurplusRecipient=${feeCollectorAddress}`, { @@ -81,14 +138,14 @@ export async function getQuote(chain: string, from: string, to: string, amount: export async function signatureForSwap({ rawQuote, signTypedDataAsync }) { const signature = await signTypedDataAsync(rawQuote.permit2.eip712).catch((err) => { - console.log(err) + console.log(err); }); return signature; } export async function swap({ tokens, fromAmount, fromAddress, rawQuote, signature, eip5792 }) { if (rawQuote.isSignatureNeededForSwap && tokens.fromToken.address !== zeroAddress && !signature) { - throw { reason: 'Signature is required' } + throw { reason: 'Signature is required' }; } const signatureLengthInHex = numberToHex(size(signature), { diff --git a/src/components/Aggregator/index.tsx b/src/components/Aggregator/index.tsx index e31b8f4e..cecc213d 100644 --- a/src/components/Aggregator/index.tsx +++ b/src/components/Aggregator/index.tsx @@ -398,6 +398,13 @@ export function AggregatorContainer() { const { gasTokenPrice, toTokenPrice, fromTokenPrice, gasPriceData } = tokenPrices || {}; + const { data: capabilities } = useCapabilities(); + + const isAtomicTxsSupported = + selectedChain && capabilities?.[selectedChain.id]?.atomic?.status + ? capabilities[selectedChain.id].atomic!.status === 'supported' + : false; + const { data: routes = [], isLoading, @@ -418,6 +425,7 @@ export function AggregatorContainer() { toToken: finalSelectedToToken, slippage, isPrivacyEnabled, + isAtomicTxsSupported, amountOut: amountOutWithDecimals } }); @@ -657,15 +665,8 @@ export function AggregatorContainer() { chain: selectedChain?.value }); - const { data: capabilities } = useCapabilities(); - const isEip5792 = - selectedRoute && - !['CowSwap', '0x Gasless'].includes(selectedRoute.name) && - selectedChain && - capabilities?.[selectedChain.id]?.atomic?.status - ? capabilities[selectedChain.id].atomic!.status === 'supported' - : false; + selectedRoute && !['CowSwap', '0x Gasless'].includes(selectedRoute.name) && isAtomicTxsSupported ? true : false; const gaslessApprovalMutation = useMutation({ mutationFn: (params: { adapter: string; rawQuote: any; isInfiniteApproval: boolean }) => gaslessApprove(params) @@ -974,7 +975,7 @@ export function AggregatorContainer() { signatureForSwapMutation.reset(); }, onError: (err: { reason: string; code: string }, variables) => { - console.log(err) + console.log(err); if (err.code !== 'ACTION_REJECTED' || err.code.toString() === '-32603') { toast(formatErrorToast(err, false)); @@ -1051,7 +1052,7 @@ export function AggregatorContainer() { approvalData: gaslessApprovalMutation?.data ?? {}, eip5792: isEip5792 ? { shouldRemoveApproval: shouldRemoveApproval ? true : false, isTokenApproved } : null, signature: signatureForSwapMutation?.data, - isSmartContractWallet: (bytecode && bytecode !== '0x') ? true : false + isSmartContractWallet: bytecode && bytecode !== '0x' ? true : false }); } };