diff --git a/.changeset/little-avocados-buy.md b/.changeset/little-avocados-buy.md new file mode 100644 index 000000000..f4258a2c5 --- /dev/null +++ b/.changeset/little-avocados-buy.md @@ -0,0 +1,7 @@ +--- +"frontend": patch +"@sovryn/sdex": patch +"@sovryn/sdk": patch +--- + +SOV-4239: add SDEX swap error logging for debugging diff --git a/apps/frontend/.env.example b/apps/frontend/.env.example index 66c9086f2..30a2abf58 100644 --- a/apps/frontend/.env.example +++ b/apps/frontend/.env.example @@ -22,3 +22,5 @@ REACT_APP_ENABLE_SERVICE_WORKER=false # All transactions will be simulated on forked mainnet, REACT_APP_ESTIMATOR_URI is required REACT_APP_SIMULATE_TX=false REACT_APP_ESTIMATOR_URI=https://simulator.sovryn.app + +REACT_APP_DATADOG_CLIENT_TOKEN= \ No newline at end of file diff --git a/packages/sdex/package.json b/packages/sdex/package.json index 95bab8b28..ba4771bf9 100644 --- a/packages/sdex/package.json +++ b/packages/sdex/package.json @@ -36,6 +36,7 @@ "typescript": "^5.3.3" }, "dependencies": { + "@datadog/browser-logs": "^5.21.0", "ethers": "^5.5.3" } } diff --git a/packages/sdex/src/index.ts b/packages/sdex/src/index.ts index bfe95da78..be78e0c9b 100644 --- a/packages/sdex/src/index.ts +++ b/packages/sdex/src/index.ts @@ -1,11 +1,12 @@ -export * from "./constants"; -export * from "./utils"; -export * from "./abis"; -export * from "./pool"; -export * from "./position"; -export * from "./swap"; -export * from "./croc"; +export * from './constants'; +export * from './utils'; +export * from './abis'; +export * from './pool'; +export * from './position'; +export * from './swap'; +export * from './croc'; +export * from './logger'; -export * from "./encoding/liquidity"; +export * from './encoding/liquidity'; -export * from "./recipes/reposition"; +export * from './recipes/reposition'; diff --git a/packages/sdex/src/logger.ts b/packages/sdex/src/logger.ts new file mode 100644 index 000000000..18c399f85 --- /dev/null +++ b/packages/sdex/src/logger.ts @@ -0,0 +1,17 @@ +import { datadogLogs } from '@datadog/browser-logs'; + +datadogLogs.init({ + clientToken: process.env.REACT_APP_DATADOG_CLIENT_TOKEN || '', + site: 'datadoghq.com', + forwardErrorsToLogs: true, + sessionSampleRate: 100, + service: 'd2', +}); + +export const logger = (type: 'info' | 'error', title: string, data: object) => { + if (type === 'error') { + datadogLogs.logger.error(title, data); + } else if (type === 'info') { + datadogLogs.logger.info(title, data); + } +}; diff --git a/packages/sdex/src/swap.ts b/packages/sdex/src/swap.ts index e675b01e2..f2ed1905b 100644 --- a/packages/sdex/src/swap.ts +++ b/packages/sdex/src/swap.ts @@ -11,6 +11,7 @@ import { decodeSurplusFlag, encodeSurplusArg, } from './encoding/flags'; +import { logger } from './logger'; import { CrocPoolView } from './pool'; import { CrocSlotReader } from './slots'; import { @@ -401,34 +402,100 @@ export class CrocSwapPlan { async calcImpact(): Promise { const TIP = 0; const limitPrice = this.sellBase ? MAX_SQRT_PRICE : MIN_SQRT_PRICE; - - const impact = await ( - await this.context - ).slipQuery.calcImpact( - this.baseToken.tokenAddr, - this.quoteToken.tokenAddr, - this.poolIndex, - this.sellBase, - this.qtyInBase, - await this.qty, - TIP, - limitPrice, - ); - - const baseQty = this.baseToken.toDisplay(impact.baseFlow.abs()); - const quoteQty = this.quoteToken.toDisplay(impact.quoteFlow.abs()); - const spotPrice = decodeCrocPrice(impact.finalPrice); - - const startPrice = this.poolView.displayPrice(); - const finalPrice = this.poolView.toDisplayPrice(spotPrice); - - return { - sellQty: this.sellBase ? await baseQty : await quoteQty, - buyQty: this.sellBase ? await quoteQty : await baseQty, - finalPrice: await finalPrice, - percentChange: - ((await finalPrice) - (await startPrice)) / (await startPrice), - }; + const qty = await this.qty; + + try { + console.log( + `this.baseToken.tokenAddr: ${ + this.baseToken.tokenAddr + } , this.quoteToken.tokenAddr: ${ + this.quoteToken.tokenAddr + } , this.poolIndex: ${this.poolIndex} , this.sellBase: ${ + this.sellBase + } , this.qtyInBase: ${this.qtyInBase} , qty: ${await this + .qty} , TIP: ${TIP} , limitPrice: ${limitPrice}`, + ); + + const impact = await ( + await this.context + ).slipQuery.calcImpact( + this.baseToken.tokenAddr, + this.quoteToken.tokenAddr, + this.poolIndex, + this.sellBase, + this.qtyInBase, + qty, + TIP, + limitPrice, + ); + + const baseQty = this.baseToken.toDisplay(impact.baseFlow.abs()); + const quoteQty = this.quoteToken.toDisplay(impact.quoteFlow.abs()); + const spotPrice = decodeCrocPrice(impact.finalPrice); + + const startPrice = this.poolView.displayPrice(); + const finalPrice = this.poolView.toDisplayPrice(spotPrice); + + logger('info', 'calcImpact', { + data: { + baseToken: this.baseToken.tokenAddr, + quoteToken: this.quoteToken.tokenAddr, + poolIndex: this.poolIndex, + sellBase: this.sellBase, + qtyInBase: this.qtyInBase, + qty, + TIP, + limitPrice, + network: process.env.REACT_APP_NETWORK, + }, + dataFormatted: { + baseToken: this.baseToken.tokenAddr, + quoteToken: this.quoteToken.tokenAddr, + poolIndex: this.poolIndex, + sellBase: this.sellBase, + qtyInBase: this.qtyInBase, + qty: qty.toString(), + TIP, + limitPrice: limitPrice.toString(), + network: process.env.REACT_APP_NETWORK, + }, + }); + return { + sellQty: this.sellBase ? await baseQty : await quoteQty, + buyQty: this.sellBase ? await quoteQty : await baseQty, + finalPrice: await finalPrice, + percentChange: + ((await finalPrice) - (await startPrice)) / (await startPrice), + }; + } catch (error) { + logger('error', 'calcImpact', { + data: { + baseToken: this.baseToken.tokenAddr, + quoteToken: this.quoteToken.tokenAddr, + poolIndex: this.poolIndex, + sellBase: this.sellBase, + qtyInBase: this.qtyInBase, + qty, + TIP, + limitPrice, + network: process.env.REACT_APP_NETWORK, + error, + }, + dataFormatted: { + baseToken: this.baseToken.tokenAddr, + quoteToken: this.quoteToken.tokenAddr, + poolIndex: this.poolIndex, + sellBase: this.sellBase, + qtyInBase: this.qtyInBase, + qty: qty.toString(), + TIP, + limitPrice: limitPrice.toString(), + network: process.env.REACT_APP_NETWORK, + error, + }, + }); + throw error; + } } public maskSurplusArgs(args?: CrocSwapExecOpts): number { diff --git a/packages/sdk/src/swaps/smart-router/utils/ambient-utils.ts b/packages/sdk/src/swaps/smart-router/utils/ambient-utils.ts index 645dacd07..87848cdb5 100644 --- a/packages/sdk/src/swaps/smart-router/utils/ambient-utils.ts +++ b/packages/sdk/src/swaps/smart-router/utils/ambient-utils.ts @@ -2,7 +2,7 @@ import { BigNumber } from 'ethers'; import { getAssetContract, getAssetDataByAddress } from '@sovryn/contracts'; import { ChainId, ChainIds } from '@sovryn/ethers-provider'; -import { CrocEnv, MAX_SQRT_PRICE, MIN_SQRT_PRICE } from '@sovryn/sdex'; +import { CrocEnv, logger, MAX_SQRT_PRICE, MIN_SQRT_PRICE } from '@sovryn/sdex'; export type PoolWithIndex = [string, string, number]; export type Pool = [string, string]; @@ -73,17 +73,47 @@ export const calcImpact = async ( inBaseQty: boolean, qty: BigNumber, ) => { - const context = await env.context; - return await context.slipQuery.calcImpact( - base, - quote, - poolIdx, - isBuy, - inBaseQty, - qty, - 0, - initialLimitPrice(isBuy), - ); + try { + const context = await env.context; + const result = await context.slipQuery.calcImpact( + base, + quote, + poolIdx, + isBuy, + inBaseQty, + qty, + 0, + initialLimitPrice(isBuy), + ); + + return result; + } catch (error) { + logger('error', 'calcImpact', { + data: { + error, + base, + quote, + poolIdx, + isBuy, + inBaseQty, + qty, + initialLimitPrice: initialLimitPrice(isBuy), + env, + }, + dataFormatted: { + error, + base, + quote, + poolIdx, + isBuy, + inBaseQty, + qty: qty.toString(), + initialLimitPrice: initialLimitPrice(isBuy).toString(), + network: process.env.REACT_APP_NETWORK, + env, + }, + }); + } }; const INDEXER = { diff --git a/yarn.lock b/yarn.lock index db40f0325..d9c85c24e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1976,6 +1976,18 @@ resolved "https://registry.yarnpkg.com/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz#1bfafe4b7ed0f3e4105837e056e0a89b108ebe36" integrity sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg== +"@datadog/browser-core@5.21.0": + version "5.21.0" + resolved "https://registry.yarnpkg.com/@datadog/browser-core/-/browser-core-5.21.0.tgz#980e43d036a4c2074d37b887bd0ba748cca75e15" + integrity sha512-F0lZCwHwGCryhyRAg9x9Aeq8FsYMxA41vwyBeyFHbh+er5UrQl/LuKdrRMSxz3kpaBdHppLP920voTSvooHiug== + +"@datadog/browser-logs@5.21.0", "@datadog/browser-logs@^5.21.0": + version "5.21.0" + resolved "https://registry.yarnpkg.com/@datadog/browser-logs/-/browser-logs-5.21.0.tgz#86afe2595a6bb5ddbc095500a2f35733b35e5aad" + integrity sha512-Nb1F2M0tQzSnDU3iI61U+fnqPskuYMuYTyMLD9KwvcKru4cGj3+k5nYVmszvwEKZZ+etYff+1iBvnpYIo2w6hw== + dependencies: + "@datadog/browser-core" "5.21.0" + "@design-systems/utils@2.12.0": version "2.12.0" resolved "https://registry.yarnpkg.com/@design-systems/utils/-/utils-2.12.0.tgz#955c108be07cb8f01532207cbfea8f848fa760c9"