Skip to content

Commit

Permalink
Add QuickSwap-deployed testnet contracts to DEX SDK (#1314)
Browse files Browse the repository at this point in the history
Co-authored-by: Pano Skylakis <[email protected]>
  • Loading branch information
pano-skylakis and Pano Skylakis authored Jan 10, 2024
1 parent b1a21cb commit e6f73e6
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 46 deletions.
79 changes: 60 additions & 19 deletions packages/internal/dex/sdk-sample-app/src/components/Example.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { ethers } from 'ethers';
import { useState } from 'react';
import { Exchange, TransactionDetails, TransactionResponse } from '@imtbl/dex-sdk';
import {
Exchange,
TransactionDetails,
TransactionResponse,
} from '@imtbl/dex-sdk';
import { configuration } from '../config';
import { ConnectAccount } from './ConnectAccount';
import { AmountInput, AmountOutput } from './AmountInput';
Expand Down Expand Up @@ -31,7 +35,10 @@ const allTokens: Token[] = [
{ symbol: 'zkYEET', address: '0x8AC26EfCbf5D700b37A27aA00E6934e6904e7B8e' },
];

const buildExchange = (secondaryFeeRecipient: string, secondaryFeePercentage: number) => {
const buildExchange = (
secondaryFeeRecipient: string,
secondaryFeePercentage: number
) => {
if (secondaryFeeRecipient && secondaryFeePercentage) {
return new Exchange({
...configuration,
Expand All @@ -52,11 +59,13 @@ export function Example() {
const [isFetching, setIsFetching] = useState(false);
const [inputAmount, setInputAmount] = useState<string>('0');
const [outputAmount, setOutputAmount] = useState<string>('0');
const [swapTransaction, setSwapTransaction] = useState<ethers.providers.TransactionReceipt | null>(null);
const [swapTransaction, setSwapTransaction] =
useState<ethers.providers.TransactionReceipt | null>(null);
const [approved, setApproved] = useState<boolean>(false);
const [result, setResult] = useState<TransactionResponse | null>();
const [error, setError] = useState<string | null>(null);
const [secondaryFeeRecipient, setSecondaryFeeRecipient] = useState<string>('');
const [secondaryFeeRecipient, setSecondaryFeeRecipient] =
useState<string>('');
const [secondaryFeePercentage, setFeePercentage] = useState<number>(0);

const [tradeType, setTradeType] = useState<TradeType>('exactInput');
Expand All @@ -75,23 +84,27 @@ export function Example() {
const getQuote = async (tokenInAddress: string, tokenOutAddress: string) => {
setIsFetching(true);
setError(null);
setApproved(false);

try {
const exchange = buildExchange(secondaryFeeRecipient, secondaryFeePercentage);
const exchange = buildExchange(
secondaryFeeRecipient,
secondaryFeePercentage
);

const txn =
tradeType === 'exactInput'
? await exchange.getUnsignedSwapTxFromAmountIn(
ethereumAccount,
tokenInAddress,
tokenOutAddress,
ethers.utils.parseEther(`${inputAmount}`),
ethers.utils.parseEther(`${inputAmount}`)
)
: await exchange.getUnsignedSwapTxFromAmountOut(
ethereumAccount,
tokenInAddress,
tokenOutAddress,
ethers.utils.parseEther(`${outputAmount}`),
ethers.utils.parseEther(`${outputAmount}`)
);

setResult(txn);
Expand All @@ -111,14 +124,18 @@ export function Example() {
const performSwap = async (result: TransactionResponse) => {
setSwapTransaction(null);
setIsFetching(true);
const provider = new ethers.providers.Web3Provider((window as any).ethereum);
const provider = new ethers.providers.Web3Provider(
(window as any).ethereum
);
const signer = provider.getSigner();

// Approve the ERC20 spend
if (!approved) {
try {
// Send the Approve transaction
const approveReceipt = await signer.sendTransaction(result.approval!.transaction);
const approveReceipt = await signer.sendTransaction(
result.approval!.transaction
);

// Wait for the Approve transaction to complete
await provider.waitForTransaction(approveReceipt.hash, 1);
Expand Down Expand Up @@ -151,7 +168,9 @@ export function Example() {

return (
<div style={{ display: 'flex', flexDirection: 'column' }}>
<h3 style={{ marginBottom: '12px' }}>Your wallet address: {ethereumAccount}</h3>
<h3 style={{ marginBottom: '12px' }}>
Your wallet address: {ethereumAccount}
</h3>

<div style={{ display: 'flex', flexDirection: 'row' }}>
<div style={{ width: 150 }}>
Expand Down Expand Up @@ -224,12 +243,21 @@ export function Example() {

<hr className='my-4' />

<SecondaryFeeInput setSecondaryFeeRecipient={setSecondaryFeeRecipient} setFeePercentage={setFeePercentage} />
<SecondaryFeeInput
setSecondaryFeeRecipient={setSecondaryFeeRecipient}
setFeePercentage={setFeePercentage}
/>
{tradeType === 'exactInput' && inputToken && (
<AmountInput tokenSymbol={inputToken.symbol} setAmount={setInputAmount} />
<AmountInput
tokenSymbol={inputToken.symbol}
setAmount={setInputAmount}
/>
)}
{tradeType === 'exactOutput' && outputToken && (
<AmountOutput tokenSymbol={outputToken.symbol} setAmount={setOutputAmount} />
<AmountOutput
tokenSymbol={outputToken.symbol}
setAmount={setOutputAmount}
/>
)}

{inputToken && outputToken && (
Expand All @@ -246,20 +274,30 @@ export function Example() {
{result && (
<>
<h3>
Expected amount: {ethers.utils.formatEther(result.quote.amount.value)}{' '}
Expected amount:{' '}
{ethers.utils.formatEther(result.quote.amount.value)}{' '}
{`${addressToSymbolMapping[result.quote.amount.token.address]}`}
</h3>
<h3>
{tradeType === 'exactInput' ? 'Minimum' : 'Maximum'} amount:{' '}
{ethers.utils.formatEther(result.quote.amountWithMaxSlippage.value)}{' '}
{`${addressToSymbolMapping[result.quote.amountWithMaxSlippage.token.address]}`}
{`${
addressToSymbolMapping[
result.quote.amountWithMaxSlippage.token.address
]
}`}
</h3>

<h3>Slippage: {result.quote.slippage}%</h3>
{result.approval && <h3>Approval Gas Estimate: {showGasEstimate(result.approval)}</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}
addressMap={addressToSymbolMapping}
/>

<>
<button
Expand All @@ -272,7 +310,8 @@ export function Example() {
{swapTransaction && (
<>
<h3 style={{ marginTop: '12px' }}>
Swap successful! Check your metamask to see updated token balances
Swap successful! Check your metamask to see updated token
balances
</h3>
<a
className='underline text-blue-600 hover:text-blue-800 visited:text-purple-600'
Expand All @@ -290,7 +329,9 @@ export function Example() {
}

const showGasEstimate = (txn: TransactionDetails) =>
txn.gasFeeEstimate ? `${ethers.utils.formatEther(txn.gasFeeEstimate.value)} IMX` : 'No gas estimate available';
txn.gasFeeEstimate
? `${ethers.utils.formatEther(txn.gasFeeEstimate.value)} IMX`
: 'No gas estimate available';

const ErrorMessage = ({ message }: { message: string }) => {
return (
Expand Down
10 changes: 6 additions & 4 deletions packages/internal/dex/sdk-sample-app/src/config/devnet.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Environment, ImmutableConfiguration } from '@imtbl/config';
import { ERC20, ExchangeConfiguration, ExchangeContracts, ExchangeOverrides, Native } from '@imtbl/dex-sdk';
import {
ERC20, ExchangeConfiguration, ExchangeContracts, ExchangeOverrides, Native,
} from '@imtbl/dex-sdk';

/**
* The configuration in this file can be used to override the default configuration values
Expand All @@ -15,9 +17,9 @@ const immutableConfig = new ImmutableConfiguration({
const contractOverrides: ExchangeContracts = {
multicall: '0x9482D1727424B6C3EeaA22B037FFBC3ae6748f66',
coreFactory: '0x8081d5F526b7Aaf4868e6C53Aa8a9d9D93c10562',
quoterV2: '0xC12B5c73951CFD922979638b5d19C593ac51dcDA',
peripheryRouter: '0x8089b5D6fa3f19C64081d5050c5CA3a66f34C5af',
secondaryFee: '0x8089b5D6fa3f19C64081d5050c5CA3a66f34C5af', // not deployed currently
quoter: '0xC12B5c73951CFD922979638b5d19C593ac51dcDA',
swapRouter: '0x8089b5D6fa3f19C64081d5050c5CA3a66f34C5af',
immutableSwapProxy: '0x0234ceca85Efb0c3a751088d328F3db3d397DDBF',
};

const wrappedNativeToken: ERC20 = {
Expand Down
12 changes: 6 additions & 6 deletions packages/internal/dex/sdk/src/config/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ describe('ExchangeConfiguration', () => {
const contractOverrides: ExchangeContracts = {
multicall: test.TEST_MULTICALL_ADDRESS,
coreFactory: test.TEST_V3_CORE_FACTORY_ADDRESS,
quoterV2: test.TEST_QUOTER_ADDRESS,
peripheryRouter: test.TEST_ROUTER_ADDRESS,
quoter: test.TEST_QUOTER_ADDRESS,
swapRouter: test.TEST_ROUTER_ADDRESS,
immutableSwapProxy: test.TEST_SWAP_PROXY_ADDRESS,
};

Expand Down Expand Up @@ -129,8 +129,8 @@ describe('ExchangeConfiguration', () => {
// contracts
expect(config.chain.contracts.coreFactory).toBe(contractOverrides.coreFactory);
expect(config.chain.contracts.multicall).toBe(contractOverrides.multicall);
expect(config.chain.contracts.peripheryRouter).toBe(contractOverrides.peripheryRouter);
expect(config.chain.contracts.quoterV2).toBe(contractOverrides.quoterV2);
expect(config.chain.contracts.swapRouter).toBe(contractOverrides.swapRouter);
expect(config.chain.contracts.quoter).toBe(contractOverrides.quoter);
// tokens
expect(config.chain.commonRoutingTokens[0].address.toLocaleLowerCase()).toEqual(
commonRoutingTokens[0].address.toLocaleLowerCase(),
Expand Down Expand Up @@ -161,8 +161,8 @@ describe('ExchangeConfiguration', () => {
const invalidContractOverrides: ExchangeContracts = {
multicall: '',
coreFactory: test.TEST_V3_CORE_FACTORY_ADDRESS,
quoterV2: test.TEST_QUOTER_ADDRESS,
peripheryRouter: test.TEST_ROUTER_ADDRESS,
quoter: test.TEST_QUOTER_ADDRESS,
swapRouter: test.TEST_ROUTER_ADDRESS,
immutableSwapProxy: test.TEST_SWAP_PROXY_ADDRESS,
};

Expand Down
8 changes: 4 additions & 4 deletions packages/internal/dex/sdk/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ import {

export const CONTRACTS_FOR_CHAIN_ID: Record<number, ExchangeContracts> = {
[IMMUTABLE_TESTNET_CHAIN_ID]: {
multicall: '0x4DB567A44451b27C1fAd7f52e1cDf64b915d62f9',
coreFactory: '0xb18c44b211065E69844FbA9AE146DA362104AfBf',
quoterV2: '0x87854A7D4b9BaC3D37f4516A1Ac7F36fB5ad539f',
peripheryRouter: '0x786ec643F231960D4C1A4E336990F8E7bF8f1277',
multicall: '0x4857Dfd11c712e862eC362cEee29F7974B70EfcD',
coreFactory: '0x56c2162254b0E4417288786eE402c2B41d4e181e',
quoter: '0xF6Ad3CcF71Abb3E12beCf6b3D2a74C963859ADCd',
swapRouter: '0x0b012055F770AE7BB7a8303968A7Fb6088A2296e',
immutableSwapProxy: '0xDdBDa144cEbe1cCd68E746CDff8a6e4Be51A9e98',
},
};
Expand Down
2 changes: 1 addition & 1 deletion packages/internal/dex/sdk/src/exchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class Exchange {
this.wrappedNativeToken = config.chain.wrappedNativeToken;
this.nativeTokenService = new NativeTokenService(this.nativeToken, this.wrappedNativeToken);
this.secondaryFees = config.secondaryFees;
this.routerContractAddress = config.chain.contracts.peripheryRouter;
this.routerContractAddress = config.chain.contracts.swapRouter;
this.swapProxyContractAddress = config.chain.contracts.immutableSwapProxy;

this.provider = new ethers.providers.StaticJsonRpcProvider({
Expand Down
8 changes: 4 additions & 4 deletions packages/internal/dex/sdk/src/lib/router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const fees = [100, 500, 10000];
const config = SUPPORTED_SANDBOX_CHAINS[IMMUTABLE_TESTNET_CHAIN_ID];
const provider = new providers.JsonRpcBatchProvider(config.rpcUrl, config.chainId);

const q = QuoterV2__factory.connect(config.contracts.quoterV2, provider);
const q = QuoterV2__factory.connect(config.contracts.quoter, provider);
const m = Multicall__factory.connect(config.contracts.multicall, provider);

const callDatas = () => fees.map((fee) => {
Expand Down Expand Up @@ -57,7 +57,7 @@ describe('router', () => {
it.each(Array(10).fill(null))('parallel', async () => {
const promises = callDatas().map(async (data) => {
const res = await provider.call({
to: config.contracts.quoterV2,
to: config.contracts.quoter,
data,
});
const { amountOut } = q.interface.decodeFunctionResult(
Expand All @@ -74,7 +74,7 @@ describe('router', () => {
it.each(Array(10).fill(null))('multicall', async () => {
const calls = callDatas().map((callData) => {
return {
target: config.contracts.quoterV2,
target: config.contracts.quoter,
callData,
gasLimit: 24_000_000,
};
Expand All @@ -94,7 +94,7 @@ describe('router', () => {
it.each(Array(1).fill(null))('batch', async () => {
const promises = callDatas().map((callData) => {
return provider.send('eth_call', [{
to: config.contracts.quoterV2,
to: config.contracts.quoter,
data: callData,
}, 'latest']);
});
Expand Down
4 changes: 2 additions & 2 deletions packages/internal/dex/sdk/src/lib/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { Multicall } from '../contracts/types';
export type RoutingContracts = {
multicall: string;
coreFactory: string;
quoterV2: string;
quoter: string;
};

export class Router {
Expand Down Expand Up @@ -72,7 +72,7 @@ export class Router {
): Promise<QuoteResult> {
const quotes = await getQuotesForRoutes(
this.provider,
this.routingContracts.quoterV2,
this.routingContracts.quoter,
routes,
amountSpecified,
tradeType,
Expand Down
6 changes: 3 additions & 3 deletions packages/internal/dex/sdk/src/test/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const TEST_SWAP_PROXY_ADDRESS = '0x8dBE1f0900C5e92ad87A54521902a33ba1598C

export const TEST_ROUTING_CONTRACTS: RoutingContracts = {
coreFactory: TEST_V3_CORE_FACTORY_ADDRESS,
quoterV2: TEST_QUOTER_ADDRESS,
quoter: TEST_QUOTER_ADDRESS,
multicall: TEST_MULTICALL_ADDRESS,
};

Expand Down Expand Up @@ -107,8 +107,8 @@ export const TEST_DEX_CONFIGURATION: ExchangeModuleConfiguration = {
exchangeContracts: {
multicall: TEST_MULTICALL_ADDRESS,
coreFactory: TEST_V3_CORE_FACTORY_ADDRESS,
quoterV2: TEST_QUOTER_ADDRESS,
peripheryRouter: TEST_ROUTER_ADDRESS,
quoter: TEST_QUOTER_ADDRESS,
swapRouter: TEST_ROUTER_ADDRESS,
immutableSwapProxy: TEST_SWAP_PROXY_ADDRESS,
},
commonRoutingTokens: [],
Expand Down
7 changes: 4 additions & 3 deletions packages/internal/dex/sdk/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { ModuleConfiguration } from '@imtbl/config';
export type ExchangeContracts = {
multicall: string;
coreFactory: string;
quoterV2: string;
peripheryRouter: string;
quoter: string;
swapRouter: string;
immutableSwapProxy: string;
};

Expand Down Expand Up @@ -183,7 +183,8 @@ export type ExchangeOverrides = {
* @property {number} chainId - The chain ID
* @property {@link SecondaryFee[]} secondaryFees - The secondary fees for a swap
*/
export interface ExchangeModuleConfiguration extends ModuleConfiguration<ExchangeOverrides> {
export interface ExchangeModuleConfiguration
extends ModuleConfiguration<ExchangeOverrides> {
chainId: number;
secondaryFees?: SecondaryFee[];
}

0 comments on commit e6f73e6

Please sign in to comment.