Skip to content
Open
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
2 changes: 2 additions & 0 deletions src/dex/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import { EtherFi } from './etherfi';
import { Spark } from './spark/spark';
import { VelodromeSlipstream } from './uniswap-v3/forks/velodrome-slipstream/velodrome-slipstream';
import { AaveV3Stata } from './aave-v3-stata/aave-v3-stata';
import { SquadswapV2 } from './squadswap-v2/squadswap-v2';

const LegacyDexes = [
CurveV2,
Expand Down Expand Up @@ -157,6 +158,7 @@ const Dexes = [
PharaohV1,
Spark,
AaveV3Stata,
SquadswapV2,
];

export type LegacyDexConstructor = new (dexHelper: IDexHelper) => IDexTxBuilder<
Expand Down
36 changes: 36 additions & 0 deletions src/dex/squadswap-v2/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { DexParams } from './types';
import { DexConfigMap, AdapterMappings } from '../../types';
import { Network, SwapSide } from '../../constants';

export const UniswapV2Config: DexConfigMap<DexParams> = {
SquadswapV2: {
[Network.BSC]: {
factoryAddress: '0x1D9F43a6195054313ac1aE423B1f810f593b6ac1',
initCode:
'0xd424455c1204e4f46a4a380651928652376a351698d3d97e2da05d3041c15fbe',
poolGasCost: 80 * 1000,
feeCode: 20,
subgraphURL:
'https://api.studio.thegraph.com/query/59394/exchangev2/version/latest',
},
},
};

export const Adapters: Record<number, AdapterMappings> = {
// TODO: add adapters for each chain
// This is an example to copy
[Network.BSC]: {
[SwapSide.SELL]: [
{
name: 'BscAdapter01',
index: 3,
},
],
[SwapSide.BUY]: [
{
name: 'BscBuyAdapter',
index: 1,
},
],
},
};
40 changes: 40 additions & 0 deletions src/dex/squadswap-v2/squadswap-v2-constant-product-pool.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { RESERVE_LIMIT } from './squadswap-v2';
import { UniswapV2PoolOrderedParams } from './types';

export class Uniswapv2ConstantProductPool {
static getSellPrice(
priceParams: UniswapV2PoolOrderedParams,
srcAmount: bigint,
feeFactor: number,
): bigint {
const { reservesIn, reservesOut, fee } = priceParams;

if (BigInt(reservesIn) + srcAmount > RESERVE_LIMIT) {
return 0n;
}

const amountInWithFee = srcAmount * BigInt(feeFactor - parseInt(fee));

const numerator = amountInWithFee * BigInt(reservesOut);

const denominator =
BigInt(reservesIn) * BigInt(feeFactor) + amountInWithFee;

return denominator === 0n ? 0n : numerator / denominator;
}

static getBuyPrice(
priceParams: UniswapV2PoolOrderedParams,
destAmount: bigint,
feeFactor: number,
): bigint {
const { reservesIn, reservesOut, fee } = priceParams;

const numerator = BigInt(reservesIn) * destAmount * BigInt(feeFactor);
const denominator =
(BigInt(feeFactor) - BigInt(fee)) * (BigInt(reservesOut) - destAmount);

if (denominator <= 0n) return 0n;
return numerator === 0n ? 0n : 1n + numerator / denominator;
}
}
192 changes: 192 additions & 0 deletions src/dex/squadswap-v2/squadswap-v2-e2e.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import dotenv from 'dotenv';
dotenv.config();

import { testE2E } from '../../../tests/utils-e2e';
import {
Tokens,
Holders,
NativeTokenSymbols,
} from '../../../tests/constants-e2e';
import { Network, ContractMethod, SwapSide } from '../../constants';
import { StaticJsonRpcProvider } from '@ethersproject/providers';
import { generateConfig } from '../../config';

function testForNetwork(
network: Network,
dexKey: string,
tokenASymbol: string,
tokenBSymbol: string,
tokenAAmount: string,
tokenBAmount: string,
nativeTokenAmount: string,
slippage?: number | undefined,
) {
const provider = new StaticJsonRpcProvider(
generateConfig(network).privateHttpProvider,
network,
);
const tokens = Tokens[network];
const holders = Holders[network];
const nativeTokenSymbol = NativeTokenSymbols[network];

const sideToContractMethods = new Map([
[
SwapSide.SELL,
[
ContractMethod.simpleSwap,
ContractMethod.multiSwap,
ContractMethod.megaSwap,
],
],
[SwapSide.BUY, [ContractMethod.simpleBuy, ContractMethod.buy]],
]);

describe(`${network}`, () => {
sideToContractMethods.forEach((contractMethods, side) =>
describe(`${side}`, () => {
contractMethods.forEach((contractMethod: ContractMethod) => {
describe(`${contractMethod}`, () => {
it(`${nativeTokenSymbol} -> ${tokenASymbol}`, async () => {
await testE2E(
tokens[nativeTokenSymbol],
tokens[tokenASymbol],
holders[nativeTokenSymbol],
side === SwapSide.SELL ? nativeTokenAmount : tokenAAmount,
side,
dexKey,
contractMethod,
network,
provider,
undefined,
undefined,
undefined,
slippage,
);
});
it(`${tokenASymbol} -> ${nativeTokenSymbol}`, async () => {
await testE2E(
tokens[tokenASymbol],
tokens[nativeTokenSymbol],
holders[tokenASymbol],
side === SwapSide.SELL ? tokenAAmount : nativeTokenAmount,
side,
dexKey,
contractMethod,
network,
provider,
undefined,
undefined,
undefined,
slippage,
);
});
it(`${tokenASymbol} -> ${tokenBSymbol}`, async () => {
await testE2E(
tokens[tokenASymbol],
tokens[tokenBSymbol],
holders[tokenASymbol],
side === SwapSide.SELL ? tokenAAmount : tokenBAmount,
side,
dexKey,
contractMethod,
network,
provider,
undefined,
undefined,
undefined,
slippage,
);
});
});
});
}),
);
});
}

describe('SquadSwap v2 BSC', () => {
const network = Network.BSC;
const dexKey = 'SquadswapV2';
const tokens = Tokens[network];
const holders = Holders[network];
const provider = new StaticJsonRpcProvider(
generateConfig(network).privateHttpProvider,
network,
);

const sideToContractMethods = new Map([
[
SwapSide.SELL,
[
ContractMethod.simpleSwap,
ContractMethod.multiSwap,
ContractMethod.megaSwap,
],
],
[SwapSide.BUY, [ContractMethod.simpleBuy, ContractMethod.buy]],
]);

const pairs: { name: string; sellAmount: string; buyAmount: string }[][] = [
[
{
name: NativeTokenSymbols[network],
sellAmount: '100000000000000000000',
buyAmount: '100000000000000000000',
},
{
name: 'USDT',
sellAmount: '100000000000000000000',
buyAmount: '10000000000000',
},
],
[
{
name: 'WBNB',
sellAmount: '1000000000000000000',
buyAmount: '10000000000000000000',
},
{
name: 'USDT',
sellAmount: '10000000000000000000',
buyAmount: '1000000000000000000',
},
],
];

sideToContractMethods.forEach((contractMethods, side) =>
describe(`${side}`, () => {
contractMethods.forEach((contractMethod: ContractMethod) => {
pairs.forEach(pair => {
describe(`${contractMethod}`, () => {
it(`${pair[0].name} -> ${pair[1].name}`, async () => {
await testE2E(
tokens[pair[0].name],
tokens[pair[1].name],
holders[pair[0].name],
side === SwapSide.SELL ? pair[0].sellAmount : pair[0].buyAmount,
side,
dexKey,
contractMethod,
network,
provider,
);
});
it(`${pair[1].name} -> ${pair[0].name}`, async () => {
await testE2E(
tokens[pair[1].name],
tokens[pair[0].name],
holders[pair[1].name],
side === SwapSide.SELL ? pair[1].sellAmount : pair[1].buyAmount,
side,
dexKey,
contractMethod,
network,
provider,
);
});
});
});
});
}),
);
});
Loading