Skip to content
Closed
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
59 changes: 32 additions & 27 deletions packages/internal/dex/sdk-sample-app/src/components/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,41 @@ import { ConnectAccount } from './ConnectAccount';
import { AmountInput, AmountOutput } from './AmountInput';
import { SecondaryFeeInput } from './SecondaryFeeInput';
import { FeeBreakdown } from './FeeBreakdown';
import { BrowserProvider, formatEther, parseEther, TransactionReceipt } from 'ethers';
import { BrowserProvider, formatEther, parseEther, parseUnits, TransactionReceipt } from 'ethers';

type Token = {
symbol: string;
address: string;
decimals: number;
};

type TradeType = 'exactInput' | 'exactOutput';

type mapping = {
[address: string]: string;
[address: string]: Token;
};

const mainnetTokens: Token[] = [
{ symbol: 'IMX', address: 'native' },
{ symbol: 'ETH', address: '0x52a6c53869ce09a731cd772f245b97a4401d3348' },
{ symbol: 'USDC', address: '0x6de8aCC0D406837030CE4dd28e7c08C5a96a30d2' },
{ symbol: 'IMX', address: 'native', decimals: 18 },
{ symbol: 'ETH', address: '0x52A6c53869Ce09a731CD772f245b97A4401d3348', decimals: 18 },
{ symbol: 'USDC', address: '0x6de8aCC0D406837030CE4dd28e7c08C5a96a30d2', decimals: 6 },
];

const testnetTokens: Token[] = [
{ symbol: 'IMX', address: 'native' },
{ symbol: 'WIMX', address: '0x1CcCa691501174B4A623CeDA58cC8f1a76dc3439' },
{ symbol: 'zkTDR', address: '0x6531F7B9158d78Ca78b46799c4Fd65C2Af8Ae506' },
{ symbol: 'zkPSP', address: '0x88B35dF96CbEDF2946586147557F7D5D0CCE7e5c' },
{ symbol: 'zkWLT', address: '0x8A5b0470ee48248bEb7D1E745c1EbA0DCA77215e' },
{ symbol: 'zkSRE', address: '0x43566cAB87CC147C95e2895E7b972E19993520e4' },
{ symbol: 'zkCORE', address: '0x4B96E7b7eA673A996F140d5De411a97b7eab934E' },
{ symbol: 'zkWAT', address: '0xaC953a0d7B67Fae17c87abf79f09D0f818AC66A2' },
{ symbol: 'zkCATS', address: '0xb95B75B4E4c09F04d5DA6349861BF1b6F163D78c' },
{ symbol: 'zkYEET', address: '0x8AC26EfCbf5D700b37A27aA00E6934e6904e7B8e' },
{ symbol: 'IMX', address: 'native', decimals: 18 },
{ symbol: 'WIMX', address: '0x1CcCa691501174B4A623CeDA58cC8f1a76dc3439', decimals: 18 },
{ symbol: 'zkTDR', address: '0x6531F7B9158d78Ca78b46799c4Fd65C2Af8Ae506', decimals: 18 },
{ symbol: 'zkPSP', address: '0x88B35dF96CbEDF2946586147557F7D5D0CCE7e5c', decimals: 18 },
{ symbol: 'zkWLT', address: '0x8A5b0470ee48248bEb7D1E745c1EbA0DCA77215e', decimals: 18 },
{ symbol: 'zkSRE', address: '0x43566cAB87CC147C95e2895E7b972E19993520e4', decimals: 18 },
{ symbol: 'zkCORE', address: '0x4B96E7b7eA673A996F140d5De411a97b7eab934E', decimals: 18 },
{ symbol: 'zkWAT', address: '0xaC953a0d7B67Fae17c87abf79f09D0f818AC66A2', decimals: 18 },
{ symbol: 'zkCATS', address: '0xb95B75B4E4c09F04d5DA6349861BF1b6F163D78c', decimals: 18 },
{ symbol: 'zkYEET', address: '0x8AC26EfCbf5D700b37A27aA00E6934e6904e7B8e', decimals: 18 },
];

/// @dev Update this to change tokens used in the app
const allTokens = testnetTokens;
const allTokens = mainnetTokens;

const buildExchange = (secondaryFeeRecipient: string, secondaryFeePercentage: number) => {
if (secondaryFeeRecipient && secondaryFeePercentage) {
Expand All @@ -56,6 +57,11 @@ const buildExchange = (secondaryFeeRecipient: string, secondaryFeePercentage: nu
return new Exchange(configuration);
};

const addressToTokenMapping = allTokens.reduce((acc, token) => {
acc[token.address] = token;
return acc;
}, {} as mapping);

export function Example() {
const [ethereumAccount, setEthereumAccount] = useState<string | null>(null);
const [isFetching, setIsFetching] = useState(false);
Expand All @@ -72,10 +78,6 @@ export function Example() {
const [inputToken, setInputToken] = useState<Token>(allTokens[0]);
const [outputToken, setOutputToken] = useState<Token>(allTokens[1]);

const addressToSymbolMapping = allTokens.reduce((acc, token) => {
acc[token.address] = token.symbol!;
return acc;
}, {} as mapping);

if (ethereumAccount === null) {
return <ConnectAccount setAccount={setEthereumAccount} />;
Expand All @@ -95,13 +97,13 @@ export function Example() {
ethereumAccount,
tokenInAddress,
tokenOutAddress,
parseEther(`${inputAmount}`),
parseUnits(`${inputAmount}`, inputToken.decimals),
)
: await exchange.getUnsignedSwapTxFromAmountOut(
ethereumAccount,
tokenInAddress,
tokenOutAddress,
parseEther(`${outputAmount}`),
parseUnits(`${outputAmount}`, outputToken.decimals),
);

setResult(txn);
Expand Down Expand Up @@ -193,7 +195,8 @@ export function Example() {
onChange={(e) => {
setInputToken({
address: e.target.value,
symbol: addressToSymbolMapping[e.target.value],
symbol: addressToTokenMapping[e.target.value].symbol,
decimals: addressToTokenMapping[e.target.value].decimals,
});
setResult(null);
setSwapTransaction(null);
Expand All @@ -218,7 +221,8 @@ export function Example() {
onChange={(e) => {
setOutputToken({
address: e.target.value,
symbol: addressToSymbolMapping[e.target.value],
symbol: addressToTokenMapping[e.target.value].symbol,
decimals: addressToTokenMapping[e.target.value].decimals,
});
setResult(null);
setSwapTransaction(null);
Expand Down Expand Up @@ -257,19 +261,20 @@ export function Example() {
<>
<h3>
Expected amount: {formatEther(result.quote.amount.value)}{' '}
{`${addressToSymbolMapping[result.quote.amount.token.address]}`}
{`${addressToTokenMapping[result.quote.amount.token.address].symbol}`}
</h3>
<h3>
{tradeType === 'exactInput' ? 'Minimum' : 'Maximum'} amount:{' '}
{formatEther(result.quote.amountWithMaxSlippage.value)}{' '}
{`${addressToSymbolMapping[result.quote.amountWithMaxSlippage.token.address]}`}
{`${addressToTokenMapping[result.quote.amountWithMaxSlippage.token.address].symbol}`}
</h3>

<h3>Slippage: {result.quote.slippage}%</h3>
<h3>Price Impact: {result.quote.priceImpact}%</h3>
{result.approval && <h3>Approval Gas Estimate: {showGasEstimate(result.approval)}</h3>}
<h3>Swap Gas estimate: {showGasEstimate(result.swap)}</h3>

<FeeBreakdown fees={result.quote.fees} addressMap={addressToSymbolMapping} />
<FeeBreakdown fees={result.quote.fees} tokenMap={addressToTokenMapping} />

<>
<button
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,40 @@
import { Fee } from "@imtbl/dex-sdk"
import { Fee } from "@imtbl/dex-sdk";
import { formatEther } from "ethers";

type Token = {
symbol: string;
address: string;
decimals: number;
};

type mapping = {
[address: string]: string;
[address: string]: Token;
};

export const FeeBreakdown = ({fees, addressMap}: {fees: Fee[], addressMap: mapping}) => {
return (<>
<h2 className="mb-1 mt-4 text-lg font-extrabold leading-none tracking-tight text-gray-900 md:text-lg lg:text-lg dark:text-white">Fee Breakdown:</h2>
{fees.map((fee, index) => {
return (<div key={index}>
<div>Recipient: {fee.recipient}</div>
<div>Basis Points: {fee.basisPoints}</div>
<div>Amount: {formatEther(fee.amount.value.toString())} {addressMap[fee.amount.token.address]}</div>
</div>)
})}
</>
)
}
export const FeeBreakdown = ({
fees,
tokenMap,
}: {
fees: Fee[];
tokenMap: mapping;
}) => {
return (
<>
<h2 className="mb-1 mt-4 text-lg font-extrabold leading-none tracking-tight text-gray-900 md:text-lg lg:text-lg dark:text-white">
Fee Breakdown:
</h2>
{fees.map((fee, index) => {
return (
<div key={index}>
<div>Recipient: {fee.recipient}</div>
<div>Basis Points: {fee.basisPoints}</div>
<div>
Amount: {formatEther(fee.amount.value.toString())}{" "}
{tokenMap[fee.amount.token.address].symbol}
</div>
</div>
);
})}
</>
);
};
4 changes: 2 additions & 2 deletions packages/internal/dex/sdk-sample-app/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ const immutableTestnetChainID = 13473; // You can optionally retrieve the chain
const immutableMainnetChainID = 13371

const baseConfig = new ImmutableConfiguration({
environment: Environment.SANDBOX,
environment: Environment.PRODUCTION,
});

export const configuration = {
chainId: immutableTestnetChainID,
chainId: immutableMainnetChainID,
baseConfig,
};
9 changes: 6 additions & 3 deletions packages/internal/dex/sdk/src/exchange.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TradeType } from '@uniswap/sdk-core';
import { Fraction, TradeType } from '@uniswap/sdk-core';
import { BigNumberish, Interface, JsonRpcProvider } from 'ethers';
import { DuplicateAddressesError, InvalidAddressError, InvalidMaxHopsError, InvalidSlippageError } from './errors';
import { calculateGasFee, fetchGasPrice } from './lib/transactionUtils/gas';
Expand Down Expand Up @@ -37,10 +37,12 @@ const toPublicQuote = (
amountWithMaxSlippage: CoinAmount<Coin>,
slippage: number,
fees: Fees,
priceImpact: Fraction,
): Quote => ({
amount: toPublicAmount(amount),
amountWithMaxSlippage: toPublicAmount(amountWithMaxSlippage),
slippage,
priceImpact: priceImpact.toFixed(10),
fees: fees.withAmounts().map((fee) => ({
...fee,
amount: toPublicAmount(fee.amount),
Expand Down Expand Up @@ -224,6 +226,7 @@ export class Exchange {
amountWithMaxSlippage: toPublicAmount(otherTokenCoinAmount),
slippage: 0,
fees: [],
priceImpact: '0',
};

const wimxInterface = WIMX__factory.createInterface();
Expand Down Expand Up @@ -317,7 +320,7 @@ export class Exchange {
secondaryFees,
);

const { quotedAmount, quotedAmountWithMaxSlippage } = prepareUserQuote(
const { quotedAmount, quotedAmountWithMaxSlippage, priceImpact } = prepareUserQuote(
this.nativeTokenService,
adjustedQuote,
slippagePercent,
Expand All @@ -340,7 +343,7 @@ export class Exchange {
? await getApproval(this.provider, fromAddress, preparedApproval, gasPrice)
: null;

const quote = toPublicQuote(quotedAmount, quotedAmountWithMaxSlippage, slippagePercent, fees);
const quote = toPublicQuote(quotedAmount, quotedAmountWithMaxSlippage, slippagePercent, fees, priceImpact);

return { quote, approval, swap };
}
Expand Down
26 changes: 20 additions & 6 deletions packages/internal/dex/sdk/src/lib/getQuotesForRoutes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Route, SwapQuoter } from '@uniswap/v3-sdk';
import { TradeType, Token } from '@uniswap/sdk-core';
import { TradeType, Token, Price, Fraction } from '@uniswap/sdk-core';
import { AbiCoder } from 'ethers';
import { CoinAmount, ERC20 } from '../types';
import {
Expand All @@ -19,6 +19,7 @@ export type QuoteResult = {
amountIn: CoinAmount<ERC20>;
amountOut: CoinAmount<ERC20>;
tradeType: TradeType;
priceImpact: Fraction;
};

export async function getQuotesForRoutes(
Expand Down Expand Up @@ -64,15 +65,28 @@ export async function getQuotesForRoutes(
const quoteAmount = decodedQuoteResult[amountIndex];
if (typeof quoteAmount !== 'bigint') throw new Error('Expected BigNumber');

const input = uniswapTokenToERC20(routes[i].input);
const output = uniswapTokenToERC20(routes[i].output);
const route = routes[i];
const { midPrice, input, output } = route;
const inputToken = uniswapTokenToERC20(input);
const outputToken = uniswapTokenToERC20(output);

const amountIn = tradeType === TradeType.EXACT_INPUT ? amountSpecified : newAmount(quoteAmount, inputToken);
const amountOut = tradeType === TradeType.EXACT_INPUT ? newAmount(quoteAmount, outputToken) : amountSpecified;

const executionPrice = new Price(
input,
output,
amountIn.value.toString(),
amountOut.value.toString(),
);

quoteResults.push({
route: routes[i],
amountIn: tradeType === TradeType.EXACT_INPUT ? amountSpecified : newAmount(quoteAmount, input),
amountOut: tradeType === TradeType.EXACT_INPUT ? newAmount(quoteAmount, output) : amountSpecified,
route,
amountIn,
amountOut,
gasEstimate: BigInt(decodedQuoteResult[gasEstimateIndex]),
tradeType,
priceImpact: executionPrice.subtract(midPrice.asFraction).divide(midPrice.asFraction).multiply(100),
});
}
} catch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export const prepareUserQuote = (
return {
quotedAmount,
quotedAmountWithMaxSlippage,
priceImpact: routerQuote.priceImpact,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -535,5 +535,6 @@ export function adjustQuoteWithFees(
amountIn: adjustedAmountIn,
amountOut: ourQuote.amountOut,
tradeType: ourQuote.tradeType,
priceImpact: ourQuote.priceImpact,
};
}
1 change: 1 addition & 0 deletions packages/internal/dex/sdk/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export type Quote = {
amountWithMaxSlippage: Amount;
slippage: number;
fees: Fee[];
priceImpact: string;
};

/**
Expand Down