From d90388139b364201b301db2276c63f41e23f01c5 Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 15 Dec 2023 09:21:05 +0100 Subject: [PATCH 1/8] wip --- modules/balancer/balancer.gql | 39 ------------- modules/balancer/balancer.resolvers.ts | 23 -------- modules/beethoven/balancer-sdk.resolvers.ts | 45 -------------- .../{beethoven => sor}/balancer-sor.test.ts | 0 .../balancer-sdk.gql => sor/sor.gql} | 49 +++++++++------- modules/sor/sor.resolvers.ts | 31 ++++++++++ modules/sor/sor.service.ts | 58 ++++++++++++++----- .../sorV1Beets}/balancer-sor.service.ts | 18 +++--- modules/sor/sorV1Beets/sorV1Beets.service.ts | 2 +- modules/sor/sorV2/sorV2.service.ts | 2 +- modules/sor/utils.ts | 5 +- 11 files changed, 119 insertions(+), 153 deletions(-) delete mode 100644 modules/balancer/balancer.gql delete mode 100644 modules/balancer/balancer.resolvers.ts delete mode 100644 modules/beethoven/balancer-sdk.resolvers.ts rename modules/{beethoven => sor}/balancer-sor.test.ts (100%) rename modules/{beethoven/balancer-sdk.gql => sor/sor.gql} (93%) create mode 100644 modules/sor/sor.resolvers.ts rename modules/{beethoven => sor/sorV1Beets}/balancer-sor.service.ts (96%) diff --git a/modules/balancer/balancer.gql b/modules/balancer/balancer.gql deleted file mode 100644 index 228429a2f..000000000 --- a/modules/balancer/balancer.gql +++ /dev/null @@ -1,39 +0,0 @@ -extend type Mutation { - balancerMutationTest: String! -} - -extend type Query { - sorGetCowSwaps( - chain: GqlChain! - tokenIn: String! - tokenOut: String! - swapType: GqlSorSwapType! - swapAmount: BigDecimal! #expected in raw amount - ): GqlCowSwapApiResponse! -} - -enum GqlSorSwapType { - EXACT_IN - EXACT_OUT -} - -type GqlCowSwapApiResponse { - tokenAddresses: [String!]! - swaps: [GqlSwap!]! - swapAmount: String! - swapAmountForSwaps: String! - returnAmount: String! - returnAmountFromSwaps: String! - returnAmountConsideringFees: String! - tokenIn: String! - tokenOut: String! - marketSp: String! -} - -type GqlSwap { - poolId: String! - assetInIndex: Int! - assetOutIndex: Int! - amount: String! - userData: String! -} diff --git a/modules/balancer/balancer.resolvers.ts b/modules/balancer/balancer.resolvers.ts deleted file mode 100644 index 3369aa1e0..000000000 --- a/modules/balancer/balancer.resolvers.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Resolvers } from '../../schema'; -import { sorService } from '../sor/sor.service'; -import { getTokenAmountRaw } from '../sor/utils'; - -const balancerResolvers: Resolvers = { - Query: { - sorGetCowSwaps: async (parent, args, context) => { - const amountToken = args.swapType === 'EXACT_IN' ? args.tokenIn : args.tokenOut; - // Use TokenAmount to help follow scaling requirements in later logic - // args.swapAmount is RawScale, e.g. 1USDC should be passed as 1000000 - const amount = await getTokenAmountRaw(amountToken, args.swapAmount, args.chain); - const swaps = await sorService.getCowSwaps({ ...args, swapAmount: amount, swapOptions: {} }); - return { ...swaps, __typename: 'GqlCowSwapApiResponse' }; - }, - }, - Mutation: { - balancerMutationTest: async (parent, {}, context) => { - return 'test'; - }, - }, -}; - -export default balancerResolvers; diff --git a/modules/beethoven/balancer-sdk.resolvers.ts b/modules/beethoven/balancer-sdk.resolvers.ts deleted file mode 100644 index a755a1e90..000000000 --- a/modules/beethoven/balancer-sdk.resolvers.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { Resolvers } from '../../schema'; -import { balancerSorService } from './balancer-sor.service'; -import { tokenService } from '../token/token.service'; -import { sorService } from '../sor/sor.service'; -import { getTokenAmountHuman } from '../sor/utils'; -import { headerChain } from '../context/header-chain'; - -const balancerSdkResolvers: Resolvers = { - Query: { - sorGetSwaps: async (parent, args, context) => { - console.log('sorGetSwaps args', JSON.stringify(args)); - - const currentChain = headerChain(); - if (!args.chain && currentChain) { - args.chain = currentChain; - } else if (!args.chain) { - throw new Error('sorGetSwaps error: Provide "chain" param'); - } - const chain = args.chain; - const tokenIn = args.tokenIn.toLowerCase(); - const tokenOut = args.tokenOut.toLowerCase(); - const amountToken = args.swapType === 'EXACT_IN' ? tokenIn : tokenOut; - // Use TokenAmount to help follow scaling requirements in later logic - // args.swapAmount is HumanScale - const amount = await getTokenAmountHuman(amountToken, args.swapAmount, args.chain); - - const swaps = await sorService.getBeetsSwaps({ - ...args, - chain, - tokenIn, - tokenOut, - swapAmount: amount, - }); - - return { ...swaps, __typename: 'GqlSorGetSwapsResponse' }; - }, - sorGetBatchSwapForTokensIn: async (parent, args, context) => { - const tokens = await tokenService.getTokens(); - - return balancerSorService.getBatchSwapForTokensIn({ ...args, tokens }); - }, - }, -}; - -export default balancerSdkResolvers; diff --git a/modules/beethoven/balancer-sor.test.ts b/modules/sor/balancer-sor.test.ts similarity index 100% rename from modules/beethoven/balancer-sor.test.ts rename to modules/sor/balancer-sor.test.ts diff --git a/modules/beethoven/balancer-sdk.gql b/modules/sor/sor.gql similarity index 93% rename from modules/beethoven/balancer-sdk.gql rename to modules/sor/sor.gql index 530ab20c2..716cf02d2 100644 --- a/modules/beethoven/balancer-sdk.gql +++ b/modules/sor/sor.gql @@ -7,6 +7,13 @@ extend type Query { swapAmount: BigDecimal! #expected in human readable form swapOptions: GqlSorSwapOptionsInput! ): GqlSorGetSwapsResponse! + sorGetCowSwaps( + chain: GqlChain! + tokenIn: String! + tokenOut: String! + swapType: GqlSorSwapType! + swapAmount: BigDecimal! #expected in raw amount + ): GqlCowSwapApiResponse! sorGetBatchSwapForTokensIn( tokensIn: [GqlTokenAmountHumanReadable!]! tokenOut: String! @@ -14,6 +21,27 @@ extend type Query { ): GqlSorGetBatchSwapForTokensInResponse! } +type GqlCowSwapApiResponse { + tokenAddresses: [String!]! + swaps: [GqlSwap!]! + swapAmount: String! + swapAmountForSwaps: String! + returnAmount: String! + returnAmountFromSwaps: String! + returnAmountConsideringFees: String! + tokenIn: String! + tokenOut: String! + marketSp: String! +} + +type GqlSwap { + poolId: String! + assetInIndex: Int! + assetOutIndex: Int! + amount: String! + userData: String! +} + enum GqlSorSwapType { EXACT_IN EXACT_OUT @@ -109,24 +137,3 @@ type GqlSorGetBatchSwapForTokensInResponse { swaps: [GqlSorSwap!]! assets: [String!]! } - -type GqlCowSwapApiResponse { - tokenAddresses: [String!]! - swaps: [GqlSwap!]! - swapAmount: String! - swapAmountForSwaps: String! - returnAmount: String! - returnAmountFromSwaps: String! - returnAmountConsideringFees: String! - tokenIn: String! - tokenOut: String! - marketSp: String! -} - -type GqlSwap { - poolId: String! - assetInIndex: Int! - assetOutIndex: Int! - amount: String! - userData: String! -} diff --git a/modules/sor/sor.resolvers.ts b/modules/sor/sor.resolvers.ts new file mode 100644 index 000000000..b4da3403d --- /dev/null +++ b/modules/sor/sor.resolvers.ts @@ -0,0 +1,31 @@ +import { Resolvers } from '../../schema'; +import { balancerSorService } from './sorV1Beets/balancer-sor.service'; +import { tokenService } from '../token/token.service'; +import { sorService } from './sor.service'; +import { getTokenAmountHuman } from './utils'; +import { headerChain } from '../context/header-chain'; + +const balancerSdkResolvers: Resolvers = { + Query: { + sorGetSwaps: async (parent, args, context) => { + const currentChain = headerChain(); + if (!args.chain && currentChain) { + args.chain = currentChain; + } else if (!args.chain) { + throw new Error('sorGetSwaps error: Provide "chain" param'); + } + + return sorService.getBeetsSwaps(args); + }, + sorGetBatchSwapForTokensIn: async (parent, args, context) => { + const tokens = await tokenService.getTokens(); + + return balancerSorService.getBatchSwapForTokensIn({ ...args, tokens }); + }, + sorGetCowSwaps: async (parent, args, context) => { + return sorService.getCowSwaps(args); + }, + }, +}; + +export default balancerSdkResolvers; diff --git a/modules/sor/sor.service.ts b/modules/sor/sor.service.ts index 33bd006a7..5019bebbb 100644 --- a/modules/sor/sor.service.ts +++ b/modules/sor/sor.service.ts @@ -1,4 +1,11 @@ -import { GqlCowSwapApiResponse, GqlSorSwapType, GqlSorGetSwapsResponse, GqlSorSwapOptionsInput } from '../../schema'; +import { + GqlCowSwapApiResponse, + GqlSorSwapType, + GqlSorGetSwapsResponse, + GqlSorSwapOptionsInput, + QuerySorGetSwapsArgs, + QuerySorGetCowSwapsArgs, +} from '../../schema'; import { sorV1BalancerService } from './sorV1Balancer/sorV1Balancer.service'; import { sorV1BeetsService } from './sorV1Beets/sorV1Beets.service'; import { sorV2Service } from './sorV2/sorV2.service'; @@ -8,32 +15,57 @@ import { publishMetric } from '../metrics/sor.metric'; import { Chain } from '@prisma/client'; import { parseUnits, formatUnits } from '@ethersproject/units'; import { tokenService } from '../token/token.service'; +import { getTokenAmountHuman, getTokenAmountRaw } from './utils'; export class SorService { - async getCowSwaps(input: GetSwapsInput): Promise { - const swap = await this.getSwap({ ...input, swapOptions: {} }); - const emptyResponse = EMPTY_COWSWAP_RESPONSE(input.tokenIn, input.tokenOut, input.swapAmount); + async getCowSwaps(args: QuerySorGetCowSwapsArgs): Promise { + const amountToken = args.swapType === 'EXACT_IN' ? args.tokenIn : args.tokenOut; + // Use TokenAmount to help follow scaling requirements in later logic + // args.swapAmount is RawScale, e.g. 1USDC should be passed as 1000000 + const amount = await getTokenAmountRaw(amountToken, args.swapAmount, args.chain!); + + const swap = await this.getSwap({ + chain: args.chain!, + swapAmount: amount, + swapType: args.swapType, + tokenIn: args.tokenIn.toLowerCase(), + tokenOut: args.tokenOut.toLowerCase(), + swapOptions: {}, + }); + const emptyResponse = EMPTY_COWSWAP_RESPONSE(args.tokenIn, args.tokenOut, amount); if (!swap) return emptyResponse; try { // Updates with latest onchain data before returning - return await swap.getCowSwapResponse(input.chain, true); + return await swap.getCowSwapResponse(args.chain!, true); } catch (err) { console.log(`Error Retrieving QuerySwap`, err); return emptyResponse; } } - async getBeetsSwaps(input: GetSwapsInput): Promise { - console.log('getBeetsSwaps input', JSON.stringify(input)); - const swap = await this.getSwap(input, sorV1BeetsService); - const emptyResponse = sorV1BeetsService.zeroResponse( - input.swapType, - input.tokenIn, - input.tokenOut, - input.swapAmount, + async getBeetsSwaps(args: QuerySorGetSwapsArgs): Promise { + console.log('getBeetsSwaps args', JSON.stringify(args)); + const tokenIn = args.tokenIn.toLowerCase(); + const tokenOut = args.tokenOut.toLowerCase(); + const amountToken = args.swapType === 'EXACT_IN' ? tokenIn : tokenOut; + // Use TokenAmount to help follow scaling requirements in later logic + // args.swapAmount is HumanScale + const amount = await getTokenAmountHuman(amountToken, args.swapAmount, args.chain!); + + const swap = await this.getSwap( + { + chain: args.chain!, + swapAmount: amount, + swapOptions: args.swapOptions, + swapType: args.swapType, + tokenIn: tokenIn, + tokenOut: tokenOut, + }, + sorV1BeetsService, ); + const emptyResponse = sorV1BeetsService.zeroResponse(args.swapType, args.tokenIn, args.tokenOut, amount); if (!swap) return emptyResponse; diff --git a/modules/beethoven/balancer-sor.service.ts b/modules/sor/sorV1Beets/balancer-sor.service.ts similarity index 96% rename from modules/beethoven/balancer-sor.service.ts rename to modules/sor/sorV1Beets/balancer-sor.service.ts index 7452f5ea0..6cf8fefa7 100644 --- a/modules/beethoven/balancer-sor.service.ts +++ b/modules/sor/sorV1Beets/balancer-sor.service.ts @@ -1,19 +1,19 @@ -import { GqlSorGetSwapsResponse, GqlSorSwapOptionsInput, GqlSorSwapType, GqlPoolMinimal } from '../../schema'; +import { GqlSorGetSwapsResponse, GqlSorSwapOptionsInput, GqlSorSwapType, GqlPoolMinimal } from '../../../schema'; import { formatFixed, parseFixed } from '@ethersproject/bignumber'; import { PrismaToken } from '@prisma/client'; -import { poolService } from '../pool/pool.service'; -import { oldBnum } from '../big-number/old-big-number'; +import { poolService } from '../../pool/pool.service'; +import { oldBnum } from '../../big-number/old-big-number'; import axios from 'axios'; import { FundManagement, SwapInfo, SwapTypes, SwapV2 } from '@balancer-labs/sdk'; -import { replaceEthWithZeroAddress, replaceZeroAddressWithEth } from '../web3/addresses'; +import { replaceEthWithZeroAddress, replaceZeroAddressWithEth } from '../../web3/addresses'; import { BigNumber } from 'ethers'; -import { TokenAmountHumanReadable } from '../common/global-types'; +import { TokenAmountHumanReadable } from '../../common/global-types'; import { AddressZero } from '@ethersproject/constants'; import { Contract } from '@ethersproject/contracts'; -import VaultAbi from '../pool/abi/Vault.json'; -import { env } from '../../app/env'; -import { networkContext } from '../network/network-context.service'; -import { DeploymentEnv } from '../network/network-config-types'; +import VaultAbi from '../../pool/abi/Vault.json'; +import { env } from '../../../app/env'; +import { networkContext } from '../../network/network-context.service'; +import { DeploymentEnv } from '../../network/network-config-types'; import * as Sentry from '@sentry/node'; import _ from 'lodash'; import { Logger } from '@ethersproject/logger'; diff --git a/modules/sor/sorV1Beets/sorV1Beets.service.ts b/modules/sor/sorV1Beets/sorV1Beets.service.ts index 067e5f87c..e91744ec7 100644 --- a/modules/sor/sorV1Beets/sorV1Beets.service.ts +++ b/modules/sor/sorV1Beets/sorV1Beets.service.ts @@ -1,7 +1,7 @@ import { formatEther } from 'viem'; import { GqlSorSwapType, GqlCowSwapApiResponse, GqlSorGetSwapsResponse, GqlSorSwapOptionsInput } from '../../../schema'; import { GetSwapsInput, SwapService, SwapResult } from '../types'; -import { BalancerSorService } from '../../beethoven/balancer-sor.service'; +import { BalancerSorService } from './balancer-sor.service'; import { tokenService } from '../../token/token.service'; import { TokenAmount } from '@balancer/sdk'; diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index a3b96fc2e..fd442dfae 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -32,7 +32,7 @@ import { prisma } from '../../../prisma/prisma-client'; import { GetSwapsInput, SwapResult, SwapService } from '../types'; import { poolService } from '../../pool/pool.service'; import { tokenService } from '../../token/token.service'; -import { BalancerSorService } from '../../beethoven/balancer-sor.service'; +import { BalancerSorService } from '../sorV1Beets/balancer-sor.service'; import { env } from '../../../app/env'; import { DeploymentEnv } from '../../network/network-config-types'; import { Cache, CacheClass } from 'memory-cache'; diff --git a/modules/sor/utils.ts b/modules/sor/utils.ts index ab47ae2b2..2c53582b9 100644 --- a/modules/sor/utils.ts +++ b/modules/sor/utils.ts @@ -22,7 +22,10 @@ export async function getTokenAmountRaw(tokenAddr: string, rawAmount: string, ch export const getToken = async (tokenAddr: string, chain: Chain): Promise => { const chainId = Number(chainToIdMap[chain]); - if (tokenAddr === NATIVE_ADDRESS && chainId in NATIVE_ASSETS) { + if ( + (tokenAddr === NATIVE_ADDRESS || tokenAddr === '0x0000000000000000000000000000000000001010') && + chainId in NATIVE_ASSETS + ) { return NATIVE_ASSETS[chainId as keyof typeof NATIVE_ASSETS]; } else { const prismaToken = await tokenService.getToken(tokenAddr, chain); From 03f51af83226135885962c1f61c3d0bc22d3a20e Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 15 Dec 2023 11:03:09 +0100 Subject: [PATCH 2/8] rename --- modules/sor/sor.resolvers.ts | 2 +- modules/sor/sor.service.ts | 47 +++--- .../sorV1Balancer/sorV1Balancer.service.ts | 135 ------------------ modules/sor/utils.ts | 1 + 4 files changed, 19 insertions(+), 166 deletions(-) delete mode 100644 modules/sor/sorV1Balancer/sorV1Balancer.service.ts diff --git a/modules/sor/sor.resolvers.ts b/modules/sor/sor.resolvers.ts index b4da3403d..31fb1c044 100644 --- a/modules/sor/sor.resolvers.ts +++ b/modules/sor/sor.resolvers.ts @@ -15,7 +15,7 @@ const balancerSdkResolvers: Resolvers = { throw new Error('sorGetSwaps error: Provide "chain" param'); } - return sorService.getBeetsSwaps(args); + return sorService.getSorSwaps(args); }, sorGetBatchSwapForTokensIn: async (parent, args, context) => { const tokens = await tokenService.getTokens(); diff --git a/modules/sor/sor.service.ts b/modules/sor/sor.service.ts index 5019bebbb..ea1abecfa 100644 --- a/modules/sor/sor.service.ts +++ b/modules/sor/sor.service.ts @@ -2,16 +2,13 @@ import { GqlCowSwapApiResponse, GqlSorSwapType, GqlSorGetSwapsResponse, - GqlSorSwapOptionsInput, QuerySorGetSwapsArgs, QuerySorGetCowSwapsArgs, } from '../../schema'; -import { sorV1BalancerService } from './sorV1Balancer/sorV1Balancer.service'; import { sorV1BeetsService } from './sorV1Beets/sorV1Beets.service'; import { sorV2Service } from './sorV2/sorV2.service'; -import { GetSwapsInput, SwapResult, SwapService } from './types'; +import { GetSwapsInput, SwapResult } from './types'; import { EMPTY_COWSWAP_RESPONSE } from './constants'; -import { publishMetric } from '../metrics/sor.metric'; import { Chain } from '@prisma/client'; import { parseUnits, formatUnits } from '@ethersproject/units'; import { tokenService } from '../token/token.service'; @@ -19,12 +16,13 @@ import { getTokenAmountHuman, getTokenAmountRaw } from './utils'; export class SorService { async getCowSwaps(args: QuerySorGetCowSwapsArgs): Promise { + console.log('getCowSwaps args', JSON.stringify(args)); const amountToken = args.swapType === 'EXACT_IN' ? args.tokenIn : args.tokenOut; // Use TokenAmount to help follow scaling requirements in later logic // args.swapAmount is RawScale, e.g. 1USDC should be passed as 1000000 const amount = await getTokenAmountRaw(amountToken, args.swapAmount, args.chain!); - const swap = await this.getSwap({ + const swap = await sorV2Service.getSwapResult({ chain: args.chain!, swapAmount: amount, swapType: args.swapType, @@ -38,15 +36,15 @@ export class SorService { try { // Updates with latest onchain data before returning - return await swap.getCowSwapResponse(args.chain!, true); + return await swap.getCowSwapResponse(true); } catch (err) { console.log(`Error Retrieving QuerySwap`, err); return emptyResponse; } } - async getBeetsSwaps(args: QuerySorGetSwapsArgs): Promise { - console.log('getBeetsSwaps args', JSON.stringify(args)); + async getSorSwaps(args: QuerySorGetSwapsArgs): Promise { + console.log('getSorSwaps args', JSON.stringify(args)); const tokenIn = args.tokenIn.toLowerCase(); const tokenOut = args.tokenOut.toLowerCase(); const amountToken = args.swapType === 'EXACT_IN' ? tokenIn : tokenOut; @@ -54,34 +52,30 @@ export class SorService { // args.swapAmount is HumanScale const amount = await getTokenAmountHuman(amountToken, args.swapAmount, args.chain!); - const swap = await this.getSwap( - { - chain: args.chain!, - swapAmount: amount, - swapOptions: args.swapOptions, - swapType: args.swapType, - tokenIn: tokenIn, - tokenOut: tokenOut, - }, - sorV1BeetsService, - ); + const swap = await this.getComparingSwap({ + chain: args.chain!, + swapAmount: amount, + swapOptions: args.swapOptions, + swapType: args.swapType, + tokenIn: tokenIn, + tokenOut: tokenOut, + }); const emptyResponse = sorV1BeetsService.zeroResponse(args.swapType, args.tokenIn, args.tokenOut, amount); if (!swap) return emptyResponse; try { // Updates with latest onchain data before returning - return swap.getBeetsSwapResponse(true); + return swap.getSorSwapResponse(true); } catch (err) { console.log(`Error Retrieving QuerySwap`, err); return emptyResponse; } } - private async getSwap(input: GetSwapsInput, v1Service: SwapService = sorV1BalancerService) { - console.log(`Running SOR for ${input.swapAmount} ${input.tokenIn} > ${input.tokenOut}`); + private async getComparingSwap(input: GetSwapsInput) { const v1Start = +new Date(); - const swapV1 = await v1Service.getSwapResult(input); + const swapV1 = await sorV1BeetsService.getSwapResult(input); const v1Time = +new Date() - v1Start; const v2Start = +new Date(); @@ -155,9 +149,6 @@ export class SorService { v1Time: number, v2Time: number, ) { - // await publishMetric(chain, `SOR_VALID_V1`, v1.isValid ? 1 : 0); - // await publishMetric(chain, `SOR_VALID_V2`, v2.isValid ? 1 : 0); - if (!version) return; let v1ResultAmount = v1.inputAmount; @@ -187,10 +178,6 @@ export class SorService { let diff = bn(diffN.toFixed(decimals), decimals); let bestResultAmount = version === 'V1' ? v1ResultAmount : v2ResultAmount; - // await publishMetric(chain, `SOR_TIME_V1`, v1Time); - // await publishMetric(chain, `SOR_TIME_V2`, v2Time); - // await publishMetric(chain, `SOR_V2_PERFORMACE`, v2Perf); - console.log( [ 'SOR_RESULT', diff --git a/modules/sor/sorV1Balancer/sorV1Balancer.service.ts b/modules/sor/sorV1Balancer/sorV1Balancer.service.ts deleted file mode 100644 index f35d98914..000000000 --- a/modules/sor/sorV1Balancer/sorV1Balancer.service.ts +++ /dev/null @@ -1,135 +0,0 @@ -import axios from 'axios'; -import * as Sentry from '@sentry/node'; -import { AddressZero } from '@ethersproject/constants'; -import { Contract } from '@ethersproject/contracts'; -import { GqlSorSwapType, GqlCowSwapApiResponse, GqlSorGetSwapsResponse } from '../../../schema'; -import { GetSwapsInput, SwapService, SwapResult } from '../types'; -import { FundManagement, SwapTypes, SwapV2 } from '@balancer-labs/sdk'; -import { env } from '../../../app/env'; -import { AllNetworkConfigs, AllNetworkConfigsKeyedOnChain, chainToIdMap } from '../../network/network-config'; -import { DeploymentEnv } from '../../network/network-config-types'; - -import VaultAbi from '../../pool/abi/Vault.json'; -import { BigNumber } from 'ethers'; -import { TokenAmount } from '@balancer/sdk'; -import { Chain } from '@prisma/client'; - -type CowSwapSwapType = 'buy' | 'sell'; - -class SwapResultV1 implements SwapResult { - public inputAmount: bigint = BigInt(0); - public outputAmount: bigint = BigInt(0); - public isValid: boolean; - - constructor(private swap: GqlCowSwapApiResponse | null, private swapType: GqlSorSwapType) { - if (swap === null) { - this.isValid = false; - this.swap = null; - } else { - this.inputAmount = swapType === 'EXACT_IN' ? BigInt(swap.swapAmount) : BigInt(swap.returnAmount); - this.outputAmount = swapType === 'EXACT_IN' ? BigInt(swap.returnAmount) : BigInt(swap.swapAmount); - this.isValid = swap.swaps.length === 0 ? false : true; - } - } - - async getCowSwapResponse(chain: Chain, queryFirst = false): Promise { - if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); - - if (queryFirst) { - const swapType = this.mapSwapType(this.swapType); - const deltas = await this.queryBatchSwap(swapType, this.swap.swaps, this.swap.tokenAddresses, chain); - const tokenInAmount = deltas[this.swap.tokenAddresses.indexOf(this.swap.tokenIn)].toString(); - const tokenOutAmount = deltas[this.swap.tokenAddresses.indexOf(this.swap.tokenOut)].abs().toString(); - // console.log(`UPDATE:`, this.inputAmount, this.outputAmount, tokenInAmount, tokenOutAmount, deltas.toString()); - return { - ...this.swap, - returnAmount: swapType === SwapTypes.SwapExactIn ? tokenOutAmount : tokenInAmount, - swapAmount: swapType === SwapTypes.SwapExactIn ? tokenInAmount : tokenOutAmount, - }; - } - return this.swap; - } - - async getBeetsSwapResponse(queryFirst: boolean): Promise { - throw new Error('Use Beets service.'); - } - - private queryBatchSwap(swapType: SwapTypes, swaps: SwapV2[], assets: string[], chain: Chain): Promise { - const vault = AllNetworkConfigsKeyedOnChain[chain].data.balancer.vault; - const provider = AllNetworkConfigsKeyedOnChain[chain].provider; - - const vaultContract = new Contract(vault, VaultAbi, provider); - const funds: FundManagement = { - sender: AddressZero, - recipient: AddressZero, - fromInternalBalance: false, - toInternalBalance: false, - }; - - return vaultContract.queryBatchSwap(swapType, swaps, assets, funds); - } - - private mapSwapType(swapType: GqlSorSwapType): SwapTypes { - return swapType === 'EXACT_IN' ? SwapTypes.SwapExactIn : SwapTypes.SwapExactOut; - } -} -export class SorV1BalancerService implements SwapService { - public async getSwapResult({ chain, tokenIn, tokenOut, swapType, swapAmount }: GetSwapsInput): Promise { - try { - const swap = await this.querySorBalancer(chain, swapType, tokenIn, tokenOut, swapAmount); - return new SwapResultV1(swap, swapType); - } catch (err: any) { - console.error( - `SOR_V1_ERROR ${err.message} - tokenIn: ${tokenIn} - tokenOut: ${tokenOut} - swapAmount: ${swapAmount.amount} - swapType: ${swapType} - chain: ${chain}`, - ); - Sentry.captureException(err.message, { - tags: { - service: 'sorV1', - tokenIn, - tokenOut, - swapAmount: swapAmount.amount, - swapType, - chain, - }, - }); - return new SwapResultV1(null, swapType); - } - } - - /** - * Query Balancer API CowSwap/SOR endpoint. - * @param swapType - * @param tokenIn - * @param tokenOut - * @param swapAmountScaled - * @param swapOptions - * @returns - */ - private async querySorBalancer( - chain: Chain, - swapType: GqlSorSwapType, - tokenIn: string, - tokenOut: string, - swapAmount: TokenAmount, - ): Promise { - const chainId = chainToIdMap[chain]; - const endPoint = `https://api.balancer.fi/sor/${chainId}`; - const gasPrice = AllNetworkConfigs[chainId].data.sor[env.DEPLOYMENT_ENV as DeploymentEnv].gasPrice.toString(); - const swapData = { - orderKind: this.mapSwapType(swapType), - sellToken: tokenIn, - buyToken: tokenOut, - amount: swapAmount.amount.toString(), - gasPrice, - }; - - const { data } = await axios.post(endPoint, swapData); - return data; - } - - private mapSwapType(swapType: GqlSorSwapType): CowSwapSwapType { - return swapType === 'EXACT_IN' ? 'sell' : 'buy'; - } -} - -export const sorV1BalancerService = new SorV1BalancerService(); diff --git a/modules/sor/utils.ts b/modules/sor/utils.ts index 2c53582b9..4e10b55c8 100644 --- a/modules/sor/utils.ts +++ b/modules/sor/utils.ts @@ -22,6 +22,7 @@ export async function getTokenAmountRaw(tokenAddr: string, rawAmount: string, ch export const getToken = async (tokenAddr: string, chain: Chain): Promise => { const chainId = Number(chainToIdMap[chain]); + // also check for the polygon native asset if ( (tokenAddr === NATIVE_ADDRESS || tokenAddr === '0x0000000000000000000000000000000000001010') && chainId in NATIVE_ASSETS From 55fd61c234a4f78d72f4d6c5a97b39f40831ca16 Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 15 Dec 2023 11:03:59 +0100 Subject: [PATCH 3/8] query first for v2 --- modules/sor/sorV1Beets/sorV1Beets.service.ts | 8 ++--- modules/sor/sorV2/sorV2.service.ts | 34 +++++++++++++------- modules/sor/types.ts | 4 +-- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/modules/sor/sorV1Beets/sorV1Beets.service.ts b/modules/sor/sorV1Beets/sorV1Beets.service.ts index e91744ec7..3b299996f 100644 --- a/modules/sor/sorV1Beets/sorV1Beets.service.ts +++ b/modules/sor/sorV1Beets/sorV1Beets.service.ts @@ -23,11 +23,11 @@ class SwapResultV1 implements SwapResult { } } - async getCowSwapResponse(chain = 'MAINNET', queryFirst = false): Promise { + async getCowSwapResponse(queryFirst = false): Promise { throw new Error('Use Balancer Service'); } - async getBeetsSwapResponse(queryFirst: boolean): Promise { + async getSorSwapResponse(queryFirst: boolean): Promise { if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); // Beets service is already querying onchain return this.swap; @@ -42,7 +42,7 @@ export class SorV1BeetsService implements SwapService { public async getSwapResult(input: GetSwapsInput & { swapOptions: GqlSorSwapOptionsInput }): Promise { try { - const swap = await this.querySorBeets(input); + const swap = await this.querySorV1(input); return new SwapResultV1(swap, input.swapType); } catch (err) { console.log(`sorV1 Service Error`, err); @@ -59,7 +59,7 @@ export class SorV1BeetsService implements SwapService { return this.sorService.zeroResponse(swapType, tokenIn, tokenOut, formatEther(swapAmount.scale18)); } - private async querySorBeets( + private async querySorV1( input: GetSwapsInput & { swapOptions: GqlSorSwapOptionsInput }, ): Promise { const tokens = await tokenService.getTokens(); diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index fd442dfae..e747531b7 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -49,43 +49,53 @@ const ALL_BASEPOOLS_CACHE_KEY = `basePools:all`; class SwapResultV2 implements SwapResult { private swap: SwapSdk | null; + private chain: Chain; public inputAmount: bigint = BigInt(0); public outputAmount: bigint = BigInt(0); public isValid: boolean; - constructor(swap: SwapSdk | null) { + constructor(swap: SwapSdk | null, chain: Chain) { if (swap === null) { this.isValid = false; this.swap = null; + this.chain = chain; } else { this.isValid = true; this.swap = swap; this.inputAmount = swap.inputAmount.amount; this.outputAmount = swap.outputAmount.amount; + this.chain = chain; } } - async getCowSwapResponse(chain: Chain, queryFirst = false): Promise { + async getCowSwapResponse(queryFirst = false): Promise { if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); if (!queryFirst) return this.mapResultToCowSwap(this.swap, this.swap.inputAmount, this.swap.outputAmount); else { - const rpcUrl = AllNetworkConfigsKeyedOnChain[chain].data.rpcUrl; - // Needs node >= 18 (https://github.com/wagmi-dev/viem/discussions/147) + const rpcUrl = AllNetworkConfigsKeyedOnChain[this.chain].data.rpcUrl; const updatedResult = await this.swap.query(rpcUrl); - // console.log(`UPDATE:`, this.swap.quote.amount.toString(), updatedResult.amount.toString()); - const ip = this.swap.swapKind === SwapKind.GivenIn ? this.swap.inputAmount : updatedResult; - const op = this.swap.swapKind === SwapKind.GivenIn ? updatedResult : this.swap.outputAmount; + const inputAmount = this.swap.swapKind === SwapKind.GivenIn ? this.swap.inputAmount : updatedResult; + const outputAmount = this.swap.swapKind === SwapKind.GivenIn ? updatedResult : this.swap.outputAmount; - return this.mapResultToCowSwap(this.swap, ip, op); + return this.mapResultToCowSwap(this.swap, inputAmount, outputAmount); } } - async getBeetsSwapResponse(queryFirst: boolean): Promise { + async getSorSwapResponse(queryFirst = false): Promise { if (!this.isValid || this.swap === null) throw new Error('No Response - Invalid Swap'); - return await this.mapResultToBeetsSwap(this.swap, this.swap.inputAmount, this.swap.outputAmount); + if (!queryFirst) return this.mapResultToBeetsSwap(this.swap, this.swap.inputAmount, this.swap.outputAmount); + else { + const rpcUrl = AllNetworkConfigsKeyedOnChain[this.chain].data.rpcUrl; + const updatedResult = await this.swap.query(rpcUrl); + + const inputAmount = this.swap.swapKind === SwapKind.GivenIn ? this.swap.inputAmount : updatedResult; + const outputAmount = this.swap.swapKind === SwapKind.GivenIn ? updatedResult : this.swap.outputAmount; + + return this.mapResultToBeetsSwap(this.swap, inputAmount, outputAmount); + } } private async mapResultToBeetsSwap( @@ -308,7 +318,7 @@ export class SorV2Service implements SwapService { if (!swap && maxNonBoostedPathDepth < 4) { return this.getSwapResult(arguments[0], maxNonBoostedPathDepth + 1); } - return new SwapResultV2(swap); + return new SwapResultV2(swap, chain); } catch (err: any) { console.error( `SOR_V2_ERROR ${err.message} - tokenIn: ${tokenIn} - tokenOut: ${tokenOut} - swapAmount: ${swapAmount.amount} - swapType: ${swapType} - chain: ${chain}`, @@ -323,7 +333,7 @@ export class SorV2Service implements SwapService { chain, }, }); - return new SwapResultV2(null); + return new SwapResultV2(null, chain); } } diff --git a/modules/sor/types.ts b/modules/sor/types.ts index 564647c84..2059332ec 100644 --- a/modules/sor/types.ts +++ b/modules/sor/types.ts @@ -19,8 +19,8 @@ export interface GraphTraversalConfig { } export interface SwapResult { - getCowSwapResponse(chain: Chain, queryFirst: boolean): Promise; - getBeetsSwapResponse(queryFirst: boolean): Promise; + getCowSwapResponse(queryFirst: boolean): Promise; + getSorSwapResponse(queryFirst: boolean): Promise; isValid: boolean; outputAmount: bigint; inputAmount: bigint; From 1c1e46476e11d080a3229cc7a0ed8f4c31560be5 Mon Sep 17 00:00:00 2001 From: franz Date: Fri, 15 Dec 2023 11:52:18 +0100 Subject: [PATCH 4/8] add polygon native address --- modules/sor/sorV1Beets/balancer-sor.service.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/sor/sorV1Beets/balancer-sor.service.ts b/modules/sor/sorV1Beets/balancer-sor.service.ts index 6cf8fefa7..19d5c2b3a 100644 --- a/modules/sor/sorV1Beets/balancer-sor.service.ts +++ b/modules/sor/sorV1Beets/balancer-sor.service.ts @@ -332,7 +332,11 @@ export class BalancerSorService { } private getTokenDecimals(tokenAddress: string, tokens: PrismaToken[]): number { - if (tokenAddress === ZERO_ADDRESS || tokenAddress === NATIVE_ADDRESS) { + if ( + tokenAddress === ZERO_ADDRESS || + tokenAddress === NATIVE_ADDRESS || + tokenAddress === '0x0000000000000000000000000000000000001010' + ) { return 18; } From a815e8c53dead366eb73ef84f310abfe878873ca Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 20 Dec 2023 14:10:39 +0100 Subject: [PATCH 5/8] add composable type --- modules/sor/sorV2/sorV2.service.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index e747531b7..b972462b7 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -521,6 +521,8 @@ export class SorV2Service implements SwapService { case PrismaPoolType.PHANTOM_STABLE: // Composablestables are PHANTOM_STABLE in Prisma. b-sdk treats Phantoms as ComposableStable. return 'ComposableStable'; + case PrismaPoolType.COMPOSABLE_STABLE: + return 'ComposableStable'; case PrismaPoolType.GYRO: return 'Gyro2'; case PrismaPoolType.GYRO3: From c812f68659e4b89186307c6aefb3961cb3e076c2 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 20 Dec 2023 14:16:52 +0100 Subject: [PATCH 6/8] only use pools with more th $50 liquidity --- modules/sor/sorV2/sorV2.service.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index b972462b7..2c983445d 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -364,6 +364,9 @@ export class SorV2Service implements SwapService { gt: 0.000000000001, }, swapEnabled: true, + totalLiquidity: { + gt: 50, + }, }, id: { notIn: [...poolIdsToExclude, ...poolsToIgnore], From 5a777bb8a613310e9797a617f99fffb519efe064 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 20 Dec 2023 14:20:12 +0100 Subject: [PATCH 7/8] increase one hop --- modules/sor/sorV2/sorV2.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 2c983445d..56caa5bb4 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -290,7 +290,7 @@ export class SorV2Service implements SwapService { public async getSwapResult( { chain, tokenIn, tokenOut, swapType, swapAmount, graphTraversalConfig }: GetSwapsInput, - maxNonBoostedPathDepth = 3, + maxNonBoostedPathDepth = 4, ): Promise { try { const poolsFromDb = await this.getBasePools(chain); From 27ca9aa88e799a781a03b727879985c501879d47 Mon Sep 17 00:00:00 2001 From: franz Date: Wed, 20 Dec 2023 14:51:22 +0100 Subject: [PATCH 8/8] remove unused cowswap fields --- modules/sor/constants.ts | 11 +++++------ modules/sor/sor.gql | 4 ---- modules/sor/sorV2/sorV2.service.ts | 4 ---- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/modules/sor/constants.ts b/modules/sor/constants.ts index 0fa69e5be..8c73cdc23 100644 --- a/modules/sor/constants.ts +++ b/modules/sor/constants.ts @@ -1,14 +1,14 @@ import { TokenAmount } from '@balancer/sdk'; import { GqlCowSwapApiResponse, GqlSorSwapType } from '../../schema'; -export const EMPTY_COWSWAP_RESPONSE = (assetIn: string, assetOut: string, amount: TokenAmount): GqlCowSwapApiResponse => { +export const EMPTY_COWSWAP_RESPONSE = ( + assetIn: string, + assetOut: string, + amount: TokenAmount, +): GqlCowSwapApiResponse => { return { - marketSp: '0', returnAmount: '0', - returnAmountConsideringFees: '0', - returnAmountFromSwaps: '0', swapAmount: amount.amount.toString(), - swapAmountForSwaps: '0', swaps: [], tokenAddresses: [], tokenIn: assetIn, @@ -352,4 +352,3 @@ export const poolsToIgnore = [ '0xbfd65c6160cfd638a85c645e6e6d8acac5dac935000000000000000000000004', '0xe274c9deb6ed34cfe4130f8d0a8a948dea5bb28600000000000000000000000d', ]; - diff --git a/modules/sor/sor.gql b/modules/sor/sor.gql index 716cf02d2..9bee51655 100644 --- a/modules/sor/sor.gql +++ b/modules/sor/sor.gql @@ -25,13 +25,9 @@ type GqlCowSwapApiResponse { tokenAddresses: [String!]! swaps: [GqlSwap!]! swapAmount: String! - swapAmountForSwaps: String! returnAmount: String! - returnAmountFromSwaps: String! - returnAmountConsideringFees: String! tokenIn: String! tokenOut: String! - marketSp: String! } type GqlSwap { diff --git a/modules/sor/sorV2/sorV2.service.ts b/modules/sor/sorV2/sorV2.service.ts index 56caa5bb4..3f0ff71f7 100644 --- a/modules/sor/sorV2/sorV2.service.ts +++ b/modules/sor/sorV2/sorV2.service.ts @@ -267,12 +267,8 @@ class SwapResultV2 implements SwapResult { const swapAmount = swap.swapKind === SwapKind.GivenIn ? inputAmount.amount.toString() : outputAmount.amount.toString(); return { - marketSp: '', // CowSwap is not using this field, confirmed. returnAmount, - returnAmountConsideringFees: returnAmount, // CowSwap is not using this field, confirmed. - returnAmountFromSwaps: returnAmount, // CowSwap is not using this field, confirmed. swapAmount, - swapAmountForSwaps: swapAmount, // CowSwap is not using this field, confirmed. swaps, tokenAddresses: swap.assets, tokenIn: swap.inputAmount.token.address,