-
Notifications
You must be signed in to change notification settings - Fork 88
1inch fusion #316
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
base: master
Are you sure you want to change the base?
1inch fusion #316
Changes from all commits
490e2fa
f7ec95d
8125fba
7a17dae
afc78f5
cb237b7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { AUTH_HEADER, CHAIN_TO_ID, SPENDERS } from '~/components/Aggregator/adapters/1inch/constants'; | ||
import { altReferralAddress } from '~/components/Aggregator/constants'; | ||
import { applyArbitrumFees } from '~/components/Aggregator/utils/arbitrumFees'; | ||
import { sendTx } from '~/components/Aggregator/utils/sendTx'; | ||
import { zeroAddress } from 'viem'; | ||
import { estimateGas } from 'wagmi/actions'; | ||
import { config } from '../../../WalletProvider'; | ||
|
||
const CLASSIC_ENDPOINT = 'http://localhost:8888/swap/v6.0/'; | ||
|
||
export async function getClassicQuote(chain: string, tokenFrom: string, tokenTo: string, amount: string, extra) { | ||
const quoteUrl = `${CLASSIC_ENDPOINT}${CHAIN_TO_ID[chain]}/quote?src=${tokenFrom}&dst=${tokenTo}&amount=${amount}&includeGas=true`; | ||
const swapUrl = extra.userAddress !== zeroAddress | ||
? `${CLASSIC_ENDPOINT}${CHAIN_TO_ID[chain]}/swap?src=${tokenFrom}&dst=${tokenTo}&amount=${amount}&from=${extra.userAddress}&slippage=${extra.slippage}&referrer=${altReferralAddress}&disableEstimate=true` | ||
: null; | ||
|
||
const quotePromise = fetch(quoteUrl, { headers: AUTH_HEADER as any }).then((r) => r.json()); | ||
const swapPromise = swapUrl | ||
? fetch(swapUrl, { headers: AUTH_HEADER as any }).then((r) => r.json()) | ||
: null; | ||
|
||
return await Promise.all([quotePromise, swapPromise]); | ||
} | ||
|
||
export async function parseClassicQuote(chain: string, quote) { | ||
const [data, swapData] = quote; | ||
const tokenApprovalAddress = SPENDERS[chain]; | ||
let gas = data.gas || 0; | ||
|
||
if (chain === 'arbitrum') | ||
gas = swapData === null ? null : await applyArbitrumFees(swapData.tx.to, swapData.tx.data, gas); | ||
|
||
return { | ||
amountReturned: swapData?.dstAmount ?? data.dstAmount, | ||
estimatedGas: gas, | ||
tokenApprovalAddress, | ||
rawQuote: swapData, | ||
logo: 'https://icons.llamao.fi/icons/protocols/1inch-network?w=48&q=75' | ||
}; | ||
} | ||
|
||
export async function classicSwap(rawQuote) { | ||
const txObject = { | ||
from: rawQuote.tx.from, | ||
to: rawQuote.tx.to, | ||
data: rawQuote.tx.data, | ||
value: rawQuote.tx.value | ||
}; | ||
|
||
const gasPrediction = await estimateGas(config, txObject).catch(() => null); | ||
Masha1996 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
const tx = await sendTx({ | ||
...txObject, | ||
// Increase gas +20% + 2 erc20 txs | ||
...(gasPrediction && { gas: (gasPrediction * 12n) / 10n + 86000n }) | ||
}); | ||
return tx; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
export const AUTH_HEADER = process.env.INCH_API_KEY ? { 'auth-key': process.env.INCH_API_KEY } : {}; | ||
|
||
export const NATIVE_TOKEN = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'; | ||
|
||
export const CHAIN_TO_ID = { | ||
ethereum: 1, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do you think about using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Everywhere in the project, chain ids are listed as objects. I suggest we leave them as they are for consistency. |
||
bsc: 56, | ||
polygon: 137, | ||
optimism: 10, | ||
arbitrum: 42161, | ||
gnosis: 100, | ||
avax: 43114, | ||
fantom: 250, | ||
klaytn: 8217, | ||
aurora: 1313161554, | ||
zksync: 324, | ||
base: 8453 | ||
}; | ||
|
||
export const SPENDERS = { | ||
ethereum: '0x111111125421ca6dc452d289314280a0f8842a65', | ||
bsc: '0x111111125421ca6dc452d289314280a0f8842a65', | ||
polygon: '0x111111125421ca6dc452d289314280a0f8842a65', | ||
optimism: '0x111111125421ca6dc452d289314280a0f8842a65', | ||
arbitrum: '0x111111125421ca6dc452d289314280a0f8842a65', | ||
gnosis: '0x111111125421ca6dc452d289314280a0f8842a65', | ||
avax: '0x111111125421ca6dc452d289314280a0f8842a65', | ||
fantom: '0x111111125421ca6dc452d289314280a0f8842a65', | ||
klaytn: '0x111111125421ca6dc452d289314280a0f8842a65', | ||
aurora: '0x111111125421ca6dc452d289314280a0f8842a65', | ||
zksync: '0x6fd4383cb451173d5f9304f041c7bcbf27d561ff', | ||
base: '0x111111125421ca6dc452d289314280a0f8842a65' | ||
}; | ||
|
||
export const AVAILABLE_CHAINS_FOR_FUSION = new Set<number>([ | ||
CHAIN_TO_ID.ethereum, | ||
CHAIN_TO_ID.bsc, | ||
CHAIN_TO_ID.polygon, | ||
CHAIN_TO_ID.optimism, | ||
CHAIN_TO_ID.arbitrum, | ||
CHAIN_TO_ID.gnosis, | ||
CHAIN_TO_ID.avax, | ||
CHAIN_TO_ID.fantom, | ||
CHAIN_TO_ID.zksync, | ||
CHAIN_TO_ID.base, | ||
]); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import { AVAILABLE_CHAINS_FOR_FUSION, CHAIN_TO_ID, SPENDERS } from './constants'; | ||
import { FusionSDK, OrderStatus } from '@1inch/fusion-sdk'; | ||
import { formatUnits, parseUnits } from 'ethers/lib/utils'; | ||
import { call, signTypedData } from 'wagmi/actions'; | ||
import { config } from '~/components/WalletProvider'; | ||
import { Hex } from 'viem/types/misc'; | ||
import { OrderInfo } from '@1inch/fusion-sdk/dist/types/src/sdk/types'; | ||
|
||
// const FUSION_QUOTE_ENDPOINT = 'https://api-defillama.1inch.io/v2.0/fusion'; | ||
const FUSION_SDK_ENDPOINT = 'http://localhost:8888/fusion'; | ||
const SOURCE = 'f012a792'; | ||
|
||
export function isFusionSupportedByChain(chainId: number): boolean { | ||
return AVAILABLE_CHAINS_FOR_FUSION.has(chainId); | ||
} | ||
|
||
export async function getFusionQuoteResponse(params: { | ||
chain: string, | ||
tokenFrom: string, | ||
tokenTo: string, | ||
amount: string, | ||
address: string, | ||
}) { | ||
const { chain, tokenFrom, tokenTo, amount, address } = params; | ||
const sdk = new FusionSDK({ | ||
url: FUSION_SDK_ENDPOINT, | ||
network: CHAIN_TO_ID[chain] | ||
}); | ||
|
||
return await sdk.getQuote({ | ||
fromTokenAddress: tokenFrom, | ||
toTokenAddress: tokenTo, | ||
amount, | ||
walletAddress: address, | ||
source: SOURCE, | ||
}); | ||
} | ||
|
||
export async function fusionSwap(chain, quote, fromAddress): Promise<OrderInfo> { | ||
const sdk = new FusionSDK({ | ||
url: FUSION_SDK_ENDPOINT, | ||
network: CHAIN_TO_ID[chain], | ||
blockchainProvider: { | ||
signTypedData: (_, typedData) => signTypedData(config, { | ||
domain: typedData.domain, | ||
types: typedData.types, | ||
primaryType: typedData.primaryType, | ||
message: typedData.message | ||
}), | ||
ethCall: (contractAddress: Hex, callData: Hex) => | ||
call( | ||
config, | ||
{ | ||
account: fromAddress, | ||
data: callData, | ||
to: contractAddress, | ||
}, | ||
).then((result) => result.data as string), | ||
} | ||
}); | ||
|
||
return await sdk.placeOrder({ | ||
fromTokenAddress: quote.params.fromTokenAddress.val, | ||
toTokenAddress: quote.params.toTokenAddress.val, | ||
walletAddress: quote.params.walletAddress.val, | ||
amount: quote.params.amount, | ||
source: SOURCE, | ||
}); | ||
} | ||
|
||
export const getOrderStatus = ({ chain, hash }) => async (onSuccess, maxRetries = 300, delay = 1000) => { | ||
const sdk = new FusionSDK({ | ||
url: FUSION_SDK_ENDPOINT, | ||
network: CHAIN_TO_ID[chain], | ||
}); | ||
|
||
let attempt = 0; | ||
|
||
while (attempt < maxRetries) { | ||
try { | ||
const data = await sdk.getOrderStatus(hash); | ||
|
||
if (data.status === OrderStatus.Filled) { | ||
onSuccess(); | ||
return; | ||
} | ||
|
||
if (data.status !== OrderStatus.Pending) { | ||
return; | ||
} | ||
} catch (error) { | ||
console.error('Error fetching fusion order status:', error); | ||
} | ||
|
||
attempt++; | ||
if (attempt >= maxRetries) { | ||
throw new Error('Max retries reached. Unable to fetch order status.'); | ||
} | ||
|
||
await new Promise((resolve) => setTimeout(resolve, delay)); | ||
} | ||
}; | ||
|
||
export function parseFusionQuote(chain: string, quote, extra) { | ||
const { presets, recommendedPreset, toTokenAmount } = quote; | ||
const { auctionStartAmount, auctionEndAmount } = presets[recommendedPreset]; | ||
const dstTokenDecimals = extra.toToken.decimals; | ||
|
||
const start = formatUnits(auctionStartAmount, dstTokenDecimals); | ||
const end = formatUnits(auctionEndAmount, dstTokenDecimals); | ||
const amount = formatUnits(toTokenAmount, dstTokenDecimals); | ||
|
||
const receivedAmount = amount < start ? amount : start; | ||
const returnedAmount = end > receivedAmount ? end : receivedAmount; | ||
|
||
return { | ||
amountReturned: parseUnits(returnedAmount, dstTokenDecimals).toString(), | ||
estimatedGas: 0, | ||
tokenApprovalAddress: SPENDERS[chain], | ||
rawQuote: quote, | ||
logo: 'https://icons.llamao.fi/icons/protocols/1inch-network?w=48&q=75' | ||
}; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.