From 0bea44e6c429197ff7b37b320c4220f86db54e6d Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Sun, 26 Nov 2023 17:57:42 +0000 Subject: [PATCH 01/35] Initial commit --- src/chains/ethereum/ethereum.ts | 5 + src/chains/ethereum/ethereum.validators.ts | 1 + src/connectors/carbon/carbon.config.ts | 54 + src/connectors/carbon/carbon.ts | 557 ++++++ src/connectors/carbon/carbon.utils.ts | 113 ++ .../carbon/carbon_controller_abi.json | 1646 +++++++++++++++++ src/connectors/connectors.routes.ts | 7 + src/services/connection-manager.ts | 3 + src/services/schema/carbon-schema.json | 54 + src/templates/carbon.yml | 20 + src/templates/root.yml | 4 + 11 files changed, 2464 insertions(+) create mode 100644 src/connectors/carbon/carbon.config.ts create mode 100644 src/connectors/carbon/carbon.ts create mode 100644 src/connectors/carbon/carbon.utils.ts create mode 100644 src/connectors/carbon/carbon_controller_abi.json create mode 100644 src/services/schema/carbon-schema.json create mode 100644 src/templates/carbon.yml diff --git a/src/chains/ethereum/ethereum.ts b/src/chains/ethereum/ethereum.ts index 383299a613..04fb7ca6fc 100644 --- a/src/chains/ethereum/ethereum.ts +++ b/src/chains/ethereum/ethereum.ts @@ -13,6 +13,7 @@ import { UniswapConfig } from '../../connectors/uniswap/uniswap.config'; import { Perp } from '../../connectors/perp/perp'; import { SushiswapConfig } from '../../connectors/sushiswap/sushiswap.config'; import { OpenoceanConfig } from '../../connectors/openocean/openocean.config'; +import { CarbonConfig } from '../../connectors/carbon/carbon.config'; // MKR does not match the ERC20 perfectly so we need to use a separate ABI. const MKR_ADDRESS = '0x9f8f72aa9304c8b593d555f12ef6589cc3a579a2'; @@ -187,6 +188,10 @@ export class Ethereum extends EthereumBase implements Ethereumish { ); } else if (reqSpender === 'uniswapLP') { spender = UniswapConfig.config.uniswapV3NftManagerAddress(this._chain); + } else if (reqSpender === 'carbon') { + spender = CarbonConfig.config.carbonContractsConfig( + this._chain + ).carbonControllerAddress; } else if (reqSpender === 'perp') { const perp = Perp.getInstance(this._chain, 'optimism'); if (!perp.ready()) { diff --git a/src/chains/ethereum/ethereum.validators.ts b/src/chains/ethereum/ethereum.validators.ts index 4fda970472..5b290ea4f2 100644 --- a/src/chains/ethereum/ethereum.validators.ts +++ b/src/chains/ethereum/ethereum.validators.ts @@ -61,6 +61,7 @@ export const validateSpender: Validator = mkValidator( val === 'vvs' || val === 'pancakeswap' || val === 'xsswap' || + val === 'carbon' || isAddress(val)) ); diff --git a/src/connectors/carbon/carbon.config.ts b/src/connectors/carbon/carbon.config.ts new file mode 100644 index 0000000000..8c39e47bd3 --- /dev/null +++ b/src/connectors/carbon/carbon.config.ts @@ -0,0 +1,54 @@ +import { ContractsConfig } from '@bancor/carbon-sdk/contracts-api'; +import { AvailableNetworks } from '../../services/config-manager-types'; +import { ConfigManagerV2 } from '../../services/config-manager-v2'; + +export namespace CarbonConfig { + export interface NetworkConfig { + allowedSlippage: string; + gasLimitEstimate: number; + ttl: number; + tradingTypes: (type: string) => Array; + chainType: string; + availableNetworks: Array; + carbonContractsConfig: (network: string) => Required; + } + + export const config: NetworkConfig = { + allowedSlippage: ConfigManagerV2.getInstance().get( + `carbon.allowedSlippage` + ), + gasLimitEstimate: ConfigManagerV2.getInstance().get( + `carbon.gasLimitEstimate` + ), + ttl: ConfigManagerV2.getInstance().get(`carbon.ttl`), + tradingTypes: (type: string) => { + return type === 'swap' ? ['AMM'] : ['CLOB_SPOT']; + }, + chainType: 'EVM', + availableNetworks: [ + { + chain: 'ethereum', + networks: Object.keys( + ConfigManagerV2.getInstance().get('carbon.contractAddresses') + ).filter((network) => + Object.keys( + ConfigManagerV2.getInstance().get('ethereum.networks') + ).includes(network) + ), + }, + ], + carbonContractsConfig: (network: string) => { + return { + carbonControllerAddress: ConfigManagerV2.getInstance().get( + `carbon.contractAddresses.${network}.carbonControllerAddress` + ), + multiCallAddress: ConfigManagerV2.getInstance().get( + `carbon.contractAddresses.${network}.multiCallAddress` + ), + voucherAddress: ConfigManagerV2.getInstance().get( + `carbon.contractAddresses.${network}.voucherAddress` + ), + }; + }, + }; +} diff --git a/src/connectors/carbon/carbon.ts b/src/connectors/carbon/carbon.ts new file mode 100644 index 0000000000..84bcca79c0 --- /dev/null +++ b/src/connectors/carbon/carbon.ts @@ -0,0 +1,557 @@ +import { Contract, providers, Signer } from 'ethers'; +import Decimal from 'decimal.js-light'; + +import { Ethereum } from '../../chains/ethereum/ethereum'; +import { TokenInfo } from '../../chains/ethereum/ethereum-base'; + +import { + ClobMarketsRequest, + ClobOrderbookRequest, + ClobTickerRequest, + ClobGetOrderRequest, + ClobPostOrderRequest, + ClobDeleteOrderRequest, + CLOBMarkets, + ClobGetOrderResponse, +} from '../../clob/clob.requests'; + +import { + CLOBish, + MarketInfo, + NetworkSelectionRequest, + Orderbook, +} from '../../services/common-interfaces'; +import { logger } from '../../services/logger'; + +import { BalanceRequest } from '../../network/network.requests'; + +import { ChainCache, initSyncedCache } from './carbon-sdk/src/chain-cache'; +import { ContractsConfig, ContractsApi } from './carbon-sdk/src/contracts-api'; +import { Toolkit } from './carbon-sdk/src/strategy-management'; +import { isETHAddress } from './carbon-sdk/src/contracts-api/utils'; +import { Strategy, TokenPair } from './carbon-sdk/src'; +import { parseUnits } from './carbon-sdk/src/utils'; + +import carbonControllerAbi from './carbon_controller_abi.json'; +import { CarbonConfig } from './carbon.config'; + +import { + OrderRow, + emptyToken, + buildOrders, + getMiddleRate, + getStep, + decodeStrategyId, +} from './carbon.utils'; + +export class CarbonCLOB implements CLOBish { + public parsedMarkets: MarketInfo = []; + public carbonContractConfig: Required; + public carbonSDK: Toolkit; + public sdkCache: ChainCache; + private static _instances: { [name: string]: CarbonCLOB }; + private _chain: Ethereum; + private _ready: boolean = false; + private _conf: CarbonConfig.NetworkConfig; + private _api: ContractsApi; + private _decimalsMap: Map; + private _nativeToken: TokenInfo; + private _startDataSync: () => Promise; + + private constructor(chain: string, network: string) { + if (chain === 'ethereum') { + this._chain = Ethereum.getInstance(network); + } else { + throw new Error('unsupported chain'); + } + + this._nativeToken = + this._chain.chainName === 'ethereum' + ? { + chainId: 1, + address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + name: 'Ethereum', + symbol: 'ETH', + decimals: 18, + } + : emptyToken; + + this._conf = CarbonConfig.config; + this.carbonContractConfig = this._conf.carbonContractsConfig(network); + + this._api = new ContractsApi( + this._chain.provider, + this.carbonContractConfig + ); + + const { cache, startDataSync } = initSyncedCache(this._api.reader); + this.sdkCache = cache; + this._startDataSync = startDataSync; + + this._decimalsMap = new Map(); + this._chain.storedTokenList.forEach((token) => { + this._decimalsMap.set(token.address, token.decimals); + }); + + this.carbonSDK = new Toolkit(this._api, this.sdkCache, (address) => + this._decimalsMap.get(address.toLowerCase()) + ); + } + + public static getInstance(chain: string, network: string): CarbonCLOB { + if (CarbonCLOB._instances === undefined) { + CarbonCLOB._instances = {}; + } + + const key = `${chain}:${network}`; + if (!(key in CarbonCLOB._instances)) { + CarbonCLOB._instances[key] = new CarbonCLOB(chain, network); + } + + return CarbonCLOB._instances[key]; + } + + // getters + + /** + * Carbon Controller ABI + */ + getContract( + contractAddress: string, + signer: providers.StaticJsonRpcProvider | Signer + ) { + return new Contract(contractAddress, carbonControllerAbi.abi, signer); + } + + public async loadMarkets() { + const contractPairs = await this._api.reader.pairs(); + + await Promise.all( + contractPairs.map(async (pair) => await this._updateMarkets(pair)) + ); + } + + public async init() { + if (!this._chain.ready() || Object.keys(this.parsedMarkets).length === 0) { + logger.info('Initialising chain...'); + await this._chain.init(); + } + + logger.info('Starting Data Sync...'); + await this._startDataSync(); + + logger.info('Loading markets...'); + await this.loadMarkets(); + + this._ready = true; + } + + public ready(): boolean { + return this._ready; + } + + public async markets( + req: ClobMarketsRequest + ): Promise<{ markets: MarketInfo }> { + if (req.market) { + if (req.market in this.parsedMarkets) + return { markets: this.parsedMarkets[req.market] }; + + const reversedMarket = req.market.split('-').reverse().join('-'); + if (reversedMarket in this.parsedMarkets) + return { markets: this.parsedMarkets[reversedMarket] }; + } + + return { markets: Object.values(this.parsedMarkets) }; + } + + public async ticker( + req: ClobTickerRequest + ): Promise<{ markets: CLOBMarkets }> { + return await this.markets(req); + } + + public async orders( + req: ClobGetOrderRequest + ): Promise<{ orders: ClobGetOrderResponse['orders'] }> { + if (!req.address) throw Error('Missing wallet address'); + + const userStrategy = await this._findUserStrategy(req.address, req.orderId); + + if (userStrategy) { + const [pairId, id] = decodeStrategyId(userStrategy.id); + return { + orders: [ + { + id, + pairId, + baseToken: userStrategy.baseToken, + quoteToken: userStrategy.quoteToken, + buyPriceLow: userStrategy.buyPriceLow, + buyPriceMarginal: userStrategy.buyPriceMarginal, + buyPriceHigh: userStrategy.buyPriceHigh, + buyBudget: userStrategy.buyBudget, + sellPriceLow: userStrategy.sellPriceLow, + sellPriceMarginal: userStrategy.sellPriceMarginal, + sellPriceHigh: userStrategy.sellPriceHigh, + sellBudget: userStrategy.sellBudget, + }, + ], + }; + } else { + return { + orders: [], + }; + } + } + + public async postOrder( + req: ClobPostOrderRequest + ): Promise<{ txHash: string }> { + this._isMarketValid(req.market); + + const tokens = this._getTokensBySymbol(req.market.split('-')); + + const [baseAddress, quoteAddress] = + req.side === 'BUY' + ? [tokens[0].address, tokens[1].address] + : [tokens[1].address, tokens[0].address]; + + if (req.orderType === 'LIMIT_MAKER') { + const createTransaction = await this.carbonSDK.createBuySellStrategy( + baseAddress, + quoteAddress, + req.price, + req.price, + req.amount, + '0', + '0', + '0' + ); + + const wallet = await this._chain.getWallet(req.address); + const tx = await wallet.sendTransaction(createTransaction); + + return { txHash: tx.hash }; + } + + return { txHash: '' }; + } + + public async deleteOrder( + req: ClobDeleteOrderRequest + ): Promise<{ txHash: string }> { + // Find user strategy wth the requested orderId + const userStrategy = await this._findUserStrategy(req.address, req.orderId); + if (!userStrategy) throw Error('Order does not exist.'); + + const deleteTransaction = await this.carbonSDK.deleteStrategy( + userStrategy.id + ); + + const wallet = await this._chain.getWallet(req.address); + const tx = await wallet.sendTransaction(deleteTransaction); + + return { txHash: tx.hash }; + } + + public estimateGas(_req: NetworkSelectionRequest): { + gasPrice: number; + gasPriceToken: string; + gasLimit: number; + gasCost: number; + } { + return { + gasPrice: this._chain.gasPrice, + gasPriceToken: this._chain.nativeTokenSymbol, + gasLimit: this._conf.gasLimitEstimate, + gasCost: this._chain.gasPrice * this._conf.gasLimitEstimate, + }; + } + + public async balances(req: BalanceRequest): Promise> { + const tokens = this._getTokensBySymbol(req.tokenSymbols); + + const userStrategies = await this.carbonSDK.getUserStrategies(req.address); + + const formattedBalances: Record = { budget: {} }; + + await Promise.all( + tokens.map((token) => { + const userStrategyBuyBudget = userStrategies.find( + (strategy) => strategy.baseToken === token.address + ); + const userStrategySellBudget = userStrategies.find( + (strategy) => strategy.quoteToken === token.address + ); + + const userTokenBalance = new Decimal( + userStrategyBuyBudget?.buyBudget || 0 + ) + .add(new Decimal(userStrategySellBudget?.sellBudget || 0)) + .toString(); + + formattedBalances.budget[token.symbol] = parseUnits( + userTokenBalance, + token.decimals + ); + }) + ); + + return formattedBalances; + } + + public async orderBook(req: ClobOrderbookRequest): Promise { + this._isMarketValid(req.market); + + const [base, quote] = this._getTokensBySymbol(req.market.split('-')); + + if (!base || !quote) throw Error('Invalid tokens'); + + const { buyHasLiq, buyStartRate, sellHasLiq, sellStartRate, step, steps } = + await this._getOrderbookParams(base, quote); + + const buy = buyHasLiq + ? await this._buildOrderBook( + true, + base.address, + quote.address, + buyStartRate, + step, + steps + ) + : []; + + const sell = sellHasLiq + ? await this._buildOrderBook( + false, + quote.address, + base.address, + sellStartRate, + step, + steps + ) + : []; + + const buyOrders = buildOrders(buy, base.decimals, quote.decimals); + const sellOrders = buildOrders(sell, base.decimals, quote.decimals); + + return { + buys: buyOrders.map((buyOrder) => { + return { + price: buyOrder.rate, + quantity: buyOrder.amount, + timestamp: Date.now(), + }; + }), + sells: sellOrders.map((sellOrder) => { + return { + price: sellOrder.rate, + quantity: sellOrder.amount, + timestamp: Date.now(), + }; + }), + }; + } + + // Helper functions + + private async _isMarketValid(market: string) { + const symbols: MarketInfo = + this.parsedMarkets[market] || + this.parsedMarkets[market.split('-').reverse().join('-')]; + + if (!symbols || symbols.length < 2) throw Error('Invalid market'); + } + + private async _updateMarkets(pair: TokenPair) { + const baseToken = this._findTokenByAddress(pair[0]); + const quoteToken = this._findTokenByAddress(pair[1]); + + if (!baseToken || !quoteToken) return; + + const ticker = + baseToken.symbol.toUpperCase() + '-' + quoteToken.symbol.toUpperCase(); + + // will handle cache miss if no pair in cache + const makerFee = await this.sdkCache.getTradingFeePPMByPair( + pair[0], + pair[1] + ); + + const market = { + ticker, + baseToken, + quoteToken, + makerFee, + }; + + this.parsedMarkets[market.ticker] = market; + } + + private _getTokensBySymbol(symbols: string[]) { + return symbols.map((symbol) => { + if (symbol == 'ETH') return this._nativeToken; + return this._chain.getTokenBySymbol(symbol) || emptyToken; + }); + } + + private async _getOrderbookParams(base: TokenInfo, quote: TokenInfo) { + const steps = 100; + const ONE = new Decimal(1); + + const buyHasLiq = await this.carbonSDK.hasLiquidityByPair( + base.address, + quote.address + ); + const sellHasLiq = await this.carbonSDK.hasLiquidityByPair( + quote.address, + base.address + ); + + const minBuy = new Decimal( + buyHasLiq + ? await this.carbonSDK.getMinRateByPair(base.address, quote.address) + : 0 + ); + const maxBuy = new Decimal( + buyHasLiq + ? await this.carbonSDK.getMaxRateByPair(base.address, quote.address) + : 0 + ); + const minSell = new Decimal( + sellHasLiq + ? await this.carbonSDK.getMinRateByPair(quote.address, base.address) + : 0 + ); + const maxSell = new Decimal( + sellHasLiq + ? await this.carbonSDK.getMaxRateByPair(quote.address, base.address) + : 0 + ); + + const minSellNormalized = ONE.div(maxSell); + const maxSellNormalized = ONE.div(minSell); + + const deltaBuy = maxBuy.minus(minBuy); + const deltaSell = maxSellNormalized.minus(minSellNormalized); + + const stepBuy = deltaBuy.div(steps); + const stepSell = deltaSell.div(steps); + + const step = getStep( + stepBuy, + stepSell, + minBuy, + maxBuy, + steps, + minSell, + maxSell + ); + + const middleRate = getMiddleRate(maxBuy, maxSell); + + const hasOverlappingRates = maxBuy.gt(minSellNormalized); + const buyStartRate = hasOverlappingRates ? middleRate : maxBuy; + const sellStartRate = hasOverlappingRates ? middleRate : minSellNormalized; + + return { buyHasLiq, buyStartRate, sellHasLiq, sellStartRate, step, steps }; + } + + private async _buildOrderBook( + buy: boolean, + baseToken: string, + quoteToken: string, + startRate: Decimal, + step: Decimal, + steps: number + ): Promise { + const orders: OrderRow[] = []; + const rates: string[] = []; + const ONE = new Decimal(1); + + for (let i = 0; i <= steps + 1; i++) { + const incrementBy = step.times(i); + let rate = startRate[buy ? 'minus' : 'plus'](incrementBy); + rate = buy ? rate : ONE.div(rate); + if (rate.gt(0)) { + rates.push(rate.toString()); + } + } + + const results = await this.carbonSDK.getRateLiquidityDepthsByPair( + baseToken, + quoteToken, + rates + ); + + results.forEach((liquidity, i) => { + const length = orders.length; + let rate = rates[i]; + let liquidityBn = new Decimal(liquidity); + let totalBn = liquidityBn; + + if (liquidityBn.eq(0)) { + console.warn('order book getRateLiquidityDepthsByPair returns 0'); + return; + } + if (buy) { + if (length === 0) { + liquidityBn = liquidityBn.div(rate); + } else { + if (liquidityBn.eq(orders[length - 1].originalTotal || '0')) { + liquidityBn = new Decimal(orders[length - 1].amount); + } else { + const firstRate = new Decimal(orders[0].rate); + const firstTotal = new Decimal(orders[0].originalTotal || '0'); + const delta = liquidityBn.minus(firstTotal); + liquidityBn = firstTotal.div(firstRate).plus(delta.div(rate)); + } + } + } else { + rate = ONE.div(rate).toString(); + totalBn = totalBn.times(rate); + } + orders.push({ + rate, + total: totalBn.toString(), + amount: liquidityBn.toString(), + originalTotal: liquidity, + }); + }); + + return orders; + } + + private async _findUserStrategy( + address: string, + orderId: string + ): Promise { + const userStrategies = await this.carbonSDK.getUserStrategies(address); + + const userStrategy = userStrategies.find((strategy) => { + const [, strategyIndex] = decodeStrategyId(strategy.id); + return strategyIndex === orderId; + }); + return userStrategy; + } + + private _findTokenByAddress(address: string): TokenInfo { + if (this._isNativeAddress(address)) { + return this._nativeToken; + } else { + return ( + this._chain.storedTokenList.find( + (token) => token.address === address + ) || emptyToken + ); + } + } + + private _isNativeAddress(address: string) { + if (this._chain.chainName === 'ethereum') { + return isETHAddress(address); + } + return false; + } +} diff --git a/src/connectors/carbon/carbon.utils.ts b/src/connectors/carbon/carbon.utils.ts new file mode 100644 index 0000000000..2e5dc84f02 --- /dev/null +++ b/src/connectors/carbon/carbon.utils.ts @@ -0,0 +1,113 @@ +import Decimal from 'decimal.js-light'; +import { TokenInfo } from '../../chains/ethereum/ethereum-base'; + +export type OrderRow = { + rate: string; + total: string; + amount: string; + originalTotal?: string; +}; + +export const emptyToken: TokenInfo = { + chainId: 1, + address: '', + name: '', + symbol: '', + decimals: 18, +}; + +const subtractPrevAmount = ( + data: OrderRow[], + amount: string, + rate: string, + i: number +) => { + const prevAmount = data[i - 1]?.amount || '0'; + const newAmount = new Decimal(amount).minus(prevAmount); + const newTotal = new Decimal(rate).times(newAmount); + + return { + rate, + amount: newAmount.toString(), + total: newTotal.toString(), + }; +}; + +export const buildOrders = ( + data: OrderRow[], + baseDecimals: number, + quoteDecimals: number +): OrderRow[] => { + const ROW_AMOUNT_MIN_THRESHOLD = 0.0001; + const buckets = 14; + + return data + .map(({ amount, rate }, i) => subtractPrevAmount(data, amount, rate, i)) + .filter(({ amount }) => new Decimal(amount).gte(ROW_AMOUNT_MIN_THRESHOLD)) + .splice(0, buckets) + .map(({ amount, rate, total }) => ({ + rate: new Decimal(rate).toFixed(quoteDecimals, 1), + amount: new Decimal(amount).toFixed(baseDecimals, 1), + total: new Decimal(total).toFixed(quoteDecimals, 1), + })); +}; + +export const getMiddleRate = (maxBuy: Decimal, maxSell: Decimal): Decimal => { + const ONE = new Decimal(1); + const isFinite = (number: Decimal) => + !['NaN', 'Infinity', '-Infinity'].some((x) => x === number.toString()); + + if (isFinite(maxBuy) && maxBuy.gt(0) && isFinite(maxSell) && maxSell.gt(0)) { + return maxBuy.plus(ONE.div(maxSell)).div(2); + } + if (isFinite(maxBuy) && maxBuy.gt(0)) { + return maxBuy; + } + if (isFinite(maxSell) && maxSell.gt(0)) { + return ONE.div(maxSell); + } + return new Decimal(0); +}; + +export const getStep = ( + stepBuy: Decimal, + stepSell: Decimal, + minBuy: Decimal, + maxBuy: Decimal, + steps: number, + minSell: Decimal, + maxSell: Decimal +): Decimal => { + const ONE = new Decimal(1); + const isFinite = (number: Decimal) => + !['NaN', 'Infinity', '-Infinity'].some((x) => x === number.toString()); + + if (isFinite(stepBuy) && stepBuy.gt(0)) { + if (isFinite(stepSell) && stepSell.gt(0)) { + return stepBuy.lte(stepSell) ? stepBuy : stepSell; + } else { + return stepBuy; + } + } else if (isFinite(stepSell) && stepSell.gt(0)) { + return stepSell; + } else { + if (minBuy.gt(0) && minBuy.eq(maxBuy)) { + return minBuy.div(steps + 2); + } + if (minSell.gt(0) && minSell.eq(maxSell)) { + return minSell.div(steps + 2); + } + return ONE.div(10000); + } +}; + +export const decodeStrategyId = (strategyIdRaw: string): string[] => { + const strategyId = BigInt(strategyIdRaw); + const pairId = (strategyId >> BigInt(128)).toString(); + + const strategyIndex = ( + strategyId & BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF') + ).toString(10); + + return [pairId, strategyIndex]; +}; diff --git a/src/connectors/carbon/carbon_controller_abi.json b/src/connectors/carbon/carbon_controller_abi.json new file mode 100644 index 0000000000..0edd803948 --- /dev/null +++ b/src/connectors/carbon/carbon_controller_abi.json @@ -0,0 +1,1646 @@ +{ + "address": "0xD59BcAbd00721B754aCB79FE668faE4B6A063fF8", + "abi": [ + { + "inputs": [ + { + "internalType": "contract IVoucher", + "name": "initVoucher", + "type": "address" + }, + { + "internalType": "address", + "name": "proxy", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "AccessDenied", + "type": "error" + }, + { + "inputs": [], + "name": "AlreadyInitialized", + "type": "error" + }, + { + "inputs": [], + "name": "BalanceMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "DeadlineExpired", + "type": "error" + }, + { + "inputs": [], + "name": "GreaterThanMaxInput", + "type": "error" + }, + { + "inputs": [], + "name": "IdenticalAddresses", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientCapacity", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientLiquidity", + "type": "error" + }, + { + "inputs": [], + "name": "InsufficientNativeTokenReceived", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidAddress", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidFee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidIndices", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidRate", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTradeActionAmount", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidTradeActionStrategyId", + "type": "error" + }, + { + "inputs": [], + "name": "LowerThanMinReturn", + "type": "error" + }, + { + "inputs": [], + "name": "NativeAmountMismatch", + "type": "error" + }, + { + "inputs": [], + "name": "OrderDisabled", + "type": "error" + }, + { + "inputs": [], + "name": "OutDated", + "type": "error" + }, + { + "inputs": [], + "name": "Overflow", + "type": "error" + }, + { + "inputs": [], + "name": "PairAlreadyExists", + "type": "error" + }, + { + "inputs": [], + "name": "PairDoesNotExist", + "type": "error" + }, + { + "inputs": [], + "name": "UnknownDelegator", + "type": "error" + }, + { + "inputs": [], + "name": "UnnecessaryNativeTokenReceived", + "type": "error" + }, + { + "inputs": [], + "name": "ZeroValue", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "Token", + "name": "token", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "FeesWithdrawn", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint128", + "name": "pairId", + "type": "uint128" + }, + { + "indexed": true, + "internalType": "Token", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "Token", + "name": "token1", + "type": "address" + } + ], + "name": "PairCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "Token", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "Token", + "name": "token1", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "prevFeePPM", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "newFeePPM", + "type": "uint32" + } + ], + "name": "PairTradingFeePPMUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "Token", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "Token", + "name": "token1", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "y", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "z", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "A", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "B", + "type": "uint64" + } + ], + "indexed": false, + "internalType": "struct Order", + "name": "order0", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "y", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "z", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "A", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "B", + "type": "uint64" + } + ], + "indexed": false, + "internalType": "struct Order", + "name": "order1", + "type": "tuple" + } + ], + "name": "StrategyCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "Token", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "Token", + "name": "token1", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "y", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "z", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "A", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "B", + "type": "uint64" + } + ], + "indexed": false, + "internalType": "struct Order", + "name": "order0", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "y", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "z", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "A", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "B", + "type": "uint64" + } + ], + "indexed": false, + "internalType": "struct Order", + "name": "order1", + "type": "tuple" + } + ], + "name": "StrategyDeleted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "Token", + "name": "token0", + "type": "address" + }, + { + "indexed": true, + "internalType": "Token", + "name": "token1", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "y", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "z", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "A", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "B", + "type": "uint64" + } + ], + "indexed": false, + "internalType": "struct Order", + "name": "order0", + "type": "tuple" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "y", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "z", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "A", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "B", + "type": "uint64" + } + ], + "indexed": false, + "internalType": "struct Order", + "name": "order1", + "type": "tuple" + }, + { + "indexed": false, + "internalType": "uint8", + "name": "reason", + "type": "uint8" + } + ], + "name": "StrategyUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "trader", + "type": "address" + }, + { + "indexed": true, + "internalType": "Token", + "name": "sourceToken", + "type": "address" + }, + { + "indexed": true, + "internalType": "Token", + "name": "targetToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "sourceAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "targetAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint128", + "name": "tradingFeeAmount", + "type": "uint128" + }, + { + "indexed": false, + "internalType": "bool", + "name": "byTargetAmount", + "type": "bool" + } + ], + "name": "TokensTraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "prevFeePPM", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "newFeePPM", + "type": "uint32" + } + ], + "name": "TradingFeePPMUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Token", + "name": "token", + "type": "address" + } + ], + "name": "accumulatedFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Token", + "name": "sourceToken", + "type": "address" + }, + { + "internalType": "Token", + "name": "targetToken", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "strategyId", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + } + ], + "internalType": "struct TradeAction[]", + "name": "tradeActions", + "type": "tuple[]" + } + ], + "name": "calculateTradeSourceAmount", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Token", + "name": "sourceToken", + "type": "address" + }, + { + "internalType": "Token", + "name": "targetToken", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "strategyId", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + } + ], + "internalType": "struct TradeAction[]", + "name": "tradeActions", + "type": "tuple[]" + } + ], + "name": "calculateTradeTargetAmount", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "controllerType", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Token", + "name": "token0", + "type": "address" + }, + { + "internalType": "Token", + "name": "token1", + "type": "address" + } + ], + "name": "createPair", + "outputs": [ + { + "components": [ + { + "internalType": "uint128", + "name": "id", + "type": "uint128" + }, + { + "internalType": "Token[2]", + "name": "tokens", + "type": "address[2]" + } + ], + "internalType": "struct Pair", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Token", + "name": "token0", + "type": "address" + }, + { + "internalType": "Token", + "name": "token1", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "y", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "z", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "A", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "B", + "type": "uint64" + } + ], + "internalType": "struct Order[2]", + "name": "orders", + "type": "tuple[2]" + } + ], + "name": "createStrategy", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "strategyId", + "type": "uint256" + } + ], + "name": "deleteStrategy", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Token", + "name": "token0", + "type": "address" + }, + { + "internalType": "Token", + "name": "token1", + "type": "address" + } + ], + "name": "pair", + "outputs": [ + { + "components": [ + { + "internalType": "uint128", + "name": "id", + "type": "uint128" + }, + { + "internalType": "Token[2]", + "name": "tokens", + "type": "address[2]" + } + ], + "internalType": "struct Pair", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Token", + "name": "token0", + "type": "address" + }, + { + "internalType": "Token", + "name": "token1", + "type": "address" + } + ], + "name": "pairTradingFeePPM", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pairs", + "outputs": [ + { + "internalType": "Token[2][]", + "name": "", + "type": "address[2][]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "postUpgrade", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "roleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "roleEmergencyStopper", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "roleFeesManager", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Token", + "name": "token0", + "type": "address" + }, + { + "internalType": "Token", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint32", + "name": "newPairTradingFeePPM", + "type": "uint32" + } + ], + "name": "setPairTradingFeePPM", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "newTradingFeePPM", + "type": "uint32" + } + ], + "name": "setTradingFeePPM", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Token", + "name": "token0", + "type": "address" + }, + { + "internalType": "Token", + "name": "token1", + "type": "address" + }, + { + "internalType": "uint256", + "name": "startIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "endIndex", + "type": "uint256" + } + ], + "name": "strategiesByPair", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "Token[2]", + "name": "tokens", + "type": "address[2]" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "y", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "z", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "A", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "B", + "type": "uint64" + } + ], + "internalType": "struct Order[2]", + "name": "orders", + "type": "tuple[2]" + } + ], + "internalType": "struct Strategy[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Token", + "name": "token0", + "type": "address" + }, + { + "internalType": "Token", + "name": "token1", + "type": "address" + } + ], + "name": "strategiesByPairCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "strategy", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "Token[2]", + "name": "tokens", + "type": "address[2]" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "y", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "z", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "A", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "B", + "type": "uint64" + } + ], + "internalType": "struct Order[2]", + "name": "orders", + "type": "tuple[2]" + } + ], + "internalType": "struct Strategy", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "interfaceId", + "type": "bytes4" + } + ], + "name": "supportsInterface", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Token", + "name": "sourceToken", + "type": "address" + }, + { + "internalType": "Token", + "name": "targetToken", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "strategyId", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + } + ], + "internalType": "struct TradeAction[]", + "name": "tradeActions", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "minReturn", + "type": "uint128" + } + ], + "name": "tradeBySourceAmount", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Token", + "name": "sourceToken", + "type": "address" + }, + { + "internalType": "Token", + "name": "targetToken", + "type": "address" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "strategyId", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "amount", + "type": "uint128" + } + ], + "internalType": "struct TradeAction[]", + "name": "tradeActions", + "type": "tuple[]" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint128", + "name": "maxInput", + "type": "uint128" + } + ], + "name": "tradeByTargetAmount", + "outputs": [ + { + "internalType": "uint128", + "name": "", + "type": "uint128" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "tradingFeePPM", + "outputs": [ + { + "internalType": "uint32", + "name": "", + "type": "uint32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "strategyId", + "type": "uint256" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "y", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "z", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "A", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "B", + "type": "uint64" + } + ], + "internalType": "struct Order[2]", + "name": "currentOrders", + "type": "tuple[2]" + }, + { + "components": [ + { + "internalType": "uint128", + "name": "y", + "type": "uint128" + }, + { + "internalType": "uint128", + "name": "z", + "type": "uint128" + }, + { + "internalType": "uint64", + "name": "A", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "B", + "type": "uint64" + } + ], + "internalType": "struct Order[2]", + "name": "newOrders", + "type": "tuple[2]" + } + ], + "name": "updateStrategy", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "Token", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "withdrawFees", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ] +} \ No newline at end of file diff --git a/src/connectors/connectors.routes.ts b/src/connectors/connectors.routes.ts index 96164c7055..bfaeb70e1a 100644 --- a/src/connectors/connectors.routes.ts +++ b/src/connectors/connectors.routes.ts @@ -20,6 +20,7 @@ import { DexalotCLOBConfig } from './dexalot/dexalot.clob.config'; import { TinymanConfig } from './tinyman/tinyman.config'; import { PlentyConfig } from './plenty/plenty.config'; import { KujiraConfig } from './kujira/kujira.config'; +import { CarbonConfig } from './carbon/carbon.config'; export namespace ConnectorsRoutes { export const router = Router(); @@ -162,6 +163,12 @@ export namespace ConnectorsRoutes { 'Enter your kujira account number (input 0 if unsure) >>> ', }, }, + { + name: 'carbon', + trading_type: CarbonConfig.config.tradingTypes('spot'), + chain_type: CarbonConfig.config.chainType, + available_networks: CarbonConfig.config.availableNetworks, + }, ], }); }) diff --git a/src/services/connection-manager.ts b/src/services/connection-manager.ts index 7cc9af8b8b..1f7088fa36 100644 --- a/src/services/connection-manager.ts +++ b/src/services/connection-manager.ts @@ -41,6 +41,7 @@ import { Tinyman } from '../connectors/tinyman/tinyman'; import { Plenty } from '../connectors/plenty/plenty'; import { Kujira } from '../chains/kujira/kujira'; import { KujiraCLOB } from '../connectors/kujira/kujira'; +import { CarbonCLOB } from '../connectors/carbon/carbon'; export type ChainUnion = | Algorand @@ -221,6 +222,8 @@ export async function getConnector( connectorInstance = Plenty.getInstance(network); } else if (chain === 'kujira' && connector === 'kujira') { connectorInstance = KujiraCLOB.getInstance(chain, network); + } else if (chain === 'ethereum' && connector === 'carbon') { + connectorInstance = CarbonCLOB.getInstance(chain, network); } else { throw new Error('unsupported chain or connector'); } diff --git a/src/services/schema/carbon-schema.json b/src/services/schema/carbon-schema.json new file mode 100644 index 0000000000..f74326d833 --- /dev/null +++ b/src/services/schema/carbon-schema.json @@ -0,0 +1,54 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "allowedSlippage": { + "type": "string" + }, + "gasLimitEstimate": { + "type": "integer" + }, + "ttl": { + "type": "integer" + }, + "matchType": { + "enum": [ + "FAST", + "BEST" + ] + }, + "contractAddresses": { + "type": "object", + "patternProperties": { + "^\\w+$": { + "type": "object", + "properties": { + "carbonControllerAddress": { + "type": "string" + }, + "multiCallAddress": { + "type": "string" + }, + "voucherAddress": { + "type": "string" + } + }, + "required": [ + "carbonControllerAddress", + "multiCallAddress", + "voucherAddress" + ], + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": [ + "allowedSlippage", + "ttl", + "contractAddresses", + "matchType" + ] +} \ No newline at end of file diff --git a/src/templates/carbon.yml b/src/templates/carbon.yml new file mode 100644 index 0000000000..46c22940f9 --- /dev/null +++ b/src/templates/carbon.yml @@ -0,0 +1,20 @@ +# allowedSlippage: how much the execution price is allowed to move unfavorably from the trade +# execution price. It uses a rational number for precision. +allowedSlippage: '0.5/100' + +# ttl: how long a trade is valid in seconds. After this time passes carbon will not +# perform the trade, but the gas will still be sent. +ttl: 300 + +# The maximum gas used to estimate gasCost for a Carbon trade. +gasLimitEstimate: 200000 + +# Type of matching using the Carbon SDK matching engine +# Available options: 'FAST', 'BEST' +matchType: 'FAST' + +contractAddresses: + mainnet: + carbonControllerAddress: '0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1' + multiCallAddress: '0x5ba1e12693dc8f9c48aad8770482f4739beed696' + voucherAddress: '0x3660F04B79751e31128f6378eAC70807e38f554E' \ No newline at end of file diff --git a/src/templates/root.yml b/src/templates/root.yml index 1ef1b47bc8..b57bd015e0 100644 --- a/src/templates/root.yml +++ b/src/templates/root.yml @@ -115,3 +115,7 @@ configurations: $namespace kujira: configurationPath: kujira.yml schemaPath: kujira-schema.json + + $namespace carbon: + configurationPath: carbon.yml + schemaPath: carbon-schema.json From f7dc80bc837b83288d50ef2f312b31b15ec3b73c Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Mon, 4 Dec 2023 09:45:39 +0000 Subject: [PATCH 02/35] Update carbon CLOB --- src/connectors/carbon/carbon.config.ts | 3 + src/connectors/carbon/carbon.ts | 62 +-- src/connectors/carbon/carbon.utils.ts | 12 + test/connectors/carbon/carbon.test.ts | 569 +++++++++++++++++++++++++ 4 files changed, 620 insertions(+), 26 deletions(-) create mode 100644 test/connectors/carbon/carbon.test.ts diff --git a/src/connectors/carbon/carbon.config.ts b/src/connectors/carbon/carbon.config.ts index 8c39e47bd3..1b88cdbea3 100644 --- a/src/connectors/carbon/carbon.config.ts +++ b/src/connectors/carbon/carbon.config.ts @@ -1,6 +1,7 @@ import { ContractsConfig } from '@bancor/carbon-sdk/contracts-api'; import { AvailableNetworks } from '../../services/config-manager-types'; import { ConfigManagerV2 } from '../../services/config-manager-v2'; +import { MatchType } from './carbon-sdk/src'; export namespace CarbonConfig { export interface NetworkConfig { @@ -9,6 +10,7 @@ export namespace CarbonConfig { ttl: number; tradingTypes: (type: string) => Array; chainType: string; + matchType: MatchType; availableNetworks: Array; carbonContractsConfig: (network: string) => Required; } @@ -25,6 +27,7 @@ export namespace CarbonConfig { return type === 'swap' ? ['AMM'] : ['CLOB_SPOT']; }, chainType: 'EVM', + matchType: MatchType.Fast, availableNetworks: [ { chain: 'ethereum', diff --git a/src/connectors/carbon/carbon.ts b/src/connectors/carbon/carbon.ts index 84bcca79c0..ebe296ff29 100644 --- a/src/connectors/carbon/carbon.ts +++ b/src/connectors/carbon/carbon.ts @@ -3,6 +3,7 @@ import Decimal from 'decimal.js-light'; import { Ethereum } from '../../chains/ethereum/ethereum'; import { TokenInfo } from '../../chains/ethereum/ethereum-base'; +import { EVMTxBroadcaster } from '../../chains/ethereum/evm.broadcaster'; import { ClobMarketsRequest, @@ -49,14 +50,12 @@ export class CarbonCLOB implements CLOBish { public carbonContractConfig: Required; public carbonSDK: Toolkit; public sdkCache: ChainCache; + public api: ContractsApi; private static _instances: { [name: string]: CarbonCLOB }; private _chain: Ethereum; private _ready: boolean = false; private _conf: CarbonConfig.NetworkConfig; - private _api: ContractsApi; - private _decimalsMap: Map; private _nativeToken: TokenInfo; - private _startDataSync: () => Promise; private constructor(chain: string, network: string) { if (chain === 'ethereum') { @@ -79,23 +78,13 @@ export class CarbonCLOB implements CLOBish { this._conf = CarbonConfig.config; this.carbonContractConfig = this._conf.carbonContractsConfig(network); - this._api = new ContractsApi( + this.api = new ContractsApi( this._chain.provider, this.carbonContractConfig ); - const { cache, startDataSync } = initSyncedCache(this._api.reader); - this.sdkCache = cache; - this._startDataSync = startDataSync; - - this._decimalsMap = new Map(); - this._chain.storedTokenList.forEach((token) => { - this._decimalsMap.set(token.address, token.decimals); - }); - - this.carbonSDK = new Toolkit(this._api, this.sdkCache, (address) => - this._decimalsMap.get(address.toLowerCase()) - ); + this.sdkCache = new ChainCache(); + this.carbonSDK = new Toolkit(this.api, this.sdkCache); } public static getInstance(chain: string, network: string): CarbonCLOB { @@ -124,7 +113,7 @@ export class CarbonCLOB implements CLOBish { } public async loadMarkets() { - const contractPairs = await this._api.reader.pairs(); + const contractPairs = await this.api.reader.pairs(); await Promise.all( contractPairs.map(async (pair) => await this._updateMarkets(pair)) @@ -137,13 +126,27 @@ export class CarbonCLOB implements CLOBish { await this._chain.init(); } - logger.info('Starting Data Sync...'); - await this._startDataSync(); + if (!this._ready) { + const { cache, startDataSync } = initSyncedCache(this.api.reader); + this.sdkCache = cache; - logger.info('Loading markets...'); - await this.loadMarkets(); + const decimalsMap = new Map(); + this._chain.storedTokenList.forEach((token) => { + decimalsMap.set(token.address, token.decimals); + }); - this._ready = true; + this.carbonSDK = new Toolkit(this.api, this.sdkCache, (address) => + decimalsMap.get(address.toLowerCase()) + ); + + logger.info('Starting Data Sync...'); + await startDataSync(); + + logger.info('Loading markets...'); + await this.loadMarkets(); + + this._ready = true; + } } public ready(): boolean { @@ -230,9 +233,13 @@ export class CarbonCLOB implements CLOBish { ); const wallet = await this._chain.getWallet(req.address); - const tx = await wallet.sendTransaction(createTransaction); - return { txHash: tx.hash }; + const txResponse = await EVMTxBroadcaster.getInstance( + this._chain, + wallet.address + ).broadcast(createTransaction); + + return { txHash: txResponse.hash }; } return { txHash: '' }; @@ -250,9 +257,12 @@ export class CarbonCLOB implements CLOBish { ); const wallet = await this._chain.getWallet(req.address); - const tx = await wallet.sendTransaction(deleteTransaction); + const txResponse = await EVMTxBroadcaster.getInstance( + this._chain, + wallet.address + ).broadcast(deleteTransaction); - return { txHash: tx.hash }; + return { txHash: txResponse.hash }; } public estimateGas(_req: NetworkSelectionRequest): { diff --git a/src/connectors/carbon/carbon.utils.ts b/src/connectors/carbon/carbon.utils.ts index 2e5dc84f02..a0b08de4ed 100644 --- a/src/connectors/carbon/carbon.utils.ts +++ b/src/connectors/carbon/carbon.utils.ts @@ -111,3 +111,15 @@ export const decodeStrategyId = (strategyIdRaw: string): string[] => { return [pairId, strategyIndex]; }; + +export const encodeStrategyId = ( + strategyIndexRaw: string, + pairIdRaw: string +): string => { + const pairId = BigInt(pairIdRaw); + const strategyIndex = BigInt(strategyIndexRaw); + + const strategyID = (pairId << BigInt(128)) | strategyIndex; + + return '0x' + strategyID.toString(16); +}; diff --git a/test/connectors/carbon/carbon.test.ts b/test/connectors/carbon/carbon.test.ts new file mode 100644 index 0000000000..6e9a32cfe2 --- /dev/null +++ b/test/connectors/carbon/carbon.test.ts @@ -0,0 +1,569 @@ +import request from 'supertest'; +import { gatewayApp } from '../../../src/app'; +import { patch, unpatch } from '../../../test/services/patch'; +import { Ethereum } from '../../../src/chains/ethereum/ethereum'; +import { EVMTxBroadcaster } from '../../../src/chains/ethereum/evm.broadcaster'; +import { CarbonCLOB } from '../../../src/connectors/carbon/carbon'; +import { + EncodedStrategy, + TokenPair, +} from '../../../src/connectors/carbon/carbon-sdk/src'; +import { encodeStrategy } from '../../../src/connectors/carbon/carbon-sdk/src/strategy-management/utils'; +import { logger } from '../../../src/services/logger'; +import { BigNumber } from '../../../src/connectors/carbon/carbon-sdk/src/utils'; +import { encodeStrategyId } from '../../../src/connectors/carbon/carbon.utils'; +import { buildStrategyObject } from '../../../src/connectors/carbon/carbon-sdk/src/strategy-management'; +import { patchEVMNonceManager } from '../../../test/evm.nonce.mock'; + +let ethereum: Ethereum; +let carbon: CarbonCLOB; + +const TX_HASH = + '0xf6f81a37796bd06a797484467302e4d6f72832409545e2e01feb86dd8b22e4b2'; // noqa: mock +const MARKET = 'DAI-USDC'; +const DEFAULT_FEE = 2000; +const NUM_ORDERBOOK_BUCKETS = 14; + +const MARKETS = [ + { + ticker: 'DAI-USDC', + baseToken: { + chainId: 1, + address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + name: 'Dai', + symbol: 'DAI', + decimals: 18, + logoURI: + 'https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734', + }, + quoteToken: { + chainId: 1, + address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + name: 'USD Coin', + symbol: 'USDC', + decimals: 6, + logoURI: + 'https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389', + }, + makerFee: 10, + }, + { + ticker: 'DAI-ETH', + baseToken: { + chainId: 1, + address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + name: 'Dai', + symbol: 'DAI', + decimals: 18, + logoURI: + 'https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734', + }, + quoteToken: { + chainId: 1, + address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + name: 'Ethereum', + symbol: 'ETH', + decimals: 18, + }, + makerFee: 2000, + }, +]; + +const ORDERS = [ + { + id: '729', + pairId: '4', + owner: '0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8', + baseToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + quoteToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + baseDecimals: 6, + quoteDecimals: 18, + buyPriceLow: '0.95', + buyPriceMarginal: '0.98', + buyPriceHigh: '0.98', + buyBudget: '1000', + sellPriceLow: '1.03', + sellPriceMarginal: '1.035', + sellPriceHigh: '1.04', + sellBudget: '1000', + }, + { + id: '730', + pairId: '4', + owner: '0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8', + baseToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + quoteToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + baseDecimals: 6, + quoteDecimals: 18, + buyPriceLow: '0.90', + buyPriceMarginal: '0.95', + buyPriceHigh: '0.95', + buyBudget: '2000', + sellPriceLow: '1.03', + sellPriceMarginal: '1.05', + sellPriceHigh: '1.05', + sellBudget: '2000', + }, + { + id: '731', + pairId: '1', + owner: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', + baseToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + quoteToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + baseDecimals: 18, + quoteDecimals: 18, + buyPriceLow: '1500', + buyPriceMarginal: '1900', + buyPriceHigh: '1900', + buyBudget: '1000', + sellPriceLow: '2200', + sellPriceMarginal: '2400', + sellPriceHigh: '2400', + sellBudget: '1000', + }, +]; + +const GAS_PRICES = { + gasPrice: '500000000', + gasPriceToken: 'ETH', + gasLimit: '1000', + gasCost: '100', +}; + +const INVALID_REQUEST = { + chain: 'unknown', + connector: 'carbon', +}; + +const TOKENS = [ + { + chainId: 1, + name: 'USD Coin', + symbol: 'USDC', + address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + decimals: 6, + }, + { + chainId: 1, + name: 'DAI', + symbol: 'DAI', + address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + decimals: 18, + }, + { + chainId: 1, + name: 'ETH', + symbol: 'Ethereum', + address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + decimals: 18, + }, +]; + +beforeAll(async () => { + ethereum = Ethereum.getInstance('mainnet'); + patchEVMNonceManager(ethereum.nonceManager); + ethereum.init(); + carbon = CarbonCLOB.getInstance('ethereum', 'mainnet'); + patchReader(); + + await carbon.init(); +}); + +beforeEach(() => { + patchReader(); +}); + +afterEach(() => { + unpatch(); +}); + +afterAll(async () => { + await ethereum.close(); +}); + +const buildEncodedStrategy = (order: { + id: string; + pairId: string; + baseToken: string; + quoteToken: string; + baseDecimals: number; + quoteDecimals: number; + buyPriceLow: string; + buyPriceHigh: string; + buyBudget: string; + sellPriceLow: string; + sellPriceHigh: string; + sellBudget: string; +}) => { + const strategyObject = buildStrategyObject( + order.baseToken, + order.quoteToken, + order.baseDecimals, + order.quoteDecimals, + order.buyPriceLow, + order.buyPriceHigh, + order.buyBudget, + order.sellPriceLow, + order.sellPriceHigh, + order.sellBudget + ); + + return { + id: BigNumber.from(encodeStrategyId(order.id, order.pairId)), + ...encodeStrategy(strategyObject), + }; +}; + +const patchReader = () => { + patch(carbon.api.reader, 'tokensByOwner', (owner: string): BigNumber[] => { + const ownerOrders = ORDERS.filter((order) => owner === order.owner); + if (!owner || ownerOrders.length === 0) return []; + return ownerOrders.map((order) => + BigNumber.from(encodeStrategyId(order.id, order.pairId)) + ); + }); + + patch(carbon.api.reader, 'pairs', (): TokenPair[] => { + return MARKETS.map((market) => [ + market.baseToken.address, + market.quoteToken.address, + ]); + }); + + patch( + carbon.api.reader, + 'strategiesByPair', + (token0: string, token1: string): EncodedStrategy[] => { + return ORDERS.filter((order) => { + return ( + (order.baseToken === token0 && order.quoteToken === token1) || + (order.baseToken === token1 && order.quoteToken === token0) + ); + }).map(buildEncodedStrategy); + } + ); + + patch( + carbon.api.reader, + 'strategies', + (ids: BigNumber[]): EncodedStrategy[] => { + return ORDERS.filter((order) => { + return ids.includes( + BigNumber.from(encodeStrategyId(order.id, order.pairId)) + ); + }).map(buildEncodedStrategy); + } + ); + + patch(carbon.api.reader, 'strategy', (id: BigNumber): EncodedStrategy => { + const order = ORDERS.find((order) => { + return encodeStrategyId(order.id, order.pairId) === id.toString(); + }); + if (!order) throw Error('No strategy found'); + + return buildEncodedStrategy(order); + }); + + patch(carbon.api.reader, 'tradingFeePPM', (): number => { + return DEFAULT_FEE; + }); + + patch( + carbon.api.reader, + 'pairsTradingFeePPM', + (pairs: TokenPair[]): [string, string, number][] => { + return pairs.map((pair) => { + const market = MARKETS.filter((market) => 'makerFee' in market).find( + (market) => { + return ( + (market.baseToken.address.toLowerCase() === + pair[0].toLowerCase() && + market.quoteToken.address.toLowerCase() === + pair[1].toLowerCase()) || + (market.baseToken.address.toLowerCase() === + pair[1].toLowerCase() && + market.quoteToken.address.toLowerCase() === + pair[0].toLowerCase()) + ); + } + ); + return [pair[0], pair[1], market?.makerFee || DEFAULT_FEE]; + }); + } + ); + patch( + carbon.api.reader, + 'getLatestStrategyCreatedStrategies', + (fromBlock: number, toBlock: number) => { + logger.info(`${fromBlock} ${toBlock}`); + return []; + } + ); + + patch( + carbon.api.reader, + 'getLatestStrategyDeletedStrategies', + (fromBlock: number, toBlock: number) => { + logger.info(`${fromBlock} ${toBlock}`); + return []; + } + ); + + patch( + carbon.api.reader, + 'getLatestTokensTradedTrades', + (fromBlock: number, toBlock: number) => { + logger.info(`${fromBlock} ${toBlock}`); + return []; + } + ); + + patch( + carbon.api.reader, + 'getLatestTradingFeeUpdates', + (fromBlock: number, toBlock: number) => { + logger.info(`${fromBlock} ${toBlock}`); + return []; + } + ); + patch( + carbon.api.reader, + 'getLatestPairTradingFeeUpdates', + (fromBlock: number, toBlock: number) => { + logger.info(`${fromBlock} ${toBlock}`); + return []; + } + ); +}; + +const patchGasPrices = () => { + patch(carbon, 'estimateGas', () => { + return GAS_PRICES; + }); +}; + +const patchGetWallet = () => { + patch(ethereum, 'getWallet', () => { + return { + privateKey: + '83d8fae2444141a142079e9aa6dc1a49962af114d9ace8db9a34ecb8fa3e6cf8', // noqa: mock + address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', + }; + }); +}; + +const patchGetTokenBySymbol = () => { + patch(ethereum, 'getTokenBySymbol', (symbol: string) => { + return TOKENS.find( + (token) => token.symbol.toUpperCase() === symbol.toUpperCase() + ); + }); +}; + +const patchMsgBroadcaster = () => { + patch(EVMTxBroadcaster, 'getInstance', () => { + return { + broadcast() { + return { + hash: TX_HASH, + }; + }, + }; + }); +}; + +describe('GET /clob/markets', () => { + it('should return 200 with proper request', async () => { + await request(gatewayApp) + .get(`/clob/markets`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => { + expect(res.body.markets.length).toEqual(2); + }); + }); + + it('should return 404 when parameters are invalid', async () => { + await request(gatewayApp) + .get(`/clob/markets`) + .query(INVALID_REQUEST) + .expect(404); + }); +}); + +describe('GET /clob/orderBook', () => { + it('should return 200 with proper request', async () => { + await request(gatewayApp) + .get(`/clob/orderBook`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + market: MARKET, + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => + expect(res.body.buys.length).toEqual(NUM_ORDERBOOK_BUCKETS) + ) + .expect((res) => + expect(res.body.sells.length).toEqual(NUM_ORDERBOOK_BUCKETS) + ) + .expect((res) => expect(Number(res.body.buys[0].price)).toBeLessThan(1)) + .expect((res) => + expect(Number(res.body.sells[0].price)).toBeGreaterThan(1) + ); + }); + + it('should return 404 when parameters are invalid', async () => { + await request(gatewayApp) + .get(`/clob/orderBook`) + .query(INVALID_REQUEST) + .expect(404); + }); +}); + +describe('GET /clob/ticker', () => { + it('should return 200 with proper request', async () => { + await request(gatewayApp) + .get(`/clob/ticker`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + market: MARKET, + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => + expect(res.body.markets.baseToken.symbol).toEqual(MARKET.split('-')[0]) + ) + .expect((res) => + expect(res.body.markets.quoteToken.symbol).toEqual(MARKET.split('-')[1]) + ); + }); + + it('should return 404 when parameters are invalid', async () => { + await request(gatewayApp) + .get(`/clob/ticker`) + .query(INVALID_REQUEST) + .expect(404); + }); +}); + +describe('GET /clob/orders', () => { + it('should return 200 with proper request', async () => { + await request(gatewayApp) + .get(`/clob/orders`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + orderId: '731', + address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', // noqa: mock + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.orders.length).toEqual(1)); + }); + + it('should return 404 when parameters are invalid', async () => { + await request(gatewayApp) + .get(`/clob/orders`) + .query(INVALID_REQUEST) + .expect(404); + }); +}); + +describe('POST /clob/orders', () => { + it('should return 200 with proper request', async () => { + patchGetWallet(); + patchGetTokenBySymbol(); + patchMsgBroadcaster(); + await request(gatewayApp) + .post(`/clob/orders`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', // noqa: mock + market: MARKET, + price: '10000.12', + amount: '0.12', + side: 'BUY', + orderType: 'LIMIT_MAKER', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.txHash).toBeTruthy()); + }); + + it('should return 404 when parameters are invalid', async () => { + await request(gatewayApp) + .post(`/clob/orders`) + .send(INVALID_REQUEST) + .expect(404); + }); +}); + +describe('DELETE /clob/orders', () => { + it('should return 200 with proper request', async () => { + patchGetWallet(); + patchGetTokenBySymbol(); + patchMsgBroadcaster(); + await request(gatewayApp) + .delete(`/clob/orders`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', // noqa: mock + orderId: '731', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.txHash).toBeTruthy()); + }); + + it('should return 404 when parameters are invalid', async () => { + await request(gatewayApp) + .delete(`/clob/orders`) + .send(INVALID_REQUEST) + .expect(404); + }); +}); + +describe('GET /clob/estimateGas', () => { + it('should return 200 with proper request', async () => { + patchGasPrices(); + await request(gatewayApp) + .get(`/clob/estimateGas`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.gasPrice).toEqual(GAS_PRICES.gasPrice)); + }); + + it('should return 404 when parameters are invalid', async () => { + await request(gatewayApp) + .get(`/clob/estimateGas`) + .query(INVALID_REQUEST) + .expect(404); + }); +}); From 2300b05c706c160d2a8f985bf8bc9dfa0c67b791 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Wed, 6 Dec 2023 00:31:12 +0000 Subject: [PATCH 03/35] Add carbon AMM connector and tests --- src/chains/ethereum/ethereum.ts | 3 +- src/chains/ethereum/ethereum.validators.ts | 1 + src/connectors/carbon/carbon.config.ts | 13 +- src/connectors/carbon/carbon.ts | 5 +- src/connectors/carbon/carbonAMM.ts | 435 ++++++++++++++++ src/connectors/connectors.routes.ts | 6 + src/services/common-interfaces.ts | 7 +- src/services/connection-manager.ts | 3 + src/templates/carbon.yml | 11 +- test/connectors/carbon/carbonAMM.test.ts | 544 +++++++++++++++++++++ 10 files changed, 1014 insertions(+), 14 deletions(-) create mode 100644 src/connectors/carbon/carbonAMM.ts create mode 100644 test/connectors/carbon/carbonAMM.test.ts diff --git a/src/chains/ethereum/ethereum.ts b/src/chains/ethereum/ethereum.ts index 04fb7ca6fc..7d982cc5db 100644 --- a/src/chains/ethereum/ethereum.ts +++ b/src/chains/ethereum/ethereum.ts @@ -188,8 +188,9 @@ export class Ethereum extends EthereumBase implements Ethereumish { ); } else if (reqSpender === 'uniswapLP') { spender = UniswapConfig.config.uniswapV3NftManagerAddress(this._chain); - } else if (reqSpender === 'carbon') { + } else if (reqSpender === 'carbon' || reqSpender === 'carbonAMM') { spender = CarbonConfig.config.carbonContractsConfig( + 'ethereum', this._chain ).carbonControllerAddress; } else if (reqSpender === 'perp') { diff --git a/src/chains/ethereum/ethereum.validators.ts b/src/chains/ethereum/ethereum.validators.ts index 5b290ea4f2..26e464d800 100644 --- a/src/chains/ethereum/ethereum.validators.ts +++ b/src/chains/ethereum/ethereum.validators.ts @@ -62,6 +62,7 @@ export const validateSpender: Validator = mkValidator( val === 'pancakeswap' || val === 'xsswap' || val === 'carbon' || + val === 'carbonAMM' || isAddress(val)) ); diff --git a/src/connectors/carbon/carbon.config.ts b/src/connectors/carbon/carbon.config.ts index 1b88cdbea3..a7ccc787fc 100644 --- a/src/connectors/carbon/carbon.config.ts +++ b/src/connectors/carbon/carbon.config.ts @@ -12,7 +12,10 @@ export namespace CarbonConfig { chainType: string; matchType: MatchType; availableNetworks: Array; - carbonContractsConfig: (network: string) => Required; + carbonContractsConfig: ( + chain: string, + network: string + ) => Required; } export const config: NetworkConfig = { @@ -40,16 +43,16 @@ export namespace CarbonConfig { ), }, ], - carbonContractsConfig: (network: string) => { + carbonContractsConfig: (chain: string, network: string) => { return { carbonControllerAddress: ConfigManagerV2.getInstance().get( - `carbon.contractAddresses.${network}.carbonControllerAddress` + `carbon.contractAddresses.${chain}.${network}.carbonControllerAddress` ), multiCallAddress: ConfigManagerV2.getInstance().get( - `carbon.contractAddresses.${network}.multiCallAddress` + `carbon.contractAddresses.${chain}.${network}.multiCallAddress` ), voucherAddress: ConfigManagerV2.getInstance().get( - `carbon.contractAddresses.${network}.voucherAddress` + `carbon.contractAddresses.${chain}.${network}.voucherAddress` ), }; }, diff --git a/src/connectors/carbon/carbon.ts b/src/connectors/carbon/carbon.ts index ebe296ff29..5488f4f90c 100644 --- a/src/connectors/carbon/carbon.ts +++ b/src/connectors/carbon/carbon.ts @@ -76,7 +76,10 @@ export class CarbonCLOB implements CLOBish { : emptyToken; this._conf = CarbonConfig.config; - this.carbonContractConfig = this._conf.carbonContractsConfig(network); + this.carbonContractConfig = this._conf.carbonContractsConfig( + chain, + network + ); this.api = new ContractsApi( this._chain.provider, diff --git a/src/connectors/carbon/carbonAMM.ts b/src/connectors/carbon/carbonAMM.ts new file mode 100644 index 0000000000..529647e3c9 --- /dev/null +++ b/src/connectors/carbon/carbonAMM.ts @@ -0,0 +1,435 @@ +import { + BigNumber, + Wallet, + ContractInterface, + Transaction, + PopulatedTransaction, +} from 'ethers'; +import { getAddress } from 'ethers/lib/utils'; +import { Token } from '@uniswap/sdk'; +import { Fraction } from '@uniswap/sdk-core'; + +import { Ethereum } from '../../chains/ethereum/ethereum'; +import { TokenInfo } from '../../chains/ethereum/ethereum-base'; +import { EVMTxBroadcaster } from '../../chains/ethereum/evm.broadcaster'; + +import { isFractionString } from '../../services/validators'; +import { percentRegexp } from '../../services/config-manager-v2'; +import { Uniswapish, UniswapishTrade } from '../../services/common-interfaces'; +import { logger } from '../../services/logger'; + +import { ChainCache, initSyncedCache } from './carbon-sdk/src/chain-cache'; +import { ContractsConfig, ContractsApi } from './carbon-sdk/src/contracts-api'; +import { Toolkit } from './carbon-sdk/src/strategy-management'; +import { Action, MatchActionBNStr, TradeActionBNStr } from './carbon-sdk/src'; +import { Decimal } from './carbon-sdk/src/utils'; +import carbonControllerAbi from './carbon-sdk/src/abis/CarbonController.json'; + +import { CarbonConfig } from './carbon.config'; + +import { emptyToken } from './carbon.utils'; + +type TradeData = { + tradeActions: TradeActionBNStr[]; + actionsTokenRes: Action[]; + totalSourceAmount: string; + totalTargetAmount: string; + effectiveRate: string; + actionsWei: MatchActionBNStr[]; +}; + +export interface CarbonTrade { + from: string; + to: string; + amount: string; + tradeData: TradeData; + executionPrice: Fraction; + tradeByTarget: boolean; +} + +export class CarbonAMM implements Uniswapish { + private static _instances: { [name: string]: CarbonAMM }; + public carbonContractConfig: Required; + public carbonSDK: Toolkit; + public sdkCache: ChainCache; + public api: ContractsApi; + private tokenList: Record = {}; + public router: any; + public routerAbi: any; + private _chain: Ethereum; + private _ready: boolean = false; + private _conf: CarbonConfig.NetworkConfig; + private _gasLimitEstimate: number; + private _ttl: number; + private _nativeToken: TokenInfo; + + private constructor(chain: string, network: string) { + if (chain === 'ethereum') { + this._chain = Ethereum.getInstance(network); + } else { + throw new Error('Unsupported chain'); + } + + this._nativeToken = + this._chain.chainName === 'ethereum' + ? { + chainId: 1, + address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + name: 'Ethereum', + symbol: 'ETH', + decimals: 18, + } + : emptyToken; + + this._conf = CarbonConfig.config; + this.carbonContractConfig = this._conf.carbonContractsConfig( + chain, + network + ); + + this.api = new ContractsApi( + this._chain.provider, + this.carbonContractConfig + ); + + this.sdkCache = new ChainCache(); + this.carbonSDK = new Toolkit(this.api, this.sdkCache); + this._gasLimitEstimate = this._conf.gasLimitEstimate; + this._ttl = this._conf.ttl; + + this.router = this.carbonContractConfig.carbonControllerAddress; + this.routerAbi = carbonControllerAbi; + } + + public static getInstance(chain: string, network: string): CarbonAMM { + if (CarbonAMM._instances === undefined) { + CarbonAMM._instances = {}; + } + if (!(chain + network in CarbonAMM._instances)) { + CarbonAMM._instances[chain + network] = new CarbonAMM(chain, network); + } + + return CarbonAMM._instances[chain + network]; + } + + public async loadTokens() { + for (const token of this._chain.storedTokenList) { + this.tokenList[token.address] = new Token( + this._chain.chainId, + token.address, + token.decimals, + token.symbol, + token.name + ); + } + } + + /** + * Given a token's address, return the connector's native representation of + * the token. + * + * @param address Token address + */ + public getTokenByAddress(address: string): Token { + if (address === this._nativeToken.address) { + return new Token( + this._nativeToken.chainId, + this._nativeToken.address, + this._nativeToken.decimals, + this._nativeToken.symbol, + this._nativeToken.name + ); + } else { + return this.tokenList[getAddress(address)]; + } + } + + /** + * Default time-to-live for swap transactions, in seconds. + */ + public get ttl(): number { + return Math.floor(new Date().getTime() / 1000) + this._ttl; + } + + public async init() { + if (!this._chain.ready()) { + await this._chain.init(); + } + + if (!this._ready) { + const { cache, startDataSync } = initSyncedCache(this.api.reader); + this.sdkCache = cache; + + const decimalsMap = new Map(); + this._chain.storedTokenList.forEach((token) => { + decimalsMap.set(token.address, token.decimals); + }); + + this.carbonSDK = new Toolkit(this.api, this.sdkCache, (address) => + decimalsMap.get(address.toLowerCase()) + ); + + logger.info('Loading tokens...'); + await this.loadTokens(); + + logger.info('Starting Data Sync...'); + await startDataSync(); + + this._ready = true; + } + } + + public ready(): boolean { + return this._ready; + } + + /** + * Default gas limit for swap transactions. + */ + public get gasLimitEstimate(): number { + return this._gasLimitEstimate; + } + + /** + * Gets the allowed slippage percent from the optional parameter or the value + * in the configuration. + * + * @param allowedSlippageStr (Optional) should be of the form '1/10'. + */ + public getAllowedSlippage(allowedSlippageStr?: string): number { + if (allowedSlippageStr != null && isFractionString(allowedSlippageStr)) { + const fractionSplit = allowedSlippageStr.split('/'); + return Number(fractionSplit[0]) / Number(fractionSplit[1]); + } + + const allowedSlippage = this._conf.allowedSlippage; + const matches = allowedSlippage.match(percentRegexp); + if (matches) return Number(matches[1]) / Number(matches[2]); + throw new Error( + 'Encountered a malformed percent string in the config for ALLOWED_SLIPPAGE.' + ); + } + + /** + * Given the amount of `baseToken` desired to acquire from a transaction, + * calculate the amount of `quoteToken` needed for the transaction. + * + * This is typically used for calculating token buy prices. + * + * @param quoteToken Token input for the transaction + * @param baseToken Token output from the transaction + * @param amount Amount of `baseToken` desired from the transaction, in wei + */ + async estimateBuyTrade( + quoteToken: Token, + baseToken: Token, + amount: BigNumber + ) { + const tradeByTarget = true; + return await this.estimateTrade( + quoteToken, + baseToken, + amount, + tradeByTarget + ); + } + + /** + * Given the amount of `baseToken` to put into a transaction, calculate the + * amount of `quoteToken` that can be expected from the transaction. + * + * This is typically used for calculating token sell prices. + * + * @param baseToken Token input for the transaction + * @param quoteToken Output from the transaction + * @param amount Amount of `baseToken` to put into the transaction + */ + async estimateSellTrade( + baseToken: Token, + quoteToken: Token, + amount: BigNumber + ) { + const tradeByTarget = false; + return await this.estimateTrade( + baseToken, + quoteToken, + amount, + tradeByTarget + ); + } + + /** + * Given the amount of `inputToken` to put into a transaction, calculate the + * amount of `outputToken` that can be expected from the transaction. + * + * Used to calculate buy and sell prices + * + * @param inputToken Token input for the transaction + * @param outputToken Output from the transaction + * @param amount Amount of `inputToken` to put into the transaction + */ + async estimateTrade( + inputToken: Token, + outputToken: Token, + amount: BigNumber, + tradeByTarget: boolean + ): Promise<{ trade: CarbonTrade; expectedAmount: Fraction }> { + const amountWei = amount.toString(); + + // Because the toolkit expects floating point amounts + const parsedAmount = new Decimal(amountWei) + .div( + new Decimal(10).pow( + tradeByTarget ? outputToken.decimals : inputToken.decimals + ) + ) + .toString(); + + const tradeData = await this.carbonSDK.getTradeData( + inputToken.address, + outputToken.address, + parsedAmount, + tradeByTarget, + this._conf.matchType + ); + + if (!tradeData || tradeData.tradeActions.length === 0) { + throw new Error( + `No trade actions possible for ${inputToken.address} to ${outputToken.address}` + ); + } + + const expectedAmountFraction = new Decimal( + tradeByTarget ? tradeData.totalSourceAmount : tradeData.totalTargetAmount + ).toFraction(); + + const expectedAmount = new Fraction( + expectedAmountFraction[0].toFixed(0).toString(), + expectedAmountFraction[1].toFixed(0).toString() + ); + + const effectiveRateFraction = new Decimal( + tradeData.effectiveRate + ).toFraction(); + + const executionPrice = new Fraction( + effectiveRateFraction[0].toFixed(0).toString(), + effectiveRateFraction[1].toFixed(0).toString() + ); + + return { + trade: { + from: inputToken.address, + to: outputToken.address, + amount: amountWei, + tradeData: tradeData, + executionPrice: executionPrice, + tradeByTarget: tradeByTarget, + }, + expectedAmount: expectedAmount, + }; + } + + /** + * Given a wallet and a Uniswap-ish trade, try to execute it on blockchain. + * + * @param wallet Wallet + * @param trade Expected trade + * @param gasPrice Base gas price, for pre-EIP1559 transactions + * @param router smart contract address + * @param ttl How long the swap is valid before expiry, in seconds + * @param abi Router contract ABI + * @param gasLimit Gas limit + * @param nonce (Optional) EVM transaction nonce + * @param maxFeePerGas (Optional) Maximum total fee per gas you want to pay + * @param maxPriorityFeePerGas (Optional) Maximum tip per gas you want to pay + */ + async executeTrade( + wallet: Wallet, + trade: UniswapishTrade, + gasPrice: number, + _router: string, + ttl: number, + _abi: ContractInterface, + gasLimit: number, + nonce?: number, + maxFeePerGas?: BigNumber, + maxPriorityFeePerGas?: BigNumber, + allowedSlippage?: string + ): Promise { + if (!wallet.address) throw Error('No wallet address specified.'); + + const carbonTrade = trade; + + let overrideParams: { + gasLimit: string | number; + value: number; + nonce: number | undefined; + maxFeePerGas?: BigNumber | undefined; + maxPriorityFeePerGas?: BigNumber | undefined; + gasPrice?: string; + }; + if (maxFeePerGas || maxPriorityFeePerGas) { + overrideParams = { + gasLimit, + value: 0, + nonce, + maxFeePerGas, + maxPriorityFeePerGas, + }; + } else { + overrideParams = { + gasPrice: (gasPrice * 1e9).toFixed(0), + gasLimit: gasLimit.toFixed(0), + value: 0, + nonce: nonce, + }; + } + + const slippage = this.getAllowedSlippage(allowedSlippage); + let deadlineMs: number; + if (ttl) { + deadlineMs = Math.floor(new Date().getTime()) + this._ttl * 1000; + } else { + deadlineMs = this.ttl * 1000; + } + let tradeTransaction: PopulatedTransaction; + + if (carbonTrade.tradeByTarget) { + const maxInput = new Decimal(1) + .add(slippage) + .times(carbonTrade.tradeData.totalSourceAmount) + .toString(); + + tradeTransaction = await this.carbonSDK.composeTradeByTargetTransaction( + carbonTrade.from, + carbonTrade.to, + carbonTrade.tradeData.tradeActions, + deadlineMs.toString(), + maxInput, + { ...overrideParams } + ); + } else { + const minReturn = new Decimal(1) + .sub(slippage) + .times(carbonTrade.tradeData.totalTargetAmount) + .toString(); + + tradeTransaction = await this.carbonSDK.composeTradeBySourceTransaction( + carbonTrade.from, + carbonTrade.to, + carbonTrade.tradeData.tradeActions, + deadlineMs.toString(), + minReturn, + { ...overrideParams } + ); + } + + const txResponse = await EVMTxBroadcaster.getInstance( + this._chain, + wallet.address + ).broadcast(tradeTransaction); + + return txResponse; + } +} diff --git a/src/connectors/connectors.routes.ts b/src/connectors/connectors.routes.ts index bfaeb70e1a..21b439c24e 100644 --- a/src/connectors/connectors.routes.ts +++ b/src/connectors/connectors.routes.ts @@ -169,6 +169,12 @@ export namespace ConnectorsRoutes { chain_type: CarbonConfig.config.chainType, available_networks: CarbonConfig.config.availableNetworks, }, + { + name: 'carbonAMM', + trading_type: CarbonConfig.config.tradingTypes('swap'), + chain_type: CarbonConfig.config.chainType, + available_networks: CarbonConfig.config.availableNetworks, + }, ], }); }) diff --git a/src/services/common-interfaces.ts b/src/services/common-interfaces.ts index d7855fb004..c109d677f1 100644 --- a/src/services/common-interfaces.ts +++ b/src/services/common-interfaces.ts @@ -108,6 +108,7 @@ import { } from '../clob/clob.requests'; import { BalanceRequest } from '../network/network.requests'; import { TradeV2 } from '@traderjoe-xyz/sdk-v2'; +import { CarbonTrade } from '../connectors/carbon/carbonAMM'; // TODO Check the possibility to have clob/solana/serum equivalents here // Check this link https://hummingbot.org/developers/gateway/building-gateway-connectors/#5-add-sdk-classes-to-uniswapish-interface @@ -144,7 +145,8 @@ export type UniswapishTrade = | MMFTrade | VVSTrade | TradeXsswap - | TradeV2; + | TradeV2 + | CarbonTrade; export type UniswapishTradeOptions = | MMFTradeOptions @@ -164,7 +166,8 @@ export type UniswapishAmount = | PancakeSwapCurrencyAmount | CurrencyAmountMMF | CurrencyAmountVVS - | CurrencyAmountXsswap; + | CurrencyAmountXsswap + | UniswapFraction; export type Fractionish = | UniswapFraction diff --git a/src/services/connection-manager.ts b/src/services/connection-manager.ts index 1f7088fa36..a75833bc58 100644 --- a/src/services/connection-manager.ts +++ b/src/services/connection-manager.ts @@ -42,6 +42,7 @@ import { Plenty } from '../connectors/plenty/plenty'; import { Kujira } from '../chains/kujira/kujira'; import { KujiraCLOB } from '../connectors/kujira/kujira'; import { CarbonCLOB } from '../connectors/carbon/carbon'; +import { CarbonAMM } from '../connectors/carbon/carbonAMM'; export type ChainUnion = | Algorand @@ -224,6 +225,8 @@ export async function getConnector( connectorInstance = KujiraCLOB.getInstance(chain, network); } else if (chain === 'ethereum' && connector === 'carbon') { connectorInstance = CarbonCLOB.getInstance(chain, network); + } else if (chain === 'ethereum' && connector === 'carbonAMM') { + connectorInstance = CarbonAMM.getInstance(chain, network); } else { throw new Error('unsupported chain or connector'); } diff --git a/src/templates/carbon.yml b/src/templates/carbon.yml index 46c22940f9..49bdacd10e 100644 --- a/src/templates/carbon.yml +++ b/src/templates/carbon.yml @@ -1,6 +1,6 @@ # allowedSlippage: how much the execution price is allowed to move unfavorably from the trade # execution price. It uses a rational number for precision. -allowedSlippage: '0.5/100' +allowedSlippage: '1/100' # ttl: how long a trade is valid in seconds. After this time passes carbon will not # perform the trade, but the gas will still be sent. @@ -14,7 +14,8 @@ gasLimitEstimate: 200000 matchType: 'FAST' contractAddresses: - mainnet: - carbonControllerAddress: '0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1' - multiCallAddress: '0x5ba1e12693dc8f9c48aad8770482f4739beed696' - voucherAddress: '0x3660F04B79751e31128f6378eAC70807e38f554E' \ No newline at end of file + ethereum: + mainnet: + carbonControllerAddress: '0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1' + multiCallAddress: '0x5ba1e12693dc8f9c48aad8770482f4739beed696' + voucherAddress: '0x3660F04B79751e31128f6378eAC70807e38f554E' \ No newline at end of file diff --git a/test/connectors/carbon/carbonAMM.test.ts b/test/connectors/carbon/carbonAMM.test.ts new file mode 100644 index 0000000000..f73df87f40 --- /dev/null +++ b/test/connectors/carbon/carbonAMM.test.ts @@ -0,0 +1,544 @@ +import request from 'supertest'; +import { gatewayApp } from '../../../src/app'; +import { patch, unpatch } from '../../../test/services/patch'; +import { Ethereum } from '../../../src/chains/ethereum/ethereum'; +import { EVMTxBroadcaster } from '../../../src/chains/ethereum/evm.broadcaster'; +import { CarbonAMM } from '../../../src/connectors/carbon/carbonAMM'; +import { + EncodedStrategy, + TokenPair, +} from '../../../src/connectors/carbon/carbon-sdk/src'; +import { encodeStrategy } from '../../../src/connectors/carbon/carbon-sdk/src/strategy-management/utils'; +import { logger } from '../../../src/services/logger'; +import { BigNumber } from '../../../src/connectors/carbon/carbon-sdk/src/utils'; +import { encodeStrategyId } from '../../../src/connectors/carbon/carbon.utils'; +import { buildStrategyObject } from '../../../src/connectors/carbon/carbon-sdk/src/strategy-management'; +import { patchEVMNonceManager } from '../../../test/evm.nonce.mock'; +import { Token } from '@uniswap/sdk'; +import Decimal from 'decimal.js-light'; + +let ethereum: Ethereum; +let carbon: CarbonAMM; + +const TX_HASH = + '0xf6f81a37796bd06a797484467302e4d6f72832409545e2e01feb86dd8b22e4b2'; // noqa: mock +const DEFAULT_FEE = 2000; + +const ETH = new Token( + 1, + '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + 18, + 'ETH' +); + +const USDC = new Token( + 1, + '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + 6, + 'USDC' +); + +const DAI = new Token( + 1, + '0x6B175474E89094C44Da98b954EedeAC495271d0F', + 18, + 'DAI' +); + +const INVALID_REQUEST = { + chain: 'unknown', + connector: 'carbon', +}; + +const MARKETS = [ + { + ticker: 'DAI-USDC', + baseToken: { + chainId: 1, + address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + name: 'Dai', + symbol: 'DAI', + decimals: 18, + logoURI: + 'https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734', + }, + quoteToken: { + chainId: 1, + address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + name: 'USD Coin', + symbol: 'USDC', + decimals: 6, + logoURI: + 'https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389', + }, + makerFee: 10, + }, + { + ticker: 'DAI-ETH', + baseToken: { + chainId: 1, + address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + name: 'Dai', + symbol: 'DAI', + decimals: 18, + logoURI: + 'https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734', + }, + quoteToken: { + chainId: 1, + address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + name: 'Ethereum', + symbol: 'ETH', + decimals: 18, + }, + makerFee: 2000, + }, +]; + +const ORDERS = [ + { + id: '729', + pairId: '4', + owner: '0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8', + baseToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + quoteToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + baseDecimals: 6, + quoteDecimals: 18, + buyPriceLow: '0.95', + buyPriceMarginal: '0.98', + buyPriceHigh: '0.98', + buyBudget: '1000', + sellPriceLow: '1.03', + sellPriceMarginal: '1.035', + sellPriceHigh: '1.04', + sellBudget: '1000', + }, + { + id: '730', + pairId: '4', + owner: '0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8', + baseToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + quoteToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + baseDecimals: 6, + quoteDecimals: 18, + buyPriceLow: '0.90', + buyPriceMarginal: '0.95', + buyPriceHigh: '0.95', + buyBudget: '2000', + sellPriceLow: '1.03', + sellPriceMarginal: '1.05', + sellPriceHigh: '1.05', + sellBudget: '2000', + }, + { + id: '731', + pairId: '1', + owner: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', + baseToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + quoteToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + baseDecimals: 18, + quoteDecimals: 18, + buyPriceLow: '1500', + buyPriceMarginal: '1900', + buyPriceHigh: '1900', + buyBudget: '1000', + sellPriceLow: '2200', + sellPriceMarginal: '2400', + sellPriceHigh: '2400', + sellBudget: '1000', + }, +]; + +beforeAll(async () => { + ethereum = Ethereum.getInstance('mainnet'); + patchEVMNonceManager(ethereum.nonceManager); + ethereum.init(); + carbon = CarbonAMM.getInstance('ethereum', 'mainnet'); + patchReader(); + + await carbon.init(); +}); + +beforeEach(() => { + patchReader(); +}); + +afterEach(() => { + unpatch(); +}); + +afterAll(async () => { + await ethereum.close(); +}); + +const buildEncodedStrategy = (order: { + id: string; + pairId: string; + baseToken: string; + quoteToken: string; + baseDecimals: number; + quoteDecimals: number; + buyPriceLow: string; + buyPriceHigh: string; + buyBudget: string; + sellPriceLow: string; + sellPriceHigh: string; + sellBudget: string; +}) => { + const strategyObject = buildStrategyObject( + order.baseToken, + order.quoteToken, + order.baseDecimals, + order.quoteDecimals, + order.buyPriceLow, + order.buyPriceHigh, + order.buyBudget, + order.sellPriceLow, + order.sellPriceHigh, + order.sellBudget + ); + + return { + id: BigNumber.from(encodeStrategyId(order.id, order.pairId)), + ...encodeStrategy(strategyObject), + }; +}; + +const patchReader = () => { + patch(carbon.api.reader, 'tokensByOwner', (owner: string): BigNumber[] => { + const ownerOrders = ORDERS.filter((order) => owner === order.owner); + if (!owner || ownerOrders.length === 0) return []; + return ownerOrders.map((order) => + BigNumber.from(encodeStrategyId(order.id, order.pairId)) + ); + }); + + patch(carbon.api.reader, 'pairs', (): TokenPair[] => { + return MARKETS.map((market) => [ + market.baseToken.address, + market.quoteToken.address, + ]); + }); + + patch( + carbon.api.reader, + 'strategiesByPair', + (token0: string, token1: string): EncodedStrategy[] => { + return ORDERS.filter((order) => { + return ( + (order.baseToken === token0 && order.quoteToken === token1) || + (order.baseToken === token1 && order.quoteToken === token0) + ); + }).map(buildEncodedStrategy); + } + ); + + patch( + carbon.api.reader, + 'strategies', + (ids: BigNumber[]): EncodedStrategy[] => { + return ORDERS.filter((order) => { + return ids.includes( + BigNumber.from(encodeStrategyId(order.id, order.pairId)) + ); + }).map(buildEncodedStrategy); + } + ); + + patch(carbon.api.reader, 'strategy', (id: BigNumber): EncodedStrategy => { + const order = ORDERS.find((order) => { + return encodeStrategyId(order.id, order.pairId) === id.toString(); + }); + if (!order) throw Error('No strategy found'); + + return buildEncodedStrategy(order); + }); + + patch(carbon.api.reader, 'tradingFeePPM', (): number => { + return DEFAULT_FEE; + }); + + patch( + carbon.api.reader, + 'pairsTradingFeePPM', + (pairs: TokenPair[]): [string, string, number][] => { + return pairs.map((pair) => { + const market = MARKETS.filter((market) => 'makerFee' in market).find( + (market) => { + return ( + (market.baseToken.address.toLowerCase() === + pair[0].toLowerCase() && + market.quoteToken.address.toLowerCase() === + pair[1].toLowerCase()) || + (market.baseToken.address.toLowerCase() === + pair[1].toLowerCase() && + market.quoteToken.address.toLowerCase() === + pair[0].toLowerCase()) + ); + } + ); + return [pair[0], pair[1], market?.makerFee || DEFAULT_FEE]; + }); + } + ); + patch( + carbon.api.reader, + 'getLatestStrategyCreatedStrategies', + (fromBlock: number, toBlock: number) => { + logger.info(`${fromBlock} ${toBlock}`); + return []; + } + ); + + patch( + carbon.api.reader, + 'getLatestStrategyDeletedStrategies', + (fromBlock: number, toBlock: number) => { + logger.info(`${fromBlock} ${toBlock}`); + return []; + } + ); + + patch( + carbon.api.reader, + 'getLatestTokensTradedTrades', + (fromBlock: number, toBlock: number) => { + logger.info(`${fromBlock} ${toBlock}`); + return []; + } + ); + + patch( + carbon.api.reader, + 'getLatestTradingFeeUpdates', + (fromBlock: number, toBlock: number) => { + logger.info(`${fromBlock} ${toBlock}`); + return []; + } + ); + patch( + carbon.api.reader, + 'getLatestPairTradingFeeUpdates', + (fromBlock: number, toBlock: number) => { + logger.info(`${fromBlock} ${toBlock}`); + return []; + } + ); +}; + +const patchGetWallet = () => { + patch(ethereum, 'getWallet', () => { + return { + privateKey: + '83d8fae2444141a142079e9aa6dc1a49962af114d9ace8db9a34ecb8fa3e6cf8', // noqa: mock + address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', + }; + }); +}; + +const patchMsgBroadcaster = () => { + patch(EVMTxBroadcaster, 'getInstance', () => { + return { + broadcast() { + return { + hash: TX_HASH, + }; + }, + }; + }); +}; + +describe('verify Carbon estimateSellTrade', () => { + const inputAmount = 0.01; + const inputAmountWei = new Decimal(inputAmount) + .times(new Decimal(10).pow(ETH.decimals)) + .toString(); + + it('Should return an ExpectedTrade when available', async () => { + const expectedTrade = await carbon.estimateSellTrade( + ETH, + DAI, + BigNumber.from(inputAmountWei) + ); + expect(expectedTrade).toHaveProperty('trade'); + expect(expectedTrade).toHaveProperty('expectedAmount'); + }); + + it('Should throw an error if no trade actions are possible', async () => { + await expect(async () => { + await carbon.estimateSellTrade(ETH, USDC, BigNumber.from(1)); + }).rejects.toThrow(Error); + }); +}); + +describe('verify Carbon estimateBuyTrade', () => { + it('Should return an ExpectedTrade when available', async () => { + const inputAmount = 0.01; + const inputAmountWei = new Decimal(inputAmount) + .times(new Decimal(10).pow(ETH.decimals)) + .toString(); + + const expectedTrade = await carbon.estimateBuyTrade( + ETH, + DAI, + BigNumber.from(inputAmountWei) + ); + expect(expectedTrade).toHaveProperty('trade'); + expect(expectedTrade).toHaveProperty('expectedAmount'); + }); + + it('Should throw an error if no trade actions are possible', async () => { + await expect(async () => { + await carbon.estimateSellTrade(ETH, USDC, BigNumber.from(1)); + }).rejects.toThrow(Error); + }); +}); + +describe('getAllowedSlippage', () => { + it('return number value when not null', () => { + const allowedSlippage = carbon.getAllowedSlippage('2/100'); + expect(allowedSlippage).toEqual(0.02); + }); + + it('return value from config when slippage is null', () => { + const allowedSlippage = carbon.getAllowedSlippage(); + expect(allowedSlippage).toEqual(0.01); + }); + + it('return value from config when input is malformed', () => { + const allowedSlippage = carbon.getAllowedSlippage('yo'); + expect(allowedSlippage).toEqual(0.01); + }); +}); + +describe('POST /amm/price SELL', () => { + it('should return 200 with proper request', async () => { + await request(gatewayApp) + .post(`/amm/price`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbonAMM', + base: 'DAI', + quote: 'USDC', + amount: '1', + side: 'SELL', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.base).toEqual(DAI.address)) + .expect((res) => expect(res.body.quote).toEqual(USDC.address)) + .expect((res) => expect(Number(res.body.amount)).toEqual(1)) + .expect((res) => expect(Number(res.body.expectedAmount)).toBeLessThan(1)) + .expect((res) => expect(Number(res.body.price)).toBeLessThan(1)); + }); + + it('should return 404 when parameters are invalid', async () => { + await request(gatewayApp) + .get(`/clob/markets`) + .send(INVALID_REQUEST) + .expect(404); + }); +}); + +describe('POST /amm/price BUY', () => { + it('should return 200 with proper request', async () => { + await request(gatewayApp) + .post(`/amm/price`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbonAMM', + base: 'DAI', + quote: 'USDC', + amount: '1', + side: 'BUY', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.base).toEqual(DAI.address)) + .expect((res) => expect(res.body.quote).toEqual(USDC.address)) + .expect((res) => expect(Number(res.body.amount)).toEqual(1)) + .expect((res) => + expect(Number(res.body.expectedAmount)).toBeGreaterThan(1) + ) + .expect((res) => expect(Number(res.body.price)).toBeGreaterThan(1)); + }); + + it('should return 404 when parameters are invalid', async () => { + await request(gatewayApp) + .get(`/clob/markets`) + .send(INVALID_REQUEST) + .expect(404); + }); +}); + +describe('POST /amm/trade SELL', () => { + it('should return 200 with proper request', async () => { + patchGetWallet(); + patchMsgBroadcaster(); + await request(gatewayApp) + .post(`/amm/trade`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbonAMM', + base: 'DAI', + quote: 'USDC', + amount: '1', + side: 'SELL', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.base).toEqual(DAI.address)) + .expect((res) => expect(res.body.quote).toEqual(USDC.address)) + .expect((res) => expect(Number(res.body.amount)).toEqual(1)) + .expect((res) => expect(Number(res.body.expectedOut)).toBeLessThan(1)) + .expect((res) => expect(Number(res.body.price)).toBeLessThan(1)) + .expect((res) => expect(res.body.txHash).toEqual(TX_HASH)); + }); + + it('should return 404 when parameters are invalid', async () => { + await request(gatewayApp) + .get(`/clob/markets`) + .send(INVALID_REQUEST) + .expect(404); + }); +}); + +describe('POST /amm/trade BUY', () => { + it('should return 200 with proper request', async () => { + patchGetWallet(); + patchMsgBroadcaster(); + await request(gatewayApp) + .post(`/amm/trade`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbonAMM', + base: 'DAI', + quote: 'USDC', + amount: '1', + side: 'BUY', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.base).toEqual(DAI.address)) + .expect((res) => expect(res.body.quote).toEqual(USDC.address)) + .expect((res) => expect(Number(res.body.amount)).toEqual(1)) + .expect((res) => expect(Number(res.body.expectedIn)).toBeGreaterThan(1)) + .expect((res) => expect(Number(res.body.price)).toBeGreaterThan(1)) + .expect((res) => expect(res.body.txHash).toEqual(TX_HASH)); + }); + + it('should return 404 when parameters are invalid', async () => { + await request(gatewayApp) + .get(`/clob/markets`) + .send(INVALID_REQUEST) + .expect(404); + }); +}); From 74503396e5aa21d7e563f4d71f052a62ae256136 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Wed, 6 Dec 2023 18:24:25 +0000 Subject: [PATCH 04/35] Add carbon-sdk --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index f9932a6b73..df66d724bc 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "test:scripts": "jest -i --verbose ./test-scripts/*.test.ts" }, "dependencies": { + "@bancor/carbon-sdk": "^0.0.89-DEV", "@cosmjs/proto-signing": "^0.30.1", "@cosmjs/stargate": "^0.30.1", "@crocswap/sdk": "^2.4.5", From 81cfb4805e1553457b97c9da464829968b0fd301 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Wed, 6 Dec 2023 18:25:10 +0000 Subject: [PATCH 05/35] Update imports to use @bancor/carbon-sdk --- src/connectors/carbon/carbon.config.ts | 2 +- src/connectors/carbon/carbon.ts | 34 ++--- src/connectors/carbon/carbon.utils.ts | 6 + src/connectors/carbon/carbonAMM.ts | 24 ++-- .../carbon/carbon_controller_abi.json | 125 +++++++++++++++++- test/connectors/carbon/carbon.test.ts | 15 ++- test/connectors/carbon/carbonAMM.test.ts | 19 +-- 7 files changed, 180 insertions(+), 45 deletions(-) diff --git a/src/connectors/carbon/carbon.config.ts b/src/connectors/carbon/carbon.config.ts index a7ccc787fc..e482fbcd4e 100644 --- a/src/connectors/carbon/carbon.config.ts +++ b/src/connectors/carbon/carbon.config.ts @@ -1,7 +1,7 @@ import { ContractsConfig } from '@bancor/carbon-sdk/contracts-api'; import { AvailableNetworks } from '../../services/config-manager-types'; import { ConfigManagerV2 } from '../../services/config-manager-v2'; -import { MatchType } from './carbon-sdk/src'; +import { MatchType } from '@bancor/carbon-sdk/'; export namespace CarbonConfig { export interface NetworkConfig { diff --git a/src/connectors/carbon/carbon.ts b/src/connectors/carbon/carbon.ts index 5488f4f90c..206d1dbd4e 100644 --- a/src/connectors/carbon/carbon.ts +++ b/src/connectors/carbon/carbon.ts @@ -1,19 +1,27 @@ -import { Contract, providers, Signer } from 'ethers'; +import { Contract, Signer, providers } from 'ethers'; import Decimal from 'decimal.js-light'; +import { ChainCache, initSyncedCache } from '@bancor/carbon-sdk/chain-cache'; +import { + ContractsApi, + ContractsConfig, +} from '@bancor/carbon-sdk/contracts-api'; +import { Toolkit } from '@bancor/carbon-sdk/strategy-management'; +import { Strategy, TokenPair } from '@bancor/carbon-sdk/'; +import { parseUnits } from '@bancor/carbon-sdk/utils'; import { Ethereum } from '../../chains/ethereum/ethereum'; import { TokenInfo } from '../../chains/ethereum/ethereum-base'; import { EVMTxBroadcaster } from '../../chains/ethereum/evm.broadcaster'; import { + CLOBMarkets, + ClobDeleteOrderRequest, + ClobGetOrderRequest, + ClobGetOrderResponse, ClobMarketsRequest, ClobOrderbookRequest, - ClobTickerRequest, - ClobGetOrderRequest, ClobPostOrderRequest, - ClobDeleteOrderRequest, - CLOBMarkets, - ClobGetOrderResponse, + ClobTickerRequest, } from '../../clob/clob.requests'; import { @@ -26,23 +34,17 @@ import { logger } from '../../services/logger'; import { BalanceRequest } from '../../network/network.requests'; -import { ChainCache, initSyncedCache } from './carbon-sdk/src/chain-cache'; -import { ContractsConfig, ContractsApi } from './carbon-sdk/src/contracts-api'; -import { Toolkit } from './carbon-sdk/src/strategy-management'; -import { isETHAddress } from './carbon-sdk/src/contracts-api/utils'; -import { Strategy, TokenPair } from './carbon-sdk/src'; -import { parseUnits } from './carbon-sdk/src/utils'; - +import { isETHAddress } from './carbon.utils'; import carbonControllerAbi from './carbon_controller_abi.json'; import { CarbonConfig } from './carbon.config'; import { OrderRow, - emptyToken, buildOrders, + decodeStrategyId, + emptyToken, getMiddleRate, getStep, - decodeStrategyId, } from './carbon.utils'; export class CarbonCLOB implements CLOBish { @@ -229,9 +231,11 @@ export class CarbonCLOB implements CLOBish { quoteAddress, req.price, req.price, + req.price, req.amount, '0', '0', + '0', '0' ); diff --git a/src/connectors/carbon/carbon.utils.ts b/src/connectors/carbon/carbon.utils.ts index a0b08de4ed..6339fc4d5a 100644 --- a/src/connectors/carbon/carbon.utils.ts +++ b/src/connectors/carbon/carbon.utils.ts @@ -1,6 +1,8 @@ import Decimal from 'decimal.js-light'; import { TokenInfo } from '../../chains/ethereum/ethereum-base'; +const ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'.toLowerCase(); + export type OrderRow = { rate: string; total: string; @@ -123,3 +125,7 @@ export const encodeStrategyId = ( return '0x' + strategyID.toString(16); }; + +export const isETHAddress = (address: string) => { + return address.toLowerCase() === ETH_ADDRESS; +}; diff --git a/src/connectors/carbon/carbonAMM.ts b/src/connectors/carbon/carbonAMM.ts index 529647e3c9..2ce5194c51 100644 --- a/src/connectors/carbon/carbonAMM.ts +++ b/src/connectors/carbon/carbonAMM.ts @@ -1,13 +1,26 @@ import { BigNumber, - Wallet, ContractInterface, - Transaction, PopulatedTransaction, + Transaction, + Wallet, } from 'ethers'; import { getAddress } from 'ethers/lib/utils'; import { Token } from '@uniswap/sdk'; import { Fraction } from '@uniswap/sdk-core'; +import { ChainCache, initSyncedCache } from '@bancor/carbon-sdk/chain-cache'; +import { + ContractsApi, + ContractsConfig, +} from '@bancor/carbon-sdk/contracts-api'; +import { Toolkit } from '@bancor/carbon-sdk/strategy-management'; +import { + Action, + MatchActionBNStr, + TradeActionBNStr, +} from '@bancor/carbon-sdk/'; + +import { Decimal } from '@bancor/carbon-sdk/utils'; import { Ethereum } from '../../chains/ethereum/ethereum'; import { TokenInfo } from '../../chains/ethereum/ethereum-base'; @@ -18,12 +31,7 @@ import { percentRegexp } from '../../services/config-manager-v2'; import { Uniswapish, UniswapishTrade } from '../../services/common-interfaces'; import { logger } from '../../services/logger'; -import { ChainCache, initSyncedCache } from './carbon-sdk/src/chain-cache'; -import { ContractsConfig, ContractsApi } from './carbon-sdk/src/contracts-api'; -import { Toolkit } from './carbon-sdk/src/strategy-management'; -import { Action, MatchActionBNStr, TradeActionBNStr } from './carbon-sdk/src'; -import { Decimal } from './carbon-sdk/src/utils'; -import carbonControllerAbi from './carbon-sdk/src/abis/CarbonController.json'; +import carbonControllerAbi from './carbon_controller_abi.json'; import { CarbonConfig } from './carbon.config'; diff --git a/src/connectors/carbon/carbon_controller_abi.json b/src/connectors/carbon/carbon_controller_abi.json index 0edd803948..26200544a9 100644 --- a/src/connectors/carbon/carbon_controller_abi.json +++ b/src/connectors/carbon/carbon_controller_abi.json @@ -1,21 +1,115 @@ { - "address": "0xD59BcAbd00721B754aCB79FE668faE4B6A063fF8", + "address": "0xC537e898CD774e2dCBa3B14Ea6f34C93d5eA45e1", "abi": [ { + "anonymous": false, "inputs": [ { - "internalType": "contract IVoucher", - "name": "initVoucher", + "indexed": false, + "internalType": "address", + "name": "previousAdmin", "type": "address" }, { + "indexed": false, + "internalType": "address", + "name": "newAdmin", + "type": "address" + } + ], + "name": "AdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "beacon", + "type": "address" + } + ], + "name": "BeaconUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, "internalType": "address", - "name": "proxy", + "name": "implementation", + "type": "address" + } + ], + "name": "Upgraded", + "type": "event" + }, + { + "stateMutability": "payable", + "type": "fallback" + }, + { + "inputs": [], + "name": "admin", + "outputs": [ + { + "internalType": "address", + "name": "admin_", "type": "address" } ], "stateMutability": "nonpayable", - "type": "constructor" + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [ + { + "internalType": "address", + "name": "implementation_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "upgradeToAndCall", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" }, { "inputs": [], @@ -1641,6 +1735,27 @@ ], "stateMutability": "nonpayable", "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_logic", + "type": "address" + }, + { + "internalType": "address", + "name": "admin_", + "type": "address" + }, + { + "internalType": "bytes", + "name": "_data", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "constructor" } ] } \ No newline at end of file diff --git a/test/connectors/carbon/carbon.test.ts b/test/connectors/carbon/carbon.test.ts index 6e9a32cfe2..3acb200a75 100644 --- a/test/connectors/carbon/carbon.test.ts +++ b/test/connectors/carbon/carbon.test.ts @@ -1,18 +1,17 @@ import request from 'supertest'; +import { EncodedStrategy, TokenPair } from '@bancor/carbon-sdk'; +import { + buildStrategyObject, + encodeStrategy, +} from '@bancor/carbon-sdk/strategy-management'; +import { BigNumber } from '@bancor/carbon-sdk/utils'; import { gatewayApp } from '../../../src/app'; import { patch, unpatch } from '../../../test/services/patch'; import { Ethereum } from '../../../src/chains/ethereum/ethereum'; import { EVMTxBroadcaster } from '../../../src/chains/ethereum/evm.broadcaster'; import { CarbonCLOB } from '../../../src/connectors/carbon/carbon'; -import { - EncodedStrategy, - TokenPair, -} from '../../../src/connectors/carbon/carbon-sdk/src'; -import { encodeStrategy } from '../../../src/connectors/carbon/carbon-sdk/src/strategy-management/utils'; import { logger } from '../../../src/services/logger'; -import { BigNumber } from '../../../src/connectors/carbon/carbon-sdk/src/utils'; import { encodeStrategyId } from '../../../src/connectors/carbon/carbon.utils'; -import { buildStrategyObject } from '../../../src/connectors/carbon/carbon-sdk/src/strategy-management'; import { patchEVMNonceManager } from '../../../test/evm.nonce.mock'; let ethereum: Ethereum; @@ -202,9 +201,11 @@ const buildEncodedStrategy = (order: { order.quoteDecimals, order.buyPriceLow, order.buyPriceHigh, + order.buyPriceHigh, order.buyBudget, order.sellPriceLow, order.sellPriceHigh, + order.sellPriceHigh, order.sellBudget ); diff --git a/test/connectors/carbon/carbonAMM.test.ts b/test/connectors/carbon/carbonAMM.test.ts index f73df87f40..d68d044628 100644 --- a/test/connectors/carbon/carbonAMM.test.ts +++ b/test/connectors/carbon/carbonAMM.test.ts @@ -1,21 +1,20 @@ import request from 'supertest'; +import Decimal from 'decimal.js-light'; +import { Token } from '@uniswap/sdk'; +import { EncodedStrategy, TokenPair } from '@bancor/carbon-sdk'; +import { + buildStrategyObject, + encodeStrategy, +} from '@bancor/carbon-sdk/strategy-management'; +import { BigNumber } from '@bancor/carbon-sdk/utils'; import { gatewayApp } from '../../../src/app'; import { patch, unpatch } from '../../../test/services/patch'; import { Ethereum } from '../../../src/chains/ethereum/ethereum'; import { EVMTxBroadcaster } from '../../../src/chains/ethereum/evm.broadcaster'; import { CarbonAMM } from '../../../src/connectors/carbon/carbonAMM'; -import { - EncodedStrategy, - TokenPair, -} from '../../../src/connectors/carbon/carbon-sdk/src'; -import { encodeStrategy } from '../../../src/connectors/carbon/carbon-sdk/src/strategy-management/utils'; import { logger } from '../../../src/services/logger'; -import { BigNumber } from '../../../src/connectors/carbon/carbon-sdk/src/utils'; import { encodeStrategyId } from '../../../src/connectors/carbon/carbon.utils'; -import { buildStrategyObject } from '../../../src/connectors/carbon/carbon-sdk/src/strategy-management'; import { patchEVMNonceManager } from '../../../test/evm.nonce.mock'; -import { Token } from '@uniswap/sdk'; -import Decimal from 'decimal.js-light'; let ethereum: Ethereum; let carbon: CarbonAMM; @@ -192,9 +191,11 @@ const buildEncodedStrategy = (order: { order.quoteDecimals, order.buyPriceLow, order.buyPriceHigh, + order.buyPriceHigh, order.buyBudget, order.sellPriceLow, order.sellPriceHigh, + order.sellPriceHigh, order.sellBudget ); From 9256a97432ba9fb5efcd80f20c11484a058a54b0 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Thu, 7 Dec 2023 10:03:44 +0000 Subject: [PATCH 06/35] Remove trailling slash --- src/connectors/carbon/carbon.config.ts | 2 +- src/connectors/carbon/carbon.ts | 2 +- src/connectors/carbon/carbonAMM.ts | 6 +----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/connectors/carbon/carbon.config.ts b/src/connectors/carbon/carbon.config.ts index e482fbcd4e..2905ba4e77 100644 --- a/src/connectors/carbon/carbon.config.ts +++ b/src/connectors/carbon/carbon.config.ts @@ -1,7 +1,7 @@ import { ContractsConfig } from '@bancor/carbon-sdk/contracts-api'; +import { MatchType } from '@bancor/carbon-sdk'; import { AvailableNetworks } from '../../services/config-manager-types'; import { ConfigManagerV2 } from '../../services/config-manager-v2'; -import { MatchType } from '@bancor/carbon-sdk/'; export namespace CarbonConfig { export interface NetworkConfig { diff --git a/src/connectors/carbon/carbon.ts b/src/connectors/carbon/carbon.ts index 206d1dbd4e..691f47f713 100644 --- a/src/connectors/carbon/carbon.ts +++ b/src/connectors/carbon/carbon.ts @@ -6,7 +6,7 @@ import { ContractsConfig, } from '@bancor/carbon-sdk/contracts-api'; import { Toolkit } from '@bancor/carbon-sdk/strategy-management'; -import { Strategy, TokenPair } from '@bancor/carbon-sdk/'; +import { Strategy, TokenPair } from '@bancor/carbon-sdk'; import { parseUnits } from '@bancor/carbon-sdk/utils'; import { Ethereum } from '../../chains/ethereum/ethereum'; diff --git a/src/connectors/carbon/carbonAMM.ts b/src/connectors/carbon/carbonAMM.ts index 2ce5194c51..3def9442e7 100644 --- a/src/connectors/carbon/carbonAMM.ts +++ b/src/connectors/carbon/carbonAMM.ts @@ -14,11 +14,7 @@ import { ContractsConfig, } from '@bancor/carbon-sdk/contracts-api'; import { Toolkit } from '@bancor/carbon-sdk/strategy-management'; -import { - Action, - MatchActionBNStr, - TradeActionBNStr, -} from '@bancor/carbon-sdk/'; +import { Action, MatchActionBNStr, TradeActionBNStr } from '@bancor/carbon-sdk'; import { Decimal } from '@bancor/carbon-sdk/utils'; From 474eb521f7f36226fbe7c2c1ae7b594f79f19161 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Thu, 7 Dec 2023 10:20:12 +0000 Subject: [PATCH 07/35] Fix carbon schema --- src/services/schema/carbon-schema.json | 31 +++++++++++++------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/services/schema/carbon-schema.json b/src/services/schema/carbon-schema.json index f74326d833..688e2c9bd8 100644 --- a/src/services/schema/carbon-schema.json +++ b/src/services/schema/carbon-schema.json @@ -20,24 +20,25 @@ "contractAddresses": { "type": "object", "patternProperties": { - "^\\w+$": { + "^[\\w-]+$": { "type": "object", - "properties": { - "carbonControllerAddress": { - "type": "string" - }, - "multiCallAddress": { - "type": "string" - }, - "voucherAddress": { - "type": "string" + "patternProperties": { + "^\\w+$": { + "type": "object", + "properties": { + "carbonControllerAddress": { + "type": "string" + }, + "multiCallAddress": { + "type": "string" + }, + "voucherAddress": { + "type": "string" + } + }, + "additionalProperties": false } }, - "required": [ - "carbonControllerAddress", - "multiCallAddress", - "voucherAddress" - ], "additionalProperties": false } }, From 158719dd8e404337def59bdbbd7c1a0bdaf9078a Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Thu, 7 Dec 2023 16:18:24 +0000 Subject: [PATCH 08/35] Update tests against new SDK version --- test/connectors/carbon/carbon.test.ts | 10 ++++++---- test/connectors/carbon/carbonAMM.test.ts | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/test/connectors/carbon/carbon.test.ts b/test/connectors/carbon/carbon.test.ts index 3acb200a75..45f702d3d7 100644 --- a/test/connectors/carbon/carbon.test.ts +++ b/test/connectors/carbon/carbon.test.ts @@ -99,7 +99,7 @@ const ORDERS = [ buyPriceHigh: '0.95', buyBudget: '2000', sellPriceLow: '1.03', - sellPriceMarginal: '1.05', + sellPriceMarginal: '1.03', sellPriceHigh: '1.05', sellBudget: '2000', }, @@ -116,7 +116,7 @@ const ORDERS = [ buyPriceHigh: '1900', buyBudget: '1000', sellPriceLow: '2200', - sellPriceMarginal: '2400', + sellPriceMarginal: '2300', sellPriceHigh: '2400', sellBudget: '1000', }, @@ -188,9 +188,11 @@ const buildEncodedStrategy = (order: { baseDecimals: number; quoteDecimals: number; buyPriceLow: string; + buyPriceMarginal: string; buyPriceHigh: string; buyBudget: string; sellPriceLow: string; + sellPriceMarginal: string; sellPriceHigh: string; sellBudget: string; }) => { @@ -200,11 +202,11 @@ const buildEncodedStrategy = (order: { order.baseDecimals, order.quoteDecimals, order.buyPriceLow, - order.buyPriceHigh, + order.buyPriceMarginal, order.buyPriceHigh, order.buyBudget, order.sellPriceLow, - order.sellPriceHigh, + order.sellPriceMarginal, order.sellPriceHigh, order.sellBudget ); diff --git a/test/connectors/carbon/carbonAMM.test.ts b/test/connectors/carbon/carbonAMM.test.ts index d68d044628..502e5e94b2 100644 --- a/test/connectors/carbon/carbonAMM.test.ts +++ b/test/connectors/carbon/carbonAMM.test.ts @@ -125,7 +125,7 @@ const ORDERS = [ buyPriceHigh: '0.95', buyBudget: '2000', sellPriceLow: '1.03', - sellPriceMarginal: '1.05', + sellPriceMarginal: '1.03', sellPriceHigh: '1.05', sellBudget: '2000', }, @@ -142,7 +142,7 @@ const ORDERS = [ buyPriceHigh: '1900', buyBudget: '1000', sellPriceLow: '2200', - sellPriceMarginal: '2400', + sellPriceMarginal: '2300', sellPriceHigh: '2400', sellBudget: '1000', }, @@ -178,9 +178,11 @@ const buildEncodedStrategy = (order: { baseDecimals: number; quoteDecimals: number; buyPriceLow: string; + buyPriceMarginal: string; buyPriceHigh: string; buyBudget: string; sellPriceLow: string; + sellPriceMarginal: string; sellPriceHigh: string; sellBudget: string; }) => { @@ -190,11 +192,11 @@ const buildEncodedStrategy = (order: { order.baseDecimals, order.quoteDecimals, order.buyPriceLow, - order.buyPriceHigh, + order.buyPriceMarginal, order.buyPriceHigh, order.buyBudget, order.sellPriceLow, - order.sellPriceHigh, + order.sellPriceMarginal, order.sellPriceHigh, order.sellBudget ); From 6ff1a0ac93f2d000f6dbd21e9eaf9eb05d815187 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Fri, 8 Dec 2023 18:40:56 +0000 Subject: [PATCH 09/35] Improve test coverage and fix bugs --- src/connectors/carbon/carbon.ts | 30 +- src/connectors/carbon/carbonAMM.ts | 41 +-- test/connectors/carbon/carbon.test.ts | 426 ++++++++++++++++++++++- test/connectors/carbon/carbonAMM.test.ts | 127 ++++++- 4 files changed, 559 insertions(+), 65 deletions(-) diff --git a/src/connectors/carbon/carbon.ts b/src/connectors/carbon/carbon.ts index 691f47f713..fb0e83b3b7 100644 --- a/src/connectors/carbon/carbon.ts +++ b/src/connectors/carbon/carbon.ts @@ -1,4 +1,3 @@ -import { Contract, Signer, providers } from 'ethers'; import Decimal from 'decimal.js-light'; import { ChainCache, initSyncedCache } from '@bancor/carbon-sdk/chain-cache'; import { @@ -35,7 +34,6 @@ import { logger } from '../../services/logger'; import { BalanceRequest } from '../../network/network.requests'; import { isETHAddress } from './carbon.utils'; -import carbonControllerAbi from './carbon_controller_abi.json'; import { CarbonConfig } from './carbon.config'; import { @@ -67,7 +65,7 @@ export class CarbonCLOB implements CLOBish { } this._nativeToken = - this._chain.chainName === 'ethereum' + chain === 'ethereum' ? { chainId: 1, address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', @@ -104,19 +102,6 @@ export class CarbonCLOB implements CLOBish { return CarbonCLOB._instances[key]; } - - // getters - - /** - * Carbon Controller ABI - */ - getContract( - contractAddress: string, - signer: providers.StaticJsonRpcProvider | Signer - ) { - return new Contract(contractAddress, carbonControllerAbi.abi, signer); - } - public async loadMarkets() { const contractPairs = await this.api.reader.pairs(); @@ -447,8 +432,8 @@ export class CarbonCLOB implements CLOBish { : 0 ); - const minSellNormalized = ONE.div(maxSell); - const maxSellNormalized = ONE.div(minSell); + const minSellNormalized = sellHasLiq ? ONE.div(maxSell) : new Decimal(0); + const maxSellNormalized = sellHasLiq ? ONE.div(minSell) : new Decimal(0); const deltaBuy = maxBuy.minus(minBuy); const deltaSell = maxSellNormalized.minus(minSellNormalized); @@ -554,7 +539,7 @@ export class CarbonCLOB implements CLOBish { } private _findTokenByAddress(address: string): TokenInfo { - if (this._isNativeAddress(address)) { + if (isETHAddress(address)) { return this._nativeToken; } else { return ( @@ -564,11 +549,4 @@ export class CarbonCLOB implements CLOBish { ); } } - - private _isNativeAddress(address: string) { - if (this._chain.chainName === 'ethereum') { - return isETHAddress(address); - } - return false; - } } diff --git a/src/connectors/carbon/carbonAMM.ts b/src/connectors/carbon/carbonAMM.ts index 3def9442e7..34f662961b 100644 --- a/src/connectors/carbon/carbonAMM.ts +++ b/src/connectors/carbon/carbonAMM.ts @@ -19,7 +19,6 @@ import { Action, MatchActionBNStr, TradeActionBNStr } from '@bancor/carbon-sdk'; import { Decimal } from '@bancor/carbon-sdk/utils'; import { Ethereum } from '../../chains/ethereum/ethereum'; -import { TokenInfo } from '../../chains/ethereum/ethereum-base'; import { EVMTxBroadcaster } from '../../chains/ethereum/evm.broadcaster'; import { isFractionString } from '../../services/validators'; @@ -31,8 +30,6 @@ import carbonControllerAbi from './carbon_controller_abi.json'; import { CarbonConfig } from './carbon.config'; -import { emptyToken } from './carbon.utils'; - type TradeData = { tradeActions: TradeActionBNStr[]; actionsTokenRes: Action[]; @@ -64,8 +61,8 @@ export class CarbonAMM implements Uniswapish { private _ready: boolean = false; private _conf: CarbonConfig.NetworkConfig; private _gasLimitEstimate: number; + private _allowedSlippage: string; private _ttl: number; - private _nativeToken: TokenInfo; private constructor(chain: string, network: string) { if (chain === 'ethereum') { @@ -74,18 +71,8 @@ export class CarbonAMM implements Uniswapish { throw new Error('Unsupported chain'); } - this._nativeToken = - this._chain.chainName === 'ethereum' - ? { - chainId: 1, - address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - name: 'Ethereum', - symbol: 'ETH', - decimals: 18, - } - : emptyToken; - this._conf = CarbonConfig.config; + this._allowedSlippage = this._conf.allowedSlippage; this.carbonContractConfig = this._conf.carbonContractsConfig( chain, network @@ -135,17 +122,7 @@ export class CarbonAMM implements Uniswapish { * @param address Token address */ public getTokenByAddress(address: string): Token { - if (address === this._nativeToken.address) { - return new Token( - this._nativeToken.chainId, - this._nativeToken.address, - this._nativeToken.decimals, - this._nativeToken.symbol, - this._nativeToken.name - ); - } else { - return this.tokenList[getAddress(address)]; - } + return this.tokenList[getAddress(address)]; } /** @@ -206,7 +183,8 @@ export class CarbonAMM implements Uniswapish { return Number(fractionSplit[0]) / Number(fractionSplit[1]); } - const allowedSlippage = this._conf.allowedSlippage; + const allowedSlippage = this._allowedSlippage; + const matches = allowedSlippage.match(percentRegexp); if (matches) return Number(matches[1]) / Number(matches[2]); throw new Error( @@ -361,8 +339,6 @@ export class CarbonAMM implements Uniswapish { maxPriorityFeePerGas?: BigNumber, allowedSlippage?: string ): Promise { - if (!wallet.address) throw Error('No wallet address specified.'); - const carbonTrade = trade; let overrideParams: { @@ -391,12 +367,7 @@ export class CarbonAMM implements Uniswapish { } const slippage = this.getAllowedSlippage(allowedSlippage); - let deadlineMs: number; - if (ttl) { - deadlineMs = Math.floor(new Date().getTime()) + this._ttl * 1000; - } else { - deadlineMs = this.ttl * 1000; - } + const deadlineMs = ttl * 1000; // in ms let tradeTransaction: PopulatedTransaction; if (carbonTrade.tradeByTarget) { diff --git a/test/connectors/carbon/carbon.test.ts b/test/connectors/carbon/carbon.test.ts index 45f702d3d7..53b1478af5 100644 --- a/test/connectors/carbon/carbon.test.ts +++ b/test/connectors/carbon/carbon.test.ts @@ -20,6 +20,11 @@ let carbon: CarbonCLOB; const TX_HASH = '0xf6f81a37796bd06a797484467302e4d6f72832409545e2e01feb86dd8b22e4b2'; // noqa: mock const MARKET = 'DAI-USDC'; +const MARKET_BUY_SIDE = 'ETH-USDC'; +const MARKET_SELL_SIDE = 'ETH-USDT'; +const MARKET_EMPTY = 'USDC-USDT'; +const MARKET_BUY_LIMIT_ORDER = 'DAI-USDT'; +const MARKET_SELL_LIMIT_ORDER = 'WBTC-USDT'; const DEFAULT_FEE = 2000; const NUM_ORDERBOOK_BUCKETS = 14; @@ -66,6 +71,104 @@ const MARKETS = [ }, makerFee: 2000, }, + { + ticker: 'USDC-ETH', + baseToken: { + chainId: 1, + address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + name: 'USD Coin', + symbol: 'USDC', + decimals: 6, + logoURI: + 'https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389', + }, + quoteToken: { + chainId: 1, + address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + name: 'Ethereum', + symbol: 'ETH', + decimals: 18, + }, + makerFee: 2000, + }, + { + ticker: 'USDT-ETH', + baseToken: { + chainId: 1, + address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + name: 'USD Token', + symbol: 'USDT', + decimals: 6, + }, + quoteToken: { + chainId: 1, + address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + name: 'Ethereum', + symbol: 'ETH', + decimals: 18, + }, + makerFee: 2000, + }, + { + ticker: 'USDC-USDT', + baseToken: { + chainId: 1, + address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + name: 'USD Coin', + symbol: 'USDC', + decimals: 6, + logoURI: + 'https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389', + }, + quoteToken: { + chainId: 1, + address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + name: 'USD Token', + symbol: 'USDT', + decimals: 6, + }, + makerFee: 2000, + }, + { + ticker: 'DAI-USDT', + baseToken: { + chainId: 1, + address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + name: 'Dai', + symbol: 'DAI', + decimals: 18, + logoURI: + 'https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734', + }, + quoteToken: { + chainId: 1, + address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + name: 'USD Token', + symbol: 'USDT', + decimals: 6, + }, + makerFee: 10, + }, + { + ticker: 'WBTC-USDT', + baseToken: { + chainId: 1, + address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', + name: 'Wrapped Bitcoin', + symbol: 'WBTC', + decimals: 8, + logoURI: + 'https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389', + }, + quoteToken: { + chainId: 1, + address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + name: 'USD Token', + symbol: 'USDT', + decimals: 6, + }, + makerFee: 2000, + }, ]; const ORDERS = [ @@ -120,6 +223,91 @@ const ORDERS = [ sellPriceHigh: '2400', sellBudget: '1000', }, + { + id: '732', + pairId: '1', + owner: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', + baseToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + quoteToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + baseDecimals: 18, + quoteDecimals: 6, + buyPriceLow: '1500', + buyPriceMarginal: '1900', + buyPriceHigh: '1900', + buyBudget: '1000', + sellPriceLow: '2200', + sellPriceMarginal: '2300', + sellPriceHigh: '2400', + sellBudget: '0', + }, + { + id: '733', + pairId: '1', + owner: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', + baseToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + quoteToken: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + baseDecimals: 18, + quoteDecimals: 6, + buyPriceLow: '1500', + buyPriceMarginal: '1900', + buyPriceHigh: '1900', + buyBudget: '0', + sellPriceLow: '2200', + sellPriceMarginal: '2300', + sellPriceHigh: '2400', + sellBudget: '1000', + }, + { + id: '734', + pairId: '1', + owner: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', + baseToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + quoteToken: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + baseDecimals: 18, + quoteDecimals: 6, + buyPriceLow: '0.90', + buyPriceMarginal: '0.95', + buyPriceHigh: '0.95', + buyBudget: '0', + sellPriceLow: '1.03', + sellPriceMarginal: '1.03', + sellPriceHigh: '1.05', + sellBudget: '0', + }, + { + id: '735', + pairId: '1', + owner: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', + baseToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', + quoteToken: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + baseDecimals: 18, + quoteDecimals: 6, + buyPriceLow: '0.95', + buyPriceMarginal: '0.95', + buyPriceHigh: '0.95', + buyBudget: '100', + sellPriceLow: '1.03', + sellPriceMarginal: '1.03', + sellPriceHigh: '1.03', + sellBudget: '0', + }, + { + id: '736', + pairId: '1', + owner: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', + baseToken: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', + quoteToken: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + baseDecimals: 8, + quoteDecimals: 6, + buyPriceLow: '0.00002', + buyPriceMarginal: '0.00002', + buyPriceHigh: '0.00002', + buyBudget: '0', + sellPriceLow: '0.000025', + sellPriceMarginal: '0.000025', + sellPriceHigh: '0.000025', + sellBudget: '1000', + }, ]; const GAS_PRICES = { @@ -156,6 +344,20 @@ const TOKENS = [ address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', decimals: 18, }, + { + chainId: 1, + name: 'USD Token', + symbol: 'USDT', + address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + decimals: 6, + }, + { + chainId: 1, + name: 'Wrapped Bitcoin', + symbol: 'WBTC', + address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', + decimals: 8, + }, ]; beforeAll(async () => { @@ -376,7 +578,7 @@ const patchMsgBroadcaster = () => { }; describe('GET /clob/markets', () => { - it('should return 200 with proper request', async () => { + it('should return 200 with proper request for all markets', async () => { await request(gatewayApp) .get(`/clob/markets`) .query({ @@ -388,7 +590,41 @@ describe('GET /clob/markets', () => { .expect('Content-Type', /json/) .expect(200) .expect((res) => { - expect(res.body.markets.length).toEqual(2); + expect(res.body.markets.length).toEqual(MARKETS.length); + }); + }); + + it('should return 200 with proper request for a specific market', async () => { + await request(gatewayApp) + .get(`/clob/markets`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + market: MARKET, + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => { + expect(res.body.markets.makerFee).toBeGreaterThan(0); + }); + }); + + it('should return 200 with proper request for a reversed order market', async () => { + await request(gatewayApp) + .get(`/clob/markets`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + market: 'USDC-DAI', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => { + expect(res.body.markets.makerFee).toBeGreaterThan(0); }); }); @@ -425,6 +661,90 @@ describe('GET /clob/orderBook', () => { ); }); + it('should return 200 with one-sided orderbook on BUY side', async () => { + await request(gatewayApp) + .get(`/clob/orderBook`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + market: MARKET_BUY_SIDE, + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => + expect(res.body.buys.length).toEqual(NUM_ORDERBOOK_BUCKETS) + ) + .expect((res) => expect(res.body.sells.length).toEqual(0)); + }); + + it('should return 200 with one-sided orderbook on SELL side', async () => { + await request(gatewayApp) + .get(`/clob/orderBook`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + market: MARKET_SELL_SIDE, + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.buys.length).toEqual(0)) + .expect((res) => + expect(res.body.sells.length).toEqual(NUM_ORDERBOOK_BUCKETS) + ); + }); + + it('should return 200 with one limit-order orderbook on BUY side', async () => { + await request(gatewayApp) + .get(`/clob/orderBook`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + market: MARKET_BUY_LIMIT_ORDER, + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.buys.length).toEqual(1)) + .expect((res) => expect(res.body.sells.length).toEqual(0)); + }); + + it('should return 200 with one limit-order orderbook on SELL side', async () => { + await request(gatewayApp) + .get(`/clob/orderBook`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + market: MARKET_SELL_LIMIT_ORDER, + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.buys.length).toEqual(0)) + .expect((res) => expect(res.body.sells.length).toEqual(1)); + }); + + it('should return 200 with empty orderbook', async () => { + await request(gatewayApp) + .get(`/clob/orderBook`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + market: MARKET_EMPTY, + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.buys.length).toEqual(0)) + .expect((res) => expect(res.body.sells.length).toEqual(0)); + }); + it('should return 404 when parameters are invalid', async () => { await request(gatewayApp) .get(`/clob/orderBook`) @@ -479,6 +799,22 @@ describe('GET /clob/orders', () => { .expect((res) => expect(res.body.orders.length).toEqual(1)); }); + it('should return 200 and empty list for user with no strategies', async () => { + await request(gatewayApp) + .get(`/clob/orders`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + orderId: '731', + address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70e', // noqa: mock + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.orders.length).toEqual(0)); + }); + it('should return 404 when parameters are invalid', async () => { await request(gatewayApp) .get(`/clob/orders`) @@ -488,7 +824,7 @@ describe('GET /clob/orders', () => { }); describe('POST /clob/orders', () => { - it('should return 200 with proper request', async () => { + it('should return 200 with proper BUY request', async () => { patchGetWallet(); patchGetTokenBySymbol(); patchMsgBroadcaster(); @@ -511,6 +847,52 @@ describe('POST /clob/orders', () => { .expect((res) => expect(res.body.txHash).toBeTruthy()); }); + it('should return 200 with proper SELL request', async () => { + patchGetWallet(); + patchGetTokenBySymbol(); + patchMsgBroadcaster(); + await request(gatewayApp) + .post(`/clob/orders`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', // noqa: mock + market: MARKET, + price: '10000.12', + amount: '0.12', + side: 'SELL', + orderType: 'LIMIT_MAKER', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.txHash).toBeTruthy()); + }); + + it('should return 200 and not execute transaction if orderType is not LIMIT_MAKER', async () => { + patchGetWallet(); + patchGetTokenBySymbol(); + patchMsgBroadcaster(); + await request(gatewayApp) + .post(`/clob/orders`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', // noqa: mock + market: MARKET, + price: '10000.12', + amount: '0.12', + side: 'SELL', + orderType: 'LIMIT', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.txHash).toBeFalsy()); + }); + it('should return 404 when parameters are invalid', async () => { await request(gatewayApp) .post(`/clob/orders`) @@ -570,3 +952,41 @@ describe('GET /clob/estimateGas', () => { .expect(404); }); }); + +describe('verify Carbon constructor', () => { + it('Should return an Error with an unsupported chain', () => { + expect(async () => { + CarbonCLOB.getInstance('avalanche', 'avalanche'); + }).rejects.toThrow(Error); + }); +}); + +describe('Requests to the connector', () => { + it('should return 503 with unsupported chain', async () => { + await request(gatewayApp) + .get(`/clob/markets`) + .query({ + chain: 'avalanche', + network: 'mainnet', + connector: 'carbon', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(503); + }); + + it('should return 503 when getting orders but no address provided', async () => { + await request(gatewayApp) + .get(`/clob/orders`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + orderId: '731', + // address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f' + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(503); + }); +}); diff --git a/test/connectors/carbon/carbonAMM.test.ts b/test/connectors/carbon/carbonAMM.test.ts index 502e5e94b2..0a7e2f048f 100644 --- a/test/connectors/carbon/carbonAMM.test.ts +++ b/test/connectors/carbon/carbonAMM.test.ts @@ -15,6 +15,7 @@ import { CarbonAMM } from '../../../src/connectors/carbon/carbonAMM'; import { logger } from '../../../src/services/logger'; import { encodeStrategyId } from '../../../src/connectors/carbon/carbon.utils'; import { patchEVMNonceManager } from '../../../test/evm.nonce.mock'; +import { Avalanche } from '../../../src/chains/avalanche/avalanche'; let ethereum: Ethereum; let carbon: CarbonAMM; @@ -92,6 +93,26 @@ const MARKETS = [ }, makerFee: 2000, }, + { + ticker: 'USDC-ETH', + baseToken: { + chainId: 1, + address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + name: 'USD Coin', + symbol: 'USDC', + decimals: 6, + logoURI: + 'https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389', + }, + quoteToken: { + chainId: 1, + address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + name: 'Ethereum', + symbol: 'ETH', + decimals: 18, + }, + makerFee: 2000, + }, ]; const ORDERS = [ @@ -207,6 +228,10 @@ const buildEncodedStrategy = (order: { }; }; +const patchAllowedSlippage = (allowedSlippage: string) => { + patch(carbon, '_allowedSlippage', allowedSlippage); +}; + const patchReader = () => { patch(carbon.api.reader, 'tokensByOwner', (owner: string): BigNumber[] => { const ownerOrders = ORDERS.filter((order) => owner === order.owner); @@ -351,6 +376,14 @@ const patchMsgBroadcaster = () => { }); }; +describe('verify Carbon constructor', () => { + it('Should return an Error with an unsupported chain', () => { + expect(async () => { + CarbonAMM.getInstance('avalanche', 'avalanche'); + }).rejects.toThrow(Error); + }); +}); + describe('verify Carbon estimateSellTrade', () => { const inputAmount = 0.01; const inputAmountWei = new Decimal(inputAmount) @@ -392,7 +425,7 @@ describe('verify Carbon estimateBuyTrade', () => { it('Should throw an error if no trade actions are possible', async () => { await expect(async () => { - await carbon.estimateSellTrade(ETH, USDC, BigNumber.from(1)); + await carbon.estimateBuyTrade(ETH, USDC, BigNumber.from(1)); }).rejects.toThrow(Error); }); }); @@ -538,6 +571,32 @@ describe('POST /amm/trade BUY', () => { .expect((res) => expect(res.body.txHash).toEqual(TX_HASH)); }); + it('should return 200 with proper request and maxFeePerGas set', async () => { + patchGetWallet(); + patchMsgBroadcaster(); + await request(gatewayApp) + .post(`/amm/trade`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbonAMM', + base: 'DAI', + quote: 'USDC', + amount: '1', + side: 'BUY', + maxFeePerGas: '5000000000', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.base).toEqual(DAI.address)) + .expect((res) => expect(res.body.quote).toEqual(USDC.address)) + .expect((res) => expect(Number(res.body.amount)).toEqual(1)) + .expect((res) => expect(Number(res.body.expectedIn)).toBeGreaterThan(1)) + .expect((res) => expect(Number(res.body.price)).toBeGreaterThan(1)) + .expect((res) => expect(res.body.txHash).toEqual(TX_HASH)); + }); + it('should return 404 when parameters are invalid', async () => { await request(gatewayApp) .get(`/clob/markets`) @@ -545,3 +604,69 @@ describe('POST /amm/trade BUY', () => { .expect(404); }); }); + +describe('Requests to the connector', () => { + it('should return 503 with unsupported chain', async () => { + patchGetWallet(); + patchMsgBroadcaster(); + const avalanche = Avalanche.getInstance('avalanche'); + patchEVMNonceManager(avalanche.nonceManager); + avalanche.init(); + await request(gatewayApp) + .post(`/amm/price`) + .send({ + chain: 'avalanche', + network: 'avalanche', + connector: 'carbonAMM', + base: 'DAI', + quote: 'USDC', + amount: '1', + side: 'BUY', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(503); + await avalanche.close(); + }); + + it('should return 404 if there is a malformed percent slippage in the request', async () => { + patchGetWallet(); + patchMsgBroadcaster(); + await request(gatewayApp) + .post(`/amm/trade`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbonAMM', + base: 'DAI', + quote: 'USDC', + amount: '1', + side: 'SELL', + allowedSlippage: '0.04', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(404); + }); + + it('should return 503 if there is a malformed percent slippage configured', async () => { + patchGetWallet(); + patchMsgBroadcaster(); + patchAllowedSlippage('0.5'); + + await request(gatewayApp) + .post(`/amm/trade`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbonAMM', + base: 'DAI', + quote: 'USDC', + amount: '1', + side: 'SELL', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(503); + }); +}); From 3135bcc3a989a6c213a982c456e553e17e96bd8d Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Thu, 14 Dec 2023 11:16:28 +0000 Subject: [PATCH 10/35] Rename carbonAMM and fix carbonCLOB balance --- src/chains/ethereum/ethereum.ts | 2 +- src/chains/ethereum/ethereum.validators.ts | 2 +- src/connectors/carbon/carbon.config.ts | 2 +- src/connectors/carbon/carbon.ts | 10 +++++++--- src/connectors/carbon/carbonAMM.ts | 16 ++++++++-------- src/connectors/connectors.routes.ts | 2 +- src/services/connection-manager.ts | 6 +++--- test/connectors/carbon/carbonAMM.test.ts | 8 ++++---- 8 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/chains/ethereum/ethereum.ts b/src/chains/ethereum/ethereum.ts index 7d982cc5db..5ce4f25dd4 100644 --- a/src/chains/ethereum/ethereum.ts +++ b/src/chains/ethereum/ethereum.ts @@ -188,7 +188,7 @@ export class Ethereum extends EthereumBase implements Ethereumish { ); } else if (reqSpender === 'uniswapLP') { spender = UniswapConfig.config.uniswapV3NftManagerAddress(this._chain); - } else if (reqSpender === 'carbon' || reqSpender === 'carbonAMM') { + } else if (reqSpender === 'carbon' || reqSpender === 'carbonamm') { spender = CarbonConfig.config.carbonContractsConfig( 'ethereum', this._chain diff --git a/src/chains/ethereum/ethereum.validators.ts b/src/chains/ethereum/ethereum.validators.ts index 26e464d800..913085d0ce 100644 --- a/src/chains/ethereum/ethereum.validators.ts +++ b/src/chains/ethereum/ethereum.validators.ts @@ -62,7 +62,7 @@ export const validateSpender: Validator = mkValidator( val === 'pancakeswap' || val === 'xsswap' || val === 'carbon' || - val === 'carbonAMM' || + val === 'carbonamm' || isAddress(val)) ); diff --git a/src/connectors/carbon/carbon.config.ts b/src/connectors/carbon/carbon.config.ts index 2905ba4e77..e8332657f0 100644 --- a/src/connectors/carbon/carbon.config.ts +++ b/src/connectors/carbon/carbon.config.ts @@ -35,7 +35,7 @@ export namespace CarbonConfig { { chain: 'ethereum', networks: Object.keys( - ConfigManagerV2.getInstance().get('carbon.contractAddresses') + ConfigManagerV2.getInstance().get('carbon.contractAddresses.ethereum') ).filter((network) => Object.keys( ConfigManagerV2.getInstance().get('ethereum.networks') diff --git a/src/connectors/carbon/carbon.ts b/src/connectors/carbon/carbon.ts index fb0e83b3b7..baf2b6567d 100644 --- a/src/connectors/carbon/carbon.ts +++ b/src/connectors/carbon/carbon.ts @@ -276,7 +276,7 @@ export class CarbonCLOB implements CLOBish { const userStrategies = await this.carbonSDK.getUserStrategies(req.address); - const formattedBalances: Record = { budget: {} }; + const formattedBalances: Record = { available: {}, total: {} }; await Promise.all( tokens.map((token) => { @@ -293,10 +293,14 @@ export class CarbonCLOB implements CLOBish { .add(new Decimal(userStrategySellBudget?.sellBudget || 0)) .toString(); - formattedBalances.budget[token.symbol] = parseUnits( + formattedBalances.available[token.symbol] = parseUnits( userTokenBalance, token.decimals - ); + ).toString(); + formattedBalances.total[token.symbol] = parseUnits( + userTokenBalance, + token.decimals + ).toString(); }) ); diff --git a/src/connectors/carbon/carbonAMM.ts b/src/connectors/carbon/carbonAMM.ts index 34f662961b..8b215adfbd 100644 --- a/src/connectors/carbon/carbonAMM.ts +++ b/src/connectors/carbon/carbonAMM.ts @@ -48,8 +48,8 @@ export interface CarbonTrade { tradeByTarget: boolean; } -export class CarbonAMM implements Uniswapish { - private static _instances: { [name: string]: CarbonAMM }; +export class Carbonamm implements Uniswapish { + private static _instances: { [name: string]: Carbonamm }; public carbonContractConfig: Required; public carbonSDK: Toolkit; public sdkCache: ChainCache; @@ -92,15 +92,15 @@ export class CarbonAMM implements Uniswapish { this.routerAbi = carbonControllerAbi; } - public static getInstance(chain: string, network: string): CarbonAMM { - if (CarbonAMM._instances === undefined) { - CarbonAMM._instances = {}; + public static getInstance(chain: string, network: string): Carbonamm { + if (Carbonamm._instances === undefined) { + Carbonamm._instances = {}; } - if (!(chain + network in CarbonAMM._instances)) { - CarbonAMM._instances[chain + network] = new CarbonAMM(chain, network); + if (!(chain + network in Carbonamm._instances)) { + Carbonamm._instances[chain + network] = new Carbonamm(chain, network); } - return CarbonAMM._instances[chain + network]; + return Carbonamm._instances[chain + network]; } public async loadTokens() { diff --git a/src/connectors/connectors.routes.ts b/src/connectors/connectors.routes.ts index 21b439c24e..085d162a87 100644 --- a/src/connectors/connectors.routes.ts +++ b/src/connectors/connectors.routes.ts @@ -170,7 +170,7 @@ export namespace ConnectorsRoutes { available_networks: CarbonConfig.config.availableNetworks, }, { - name: 'carbonAMM', + name: 'carbonamm', trading_type: CarbonConfig.config.tradingTypes('swap'), chain_type: CarbonConfig.config.chainType, available_networks: CarbonConfig.config.availableNetworks, diff --git a/src/services/connection-manager.ts b/src/services/connection-manager.ts index a75833bc58..6c7e3f80c5 100644 --- a/src/services/connection-manager.ts +++ b/src/services/connection-manager.ts @@ -42,7 +42,7 @@ import { Plenty } from '../connectors/plenty/plenty'; import { Kujira } from '../chains/kujira/kujira'; import { KujiraCLOB } from '../connectors/kujira/kujira'; import { CarbonCLOB } from '../connectors/carbon/carbon'; -import { CarbonAMM } from '../connectors/carbon/carbonAMM'; +import { Carbonamm } from '../connectors/carbon/carbonAMM'; export type ChainUnion = | Algorand @@ -225,8 +225,8 @@ export async function getConnector( connectorInstance = KujiraCLOB.getInstance(chain, network); } else if (chain === 'ethereum' && connector === 'carbon') { connectorInstance = CarbonCLOB.getInstance(chain, network); - } else if (chain === 'ethereum' && connector === 'carbonAMM') { - connectorInstance = CarbonAMM.getInstance(chain, network); + } else if (chain === 'ethereum' && connector === 'carbonamm') { + connectorInstance = Carbonamm.getInstance(chain, network); } else { throw new Error('unsupported chain or connector'); } diff --git a/test/connectors/carbon/carbonAMM.test.ts b/test/connectors/carbon/carbonAMM.test.ts index 0a7e2f048f..a9104f1c77 100644 --- a/test/connectors/carbon/carbonAMM.test.ts +++ b/test/connectors/carbon/carbonAMM.test.ts @@ -11,14 +11,14 @@ import { gatewayApp } from '../../../src/app'; import { patch, unpatch } from '../../../test/services/patch'; import { Ethereum } from '../../../src/chains/ethereum/ethereum'; import { EVMTxBroadcaster } from '../../../src/chains/ethereum/evm.broadcaster'; -import { CarbonAMM } from '../../../src/connectors/carbon/carbonAMM'; +import { Carbonamm } from '../../../src/connectors/carbon/carbonAMM'; import { logger } from '../../../src/services/logger'; import { encodeStrategyId } from '../../../src/connectors/carbon/carbon.utils'; import { patchEVMNonceManager } from '../../../test/evm.nonce.mock'; import { Avalanche } from '../../../src/chains/avalanche/avalanche'; let ethereum: Ethereum; -let carbon: CarbonAMM; +let carbon: Carbonamm; const TX_HASH = '0xf6f81a37796bd06a797484467302e4d6f72832409545e2e01feb86dd8b22e4b2'; // noqa: mock @@ -173,7 +173,7 @@ beforeAll(async () => { ethereum = Ethereum.getInstance('mainnet'); patchEVMNonceManager(ethereum.nonceManager); ethereum.init(); - carbon = CarbonAMM.getInstance('ethereum', 'mainnet'); + carbon = Carbonamm.getInstance('ethereum', 'mainnet'); patchReader(); await carbon.init(); @@ -379,7 +379,7 @@ const patchMsgBroadcaster = () => { describe('verify Carbon constructor', () => { it('Should return an Error with an unsupported chain', () => { expect(async () => { - CarbonAMM.getInstance('avalanche', 'avalanche'); + Carbonamm.getInstance('avalanche', 'avalanche'); }).rejects.toThrow(Error); }); }); From 651f201d4cb48952e09020901a6f4aea3a227918 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Mon, 18 Dec 2023 10:29:25 +0000 Subject: [PATCH 11/35] Update Carbon SDK --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index df66d724bc..9de73e497a 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "test:scripts": "jest -i --verbose ./test-scripts/*.test.ts" }, "dependencies": { - "@bancor/carbon-sdk": "^0.0.89-DEV", + "@bancor/carbon-sdk": "^0.0.93-DEV", "@cosmjs/proto-signing": "^0.30.1", "@cosmjs/stargate": "^0.30.1", "@crocswap/sdk": "^2.4.5", From 9bf62fcf840cd79c58a876f2c8436c219748e8aa Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Mon, 18 Dec 2023 16:40:27 +0000 Subject: [PATCH 12/35] Update yarn.lock --- yarn.lock | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/yarn.lock b/yarn.lock index b8bd43eef5..c26d12c709 100644 --- a/yarn.lock +++ b/yarn.lock @@ -517,6 +517,20 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" +"@bancor/carbon-sdk@^0.0.93-DEV": + version "0.0.93-DEV" + resolved "https://registry.yarnpkg.com/@bancor/carbon-sdk/-/carbon-sdk-0.0.93-DEV.tgz#2071f1dc03c25c3897fd4cd9133f9345abdeb2db" + integrity sha512-qGu/twxhkYRsKKgrRt+2P9LOtJb5qjthR04QaYyDE2/+DFWVuzmvctXOAESFaUVrpHctZab0vB019KWS13upow== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/contracts" "^5.7.0" + "@ethersproject/providers" "^5.7.2" + "@ethersproject/units" "^5.7.0" + decimal.js "^10.4.3" + ethers "^5.7.2" + events "^3.3.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" From 76d183b58990b496fb987456fb5ac6494a814068 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 19 Dec 2023 16:16:14 +0000 Subject: [PATCH 13/35] Change name in Carbon AMM test --- test/connectors/carbon/carbonAMM.test.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/connectors/carbon/carbonAMM.test.ts b/test/connectors/carbon/carbonAMM.test.ts index a9104f1c77..d6407ba7f2 100644 --- a/test/connectors/carbon/carbonAMM.test.ts +++ b/test/connectors/carbon/carbonAMM.test.ts @@ -454,7 +454,7 @@ describe('POST /amm/price SELL', () => { .send({ chain: 'ethereum', network: 'mainnet', - connector: 'carbonAMM', + connector: 'carbonamm', base: 'DAI', quote: 'USDC', amount: '1', @@ -485,7 +485,7 @@ describe('POST /amm/price BUY', () => { .send({ chain: 'ethereum', network: 'mainnet', - connector: 'carbonAMM', + connector: 'carbonamm', base: 'DAI', quote: 'USDC', amount: '1', @@ -520,7 +520,7 @@ describe('POST /amm/trade SELL', () => { .send({ chain: 'ethereum', network: 'mainnet', - connector: 'carbonAMM', + connector: 'carbonamm', base: 'DAI', quote: 'USDC', amount: '1', @@ -554,7 +554,7 @@ describe('POST /amm/trade BUY', () => { .send({ chain: 'ethereum', network: 'mainnet', - connector: 'carbonAMM', + connector: 'carbonamm', base: 'DAI', quote: 'USDC', amount: '1', @@ -579,7 +579,7 @@ describe('POST /amm/trade BUY', () => { .send({ chain: 'ethereum', network: 'mainnet', - connector: 'carbonAMM', + connector: 'carbonamm', base: 'DAI', quote: 'USDC', amount: '1', @@ -617,7 +617,7 @@ describe('Requests to the connector', () => { .send({ chain: 'avalanche', network: 'avalanche', - connector: 'carbonAMM', + connector: 'carbonamm', base: 'DAI', quote: 'USDC', amount: '1', @@ -637,7 +637,7 @@ describe('Requests to the connector', () => { .send({ chain: 'ethereum', network: 'mainnet', - connector: 'carbonAMM', + connector: 'carbonamm', base: 'DAI', quote: 'USDC', amount: '1', @@ -659,7 +659,7 @@ describe('Requests to the connector', () => { .send({ chain: 'ethereum', network: 'mainnet', - connector: 'carbonAMM', + connector: 'carbonamm', base: 'DAI', quote: 'USDC', amount: '1', From d1c5a25325a667f951753ef50e18067cf1b43b19 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 19 Dec 2023 16:21:01 +0000 Subject: [PATCH 14/35] Add carbon-sdk import paths to jest config (jestjs/jest#12270) --- jest.config.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/jest.config.js b/jest.config.js index dbd4b77a3a..5c7ce4a695 100644 --- a/jest.config.js +++ b/jest.config.js @@ -36,6 +36,15 @@ module.exports = { globalTeardown: '/test/teardown.ts', moduleNameMapper: { eccrypto: '/test/mock/eccrypto-mock.js', + // Add carbon sdk subpath imports that are unsupported until jest v29.4.0 + '@bancor/carbon-sdk/strategy-management': + '/node_modules/@bancor/carbon-sdk/dist/strategy-management/index.cjs', + '@bancor/carbon-sdk/utils': + '/node_modules/@bancor/carbon-sdk/dist/utils/index.cjs', + '@bancor/carbon-sdk/contracts-api': + '/node_modules/@bancor/carbon-sdk/dist/contracts-api/index.cjs', + '@bancor/carbon-sdk/chain-cache': + '/node_modules/@bancor/carbon-sdk/dist/chain-cache/index.cjs', }, testPathIgnorePatterns: ['/node_modules/', 'test-helpers'], }; From 4633fe070843101343b52ec002ac3b4fa08493d2 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 19 Dec 2023 17:34:46 +0000 Subject: [PATCH 15/35] Clean-up Carbon CLOB tests --- test/connectors/carbon/carbon.test.ts | 74 ++++++--------------------- 1 file changed, 15 insertions(+), 59 deletions(-) diff --git a/test/connectors/carbon/carbon.test.ts b/test/connectors/carbon/carbon.test.ts index 53b1478af5..bbcf3b1f3c 100644 --- a/test/connectors/carbon/carbon.test.ts +++ b/test/connectors/carbon/carbon.test.ts @@ -10,7 +10,6 @@ import { patch, unpatch } from '../../../test/services/patch'; import { Ethereum } from '../../../src/chains/ethereum/ethereum'; import { EVMTxBroadcaster } from '../../../src/chains/ethereum/evm.broadcaster'; import { CarbonCLOB } from '../../../src/connectors/carbon/carbon'; -import { logger } from '../../../src/services/logger'; import { encodeStrategyId } from '../../../src/connectors/carbon/carbon.utils'; import { patchEVMNonceManager } from '../../../test/evm.nonce.mock'; @@ -496,49 +495,6 @@ const patchReader = () => { }); } ); - patch( - carbon.api.reader, - 'getLatestStrategyCreatedStrategies', - (fromBlock: number, toBlock: number) => { - logger.info(`${fromBlock} ${toBlock}`); - return []; - } - ); - - patch( - carbon.api.reader, - 'getLatestStrategyDeletedStrategies', - (fromBlock: number, toBlock: number) => { - logger.info(`${fromBlock} ${toBlock}`); - return []; - } - ); - - patch( - carbon.api.reader, - 'getLatestTokensTradedTrades', - (fromBlock: number, toBlock: number) => { - logger.info(`${fromBlock} ${toBlock}`); - return []; - } - ); - - patch( - carbon.api.reader, - 'getLatestTradingFeeUpdates', - (fromBlock: number, toBlock: number) => { - logger.info(`${fromBlock} ${toBlock}`); - return []; - } - ); - patch( - carbon.api.reader, - 'getLatestPairTradingFeeUpdates', - (fromBlock: number, toBlock: number) => { - logger.info(`${fromBlock} ${toBlock}`); - return []; - } - ); }; const patchGasPrices = () => { @@ -821,6 +777,21 @@ describe('GET /clob/orders', () => { .query(INVALID_REQUEST) .expect(404); }); + + it('should return 503 when getting orders but no address provided', async () => { + await request(gatewayApp) + .get(`/clob/orders`) + .query({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + orderId: '731', + // address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f' + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(503); + }); }); describe('POST /clob/orders', () => { @@ -974,19 +945,4 @@ describe('Requests to the connector', () => { .expect('Content-Type', /json/) .expect(503); }); - - it('should return 503 when getting orders but no address provided', async () => { - await request(gatewayApp) - .get(`/clob/orders`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - orderId: '731', - // address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f' - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(503); - }); }); From 083d467d87a2ca8d5f388342f4c8e4e06e560e54 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 19 Dec 2023 17:35:03 +0000 Subject: [PATCH 16/35] Clean-up Carbon AMM tests --- test/connectors/carbon/carbonAMM.test.ts | 44 ------------------------ 1 file changed, 44 deletions(-) diff --git a/test/connectors/carbon/carbonAMM.test.ts b/test/connectors/carbon/carbonAMM.test.ts index d6407ba7f2..20e4419edf 100644 --- a/test/connectors/carbon/carbonAMM.test.ts +++ b/test/connectors/carbon/carbonAMM.test.ts @@ -12,7 +12,6 @@ import { patch, unpatch } from '../../../test/services/patch'; import { Ethereum } from '../../../src/chains/ethereum/ethereum'; import { EVMTxBroadcaster } from '../../../src/chains/ethereum/evm.broadcaster'; import { Carbonamm } from '../../../src/connectors/carbon/carbonAMM'; -import { logger } from '../../../src/services/logger'; import { encodeStrategyId } from '../../../src/connectors/carbon/carbon.utils'; import { patchEVMNonceManager } from '../../../test/evm.nonce.mock'; import { Avalanche } from '../../../src/chains/avalanche/avalanche'; @@ -309,49 +308,6 @@ const patchReader = () => { }); } ); - patch( - carbon.api.reader, - 'getLatestStrategyCreatedStrategies', - (fromBlock: number, toBlock: number) => { - logger.info(`${fromBlock} ${toBlock}`); - return []; - } - ); - - patch( - carbon.api.reader, - 'getLatestStrategyDeletedStrategies', - (fromBlock: number, toBlock: number) => { - logger.info(`${fromBlock} ${toBlock}`); - return []; - } - ); - - patch( - carbon.api.reader, - 'getLatestTokensTradedTrades', - (fromBlock: number, toBlock: number) => { - logger.info(`${fromBlock} ${toBlock}`); - return []; - } - ); - - patch( - carbon.api.reader, - 'getLatestTradingFeeUpdates', - (fromBlock: number, toBlock: number) => { - logger.info(`${fromBlock} ${toBlock}`); - return []; - } - ); - patch( - carbon.api.reader, - 'getLatestPairTradingFeeUpdates', - (fromBlock: number, toBlock: number) => { - logger.info(`${fromBlock} ${toBlock}`); - return []; - } - ); }; const patchGetWallet = () => { From 7ee79ae47dc7599d612c0c2af5a3098b3aac9afd Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Wed, 20 Dec 2023 13:15:51 +0000 Subject: [PATCH 17/35] Clean-up constructors --- src/connectors/carbon/carbon.ts | 7 ++----- src/connectors/carbon/carbonAMM.ts | 20 +++++++++----------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/connectors/carbon/carbon.ts b/src/connectors/carbon/carbon.ts index baf2b6567d..09da4b5298 100644 --- a/src/connectors/carbon/carbon.ts +++ b/src/connectors/carbon/carbon.ts @@ -48,8 +48,8 @@ import { export class CarbonCLOB implements CLOBish { public parsedMarkets: MarketInfo = []; public carbonContractConfig: Required; - public carbonSDK: Toolkit; - public sdkCache: ChainCache; + public carbonSDK!: Toolkit; // will be initialized in init() + public sdkCache!: ChainCache; // will be initialized in init() public api: ContractsApi; private static _instances: { [name: string]: CarbonCLOB }; private _chain: Ethereum; @@ -85,9 +85,6 @@ export class CarbonCLOB implements CLOBish { this._chain.provider, this.carbonContractConfig ); - - this.sdkCache = new ChainCache(); - this.carbonSDK = new Toolkit(this.api, this.sdkCache); } public static getInstance(chain: string, network: string): CarbonCLOB { diff --git a/src/connectors/carbon/carbonAMM.ts b/src/connectors/carbon/carbonAMM.ts index 8b215adfbd..5d1d3947db 100644 --- a/src/connectors/carbon/carbonAMM.ts +++ b/src/connectors/carbon/carbonAMM.ts @@ -51,8 +51,8 @@ export interface CarbonTrade { export class Carbonamm implements Uniswapish { private static _instances: { [name: string]: Carbonamm }; public carbonContractConfig: Required; - public carbonSDK: Toolkit; - public sdkCache: ChainCache; + public carbonSDK!: Toolkit; // will be initialized in init() + public sdkCache!: ChainCache; // will be initialized in init() public api: ContractsApi; private tokenList: Record = {}; public router: any; @@ -83,8 +83,6 @@ export class Carbonamm implements Uniswapish { this.carbonContractConfig ); - this.sdkCache = new ChainCache(); - this.carbonSDK = new Toolkit(this.api, this.sdkCache); this._gasLimitEstimate = this._conf.gasLimitEstimate; this._ttl = this._conf.ttl; @@ -132,6 +130,13 @@ export class Carbonamm implements Uniswapish { return Math.floor(new Date().getTime() / 1000) + this._ttl; } + /** + * Default gas limit for swap transactions. + */ + public get gasLimitEstimate(): number { + return this._gasLimitEstimate; + } + public async init() { if (!this._chain.ready()) { await this._chain.init(); @@ -164,13 +169,6 @@ export class Carbonamm implements Uniswapish { return this._ready; } - /** - * Default gas limit for swap transactions. - */ - public get gasLimitEstimate(): number { - return this._gasLimitEstimate; - } - /** * Gets the allowed slippage percent from the optional parameter or the value * in the configuration. From 5ae72942124626122b7c54fd92bca450f8a966e8 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Fri, 22 Dec 2023 18:35:35 +0000 Subject: [PATCH 18/35] Update balances to remove decimals and count from multiple user strategies --- src/connectors/carbon/carbon.ts | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/src/connectors/carbon/carbon.ts b/src/connectors/carbon/carbon.ts index 09da4b5298..cd633c5be3 100644 --- a/src/connectors/carbon/carbon.ts +++ b/src/connectors/carbon/carbon.ts @@ -277,17 +277,36 @@ export class CarbonCLOB implements CLOBish { await Promise.all( tokens.map((token) => { - const userStrategyBuyBudget = userStrategies.find( + const userStrategiesBuyBudgetRaw = userStrategies.filter( (strategy) => strategy.baseToken === token.address ); - const userStrategySellBudget = userStrategies.find( + const userStrategyBuyBudget = userStrategiesBuyBudgetRaw.reduce( + (acc, strategy) => { + return acc.add( + new Decimal(strategy?.sellBudget || 0).div( + new Decimal(10).pow(token.decimals) + ) + ); + }, + new Decimal(0) + ); + + const userStrategiesSellBudgetRaw = userStrategies.filter( (strategy) => strategy.quoteToken === token.address ); + const userStrategySellBudget = userStrategiesSellBudgetRaw.reduce( + (acc, strategy) => { + return acc.add( + new Decimal(strategy?.buyBudget || 0).div( + new Decimal(10).pow(token.decimals) + ) + ); + }, + new Decimal(0) + ); - const userTokenBalance = new Decimal( - userStrategyBuyBudget?.buyBudget || 0 - ) - .add(new Decimal(userStrategySellBudget?.sellBudget || 0)) + const userTokenBalance = userStrategyBuyBudget + .add(userStrategySellBudget) .toString(); formattedBalances.available[token.symbol] = parseUnits( From a2ffd6efb558ec39baac9a80d8b3c70f424e38bc Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Fri, 22 Dec 2023 18:35:43 +0000 Subject: [PATCH 19/35] Add test to balances endpoint --- test/connectors/carbon/carbon.test.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/connectors/carbon/carbon.test.ts b/test/connectors/carbon/carbon.test.ts index bbcf3b1f3c..8f22f839d0 100644 --- a/test/connectors/carbon/carbon.test.ts +++ b/test/connectors/carbon/carbon.test.ts @@ -924,6 +924,30 @@ describe('GET /clob/estimateGas', () => { }); }); +describe('POST /chain/balances', () => { + it('should return 200 with proper request', async () => { + patchGetWallet(); + patchGetTokenBySymbol(); + patchMsgBroadcaster(); + await request(gatewayApp) + .post(`/chain/balances`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbon', + address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', // noqa: mock + tokenSymbols: ['ETH', 'DAI', 'USDC', 'USDT'], + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.balances.total.ETH).toEqual('2000')) + .expect((res) => expect(res.body.balances.total.DAI).toEqual('1000')) + .expect((res) => expect(res.body.balances.total.USDC).toEqual('1000')) + .expect((res) => expect(res.body.balances.total.USDT).toEqual('100')); + }); +}); + describe('verify Carbon constructor', () => { it('Should return an Error with an unsupported chain', () => { expect(async () => { From 2b96eee8d75d155873b3f3b45d25e97e724826ad Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 9 Jan 2024 02:07:34 +0000 Subject: [PATCH 20/35] Move tests to test-bronze folder --- {test => test-bronze}/connectors/carbon/carbon.test.ts | 0 {test => test-bronze}/connectors/carbon/carbonAMM.test.ts | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {test => test-bronze}/connectors/carbon/carbon.test.ts (100%) rename {test => test-bronze}/connectors/carbon/carbonAMM.test.ts (100%) diff --git a/test/connectors/carbon/carbon.test.ts b/test-bronze/connectors/carbon/carbon.test.ts similarity index 100% rename from test/connectors/carbon/carbon.test.ts rename to test-bronze/connectors/carbon/carbon.test.ts diff --git a/test/connectors/carbon/carbonAMM.test.ts b/test-bronze/connectors/carbon/carbonAMM.test.ts similarity index 100% rename from test/connectors/carbon/carbonAMM.test.ts rename to test-bronze/connectors/carbon/carbonAMM.test.ts From c4cc494bf77b0a55c0649c52e87459b62db272ef Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 9 Jan 2024 18:01:43 +0000 Subject: [PATCH 21/35] Add ETH tests --- test-bronze/connectors/carbon/carbon.test.ts | 972 ------------------ .../connectors/carbon/carbonAMM.test.ts | 48 + 2 files changed, 48 insertions(+), 972 deletions(-) delete mode 100644 test-bronze/connectors/carbon/carbon.test.ts rename {test-bronze => test}/connectors/carbon/carbonAMM.test.ts (91%) diff --git a/test-bronze/connectors/carbon/carbon.test.ts b/test-bronze/connectors/carbon/carbon.test.ts deleted file mode 100644 index 8f22f839d0..0000000000 --- a/test-bronze/connectors/carbon/carbon.test.ts +++ /dev/null @@ -1,972 +0,0 @@ -import request from 'supertest'; -import { EncodedStrategy, TokenPair } from '@bancor/carbon-sdk'; -import { - buildStrategyObject, - encodeStrategy, -} from '@bancor/carbon-sdk/strategy-management'; -import { BigNumber } from '@bancor/carbon-sdk/utils'; -import { gatewayApp } from '../../../src/app'; -import { patch, unpatch } from '../../../test/services/patch'; -import { Ethereum } from '../../../src/chains/ethereum/ethereum'; -import { EVMTxBroadcaster } from '../../../src/chains/ethereum/evm.broadcaster'; -import { CarbonCLOB } from '../../../src/connectors/carbon/carbon'; -import { encodeStrategyId } from '../../../src/connectors/carbon/carbon.utils'; -import { patchEVMNonceManager } from '../../../test/evm.nonce.mock'; - -let ethereum: Ethereum; -let carbon: CarbonCLOB; - -const TX_HASH = - '0xf6f81a37796bd06a797484467302e4d6f72832409545e2e01feb86dd8b22e4b2'; // noqa: mock -const MARKET = 'DAI-USDC'; -const MARKET_BUY_SIDE = 'ETH-USDC'; -const MARKET_SELL_SIDE = 'ETH-USDT'; -const MARKET_EMPTY = 'USDC-USDT'; -const MARKET_BUY_LIMIT_ORDER = 'DAI-USDT'; -const MARKET_SELL_LIMIT_ORDER = 'WBTC-USDT'; -const DEFAULT_FEE = 2000; -const NUM_ORDERBOOK_BUCKETS = 14; - -const MARKETS = [ - { - ticker: 'DAI-USDC', - baseToken: { - chainId: 1, - address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', - name: 'Dai', - symbol: 'DAI', - decimals: 18, - logoURI: - 'https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734', - }, - quoteToken: { - chainId: 1, - address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - name: 'USD Coin', - symbol: 'USDC', - decimals: 6, - logoURI: - 'https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389', - }, - makerFee: 10, - }, - { - ticker: 'DAI-ETH', - baseToken: { - chainId: 1, - address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', - name: 'Dai', - symbol: 'DAI', - decimals: 18, - logoURI: - 'https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734', - }, - quoteToken: { - chainId: 1, - address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - name: 'Ethereum', - symbol: 'ETH', - decimals: 18, - }, - makerFee: 2000, - }, - { - ticker: 'USDC-ETH', - baseToken: { - chainId: 1, - address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - name: 'USD Coin', - symbol: 'USDC', - decimals: 6, - logoURI: - 'https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389', - }, - quoteToken: { - chainId: 1, - address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - name: 'Ethereum', - symbol: 'ETH', - decimals: 18, - }, - makerFee: 2000, - }, - { - ticker: 'USDT-ETH', - baseToken: { - chainId: 1, - address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', - name: 'USD Token', - symbol: 'USDT', - decimals: 6, - }, - quoteToken: { - chainId: 1, - address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - name: 'Ethereum', - symbol: 'ETH', - decimals: 18, - }, - makerFee: 2000, - }, - { - ticker: 'USDC-USDT', - baseToken: { - chainId: 1, - address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - name: 'USD Coin', - symbol: 'USDC', - decimals: 6, - logoURI: - 'https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389', - }, - quoteToken: { - chainId: 1, - address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', - name: 'USD Token', - symbol: 'USDT', - decimals: 6, - }, - makerFee: 2000, - }, - { - ticker: 'DAI-USDT', - baseToken: { - chainId: 1, - address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', - name: 'Dai', - symbol: 'DAI', - decimals: 18, - logoURI: - 'https://assets.coingecko.com/coins/images/9956/thumb/4943.png?1636636734', - }, - quoteToken: { - chainId: 1, - address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', - name: 'USD Token', - symbol: 'USDT', - decimals: 6, - }, - makerFee: 10, - }, - { - ticker: 'WBTC-USDT', - baseToken: { - chainId: 1, - address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', - name: 'Wrapped Bitcoin', - symbol: 'WBTC', - decimals: 8, - logoURI: - 'https://assets.coingecko.com/coins/images/6319/thumb/USD_Coin_icon.png?1547042389', - }, - quoteToken: { - chainId: 1, - address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', - name: 'USD Token', - symbol: 'USDT', - decimals: 6, - }, - makerFee: 2000, - }, -]; - -const ORDERS = [ - { - id: '729', - pairId: '4', - owner: '0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8', - baseToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - quoteToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', - baseDecimals: 6, - quoteDecimals: 18, - buyPriceLow: '0.95', - buyPriceMarginal: '0.98', - buyPriceHigh: '0.98', - buyBudget: '1000', - sellPriceLow: '1.03', - sellPriceMarginal: '1.035', - sellPriceHigh: '1.04', - sellBudget: '1000', - }, - { - id: '730', - pairId: '4', - owner: '0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8', - baseToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - quoteToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', - baseDecimals: 6, - quoteDecimals: 18, - buyPriceLow: '0.90', - buyPriceMarginal: '0.95', - buyPriceHigh: '0.95', - buyBudget: '2000', - sellPriceLow: '1.03', - sellPriceMarginal: '1.03', - sellPriceHigh: '1.05', - sellBudget: '2000', - }, - { - id: '731', - pairId: '1', - owner: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', - baseToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - quoteToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', - baseDecimals: 18, - quoteDecimals: 18, - buyPriceLow: '1500', - buyPriceMarginal: '1900', - buyPriceHigh: '1900', - buyBudget: '1000', - sellPriceLow: '2200', - sellPriceMarginal: '2300', - sellPriceHigh: '2400', - sellBudget: '1000', - }, - { - id: '732', - pairId: '1', - owner: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', - baseToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - quoteToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - baseDecimals: 18, - quoteDecimals: 6, - buyPriceLow: '1500', - buyPriceMarginal: '1900', - buyPriceHigh: '1900', - buyBudget: '1000', - sellPriceLow: '2200', - sellPriceMarginal: '2300', - sellPriceHigh: '2400', - sellBudget: '0', - }, - { - id: '733', - pairId: '1', - owner: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', - baseToken: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - quoteToken: '0xdAC17F958D2ee523a2206206994597C13D831ec7', - baseDecimals: 18, - quoteDecimals: 6, - buyPriceLow: '1500', - buyPriceMarginal: '1900', - buyPriceHigh: '1900', - buyBudget: '0', - sellPriceLow: '2200', - sellPriceMarginal: '2300', - sellPriceHigh: '2400', - sellBudget: '1000', - }, - { - id: '734', - pairId: '1', - owner: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', - baseToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - quoteToken: '0xdAC17F958D2ee523a2206206994597C13D831ec7', - baseDecimals: 18, - quoteDecimals: 6, - buyPriceLow: '0.90', - buyPriceMarginal: '0.95', - buyPriceHigh: '0.95', - buyBudget: '0', - sellPriceLow: '1.03', - sellPriceMarginal: '1.03', - sellPriceHigh: '1.05', - sellBudget: '0', - }, - { - id: '735', - pairId: '1', - owner: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', - baseToken: '0x6B175474E89094C44Da98b954EedeAC495271d0F', - quoteToken: '0xdAC17F958D2ee523a2206206994597C13D831ec7', - baseDecimals: 18, - quoteDecimals: 6, - buyPriceLow: '0.95', - buyPriceMarginal: '0.95', - buyPriceHigh: '0.95', - buyBudget: '100', - sellPriceLow: '1.03', - sellPriceMarginal: '1.03', - sellPriceHigh: '1.03', - sellBudget: '0', - }, - { - id: '736', - pairId: '1', - owner: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', - baseToken: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', - quoteToken: '0xdAC17F958D2ee523a2206206994597C13D831ec7', - baseDecimals: 8, - quoteDecimals: 6, - buyPriceLow: '0.00002', - buyPriceMarginal: '0.00002', - buyPriceHigh: '0.00002', - buyBudget: '0', - sellPriceLow: '0.000025', - sellPriceMarginal: '0.000025', - sellPriceHigh: '0.000025', - sellBudget: '1000', - }, -]; - -const GAS_PRICES = { - gasPrice: '500000000', - gasPriceToken: 'ETH', - gasLimit: '1000', - gasCost: '100', -}; - -const INVALID_REQUEST = { - chain: 'unknown', - connector: 'carbon', -}; - -const TOKENS = [ - { - chainId: 1, - name: 'USD Coin', - symbol: 'USDC', - address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', - decimals: 6, - }, - { - chainId: 1, - name: 'DAI', - symbol: 'DAI', - address: '0x6B175474E89094C44Da98b954EedeAC495271d0F', - decimals: 18, - }, - { - chainId: 1, - name: 'ETH', - symbol: 'Ethereum', - address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - decimals: 18, - }, - { - chainId: 1, - name: 'USD Token', - symbol: 'USDT', - address: '0xdAC17F958D2ee523a2206206994597C13D831ec7', - decimals: 6, - }, - { - chainId: 1, - name: 'Wrapped Bitcoin', - symbol: 'WBTC', - address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', - decimals: 8, - }, -]; - -beforeAll(async () => { - ethereum = Ethereum.getInstance('mainnet'); - patchEVMNonceManager(ethereum.nonceManager); - ethereum.init(); - carbon = CarbonCLOB.getInstance('ethereum', 'mainnet'); - patchReader(); - - await carbon.init(); -}); - -beforeEach(() => { - patchReader(); -}); - -afterEach(() => { - unpatch(); -}); - -afterAll(async () => { - await ethereum.close(); -}); - -const buildEncodedStrategy = (order: { - id: string; - pairId: string; - baseToken: string; - quoteToken: string; - baseDecimals: number; - quoteDecimals: number; - buyPriceLow: string; - buyPriceMarginal: string; - buyPriceHigh: string; - buyBudget: string; - sellPriceLow: string; - sellPriceMarginal: string; - sellPriceHigh: string; - sellBudget: string; -}) => { - const strategyObject = buildStrategyObject( - order.baseToken, - order.quoteToken, - order.baseDecimals, - order.quoteDecimals, - order.buyPriceLow, - order.buyPriceMarginal, - order.buyPriceHigh, - order.buyBudget, - order.sellPriceLow, - order.sellPriceMarginal, - order.sellPriceHigh, - order.sellBudget - ); - - return { - id: BigNumber.from(encodeStrategyId(order.id, order.pairId)), - ...encodeStrategy(strategyObject), - }; -}; - -const patchReader = () => { - patch(carbon.api.reader, 'tokensByOwner', (owner: string): BigNumber[] => { - const ownerOrders = ORDERS.filter((order) => owner === order.owner); - if (!owner || ownerOrders.length === 0) return []; - return ownerOrders.map((order) => - BigNumber.from(encodeStrategyId(order.id, order.pairId)) - ); - }); - - patch(carbon.api.reader, 'pairs', (): TokenPair[] => { - return MARKETS.map((market) => [ - market.baseToken.address, - market.quoteToken.address, - ]); - }); - - patch( - carbon.api.reader, - 'strategiesByPair', - (token0: string, token1: string): EncodedStrategy[] => { - return ORDERS.filter((order) => { - return ( - (order.baseToken === token0 && order.quoteToken === token1) || - (order.baseToken === token1 && order.quoteToken === token0) - ); - }).map(buildEncodedStrategy); - } - ); - - patch( - carbon.api.reader, - 'strategies', - (ids: BigNumber[]): EncodedStrategy[] => { - return ORDERS.filter((order) => { - return ids.includes( - BigNumber.from(encodeStrategyId(order.id, order.pairId)) - ); - }).map(buildEncodedStrategy); - } - ); - - patch(carbon.api.reader, 'strategy', (id: BigNumber): EncodedStrategy => { - const order = ORDERS.find((order) => { - return encodeStrategyId(order.id, order.pairId) === id.toString(); - }); - if (!order) throw Error('No strategy found'); - - return buildEncodedStrategy(order); - }); - - patch(carbon.api.reader, 'tradingFeePPM', (): number => { - return DEFAULT_FEE; - }); - - patch( - carbon.api.reader, - 'pairsTradingFeePPM', - (pairs: TokenPair[]): [string, string, number][] => { - return pairs.map((pair) => { - const market = MARKETS.filter((market) => 'makerFee' in market).find( - (market) => { - return ( - (market.baseToken.address.toLowerCase() === - pair[0].toLowerCase() && - market.quoteToken.address.toLowerCase() === - pair[1].toLowerCase()) || - (market.baseToken.address.toLowerCase() === - pair[1].toLowerCase() && - market.quoteToken.address.toLowerCase() === - pair[0].toLowerCase()) - ); - } - ); - return [pair[0], pair[1], market?.makerFee || DEFAULT_FEE]; - }); - } - ); -}; - -const patchGasPrices = () => { - patch(carbon, 'estimateGas', () => { - return GAS_PRICES; - }); -}; - -const patchGetWallet = () => { - patch(ethereum, 'getWallet', () => { - return { - privateKey: - '83d8fae2444141a142079e9aa6dc1a49962af114d9ace8db9a34ecb8fa3e6cf8', // noqa: mock - address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', - }; - }); -}; - -const patchGetTokenBySymbol = () => { - patch(ethereum, 'getTokenBySymbol', (symbol: string) => { - return TOKENS.find( - (token) => token.symbol.toUpperCase() === symbol.toUpperCase() - ); - }); -}; - -const patchMsgBroadcaster = () => { - patch(EVMTxBroadcaster, 'getInstance', () => { - return { - broadcast() { - return { - hash: TX_HASH, - }; - }, - }; - }); -}; - -describe('GET /clob/markets', () => { - it('should return 200 with proper request for all markets', async () => { - await request(gatewayApp) - .get(`/clob/markets`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => { - expect(res.body.markets.length).toEqual(MARKETS.length); - }); - }); - - it('should return 200 with proper request for a specific market', async () => { - await request(gatewayApp) - .get(`/clob/markets`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - market: MARKET, - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => { - expect(res.body.markets.makerFee).toBeGreaterThan(0); - }); - }); - - it('should return 200 with proper request for a reversed order market', async () => { - await request(gatewayApp) - .get(`/clob/markets`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - market: 'USDC-DAI', - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => { - expect(res.body.markets.makerFee).toBeGreaterThan(0); - }); - }); - - it('should return 404 when parameters are invalid', async () => { - await request(gatewayApp) - .get(`/clob/markets`) - .query(INVALID_REQUEST) - .expect(404); - }); -}); - -describe('GET /clob/orderBook', () => { - it('should return 200 with proper request', async () => { - await request(gatewayApp) - .get(`/clob/orderBook`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - market: MARKET, - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => - expect(res.body.buys.length).toEqual(NUM_ORDERBOOK_BUCKETS) - ) - .expect((res) => - expect(res.body.sells.length).toEqual(NUM_ORDERBOOK_BUCKETS) - ) - .expect((res) => expect(Number(res.body.buys[0].price)).toBeLessThan(1)) - .expect((res) => - expect(Number(res.body.sells[0].price)).toBeGreaterThan(1) - ); - }); - - it('should return 200 with one-sided orderbook on BUY side', async () => { - await request(gatewayApp) - .get(`/clob/orderBook`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - market: MARKET_BUY_SIDE, - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => - expect(res.body.buys.length).toEqual(NUM_ORDERBOOK_BUCKETS) - ) - .expect((res) => expect(res.body.sells.length).toEqual(0)); - }); - - it('should return 200 with one-sided orderbook on SELL side', async () => { - await request(gatewayApp) - .get(`/clob/orderBook`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - market: MARKET_SELL_SIDE, - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => expect(res.body.buys.length).toEqual(0)) - .expect((res) => - expect(res.body.sells.length).toEqual(NUM_ORDERBOOK_BUCKETS) - ); - }); - - it('should return 200 with one limit-order orderbook on BUY side', async () => { - await request(gatewayApp) - .get(`/clob/orderBook`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - market: MARKET_BUY_LIMIT_ORDER, - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => expect(res.body.buys.length).toEqual(1)) - .expect((res) => expect(res.body.sells.length).toEqual(0)); - }); - - it('should return 200 with one limit-order orderbook on SELL side', async () => { - await request(gatewayApp) - .get(`/clob/orderBook`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - market: MARKET_SELL_LIMIT_ORDER, - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => expect(res.body.buys.length).toEqual(0)) - .expect((res) => expect(res.body.sells.length).toEqual(1)); - }); - - it('should return 200 with empty orderbook', async () => { - await request(gatewayApp) - .get(`/clob/orderBook`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - market: MARKET_EMPTY, - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => expect(res.body.buys.length).toEqual(0)) - .expect((res) => expect(res.body.sells.length).toEqual(0)); - }); - - it('should return 404 when parameters are invalid', async () => { - await request(gatewayApp) - .get(`/clob/orderBook`) - .query(INVALID_REQUEST) - .expect(404); - }); -}); - -describe('GET /clob/ticker', () => { - it('should return 200 with proper request', async () => { - await request(gatewayApp) - .get(`/clob/ticker`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - market: MARKET, - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => - expect(res.body.markets.baseToken.symbol).toEqual(MARKET.split('-')[0]) - ) - .expect((res) => - expect(res.body.markets.quoteToken.symbol).toEqual(MARKET.split('-')[1]) - ); - }); - - it('should return 404 when parameters are invalid', async () => { - await request(gatewayApp) - .get(`/clob/ticker`) - .query(INVALID_REQUEST) - .expect(404); - }); -}); - -describe('GET /clob/orders', () => { - it('should return 200 with proper request', async () => { - await request(gatewayApp) - .get(`/clob/orders`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - orderId: '731', - address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', // noqa: mock - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => expect(res.body.orders.length).toEqual(1)); - }); - - it('should return 200 and empty list for user with no strategies', async () => { - await request(gatewayApp) - .get(`/clob/orders`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - orderId: '731', - address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70e', // noqa: mock - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => expect(res.body.orders.length).toEqual(0)); - }); - - it('should return 404 when parameters are invalid', async () => { - await request(gatewayApp) - .get(`/clob/orders`) - .query(INVALID_REQUEST) - .expect(404); - }); - - it('should return 503 when getting orders but no address provided', async () => { - await request(gatewayApp) - .get(`/clob/orders`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - orderId: '731', - // address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f' - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(503); - }); -}); - -describe('POST /clob/orders', () => { - it('should return 200 with proper BUY request', async () => { - patchGetWallet(); - patchGetTokenBySymbol(); - patchMsgBroadcaster(); - await request(gatewayApp) - .post(`/clob/orders`) - .send({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', // noqa: mock - market: MARKET, - price: '10000.12', - amount: '0.12', - side: 'BUY', - orderType: 'LIMIT_MAKER', - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => expect(res.body.txHash).toBeTruthy()); - }); - - it('should return 200 with proper SELL request', async () => { - patchGetWallet(); - patchGetTokenBySymbol(); - patchMsgBroadcaster(); - await request(gatewayApp) - .post(`/clob/orders`) - .send({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', // noqa: mock - market: MARKET, - price: '10000.12', - amount: '0.12', - side: 'SELL', - orderType: 'LIMIT_MAKER', - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => expect(res.body.txHash).toBeTruthy()); - }); - - it('should return 200 and not execute transaction if orderType is not LIMIT_MAKER', async () => { - patchGetWallet(); - patchGetTokenBySymbol(); - patchMsgBroadcaster(); - await request(gatewayApp) - .post(`/clob/orders`) - .send({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', // noqa: mock - market: MARKET, - price: '10000.12', - amount: '0.12', - side: 'SELL', - orderType: 'LIMIT', - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => expect(res.body.txHash).toBeFalsy()); - }); - - it('should return 404 when parameters are invalid', async () => { - await request(gatewayApp) - .post(`/clob/orders`) - .send(INVALID_REQUEST) - .expect(404); - }); -}); - -describe('DELETE /clob/orders', () => { - it('should return 200 with proper request', async () => { - patchGetWallet(); - patchGetTokenBySymbol(); - patchMsgBroadcaster(); - await request(gatewayApp) - .delete(`/clob/orders`) - .send({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', // noqa: mock - orderId: '731', - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => expect(res.body.txHash).toBeTruthy()); - }); - - it('should return 404 when parameters are invalid', async () => { - await request(gatewayApp) - .delete(`/clob/orders`) - .send(INVALID_REQUEST) - .expect(404); - }); -}); - -describe('GET /clob/estimateGas', () => { - it('should return 200 with proper request', async () => { - patchGasPrices(); - await request(gatewayApp) - .get(`/clob/estimateGas`) - .query({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => expect(res.body.gasPrice).toEqual(GAS_PRICES.gasPrice)); - }); - - it('should return 404 when parameters are invalid', async () => { - await request(gatewayApp) - .get(`/clob/estimateGas`) - .query(INVALID_REQUEST) - .expect(404); - }); -}); - -describe('POST /chain/balances', () => { - it('should return 200 with proper request', async () => { - patchGetWallet(); - patchGetTokenBySymbol(); - patchMsgBroadcaster(); - await request(gatewayApp) - .post(`/chain/balances`) - .send({ - chain: 'ethereum', - network: 'mainnet', - connector: 'carbon', - address: '0x7e57780cf01209a1522b9dCeFa9ff191DDd1c70f', // noqa: mock - tokenSymbols: ['ETH', 'DAI', 'USDC', 'USDT'], - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(200) - .expect((res) => expect(res.body.balances.total.ETH).toEqual('2000')) - .expect((res) => expect(res.body.balances.total.DAI).toEqual('1000')) - .expect((res) => expect(res.body.balances.total.USDC).toEqual('1000')) - .expect((res) => expect(res.body.balances.total.USDT).toEqual('100')); - }); -}); - -describe('verify Carbon constructor', () => { - it('Should return an Error with an unsupported chain', () => { - expect(async () => { - CarbonCLOB.getInstance('avalanche', 'avalanche'); - }).rejects.toThrow(Error); - }); -}); - -describe('Requests to the connector', () => { - it('should return 503 with unsupported chain', async () => { - await request(gatewayApp) - .get(`/clob/markets`) - .query({ - chain: 'avalanche', - network: 'mainnet', - connector: 'carbon', - }) - .set('Accept', 'application/json') - .expect('Content-Type', /json/) - .expect(503); - }); -}); diff --git a/test-bronze/connectors/carbon/carbonAMM.test.ts b/test/connectors/carbon/carbonAMM.test.ts similarity index 91% rename from test-bronze/connectors/carbon/carbonAMM.test.ts rename to test/connectors/carbon/carbonAMM.test.ts index 20e4419edf..8f51873e68 100644 --- a/test-bronze/connectors/carbon/carbonAMM.test.ts +++ b/test/connectors/carbon/carbonAMM.test.ts @@ -426,6 +426,30 @@ describe('POST /amm/price SELL', () => { .expect((res) => expect(Number(res.body.price)).toBeLessThan(1)); }); + it('should return 200 with ETH request', async () => { + await request(gatewayApp) + .post(`/amm/price`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbonamm', + base: 'ETH', + quote: 'DAI', + amount: '1', + side: 'SELL', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.base).toEqual(ETH.address)) + .expect((res) => expect(res.body.quote).toEqual(DAI.address)) + .expect((res) => expect(Number(res.body.amount)).toEqual(1)) + .expect((res) => + expect(Number(res.body.expectedAmount)).toBeGreaterThan(1) + ) + .expect((res) => expect(Number(res.body.price)).toBeGreaterThan(1)); + }); + it('should return 404 when parameters are invalid', async () => { await request(gatewayApp) .get(`/clob/markets`) @@ -493,6 +517,30 @@ describe('POST /amm/trade SELL', () => { .expect((res) => expect(res.body.txHash).toEqual(TX_HASH)); }); + it('should return 200 with ETH request', async () => { + patchGetWallet(); + patchMsgBroadcaster(); + await request(gatewayApp) + .post(`/amm/trade`) + .send({ + chain: 'ethereum', + network: 'mainnet', + connector: 'carbonamm', + base: 'ETH', + quote: 'DAI', + amount: '1', + side: 'SELL', + }) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(200) + .expect((res) => expect(res.body.base).toEqual(ETH.address)) + .expect((res) => expect(res.body.quote).toEqual(DAI.address)) + .expect((res) => expect(Number(res.body.amount)).toEqual(1)) + .expect((res) => expect(Number(res.body.expectedOut)).toBeGreaterThan(1)) + .expect((res) => expect(Number(res.body.price)).toBeGreaterThan(1)); + }); + it('should return 404 when parameters are invalid', async () => { await request(gatewayApp) .get(`/clob/markets`) From b249d5b66395739071b7efb6e3cbc3342daa3e2b Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 9 Jan 2024 18:02:18 +0000 Subject: [PATCH 22/35] Add native token to getTokenByAddress --- src/connectors/carbon/carbonAMM.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/connectors/carbon/carbonAMM.ts b/src/connectors/carbon/carbonAMM.ts index 5d1d3947db..692e6c7d8e 100644 --- a/src/connectors/carbon/carbonAMM.ts +++ b/src/connectors/carbon/carbonAMM.ts @@ -29,6 +29,7 @@ import { logger } from '../../services/logger'; import carbonControllerAbi from './carbon_controller_abi.json'; import { CarbonConfig } from './carbon.config'; +import { isETHAddress } from './carbon.utils'; type TradeData = { tradeActions: TradeActionBNStr[]; @@ -63,6 +64,7 @@ export class Carbonamm implements Uniswapish { private _gasLimitEstimate: number; private _allowedSlippage: string; private _ttl: number; + private _nativeToken: Token; private constructor(chain: string, network: string) { if (chain === 'ethereum') { @@ -71,6 +73,17 @@ export class Carbonamm implements Uniswapish { throw new Error('Unsupported chain'); } + this._nativeToken = + chain === 'ethereum' + ? new Token( + 1, + '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', + 18, + 'ETH', + 'Ethereum' + ) + : new Token(1, '', 18); + this._conf = CarbonConfig.config; this._allowedSlippage = this._conf.allowedSlippage; this.carbonContractConfig = this._conf.carbonContractsConfig( @@ -113,6 +126,11 @@ export class Carbonamm implements Uniswapish { } } + private isNativeAddress(address: string) { + if (this._chain.chainName === 'ethereum') return isETHAddress(address); + return false; + } + /** * Given a token's address, return the connector's native representation of * the token. @@ -120,7 +138,9 @@ export class Carbonamm implements Uniswapish { * @param address Token address */ public getTokenByAddress(address: string): Token { - return this.tokenList[getAddress(address)]; + return this.isNativeAddress(address) + ? this._nativeToken + : this.tokenList[getAddress(address)]; } /** From 143ea6b9613a54ddc020aaa5b90f83eaeaa074fb Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 9 Jan 2024 18:02:33 +0000 Subject: [PATCH 23/35] Add carbon controllers --- src/connectors/carbon/carbon.controllers.ts | 396 ++++++++++++++++++++ 1 file changed, 396 insertions(+) create mode 100644 src/connectors/carbon/carbon.controllers.ts diff --git a/src/connectors/carbon/carbon.controllers.ts b/src/connectors/carbon/carbon.controllers.ts new file mode 100644 index 0000000000..1ee4d9dce6 --- /dev/null +++ b/src/connectors/carbon/carbon.controllers.ts @@ -0,0 +1,396 @@ +import Decimal from 'decimal.js-light'; +import { BigNumber, Wallet } from 'ethers'; +import { Token } from '@uniswap/sdk-core'; +import { + HttpException, + LOAD_WALLET_ERROR_CODE, + LOAD_WALLET_ERROR_MESSAGE, + TOKEN_NOT_SUPPORTED_ERROR_CODE, + TOKEN_NOT_SUPPORTED_ERROR_MESSAGE, + PRICE_FAILED_ERROR_CODE, + PRICE_FAILED_ERROR_MESSAGE, + TRADE_FAILED_ERROR_CODE, + TRADE_FAILED_ERROR_MESSAGE, + SWAP_PRICE_EXCEEDS_LIMIT_PRICE_ERROR_CODE, + SWAP_PRICE_EXCEEDS_LIMIT_PRICE_ERROR_MESSAGE, + SWAP_PRICE_LOWER_THAN_LIMIT_PRICE_ERROR_CODE, + SWAP_PRICE_LOWER_THAN_LIMIT_PRICE_ERROR_MESSAGE, + UNKNOWN_ERROR_ERROR_CODE, + UNKNOWN_ERROR_MESSAGE, +} from '../../services/error-handler'; +import { TokenInfo } from '../../chains/ethereum/ethereum-base'; +import { latency, gasCostInEthString } from '../../services/base'; +import { + Ethereumish, + ExpectedTrade, + Uniswapish, + UniswapLPish, + Tokenish, + Fractionish, +} from '../../services/common-interfaces'; +import { logger } from '../../services/logger'; +import { + EstimateGasResponse, + PriceRequest, + PriceResponse, + TradeRequest, + TradeResponse, +} from '../../amm/amm.requests'; +import { Carbonamm } from '../carbon/carbonAMM'; + +export interface TradeInfo { + baseToken: Tokenish; + quoteToken: Tokenish; + requestAmount: BigNumber; + expectedTrade: ExpectedTrade; +} + +export async function txWriteData( + ethereumish: Ethereumish, + address: string, + maxFeePerGas?: string, + maxPriorityFeePerGas?: string +): Promise<{ + wallet: Wallet; + maxFeePerGasBigNumber: BigNumber | undefined; + maxPriorityFeePerGasBigNumber: BigNumber | undefined; +}> { + let maxFeePerGasBigNumber: BigNumber | undefined; + if (maxFeePerGas) { + maxFeePerGasBigNumber = BigNumber.from(maxFeePerGas); + } + let maxPriorityFeePerGasBigNumber: BigNumber | undefined; + if (maxPriorityFeePerGas) { + maxPriorityFeePerGasBigNumber = BigNumber.from(maxPriorityFeePerGas); + } + + let wallet: Wallet; + try { + wallet = await ethereumish.getWallet(address); + } catch (err) { + logger.error(`Wallet ${address} not available.`); + throw new HttpException( + 500, + LOAD_WALLET_ERROR_MESSAGE + err, + LOAD_WALLET_ERROR_CODE + ); + } + return { wallet, maxFeePerGasBigNumber, maxPriorityFeePerGasBigNumber }; +} + +export async function getTradeInfo( + ethereumish: Ethereumish, + uniswapish: Uniswapish, + baseAsset: string, + quoteAsset: string, + baseAmount: Decimal, + tradeSide: string, + allowedSlippage?: string +): Promise { + const baseToken: Tokenish = getFullTokenFromSymbol( + ethereumish, + uniswapish, + baseAsset + ); + const quoteToken: Tokenish = getFullTokenFromSymbol( + ethereumish, + uniswapish, + quoteAsset + ); + const requestAmount: BigNumber = BigNumber.from( + baseAmount.toFixed(baseToken.decimals).replace('.', '') + ); + + let expectedTrade: ExpectedTrade; + if (tradeSide === 'BUY') { + expectedTrade = await uniswapish.estimateBuyTrade( + quoteToken, + baseToken, + requestAmount, + allowedSlippage + ); + } else { + expectedTrade = await uniswapish.estimateSellTrade( + baseToken, + quoteToken, + requestAmount, + allowedSlippage + ); + } + + return { + baseToken, + quoteToken, + requestAmount, + expectedTrade, + }; +} + +export async function price( + ethereumish: Ethereumish, + uniswapish: Uniswapish, + req: PriceRequest +): Promise { + const startTimestamp: number = Date.now(); + let tradeInfo: TradeInfo; + try { + tradeInfo = await getTradeInfo( + ethereumish, + uniswapish, + req.base, + req.quote, + new Decimal(req.amount), + req.side, + req.allowedSlippage + ); + } catch (e) { + if (e instanceof Error) { + throw new HttpException( + 500, + PRICE_FAILED_ERROR_MESSAGE + e.message, + PRICE_FAILED_ERROR_CODE + ); + } else { + throw new HttpException( + 500, + UNKNOWN_ERROR_MESSAGE, + UNKNOWN_ERROR_ERROR_CODE + ); + } + } + + const trade = tradeInfo.expectedTrade.trade; + const expectedAmount = tradeInfo.expectedTrade.expectedAmount; + + const tradePrice = + req.side === 'BUY' ? trade.executionPrice.invert() : trade.executionPrice; + + const gasLimitTransaction = ethereumish.gasLimitTransaction; + const gasPrice = ethereumish.gasPrice; + const gasLimitEstimate = uniswapish.gasLimitEstimate; + return { + network: ethereumish.chain, + timestamp: startTimestamp, + latency: latency(startTimestamp, Date.now()), + base: tradeInfo.baseToken.address, + quote: tradeInfo.quoteToken.address, + amount: new Decimal(req.amount).toFixed(tradeInfo.baseToken.decimals), + rawAmount: tradeInfo.requestAmount.toString(), + expectedAmount: expectedAmount.toSignificant(8), + price: tradePrice.toSignificant(8), + gasPrice: gasPrice, + gasPriceToken: ethereumish.nativeTokenSymbol, + gasLimit: gasLimitTransaction, + gasCost: gasCostInEthString(gasPrice, gasLimitEstimate), + }; +} + +export async function trade( + ethereumish: Ethereumish, + uniswapish: Uniswapish, + req: TradeRequest +): Promise { + const startTimestamp: number = Date.now(); + + const limitPrice = req.limitPrice; + const { wallet, maxFeePerGasBigNumber, maxPriorityFeePerGasBigNumber } = + await txWriteData( + ethereumish, + req.address, + req.maxFeePerGas, + req.maxPriorityFeePerGas + ); + + let tradeInfo: TradeInfo; + try { + tradeInfo = await getTradeInfo( + ethereumish, + uniswapish, + req.base, + req.quote, + new Decimal(req.amount), + req.side + ); + } catch (e) { + if (e instanceof Error) { + logger.error(`Could not get trade info. ${e.message}`); + throw new HttpException( + 500, + TRADE_FAILED_ERROR_MESSAGE + e.message, + TRADE_FAILED_ERROR_CODE + ); + } else { + logger.error('Unknown error trying to get trade info.'); + throw new HttpException( + 500, + UNKNOWN_ERROR_MESSAGE, + UNKNOWN_ERROR_ERROR_CODE + ); + } + } + + const gasPrice: number = ethereumish.gasPrice; + const gasLimitTransaction: number = ethereumish.gasLimitTransaction; + const gasLimitEstimate: number = uniswapish.gasLimitEstimate; + + if (req.side === 'BUY') { + const price: Fractionish = + tradeInfo.expectedTrade.trade.executionPrice.invert(); + if ( + limitPrice && + new Decimal(price.toFixed(8)).gt(new Decimal(limitPrice)) + ) { + logger.error('Swap price exceeded limit price.'); + throw new HttpException( + 500, + SWAP_PRICE_EXCEEDS_LIMIT_PRICE_ERROR_MESSAGE( + price.toFixed(8), + limitPrice + ), + SWAP_PRICE_EXCEEDS_LIMIT_PRICE_ERROR_CODE + ); + } + + const tx = await uniswapish.executeTrade( + wallet, + tradeInfo.expectedTrade.trade, + gasPrice, + uniswapish.router, + uniswapish.ttl, + uniswapish.routerAbi, + gasLimitTransaction, + req.nonce, + maxFeePerGasBigNumber, + maxPriorityFeePerGasBigNumber, + req.allowedSlippage + ); + + if (tx.hash) { + await ethereumish.txStorage.saveTx( + ethereumish.chain, + ethereumish.chainId, + tx.hash, + new Date(), + ethereumish.gasPrice + ); + } + + logger.info( + `Trade has been executed, txHash is ${tx.hash}, nonce is ${tx.nonce}, gasPrice is ${gasPrice}.` + ); + + return { + network: ethereumish.chain, + timestamp: startTimestamp, + latency: latency(startTimestamp, Date.now()), + base: tradeInfo.baseToken.address, + quote: tradeInfo.quoteToken.address, + amount: new Decimal(req.amount).toFixed(tradeInfo.baseToken.decimals), + rawAmount: tradeInfo.requestAmount.toString(), + expectedIn: tradeInfo.expectedTrade.expectedAmount.toSignificant(8), + price: price.toSignificant(8), + gasPrice: gasPrice, + gasPriceToken: ethereumish.nativeTokenSymbol, + gasLimit: gasLimitTransaction, + gasCost: gasCostInEthString(gasPrice, gasLimitEstimate), + nonce: tx.nonce, + txHash: tx.hash, + }; + } else { + const price: Fractionish = tradeInfo.expectedTrade.trade.executionPrice; + logger.info( + `Expected execution price is ${price.toFixed(6)}, ` + + `limit price is ${limitPrice}.` + ); + if ( + limitPrice && + new Decimal(price.toFixed(8)).lt(new Decimal(limitPrice)) + ) { + logger.error('Swap price lower than limit price.'); + throw new HttpException( + 500, + SWAP_PRICE_LOWER_THAN_LIMIT_PRICE_ERROR_MESSAGE( + price.toFixed(8), + limitPrice + ), + SWAP_PRICE_LOWER_THAN_LIMIT_PRICE_ERROR_CODE + ); + } + + const tx = await uniswapish.executeTrade( + wallet, + tradeInfo.expectedTrade.trade, + gasPrice, + uniswapish.router, + uniswapish.ttl, + uniswapish.routerAbi, + gasLimitTransaction, + req.nonce, + maxFeePerGasBigNumber, + maxPriorityFeePerGasBigNumber + ); + + logger.info( + `Trade has been executed, txHash is ${tx.hash}, nonce is ${tx.nonce}, gasPrice is ${gasPrice}.` + ); + + return { + network: ethereumish.chain, + timestamp: startTimestamp, + latency: latency(startTimestamp, Date.now()), + base: tradeInfo.baseToken.address, + quote: tradeInfo.quoteToken.address, + amount: new Decimal(req.amount).toFixed(tradeInfo.baseToken.decimals), + rawAmount: tradeInfo.requestAmount.toString(), + expectedOut: tradeInfo.expectedTrade.expectedAmount.toSignificant(8), + price: price.toSignificant(8), + gasPrice: gasPrice, + gasPriceToken: ethereumish.nativeTokenSymbol, + gasLimit: gasLimitTransaction, + gasCost: gasCostInEthString(gasPrice, gasLimitEstimate), + nonce: tx.nonce, + txHash: tx.hash, + }; + } +} + +export function getFullTokenFromSymbol( + ethereumish: Ethereumish, + uniswapish: Uniswapish | UniswapLPish, + tokenSymbol: string +): Tokenish | Token { + const tokenInfo: TokenInfo | undefined = + ethereumish.getTokenBySymbol(tokenSymbol); + let fullToken: Tokenish | Token | undefined; + if (tokenInfo) { + fullToken = uniswapish.getTokenByAddress(tokenInfo.address); + } else if (tokenSymbol === 'ETH' && uniswapish instanceof Carbonamm) { + fullToken = uniswapish.getTokenByAddress( + '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' + ); + } + if (!fullToken) + throw new HttpException( + 500, + TOKEN_NOT_SUPPORTED_ERROR_MESSAGE + tokenSymbol, + TOKEN_NOT_SUPPORTED_ERROR_CODE + ); + return fullToken; +} + +export async function estimateGas( + ethereumish: Ethereumish, + uniswapish: Uniswapish +): Promise { + const gasPrice: number = ethereumish.gasPrice; + const gasLimitTransaction: number = ethereumish.gasLimitTransaction; + const gasLimitEstimate: number = uniswapish.gasLimitEstimate; + return { + network: ethereumish.chain, + timestamp: Date.now(), + gasPrice, + gasPriceToken: ethereumish.nativeTokenSymbol, + gasLimit: gasLimitTransaction, + gasCost: gasCostInEthString(gasPrice, gasLimitEstimate), + }; +} From b464c89b45f20c0792a3e5c5e1f9e68c0ce363aa Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 9 Jan 2024 18:02:45 +0000 Subject: [PATCH 24/35] Add carbon controllers to amm controllers --- src/amm/amm.controllers.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/amm/amm.controllers.ts b/src/amm/amm.controllers.ts index de2d90b776..c33efcc0e0 100644 --- a/src/amm/amm.controllers.ts +++ b/src/amm/amm.controllers.ts @@ -34,6 +34,11 @@ import { poolPrice as uniswapV3PoolPrice, estimateGas as uniswapEstimateGas, } from '../connectors/uniswap/uniswap.controllers'; +import { + price as carbonPrice, + trade as carbonTrade, + estimateGas as carbonEstimateGas, +} from '../connectors/carbon/carbon.controllers'; import { price as refPrice, trade as refTrade, @@ -75,6 +80,7 @@ import { import { Algorand } from '../chains/algorand/algorand'; import { Tinyman } from '../connectors/tinyman/tinyman'; import { Plenty } from '../connectors/plenty/plenty'; +import { Carbonamm } from '../connectors/carbon/carbonAMM'; export async function price(req: PriceRequest): Promise { const chain = await getInitializedChain< @@ -89,6 +95,8 @@ export async function price(req: PriceRequest): Promise { if (connector instanceof Plenty) { return plentyPrice(chain, connector, req); + } else if (connector instanceof Carbonamm) { + return carbonPrice(chain, connector, req); } else if ('routerAbi' in connector) { // we currently use the presence of routerAbi to distinguish Uniswapish from RefAMMish return uniswapPrice(chain, connector, req); @@ -112,6 +120,8 @@ export async function trade(req: TradeRequest): Promise { if (connector instanceof Plenty) { return plentyTrade(chain, connector, req); + } else if (connector instanceof Carbonamm) { + return carbonTrade(chain, connector, req); } else if ('routerAbi' in connector) { // we currently use the presence of routerAbi to distinguish Uniswapish from RefAMMish return uniswapTrade(chain, connector, req); @@ -199,6 +209,8 @@ export async function estimateGas( if (connector instanceof Plenty) { return plentyEstimateGas(chain, connector); + } else if (connector instanceof Carbonamm) { + return carbonEstimateGas(chain, connector); } else if ('routerAbi' in connector) { // we currently use the presence of routerAbi to distinguish Uniswapish from RefAMMish return uniswapEstimateGas(chain, connector); From 98102fe59d491e93e49ccc7c67b14e1c5e556f29 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 9 Jan 2024 18:22:33 +0000 Subject: [PATCH 25/35] Remove CarbonCLOB --- src/chains/ethereum/ethereum.ts | 2 +- src/chains/ethereum/ethereum.validators.ts | 1 - src/connectors/carbon/carbon.config.ts | 6 +- src/connectors/carbon/carbon.ts | 572 ------------------ src/connectors/carbon/carbon.utils.ts | 103 ---- src/connectors/connectors.routes.ts | 8 +- src/services/connection-manager.ts | 3 - .../connectors/carbon/carbonAMM.test.ts | 0 8 files changed, 4 insertions(+), 691 deletions(-) delete mode 100644 src/connectors/carbon/carbon.ts rename {test => test-bronze}/connectors/carbon/carbonAMM.test.ts (100%) diff --git a/src/chains/ethereum/ethereum.ts b/src/chains/ethereum/ethereum.ts index 2bdc408ddd..1694ac7820 100644 --- a/src/chains/ethereum/ethereum.ts +++ b/src/chains/ethereum/ethereum.ts @@ -189,7 +189,7 @@ export class Ethereum extends EthereumBase implements Ethereumish { ); } else if (reqSpender === 'uniswapLP') { spender = UniswapConfig.config.uniswapV3NftManagerAddress(this._chain); - } else if (reqSpender === 'carbon' || reqSpender === 'carbonamm') { + } else if (reqSpender === 'carbonamm') { spender = CarbonConfig.config.carbonContractsConfig( 'ethereum', this._chain diff --git a/src/chains/ethereum/ethereum.validators.ts b/src/chains/ethereum/ethereum.validators.ts index 5ea54a88c1..e8280547b2 100644 --- a/src/chains/ethereum/ethereum.validators.ts +++ b/src/chains/ethereum/ethereum.validators.ts @@ -63,7 +63,6 @@ export const validateSpender: Validator = mkValidator( val === 'pancakeswapLP' || val === 'xsswap' || val === 'curve' || - val === 'carbon' || val === 'carbonamm' || isAddress(val)) ); diff --git a/src/connectors/carbon/carbon.config.ts b/src/connectors/carbon/carbon.config.ts index e8332657f0..1d35a64167 100644 --- a/src/connectors/carbon/carbon.config.ts +++ b/src/connectors/carbon/carbon.config.ts @@ -8,7 +8,7 @@ export namespace CarbonConfig { allowedSlippage: string; gasLimitEstimate: number; ttl: number; - tradingTypes: (type: string) => Array; + tradingTypes: Array; chainType: string; matchType: MatchType; availableNetworks: Array; @@ -26,9 +26,7 @@ export namespace CarbonConfig { `carbon.gasLimitEstimate` ), ttl: ConfigManagerV2.getInstance().get(`carbon.ttl`), - tradingTypes: (type: string) => { - return type === 'swap' ? ['AMM'] : ['CLOB_SPOT']; - }, + tradingTypes: ['AMM'], chainType: 'EVM', matchType: MatchType.Fast, availableNetworks: [ diff --git a/src/connectors/carbon/carbon.ts b/src/connectors/carbon/carbon.ts deleted file mode 100644 index cd633c5be3..0000000000 --- a/src/connectors/carbon/carbon.ts +++ /dev/null @@ -1,572 +0,0 @@ -import Decimal from 'decimal.js-light'; -import { ChainCache, initSyncedCache } from '@bancor/carbon-sdk/chain-cache'; -import { - ContractsApi, - ContractsConfig, -} from '@bancor/carbon-sdk/contracts-api'; -import { Toolkit } from '@bancor/carbon-sdk/strategy-management'; -import { Strategy, TokenPair } from '@bancor/carbon-sdk'; -import { parseUnits } from '@bancor/carbon-sdk/utils'; - -import { Ethereum } from '../../chains/ethereum/ethereum'; -import { TokenInfo } from '../../chains/ethereum/ethereum-base'; -import { EVMTxBroadcaster } from '../../chains/ethereum/evm.broadcaster'; - -import { - CLOBMarkets, - ClobDeleteOrderRequest, - ClobGetOrderRequest, - ClobGetOrderResponse, - ClobMarketsRequest, - ClobOrderbookRequest, - ClobPostOrderRequest, - ClobTickerRequest, -} from '../../clob/clob.requests'; - -import { - CLOBish, - MarketInfo, - NetworkSelectionRequest, - Orderbook, -} from '../../services/common-interfaces'; -import { logger } from '../../services/logger'; - -import { BalanceRequest } from '../../network/network.requests'; - -import { isETHAddress } from './carbon.utils'; -import { CarbonConfig } from './carbon.config'; - -import { - OrderRow, - buildOrders, - decodeStrategyId, - emptyToken, - getMiddleRate, - getStep, -} from './carbon.utils'; - -export class CarbonCLOB implements CLOBish { - public parsedMarkets: MarketInfo = []; - public carbonContractConfig: Required; - public carbonSDK!: Toolkit; // will be initialized in init() - public sdkCache!: ChainCache; // will be initialized in init() - public api: ContractsApi; - private static _instances: { [name: string]: CarbonCLOB }; - private _chain: Ethereum; - private _ready: boolean = false; - private _conf: CarbonConfig.NetworkConfig; - private _nativeToken: TokenInfo; - - private constructor(chain: string, network: string) { - if (chain === 'ethereum') { - this._chain = Ethereum.getInstance(network); - } else { - throw new Error('unsupported chain'); - } - - this._nativeToken = - chain === 'ethereum' - ? { - chainId: 1, - address: '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE', - name: 'Ethereum', - symbol: 'ETH', - decimals: 18, - } - : emptyToken; - - this._conf = CarbonConfig.config; - this.carbonContractConfig = this._conf.carbonContractsConfig( - chain, - network - ); - - this.api = new ContractsApi( - this._chain.provider, - this.carbonContractConfig - ); - } - - public static getInstance(chain: string, network: string): CarbonCLOB { - if (CarbonCLOB._instances === undefined) { - CarbonCLOB._instances = {}; - } - - const key = `${chain}:${network}`; - if (!(key in CarbonCLOB._instances)) { - CarbonCLOB._instances[key] = new CarbonCLOB(chain, network); - } - - return CarbonCLOB._instances[key]; - } - public async loadMarkets() { - const contractPairs = await this.api.reader.pairs(); - - await Promise.all( - contractPairs.map(async (pair) => await this._updateMarkets(pair)) - ); - } - - public async init() { - if (!this._chain.ready() || Object.keys(this.parsedMarkets).length === 0) { - logger.info('Initialising chain...'); - await this._chain.init(); - } - - if (!this._ready) { - const { cache, startDataSync } = initSyncedCache(this.api.reader); - this.sdkCache = cache; - - const decimalsMap = new Map(); - this._chain.storedTokenList.forEach((token) => { - decimalsMap.set(token.address, token.decimals); - }); - - this.carbonSDK = new Toolkit(this.api, this.sdkCache, (address) => - decimalsMap.get(address.toLowerCase()) - ); - - logger.info('Starting Data Sync...'); - await startDataSync(); - - logger.info('Loading markets...'); - await this.loadMarkets(); - - this._ready = true; - } - } - - public ready(): boolean { - return this._ready; - } - - public async markets( - req: ClobMarketsRequest - ): Promise<{ markets: MarketInfo }> { - if (req.market) { - if (req.market in this.parsedMarkets) - return { markets: this.parsedMarkets[req.market] }; - - const reversedMarket = req.market.split('-').reverse().join('-'); - if (reversedMarket in this.parsedMarkets) - return { markets: this.parsedMarkets[reversedMarket] }; - } - - return { markets: Object.values(this.parsedMarkets) }; - } - - public async ticker( - req: ClobTickerRequest - ): Promise<{ markets: CLOBMarkets }> { - return await this.markets(req); - } - - public async orders( - req: ClobGetOrderRequest - ): Promise<{ orders: ClobGetOrderResponse['orders'] }> { - if (!req.address) throw Error('Missing wallet address'); - - const userStrategy = await this._findUserStrategy(req.address, req.orderId); - - if (userStrategy) { - const [pairId, id] = decodeStrategyId(userStrategy.id); - return { - orders: [ - { - id, - pairId, - baseToken: userStrategy.baseToken, - quoteToken: userStrategy.quoteToken, - buyPriceLow: userStrategy.buyPriceLow, - buyPriceMarginal: userStrategy.buyPriceMarginal, - buyPriceHigh: userStrategy.buyPriceHigh, - buyBudget: userStrategy.buyBudget, - sellPriceLow: userStrategy.sellPriceLow, - sellPriceMarginal: userStrategy.sellPriceMarginal, - sellPriceHigh: userStrategy.sellPriceHigh, - sellBudget: userStrategy.sellBudget, - }, - ], - }; - } else { - return { - orders: [], - }; - } - } - - public async postOrder( - req: ClobPostOrderRequest - ): Promise<{ txHash: string }> { - this._isMarketValid(req.market); - - const tokens = this._getTokensBySymbol(req.market.split('-')); - - const [baseAddress, quoteAddress] = - req.side === 'BUY' - ? [tokens[0].address, tokens[1].address] - : [tokens[1].address, tokens[0].address]; - - if (req.orderType === 'LIMIT_MAKER') { - const createTransaction = await this.carbonSDK.createBuySellStrategy( - baseAddress, - quoteAddress, - req.price, - req.price, - req.price, - req.amount, - '0', - '0', - '0', - '0' - ); - - const wallet = await this._chain.getWallet(req.address); - - const txResponse = await EVMTxBroadcaster.getInstance( - this._chain, - wallet.address - ).broadcast(createTransaction); - - return { txHash: txResponse.hash }; - } - - return { txHash: '' }; - } - - public async deleteOrder( - req: ClobDeleteOrderRequest - ): Promise<{ txHash: string }> { - // Find user strategy wth the requested orderId - const userStrategy = await this._findUserStrategy(req.address, req.orderId); - if (!userStrategy) throw Error('Order does not exist.'); - - const deleteTransaction = await this.carbonSDK.deleteStrategy( - userStrategy.id - ); - - const wallet = await this._chain.getWallet(req.address); - const txResponse = await EVMTxBroadcaster.getInstance( - this._chain, - wallet.address - ).broadcast(deleteTransaction); - - return { txHash: txResponse.hash }; - } - - public estimateGas(_req: NetworkSelectionRequest): { - gasPrice: number; - gasPriceToken: string; - gasLimit: number; - gasCost: number; - } { - return { - gasPrice: this._chain.gasPrice, - gasPriceToken: this._chain.nativeTokenSymbol, - gasLimit: this._conf.gasLimitEstimate, - gasCost: this._chain.gasPrice * this._conf.gasLimitEstimate, - }; - } - - public async balances(req: BalanceRequest): Promise> { - const tokens = this._getTokensBySymbol(req.tokenSymbols); - - const userStrategies = await this.carbonSDK.getUserStrategies(req.address); - - const formattedBalances: Record = { available: {}, total: {} }; - - await Promise.all( - tokens.map((token) => { - const userStrategiesBuyBudgetRaw = userStrategies.filter( - (strategy) => strategy.baseToken === token.address - ); - const userStrategyBuyBudget = userStrategiesBuyBudgetRaw.reduce( - (acc, strategy) => { - return acc.add( - new Decimal(strategy?.sellBudget || 0).div( - new Decimal(10).pow(token.decimals) - ) - ); - }, - new Decimal(0) - ); - - const userStrategiesSellBudgetRaw = userStrategies.filter( - (strategy) => strategy.quoteToken === token.address - ); - const userStrategySellBudget = userStrategiesSellBudgetRaw.reduce( - (acc, strategy) => { - return acc.add( - new Decimal(strategy?.buyBudget || 0).div( - new Decimal(10).pow(token.decimals) - ) - ); - }, - new Decimal(0) - ); - - const userTokenBalance = userStrategyBuyBudget - .add(userStrategySellBudget) - .toString(); - - formattedBalances.available[token.symbol] = parseUnits( - userTokenBalance, - token.decimals - ).toString(); - formattedBalances.total[token.symbol] = parseUnits( - userTokenBalance, - token.decimals - ).toString(); - }) - ); - - return formattedBalances; - } - - public async orderBook(req: ClobOrderbookRequest): Promise { - this._isMarketValid(req.market); - - const [base, quote] = this._getTokensBySymbol(req.market.split('-')); - - if (!base || !quote) throw Error('Invalid tokens'); - - const { buyHasLiq, buyStartRate, sellHasLiq, sellStartRate, step, steps } = - await this._getOrderbookParams(base, quote); - - const buy = buyHasLiq - ? await this._buildOrderBook( - true, - base.address, - quote.address, - buyStartRate, - step, - steps - ) - : []; - - const sell = sellHasLiq - ? await this._buildOrderBook( - false, - quote.address, - base.address, - sellStartRate, - step, - steps - ) - : []; - - const buyOrders = buildOrders(buy, base.decimals, quote.decimals); - const sellOrders = buildOrders(sell, base.decimals, quote.decimals); - - return { - buys: buyOrders.map((buyOrder) => { - return { - price: buyOrder.rate, - quantity: buyOrder.amount, - timestamp: Date.now(), - }; - }), - sells: sellOrders.map((sellOrder) => { - return { - price: sellOrder.rate, - quantity: sellOrder.amount, - timestamp: Date.now(), - }; - }), - }; - } - - // Helper functions - - private async _isMarketValid(market: string) { - const symbols: MarketInfo = - this.parsedMarkets[market] || - this.parsedMarkets[market.split('-').reverse().join('-')]; - - if (!symbols || symbols.length < 2) throw Error('Invalid market'); - } - - private async _updateMarkets(pair: TokenPair) { - const baseToken = this._findTokenByAddress(pair[0]); - const quoteToken = this._findTokenByAddress(pair[1]); - - if (!baseToken || !quoteToken) return; - - const ticker = - baseToken.symbol.toUpperCase() + '-' + quoteToken.symbol.toUpperCase(); - - // will handle cache miss if no pair in cache - const makerFee = await this.sdkCache.getTradingFeePPMByPair( - pair[0], - pair[1] - ); - - const market = { - ticker, - baseToken, - quoteToken, - makerFee, - }; - - this.parsedMarkets[market.ticker] = market; - } - - private _getTokensBySymbol(symbols: string[]) { - return symbols.map((symbol) => { - if (symbol == 'ETH') return this._nativeToken; - return this._chain.getTokenBySymbol(symbol) || emptyToken; - }); - } - - private async _getOrderbookParams(base: TokenInfo, quote: TokenInfo) { - const steps = 100; - const ONE = new Decimal(1); - - const buyHasLiq = await this.carbonSDK.hasLiquidityByPair( - base.address, - quote.address - ); - const sellHasLiq = await this.carbonSDK.hasLiquidityByPair( - quote.address, - base.address - ); - - const minBuy = new Decimal( - buyHasLiq - ? await this.carbonSDK.getMinRateByPair(base.address, quote.address) - : 0 - ); - const maxBuy = new Decimal( - buyHasLiq - ? await this.carbonSDK.getMaxRateByPair(base.address, quote.address) - : 0 - ); - const minSell = new Decimal( - sellHasLiq - ? await this.carbonSDK.getMinRateByPair(quote.address, base.address) - : 0 - ); - const maxSell = new Decimal( - sellHasLiq - ? await this.carbonSDK.getMaxRateByPair(quote.address, base.address) - : 0 - ); - - const minSellNormalized = sellHasLiq ? ONE.div(maxSell) : new Decimal(0); - const maxSellNormalized = sellHasLiq ? ONE.div(minSell) : new Decimal(0); - - const deltaBuy = maxBuy.minus(minBuy); - const deltaSell = maxSellNormalized.minus(minSellNormalized); - - const stepBuy = deltaBuy.div(steps); - const stepSell = deltaSell.div(steps); - - const step = getStep( - stepBuy, - stepSell, - minBuy, - maxBuy, - steps, - minSell, - maxSell - ); - - const middleRate = getMiddleRate(maxBuy, maxSell); - - const hasOverlappingRates = maxBuy.gt(minSellNormalized); - const buyStartRate = hasOverlappingRates ? middleRate : maxBuy; - const sellStartRate = hasOverlappingRates ? middleRate : minSellNormalized; - - return { buyHasLiq, buyStartRate, sellHasLiq, sellStartRate, step, steps }; - } - - private async _buildOrderBook( - buy: boolean, - baseToken: string, - quoteToken: string, - startRate: Decimal, - step: Decimal, - steps: number - ): Promise { - const orders: OrderRow[] = []; - const rates: string[] = []; - const ONE = new Decimal(1); - - for (let i = 0; i <= steps + 1; i++) { - const incrementBy = step.times(i); - let rate = startRate[buy ? 'minus' : 'plus'](incrementBy); - rate = buy ? rate : ONE.div(rate); - if (rate.gt(0)) { - rates.push(rate.toString()); - } - } - - const results = await this.carbonSDK.getRateLiquidityDepthsByPair( - baseToken, - quoteToken, - rates - ); - - results.forEach((liquidity, i) => { - const length = orders.length; - let rate = rates[i]; - let liquidityBn = new Decimal(liquidity); - let totalBn = liquidityBn; - - if (liquidityBn.eq(0)) { - console.warn('order book getRateLiquidityDepthsByPair returns 0'); - return; - } - if (buy) { - if (length === 0) { - liquidityBn = liquidityBn.div(rate); - } else { - if (liquidityBn.eq(orders[length - 1].originalTotal || '0')) { - liquidityBn = new Decimal(orders[length - 1].amount); - } else { - const firstRate = new Decimal(orders[0].rate); - const firstTotal = new Decimal(orders[0].originalTotal || '0'); - const delta = liquidityBn.minus(firstTotal); - liquidityBn = firstTotal.div(firstRate).plus(delta.div(rate)); - } - } - } else { - rate = ONE.div(rate).toString(); - totalBn = totalBn.times(rate); - } - orders.push({ - rate, - total: totalBn.toString(), - amount: liquidityBn.toString(), - originalTotal: liquidity, - }); - }); - - return orders; - } - - private async _findUserStrategy( - address: string, - orderId: string - ): Promise { - const userStrategies = await this.carbonSDK.getUserStrategies(address); - - const userStrategy = userStrategies.find((strategy) => { - const [, strategyIndex] = decodeStrategyId(strategy.id); - return strategyIndex === orderId; - }); - return userStrategy; - } - - private _findTokenByAddress(address: string): TokenInfo { - if (isETHAddress(address)) { - return this._nativeToken; - } else { - return ( - this._chain.storedTokenList.find( - (token) => token.address === address - ) || emptyToken - ); - } - } -} diff --git a/src/connectors/carbon/carbon.utils.ts b/src/connectors/carbon/carbon.utils.ts index 6339fc4d5a..ec2ab5e755 100644 --- a/src/connectors/carbon/carbon.utils.ts +++ b/src/connectors/carbon/carbon.utils.ts @@ -1,108 +1,5 @@ -import Decimal from 'decimal.js-light'; -import { TokenInfo } from '../../chains/ethereum/ethereum-base'; - const ETH_ADDRESS = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE'.toLowerCase(); -export type OrderRow = { - rate: string; - total: string; - amount: string; - originalTotal?: string; -}; - -export const emptyToken: TokenInfo = { - chainId: 1, - address: '', - name: '', - symbol: '', - decimals: 18, -}; - -const subtractPrevAmount = ( - data: OrderRow[], - amount: string, - rate: string, - i: number -) => { - const prevAmount = data[i - 1]?.amount || '0'; - const newAmount = new Decimal(amount).minus(prevAmount); - const newTotal = new Decimal(rate).times(newAmount); - - return { - rate, - amount: newAmount.toString(), - total: newTotal.toString(), - }; -}; - -export const buildOrders = ( - data: OrderRow[], - baseDecimals: number, - quoteDecimals: number -): OrderRow[] => { - const ROW_AMOUNT_MIN_THRESHOLD = 0.0001; - const buckets = 14; - - return data - .map(({ amount, rate }, i) => subtractPrevAmount(data, amount, rate, i)) - .filter(({ amount }) => new Decimal(amount).gte(ROW_AMOUNT_MIN_THRESHOLD)) - .splice(0, buckets) - .map(({ amount, rate, total }) => ({ - rate: new Decimal(rate).toFixed(quoteDecimals, 1), - amount: new Decimal(amount).toFixed(baseDecimals, 1), - total: new Decimal(total).toFixed(quoteDecimals, 1), - })); -}; - -export const getMiddleRate = (maxBuy: Decimal, maxSell: Decimal): Decimal => { - const ONE = new Decimal(1); - const isFinite = (number: Decimal) => - !['NaN', 'Infinity', '-Infinity'].some((x) => x === number.toString()); - - if (isFinite(maxBuy) && maxBuy.gt(0) && isFinite(maxSell) && maxSell.gt(0)) { - return maxBuy.plus(ONE.div(maxSell)).div(2); - } - if (isFinite(maxBuy) && maxBuy.gt(0)) { - return maxBuy; - } - if (isFinite(maxSell) && maxSell.gt(0)) { - return ONE.div(maxSell); - } - return new Decimal(0); -}; - -export const getStep = ( - stepBuy: Decimal, - stepSell: Decimal, - minBuy: Decimal, - maxBuy: Decimal, - steps: number, - minSell: Decimal, - maxSell: Decimal -): Decimal => { - const ONE = new Decimal(1); - const isFinite = (number: Decimal) => - !['NaN', 'Infinity', '-Infinity'].some((x) => x === number.toString()); - - if (isFinite(stepBuy) && stepBuy.gt(0)) { - if (isFinite(stepSell) && stepSell.gt(0)) { - return stepBuy.lte(stepSell) ? stepBuy : stepSell; - } else { - return stepBuy; - } - } else if (isFinite(stepSell) && stepSell.gt(0)) { - return stepSell; - } else { - if (minBuy.gt(0) && minBuy.eq(maxBuy)) { - return minBuy.div(steps + 2); - } - if (minSell.gt(0) && minSell.eq(maxSell)) { - return minSell.div(steps + 2); - } - return ONE.div(10000); - } -}; - export const decodeStrategyId = (strategyIdRaw: string): string[] => { const strategyId = BigInt(strategyIdRaw); const pairId = (strategyId >> BigInt(128)).toString(); diff --git a/src/connectors/connectors.routes.ts b/src/connectors/connectors.routes.ts index d923a9ba1f..aaa55897d1 100644 --- a/src/connectors/connectors.routes.ts +++ b/src/connectors/connectors.routes.ts @@ -163,15 +163,9 @@ export namespace ConnectorsRoutes { 'Enter your kujira account number (input 0 if unsure) >>> ', }, }, - { - name: 'carbon', - trading_type: CarbonConfig.config.tradingTypes('spot'), - chain_type: CarbonConfig.config.chainType, - available_networks: CarbonConfig.config.availableNetworks, - }, { name: 'carbonamm', - trading_type: CarbonConfig.config.tradingTypes('swap'), + trading_type: CarbonConfig.config.tradingTypes, chain_type: CarbonConfig.config.chainType, available_networks: CarbonConfig.config.availableNetworks, }, diff --git a/src/services/connection-manager.ts b/src/services/connection-manager.ts index a52c8953ae..7db0029ea9 100644 --- a/src/services/connection-manager.ts +++ b/src/services/connection-manager.ts @@ -43,7 +43,6 @@ import { KujiraCLOB } from '../connectors/kujira/kujira'; import { PancakeswapLP } from '../connectors/pancakeswap/pancakeswap.lp'; import { XRPLCLOB } from '../connectors/xrpl/xrpl'; import { Carbonamm } from '../connectors/carbon/carbonAMM'; -import { CarbonCLOB } from '../connectors/carbon/carbon'; export type ChainUnion = | Algorand @@ -230,8 +229,6 @@ export async function getConnector( connector === 'curve' ) { connectorInstance = Curve.getInstance(chain, network); - } else if (chain === 'ethereum' && connector === 'carbon') { - connectorInstance = CarbonCLOB.getInstance(chain, network); } else if (chain === 'ethereum' && connector === 'carbonamm') { connectorInstance = Carbonamm.getInstance(chain, network); } else { diff --git a/test/connectors/carbon/carbonAMM.test.ts b/test-bronze/connectors/carbon/carbonAMM.test.ts similarity index 100% rename from test/connectors/carbon/carbonAMM.test.ts rename to test-bronze/connectors/carbon/carbonAMM.test.ts From ac7d235ad82285b5367365f6ee548d0fcec1803b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 11:27:37 +0000 Subject: [PATCH 26/35] Bump follow-redirects from 1.15.2 to 1.15.4 Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.4. - [Release notes](https://github.com/follow-redirects/follow-redirects/releases) - [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.4) --- updated-dependencies: - dependency-name: follow-redirects dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6c5493e1fe..122b797b8d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8073,9 +8073,9 @@ fn.name@1.x.x: integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== follow-redirects@^1.12.1, follow-redirects@^1.14.0, follow-redirects@^1.14.8, follow-redirects@^1.14.9, follow-redirects@^1.15.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + version "1.15.4" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" + integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== for-each@^0.3.3: version "0.3.3" From cd7f1f49c90c4849cf94ad913d49feb27be4ab28 Mon Sep 17 00:00:00 2001 From: Ralph Comia Date: Tue, 30 Jan 2024 11:59:07 +0800 Subject: [PATCH 27/35] update gateway development version to dev-1.25.0 --- src/app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app.ts b/src/app.ts index 0758a227de..a2db518650 100644 --- a/src/app.ts +++ b/src/app.ts @@ -112,7 +112,7 @@ export const startSwagger = async () => { export const startGateway = async () => { const port = ConfigManagerV2.getInstance().get('server.port'); - const gateway_version="dev-1.24.0"; // gateway version + const gateway_version="dev-1.25.0"; // gateway version if (!ConfigManagerV2.getInstance().get('server.id')) { ConfigManagerV2.getInstance().set( 'server.id', From 5d8ea6b2dbba34acb289711df008e3da1a982223 Mon Sep 17 00:00:00 2001 From: mlguys Date: Tue, 30 Jan 2024 23:48:02 +0700 Subject: [PATCH 28/35] Add market order + fix connector not load markets at init time --- src/clob/clob.requests.ts | 4 +++- src/connectors/xrpl/xrpl.ts | 30 +++++++++++++++++++++++++----- src/connectors/xrpl/xrpl.types.ts | 4 ++-- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/clob/clob.requests.ts b/src/clob/clob.requests.ts index 5975ba95f3..f0f2cc26bf 100644 --- a/src/clob/clob.requests.ts +++ b/src/clob/clob.requests.ts @@ -5,9 +5,11 @@ import { PerpetualMarket, Position, } from '@injectivelabs/sdk-ts'; -import { OrderType, Side } from '../amm/amm.requests'; import { NetworkSelectionRequest } from '../services/common-interfaces'; +export type OrderType = 'LIMIT' | 'LIMIT_MAKER' | 'MARKET'; +export type Side = 'BUY' | 'SELL'; + export interface ClobMarketsRequest extends NetworkSelectionRequest { market?: string; } diff --git a/src/connectors/xrpl/xrpl.ts b/src/connectors/xrpl/xrpl.ts index 75fc418bda..02290a2639 100644 --- a/src/connectors/xrpl/xrpl.ts +++ b/src/connectors/xrpl/xrpl.ts @@ -85,6 +85,11 @@ export class XRPLCLOB implements CLOBish { } public async loadMarkets() { + // Make a while loop and wait for the XRPL to be ready + while (!this._xrpl.ready()) { + await new Promise((resolve) => setTimeout(resolve, 100)); + } + const rawMarkets = await this.fetchMarkets(); for (const market of rawMarkets) { this.parsedMarkets[market.marketId] = market; @@ -92,11 +97,9 @@ export class XRPLCLOB implements CLOBish { } public async init() { - if (!this._xrpl.ready() || Object.keys(this.parsedMarkets).length === 0) { - await this._xrpl.init(); - await this.loadMarkets(); - this._ready = true; - } + await this._xrpl.init(); + await this.loadMarkets(); + this._ready = true; } public ready(): boolean { @@ -436,8 +439,25 @@ export class XRPLCLOB implements CLOBish { we_get.value = xrpToDrops(we_get.value); } + let flag = 0; + + switch (req.orderType) { + case 'LIMIT': + flag = 0; + break; + case 'LIMIT_MAKER': + flag = 65536; + break; + case 'MARKET': + flag = 524288; + break; + default: + throw new Error('Order type not supported'); + } + const offer: Transaction = { TransactionType: 'OfferCreate', + Flags: flag, Account: wallet.classicAddress, TakerGets: we_pay.currency == 'XRP' ? we_pay.value : we_pay, TakerPays: we_get.currency == 'XRP' ? we_get.value : we_get, diff --git a/src/connectors/xrpl/xrpl.types.ts b/src/connectors/xrpl/xrpl.types.ts index 71db38ae42..1c7eea59c8 100644 --- a/src/connectors/xrpl/xrpl.types.ts +++ b/src/connectors/xrpl/xrpl.types.ts @@ -31,10 +31,10 @@ export enum OrderStatus { export enum OrderType { LIMIT = 'LIMIT', - PASSIVE = 'PASSIVE', + LIMIT_MAKER = 'LIMIT_MAKER', // Limit Maker or Post Only Order IOC = 'IOC', // Immediate or Cancel FOK = 'FOK', // Fill or Kill - SELL = 'SELL', // Sell + MARKET = 'MARKET', // Sell or Market Order } export enum TransactionIntentType { From e451c054b94b68472d8b7c649299db8f3691e072 Mon Sep 17 00:00:00 2001 From: mlguys Date: Wed, 31 Jan 2024 18:05:52 +0700 Subject: [PATCH 29/35] Fix market order + update token and market list --- src/chains/xrpl/xrpl.ts | 4 ++++ src/connectors/xrpl/xrpl.ts | 19 ++++++++++++++++--- src/templates/lists/xrpl_markets.json | 26 +++++++++++++++++++++++--- src/templates/lists/xrpl_tokens.json | 2 +- 4 files changed, 44 insertions(+), 7 deletions(-) diff --git a/src/chains/xrpl/xrpl.ts b/src/chains/xrpl/xrpl.ts index f35d5cdfc2..f7a13a45ac 100644 --- a/src/chains/xrpl/xrpl.ts +++ b/src/chains/xrpl/xrpl.ts @@ -291,6 +291,10 @@ export class XRPL implements XRPLish { query = '534F4C4F00000000000000000000000000000000'; } + if (code === 'USDC') { + query = '5553444300000000000000000000000000000000' + } + return this._tokenMap[query] ? this._tokenMap[query] : undefined; } diff --git a/src/connectors/xrpl/xrpl.ts b/src/connectors/xrpl/xrpl.ts index 02290a2639..52fd557542 100644 --- a/src/connectors/xrpl/xrpl.ts +++ b/src/connectors/xrpl/xrpl.ts @@ -392,9 +392,22 @@ export class XRPLCLOB implements CLOBish { const quoteCurrency = market.quoteCurrency; const baseIssuer = market.baseIssuer; const quoteIssuer = market.quoteIssuer; + let price = parseFloat(req.price) + + // If it is market order + // Increase price by 3% if it is buy order + // Decrease price by 3% if it is sell order + if (req.orderType == 'MARKET') { + const midPrice = await this.getMidPriceForMarket(market); + if (req.side == TradeType.BUY) { + price = midPrice * 1.03; + } else { + price = midPrice * 0.97; + } + } const wallet = await this.getWallet(req.address); - const total = parseFloat(req.price) * parseFloat(req.amount); + const total = price * parseFloat(req.amount); let we_pay: Token = { currency: '', @@ -449,7 +462,7 @@ export class XRPLCLOB implements CLOBish { flag = 65536; break; case 'MARKET': - flag = 524288; + flag = 131072; break; default: throw new Error('Order type not supported'); @@ -479,7 +492,7 @@ export class XRPLCLOB implements CLOBish { filledAmount: '0', state: 'PENDING_OPEN', tradeType: req.side, - orderType: 'LIMIT', + orderType: req.orderType, createdAt: currentTime, createdAtLedgerIndex: currentLedgerIndex, updatedAt: currentTime, diff --git a/src/templates/lists/xrpl_markets.json b/src/templates/lists/xrpl_markets.json index ec4a4a1e9b..2b8fa51851 100644 --- a/src/templates/lists/xrpl_markets.json +++ b/src/templates/lists/xrpl_markets.json @@ -15,7 +15,7 @@ "baseIssuer": "rsoLo2S1kiGeCcn6hCUXVrCpGMWLrRrLZz", "baseCode": "534F4C4F00000000000000000000000000000000", "quoteIssuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", - "quoteCode": "USDC", + "quoteCode": "5553444300000000000000000000000000000000", "baseTokenID": 31, "quoteTokenID": 18465 }, @@ -23,7 +23,7 @@ "id": 3, "marketId": "USDC-XRP", "baseIssuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", - "baseCode": "USDC", + "baseCode": "5553444300000000000000000000000000000000", "quoteIssuer": "", "quoteCode": "XRP", "baseTokenID": 18465, @@ -33,7 +33,7 @@ "id": 4, "marketId": "USDC-USD", "baseIssuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", - "baseCode": "USDC", + "baseCode": "5553444300000000000000000000000000000000", "quoteIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", "quoteCode": "USD", "baseTokenID": 18465, @@ -69,5 +69,25 @@ "quoteCode": "USD", "baseTokenID": 0, "quoteTokenID": 16603 + }, + { + "id": 8, + "marketId": "SOLO-USD", + "baseIssuer": "rsoLo2S1kiGeCcn6hCUXVrCpGMWLrRrLZz", + "baseCode": "534F4C4F00000000000000000000000000000000", + "quoteIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "quoteCode": "USD", + "baseTokenID": 31, + "quoteTokenID": 16603 + }, + { + "id": 9, + "marketId": "XRP-USDC", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "quoteCode": "5553444300000000000000000000000000000000", + "baseTokenID": 0, + "quoteTokenID": 18465 } ] diff --git a/src/templates/lists/xrpl_tokens.json b/src/templates/lists/xrpl_tokens.json index c343b41892..f977435ef1 100644 --- a/src/templates/lists/xrpl_tokens.json +++ b/src/templates/lists/xrpl_tokens.json @@ -4225,7 +4225,7 @@ }, { "id": 18465, - "code": "USDC", + "code": "5553444300000000000000000000000000000000", "issuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", "title": "GateHub USDC", "trustlines": 3407, From 6431b0ea7556ada00c3ef8299291bd230568de0d Mon Sep 17 00:00:00 2001 From: nikspz <83953535+nikspz@users.noreply.github.com> Date: Fri, 2 Feb 2024 16:13:38 +0700 Subject: [PATCH 30/35] fix/ Update package.json to dev-1.25.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c362effe9b..7c8fbd97f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hummingbot-gateway", - "version": "dev-1.24.0", + "version": "dev-1.25.0", "description": "Middleware that helps Hummingbot clients access standardized DEX API endpoints on different blockchain networks", "main": "index.js", "license": "Apache-2.0", From 6590cb9089380101ef1a5cd54b5cbd725e34e3f7 Mon Sep 17 00:00:00 2001 From: mlguys Date: Mon, 12 Feb 2024 20:43:54 +0700 Subject: [PATCH 31/35] Add convertStringToHex function and update markets list --- src/chains/xrpl/xrpl.ts | 15 +- src/connectors/xrpl/xrpl.ts | 9 +- src/connectors/xrpl/xrpl.utils.ts | 12 + src/templates/lists/xrpl_markets.json | 515 +++++++++++++++++++++++--- src/templates/lists/xrpl_tokens.json | 25 +- 5 files changed, 499 insertions(+), 77 deletions(-) diff --git a/src/chains/xrpl/xrpl.ts b/src/chains/xrpl/xrpl.ts index f7a13a45ac..408915b7b2 100644 --- a/src/chains/xrpl/xrpl.ts +++ b/src/chains/xrpl/xrpl.ts @@ -45,8 +45,6 @@ export type MarketInfo = { baseCode: string; quoteIssuer: string; quoteCode: string; - baseTokenID: number; - quoteTokenID: number; }; export type Fee = { @@ -219,6 +217,8 @@ export class XRPL implements XRPLish { this.tokenList = await this.getTokenList(tokenListSource, tokenListType); if (this.tokenList) { this.tokenList.forEach((token: XRPTokenInfo) => { + //TODO: There are duplicate codes with different issuer, + // find way to resolve this. if (!this._tokenMap[token.code]) { this._tokenMap[token.code] = []; } @@ -284,16 +284,7 @@ export class XRPL implements XRPLish { } public getTokenForSymbol(code: string): XRPTokenInfo[] | undefined { - let query = code; - - // Special case for SOLO on mainnet - if (code === 'SOLO') { - query = '534F4C4F00000000000000000000000000000000'; - } - - if (code === 'USDC') { - query = '5553444300000000000000000000000000000000' - } + let query = convertHexToString(code); return this._tokenMap[query] ? this._tokenMap[query] : undefined; } diff --git a/src/connectors/xrpl/xrpl.ts b/src/connectors/xrpl/xrpl.ts index 52fd557542..c1b574ad8f 100644 --- a/src/connectors/xrpl/xrpl.ts +++ b/src/connectors/xrpl/xrpl.ts @@ -43,6 +43,7 @@ import { import LRUCache from 'lru-cache'; import { getXRPLConfig } from '../../chains/xrpl/xrpl.config'; import { isUndefined } from 'mathjs'; +import { convertStringToHex } from './xrpl.utils'; // const XRP_FACTOR = 1000000; const ORDERBOOK_LIMIT = 50; @@ -124,8 +125,8 @@ export class XRPLCLOB implements CLOBish { } const marketsAsArray: Array = []; - for (const market in this.parsedMarkets) { - marketsAsArray.push(this.parsedMarkets[market]); + for (const marketId in this.parsedMarkets) { + marketsAsArray.push(this.parsedMarkets[marketId]); } return { markets: marketsAsArray }; @@ -157,8 +158,8 @@ export class XRPLCLOB implements CLOBish { quoteTransferRate: number; const zeroTransferRate = 1000000000; - const baseCurrency = market.baseCode; - const quoteCurrency = market.quoteCode; + const baseCurrency = convertStringToHex(market.baseCode); + const quoteCurrency = convertStringToHex(market.quoteCode); const baseIssuer = market.baseIssuer; const quoteIssuer = market.quoteIssuer; diff --git a/src/connectors/xrpl/xrpl.utils.ts b/src/connectors/xrpl/xrpl.utils.ts index ba36bf655e..f1656c3c96 100644 --- a/src/connectors/xrpl/xrpl.utils.ts +++ b/src/connectors/xrpl/xrpl.utils.ts @@ -122,3 +122,15 @@ export function convertHexToString(hex: string): string { return hex; } + +export function convertStringToHex(str: string): string { + if (str.length > 3) { + let hex = Buffer.from(str).toString('hex'); + while (hex.length < 40) { + hex += '00'; // pad with zeros to reach 160 bits (40 hex characters) + } + return hex; + } + + return str; +} \ No newline at end of file diff --git a/src/templates/lists/xrpl_markets.json b/src/templates/lists/xrpl_markets.json index 2b8fa51851..8d55f78d90 100644 --- a/src/templates/lists/xrpl_markets.json +++ b/src/templates/lists/xrpl_markets.json @@ -1,93 +1,490 @@ [ { "id": 1, - "marketId": "SOLO-XRP", - "baseIssuer": "rsoLo2S1kiGeCcn6hCUXVrCpGMWLrRrLZz", - "baseCode": "534F4C4F00000000000000000000000000000000", - "quoteIssuer": "", - "quoteCode": "XRP", - "baseTokenID": 31, - "quoteTokenID": 0 + "marketId": "XRP-USD", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "quoteCode": "USD" }, { "id": 2, - "marketId": "SOLO-USDC", - "baseIssuer": "rsoLo2S1kiGeCcn6hCUXVrCpGMWLrRrLZz", - "baseCode": "534F4C4F00000000000000000000000000000000", - "quoteIssuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", - "quoteCode": "5553444300000000000000000000000000000000", - "baseTokenID": 31, - "quoteTokenID": 18465 + "marketId": "XRP-EUR", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "quoteCode": "EUR" }, { "id": 3, - "marketId": "USDC-XRP", - "baseIssuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", - "baseCode": "5553444300000000000000000000000000000000", - "quoteIssuer": "", - "quoteCode": "XRP", - "baseTokenID": 18465, - "quoteTokenID": 0 + "marketId": "XRP-GBP", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "r4GN9eEoz9K4BhMQXe4H1eYNtvtkwGdt8g", + "quoteCode": "GBP" }, { "id": 4, - "marketId": "USDC-USD", - "baseIssuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", - "baseCode": "5553444300000000000000000000000000000000", - "quoteIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", - "quoteCode": "USD", - "baseTokenID": 18465, - "quoteTokenID": 16603 + "marketId": "XRP-BTC", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rchGBxcD1A1C2tdxF6papQYZ8kjRKMYcL", + "quoteCode": "BTC" }, { "id": 5, - "marketId": "BTC-XRP", - "baseIssuer": "rchGBxcD1A1C2tdxF6papQYZ8kjRKMYcL", - "baseCode": "BTC", - "quoteIssuer": "", - "quoteCode": "XRP", - "baseTokenID": 1381, - "quoteTokenID": 0 + "marketId": "XRP-ETH", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rcA8X3TVMST1n3CJeAdGk1RdRCHii7N2h", + "quoteCode": "ETH" }, { "id": 6, - "marketId": "BTC-USD", - "baseIssuer": "rchGBxcD1A1C2tdxF6papQYZ8kjRKMYcL", - "baseCode": "BTC", - "quoteIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", - "quoteCode": "USD", - "baseTokenID": 1381, - "quoteTokenID": 16603 - } - , + "marketId": "XRP-LTC", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rcRzGWq6Ng3jeYhqnmM4zcWcUh69hrQ8V", + "quoteCode": "LTC" + }, { "id": 7, - "marketId": "XRP-USD", + "marketId": "XRP-CNY", "baseIssuer": "", "baseCode": "XRP", - "quoteIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", - "quoteCode": "USD", - "baseTokenID": 0, - "quoteTokenID": 16603 + "quoteIssuer": "rKiCet8SdvWxPXnAgYarFUXMh1zCPz432Y", + "quoteCode": "CNY" }, { "id": 8, - "marketId": "SOLO-USD", - "baseIssuer": "rsoLo2S1kiGeCcn6hCUXVrCpGMWLrRrLZz", - "baseCode": "534F4C4F00000000000000000000000000000000", - "quoteIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", - "quoteCode": "USD", - "baseTokenID": 31, - "quoteTokenID": 16603 + "marketId": "XRP-BCH", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rcyS4CeCZVYvTiKcxj6Sx32ibKwcDHLds", + "quoteCode": "BCH" }, { "id": 9, + "marketId": "XRP-ETC", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rDAN8tzydyNfnNf2bfUQY6iR96UbpvNsze", + "quoteCode": "ETC" + }, + { + "id": 10, + "marketId": "XRP-DSH", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rcXY84C4g14iFp6taFXjjQGVeHqSCh9RX", + "quoteCode": "DSH" + }, + { + "id": 11, + "marketId": "XRP-XAU", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rcoef87SYMJ58NAFx7fNM5frVknmvHsvJ", + "quoteCode": "XAU" + }, + { + "id": 12, + "marketId": "XRP-SGB", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rctArjqVvTHihekzDeecKo6mkTYTUSBNc", + "quoteCode": "SGB" + }, + { + "id": 13, + "marketId": "XRP-USDT", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rcvxE9PS9YBwxtGg1qNeewV6ZB3wGubZq", + "quoteCode": "USDT" + }, + { + "id": 14, "marketId": "XRP-USDC", "baseIssuer": "", "baseCode": "XRP", "quoteIssuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", - "quoteCode": "5553444300000000000000000000000000000000", - "baseTokenID": 0, - "quoteTokenID": 18465 + "quoteCode": "USDC" + }, + { + "id": 15, + "marketId": "XRP-WXRP", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rEa5QY8tdbjgitLyfKF1E5Qx3VGgvbUhB3", + "quoteCode": "WXRP" + }, + { + "id": 16, + "marketId": "XRP-GALA", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rf5YPb9y9P3fTjhxNaZqmrwaj5ar8PG1gM", + "quoteCode": "GALA" + }, + { + "id": 17, + "marketId": "XRP-FLR", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rcxJwVnftZzXqyH9YheB8TgeiZUhNo1Eu", + "quoteCode": "FLR" + }, + { + "id": 18, + "marketId": "XRP-XAH", + "baseIssuer": "", + "baseCode": "XRP", + "quoteIssuer": "rswh1fvyLqHizBS2awu1vs6QcmwTBd9qiv", + "quoteCode": "XAH" + }, + { + "id": 19, + "marketId": "USD-XRP", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "USD", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 20, + "marketId": "USD-EUR", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "USD", + "quoteIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "quoteCode": "EUR" + }, + { + "id": 21, + "marketId": "USD-GBP", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "USD", + "quoteIssuer": "r4GN9eEoz9K4BhMQXe4H1eYNtvtkwGdt8g", + "quoteCode": "GBP" + }, + { + "id": 22, + "marketId": "USD-BTC", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "USD", + "quoteIssuer": "rchGBxcD1A1C2tdxF6papQYZ8kjRKMYcL", + "quoteCode": "BTC" + }, + { + "id": 23, + "marketId": "USD-BCH", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "USD", + "quoteIssuer": "rcyS4CeCZVYvTiKcxj6Sx32ibKwcDHLds", + "quoteCode": "BCH" + }, + { + "id": 24, + "marketId": "USD-LTC", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "USD", + "quoteIssuer": "rcRzGWq6Ng3jeYhqnmM4zcWcUh69hrQ8V", + "quoteCode": "LTC" + }, + { + "id": 25, + "marketId": "USD.b-XRP", + "baseIssuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", + "baseCode": "USD", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 26, + "marketId": "USD-USDT", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "USD", + "quoteIssuer": "rcvxE9PS9YBwxtGg1qNeewV6ZB3wGubZq", + "quoteCode": "USDT" + }, + { + "id": 27, + "marketId": "USD-USDC", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "USD", + "quoteIssuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "quoteCode": "USDC" + }, + { + "id": 28, + "marketId": "USD-WXRP", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "USD", + "quoteIssuer": "rEa5QY8tdbjgitLyfKF1E5Qx3VGgvbUhB3", + "quoteCode": "WXRP" + }, + { + "id": 29, + "marketId": "USD-GALA", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "USD", + "quoteIssuer": "rf5YPb9y9P3fTjhxNaZqmrwaj5ar8PG1gM", + "quoteCode": "GALA" + }, + { + "id": 30, + "marketId": "USD-FLR", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "USD", + "quoteIssuer": "rcxJwVnftZzXqyH9YheB8TgeiZUhNo1Eu", + "quoteCode": "FLR" + }, + { + "id": 30, + "marketId": "EUR-XRP", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "EUR", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 31, + "marketId": "EUR-USD", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "EUR", + "quoteIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "quoteCode": "USD" + }, + { + "id": 32, + "marketId": "EUR-GBP", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "EUR", + "quoteIssuer": "r4GN9eEoz9K4BhMQXe4H1eYNtvtkwGdt8g", + "quoteCode": "GBP" + }, + { + "id": 33, + "marketId": "EUR-USD.b", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "EUR", + "quoteIssuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", + "quoteCode": "USD" + }, + { + "id": 34, + "marketId": "EUR-BTC", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "EUR", + "quoteIssuer": "rchGBxcD1A1C2tdxF6papQYZ8kjRKMYcL", + "quoteCode": "BTC" + }, + { + "id": 35, + "marketId": "EUR-BCH", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "EUR", + "quoteIssuer": "rcyS4CeCZVYvTiKcxj6Sx32ibKwcDHLds", + "quoteCode": "BCH" + }, + { + "id": 36, + "marketId": "EUR-LTC", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "EUR", + "quoteIssuer": "rcRzGWq6Ng3jeYhqnmM4zcWcUh69hrQ8V", + "quoteCode": "LTC" + }, + { + "id": 37, + "marketId": "EUR-USDT", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "EUR", + "quoteIssuer": "rcvxE9PS9YBwxtGg1qNeewV6ZB3wGubZq", + "quoteCode": "USDT" + }, + { + "id": 38, + "marketId": "EUR-USDC", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "EUR", + "quoteIssuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "quoteCode": "USDC" + }, + { + "id": 39, + "marketId": "EUR-WXRP", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "EUR", + "quoteIssuer": "rEa5QY8tdbjgitLyfKF1E5Qx3VGgvbUhB3", + "quoteCode": "WXRP" + }, + { + "id": 40, + "marketId": "EUR-GALA", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "EUR", + "quoteIssuer": "rf5YPb9y9P3fTjhxNaZqmrwaj5ar8PG1gM", + "quoteCode": "GALA" + }, + { + "id": 41, + "marketId": "EUR-FLR", + "baseIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "baseCode": "EUR", + "quoteIssuer": "rcxJwVnftZzXqyH9YheB8TgeiZUhNo1Eu", + "quoteCode": "FLR" + }, + { + "id": 42, + "marketId": "SGB-XRP", + "baseIssuer": "rctArjqVvTHihekzDeecKo6mkTYTUSBNc", + "baseCode": "SGB", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 43, + "marketId": "ELS-XRP", + "baseIssuer": "rHXuEaRYnnJHbDeuBH5w8yPh5uwNVh5zAg", + "baseCode": "ELS", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 44, + "marketId": "USDT-XRP", + "baseIssuer": "rcvxE9PS9YBwxtGg1qNeewV6ZB3wGubZq", + "baseCode": "USDT", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 45, + "marketId": "USDC-XRP", + "baseIssuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", + "baseCode": "USDC", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 46, + "marketId": "SOLO-XRP", + "baseIssuer": "rsoLo2S1kiGeCcn6hCUXVrCpGMWLrRrLZz", + "baseCode": "SOLO", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 47, + "marketId": "WXRP-XRP", + "baseIssuer": "rEa5QY8tdbjgitLyfKF1E5Qx3VGgvbUhB3", + "baseCode": "WXRP", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 48, + "marketId": "GALA-XRP", + "baseIssuer": "rf5YPb9y9P3fTjhxNaZqmrwaj5ar8PG1gM", + "baseCode": "GALA", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 49, + "marketId": "FLR-XRP", + "baseIssuer": "rcxJwVnftZzXqyH9YheB8TgeiZUhNo1Eu", + "baseCode": "FLR", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 50, + "marketId": "SOLO-USD", + "baseIssuer": "rsoLo2S1kiGeCcn6hCUXVrCpGMWLrRrLZz", + "baseCode": "SOLO", + "quoteIssuer": "rhub8VRN55s94qWKDv6jmDy1pUykJzF3wq", + "quoteCode": "USD" + }, + { + "id": 51, + "marketId": "SOLO-USD.b", + "baseIssuer": "rsoLo2S1kiGeCcn6hCUXVrCpGMWLrRrLZz", + "baseCode": "SOLO", + "quoteIssuer": "rvYAfWj5gh67oV6fW32ZzP3Aw4Eubs59B", + "quoteCode": "USD" + }, + { + "id": 52, + "marketId": "ICOIN-XRP", + "baseIssuer": "rJSTh1VLk52tFC3VRXkNWu7Q4nYmfZv7BZ", + "baseCode": "icoin", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 53, + "marketId": "CORE-XRP", + "baseIssuer": "rcoreNywaoz2ZCQ8Lg2EbSLnGuRBmun6D", + "baseCode": "CORE", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 54, + "marketId": "XMEME-XRP", + "baseIssuer": "r4UPddYeGeZgDhSGPkooURsQtmGda4oYQW", + "baseCode": "XMEME", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 55, + "marketId": "CSC-XRP", + "baseIssuer": "rCSCManTZ8ME9EoLrSHHYKW8PPwWMgkwr", + "baseCode": "CSC", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 56, + "marketId": "FURY-XRP", + "baseIssuer": "rnoKi9s9b6WYaNGWQy4qVdnKo6Lj2eHE1D", + "baseCode": "FURY", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 57, + "marketId": "XSPECTAR-XRP", + "baseIssuer": "rh5jzTCdMRCVjQ7LT6zucjezC47KATkuvv", + "baseCode": "xSPECTAR", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 58, + "marketId": "RPR-XRP", + "baseIssuer": "r3qWgpz2ry3BhcRJ8JE6rxM8esrfhuKp4R", + "baseCode": "RPR", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 59, + "marketId": "XRDOGE-XRP", + "baseIssuer": "rLqUC2eCPohYvJCEBJ77eCCqVL2uEiczjA", + "baseCode": "XRdoge", + "quoteIssuer": "", + "quoteCode": "XRP" + }, + { + "id": 60, + "marketId": "EQUILIBRIUM-XRP", + "baseIssuer": "rpakCr61Q92abPXJnVboKENmpKssWyHpwu", + "baseCode": "Equilibrium", + "quoteIssuer": "", + "quoteCode": "XRP" } ] diff --git a/src/templates/lists/xrpl_tokens.json b/src/templates/lists/xrpl_tokens.json index f977435ef1..af6ad9de2a 100644 --- a/src/templates/lists/xrpl_tokens.json +++ b/src/templates/lists/xrpl_tokens.json @@ -9,7 +9,7 @@ }, { "id": 31, - "code": "534F4C4F00000000000000000000000000000000", + "code": "SOLO", "issuer": "rsoLo2S1kiGeCcn6hCUXVrCpGMWLrRrLZz", "title": "Sologenic", "trustlines": 288976, @@ -4225,7 +4225,7 @@ }, { "id": 18465, - "code": "5553444300000000000000000000000000000000", + "code": "USDC", "issuer": "rcEGREd8NmkKRE8GE424sksyt1tJVFZwu", "title": "GateHub USDC", "trustlines": 3407, @@ -12374,5 +12374,26 @@ "issuer": "rPK6DvvoF8CVHGURSGNccCxKDXMnRAchHD", "trustlines": 443, "placeInTop": null + }, + { + "id": 99001, + "code": "XAH", + "issuer": "rswh1fvyLqHizBS2awu1vs6QcmwTBd9qiv", + "trustlines": 0, + "placeInTop": null + }, + { + "id": 99002, + "code": "FURY", + "issuer": "rnoKi9s9b6WYaNGWQy4qVdnKo6Lj2eHE1D", + "trustlines": 0, + "placeInTop": null + }, + { + "id": 99003, + "code": "XMEME", + "issuer": "r4UPddYeGeZgDhSGPkooURsQtmGda4oYQW", + "trustlines": 0, + "placeInTop": null } ] From 72ba6e26df02d437288cf73fc7b8049e1bac281a Mon Sep 17 00:00:00 2001 From: mlguys Date: Mon, 12 Feb 2024 21:25:37 +0700 Subject: [PATCH 32/35] Update XRPL network timeouts --- src/templates/xrpl.yml | 4 +- yarn.lock | 375 +++++++++++++++++++++-------------------- 2 files changed, 192 insertions(+), 187 deletions(-) diff --git a/src/templates/xrpl.yml b/src/templates/xrpl.yml index f5072942e8..8eaeccb4e7 100644 --- a/src/templates/xrpl.yml +++ b/src/templates/xrpl.yml @@ -21,8 +21,8 @@ networks: marketListSource: "/home/gateway/conf/lists/xrpl_markets_devnet.json" nativeCurrencySymbol: "XRP" network: "xrpl" -requestTimeout: 5000 -connectionTimeout: 5000 +requestTimeout: 10000 +connectionTimeout: 10000 feeCushion: 1.2 maxFeeXRP: "2" orderDbPath: "db/xrpl.level" diff --git a/yarn.lock b/yarn.lock index f0713bb9ad..f7a8456af1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1174,137 +1174,137 @@ "@ethersproject-xdc/abi@file:vendor/@ethersproject-xdc/abi": version "5.7.0" dependencies: - "@ethersproject-xdc/address" "file:vendor/@ethersproject-xdc/address" - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/constants" "file:vendor/@ethersproject-xdc/constants" - "@ethersproject-xdc/hash" "file:vendor/@ethersproject-xdc/hash" - "@ethersproject-xdc/keccak256" "file:vendor/@ethersproject-xdc/keccak256" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" - "@ethersproject-xdc/strings" "file:vendor/@ethersproject-xdc/strings" + "@ethersproject-xdc/address" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abi-5.7.0-b7b4af72-b45f-4077-b151-8f0741d50a55-1707746220367/node_modules/@ethersproject-xdc/address" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abi-5.7.0-b7b4af72-b45f-4077-b151-8f0741d50a55-1707746220367/node_modules/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abi-5.7.0-b7b4af72-b45f-4077-b151-8f0741d50a55-1707746220367/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/constants" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abi-5.7.0-b7b4af72-b45f-4077-b151-8f0741d50a55-1707746220367/node_modules/@ethersproject-xdc/constants" + "@ethersproject-xdc/hash" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abi-5.7.0-b7b4af72-b45f-4077-b151-8f0741d50a55-1707746220367/node_modules/@ethersproject-xdc/hash" + "@ethersproject-xdc/keccak256" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abi-5.7.0-b7b4af72-b45f-4077-b151-8f0741d50a55-1707746220367/node_modules/@ethersproject-xdc/keccak256" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abi-5.7.0-b7b4af72-b45f-4077-b151-8f0741d50a55-1707746220367/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abi-5.7.0-b7b4af72-b45f-4077-b151-8f0741d50a55-1707746220367/node_modules/@ethersproject-xdc/properties" + "@ethersproject-xdc/strings" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abi-5.7.0-b7b4af72-b45f-4077-b151-8f0741d50a55-1707746220367/node_modules/@ethersproject-xdc/strings" "@ethersproject-xdc/abstract-provider@file:vendor/@ethersproject-xdc/abstract-provider": version "5.7.0" dependencies: - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/networks" "file:vendor/@ethersproject-xdc/networks" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" - "@ethersproject-xdc/transactions" "file:vendor/@ethersproject-xdc/transactions" - "@ethersproject-xdc/web" "file:vendor/@ethersproject-xdc/web" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abstract-provider-5.7.0-7ed4cfe0-b44c-4745-88b9-854ebe82e0bf-1707746220366/node_modules/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abstract-provider-5.7.0-7ed4cfe0-b44c-4745-88b9-854ebe82e0bf-1707746220366/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abstract-provider-5.7.0-7ed4cfe0-b44c-4745-88b9-854ebe82e0bf-1707746220366/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/networks" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abstract-provider-5.7.0-7ed4cfe0-b44c-4745-88b9-854ebe82e0bf-1707746220366/node_modules/@ethersproject-xdc/networks" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abstract-provider-5.7.0-7ed4cfe0-b44c-4745-88b9-854ebe82e0bf-1707746220366/node_modules/@ethersproject-xdc/properties" + "@ethersproject-xdc/transactions" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abstract-provider-5.7.0-7ed4cfe0-b44c-4745-88b9-854ebe82e0bf-1707746220366/node_modules/@ethersproject-xdc/transactions" + "@ethersproject-xdc/web" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abstract-provider-5.7.0-7ed4cfe0-b44c-4745-88b9-854ebe82e0bf-1707746220366/node_modules/@ethersproject-xdc/web" "@ethersproject-xdc/abstract-signer@file:vendor/@ethersproject-xdc/abstract-signer": version "5.7.0" dependencies: - "@ethersproject-xdc/abstract-provider" "file:vendor/@ethersproject-xdc/abstract-provider" - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" + "@ethersproject-xdc/abstract-provider" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abstract-signer-5.7.0-881f7628-fc63-4936-a4d8-c3dd78e2ea10-1707746220367/node_modules/@ethersproject-xdc/abstract-provider" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abstract-signer-5.7.0-881f7628-fc63-4936-a4d8-c3dd78e2ea10-1707746220367/node_modules/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abstract-signer-5.7.0-881f7628-fc63-4936-a4d8-c3dd78e2ea10-1707746220367/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abstract-signer-5.7.0-881f7628-fc63-4936-a4d8-c3dd78e2ea10-1707746220367/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-abstract-signer-5.7.0-881f7628-fc63-4936-a4d8-c3dd78e2ea10-1707746220367/node_modules/@ethersproject-xdc/properties" "@ethersproject-xdc/address@file:vendor/@ethersproject-xdc/address": version "5.7.0" dependencies: - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/keccak256" "file:vendor/@ethersproject-xdc/keccak256" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/rlp" "file:vendor/@ethersproject-xdc/rlp" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-address-5.7.0-fe34d301-d5ee-4904-8ac4-bbd60ce306c5-1707746220367/node_modules/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-address-5.7.0-fe34d301-d5ee-4904-8ac4-bbd60ce306c5-1707746220367/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/keccak256" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-address-5.7.0-fe34d301-d5ee-4904-8ac4-bbd60ce306c5-1707746220367/node_modules/@ethersproject-xdc/keccak256" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-address-5.7.0-fe34d301-d5ee-4904-8ac4-bbd60ce306c5-1707746220367/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/rlp" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-address-5.7.0-fe34d301-d5ee-4904-8ac4-bbd60ce306c5-1707746220367/node_modules/@ethersproject-xdc/rlp" "@ethersproject-xdc/base64@file:vendor/@ethersproject-xdc/base64": version "5.7.0" dependencies: - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-base64-5.7.0-85441adf-e89e-45ba-8b8e-fe505e45b4e3-1707746220368/node_modules/@ethersproject-xdc/bytes" "@ethersproject-xdc/basex@file:vendor/@ethersproject-xdc/basex": version "5.7.0" dependencies: - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-basex-5.7.0-cffbf6ae-ebf3-40ac-8b29-03448e71bda2-1707746220368/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-basex-5.7.0-cffbf6ae-ebf3-40ac-8b29-03448e71bda2-1707746220368/node_modules/@ethersproject-xdc/properties" "@ethersproject-xdc/bignumber@file:vendor/@ethersproject-xdc/bignumber": version "5.7.0" dependencies: - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-bignumber-5.7.0-b9234e3c-743c-41d0-afa8-98626be8151a-1707746220368/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-bignumber-5.7.0-b9234e3c-743c-41d0-afa8-98626be8151a-1707746220368/node_modules/@ethersproject-xdc/logger" bn.js "^5.2.1" "@ethersproject-xdc/bytes@file:vendor/@ethersproject-xdc/bytes": version "5.7.0" dependencies: - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-bytes-5.7.0-54859421-d04c-49e3-af47-5b3796198683-1707746220368/node_modules/@ethersproject-xdc/logger" "@ethersproject-xdc/constants@file:vendor/@ethersproject-xdc/constants": version "5.7.0" dependencies: - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-constants-5.7.0-4cc25760-f72f-4028-aae8-3d9e690944ea-1707746220368/node_modules/@ethersproject-xdc/bignumber" "@ethersproject-xdc/contracts@file:vendor/@ethersproject-xdc/contracts": version "5.6.0" dependencies: - "@ethersproject-xdc/abi" "file:vendor/@ethersproject-xdc/abi" - "@ethersproject-xdc/abstract-provider" "file:vendor/@ethersproject-xdc/abstract-provider" - "@ethersproject-xdc/abstract-signer" "file:vendor/@ethersproject-xdc/abstract-signer" - "@ethersproject-xdc/address" "file:vendor/@ethersproject-xdc/address" - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/constants" "file:vendor/@ethersproject-xdc/constants" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" - "@ethersproject-xdc/transactions" "file:vendor/@ethersproject-xdc/transactions" + "@ethersproject-xdc/abi" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-contracts-5.6.0-7cd30a41-9596-405e-8019-6d01651e414c-1707746220369/node_modules/@ethersproject-xdc/abi" + "@ethersproject-xdc/abstract-provider" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-contracts-5.6.0-7cd30a41-9596-405e-8019-6d01651e414c-1707746220369/node_modules/@ethersproject-xdc/abstract-provider" + "@ethersproject-xdc/abstract-signer" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-contracts-5.6.0-7cd30a41-9596-405e-8019-6d01651e414c-1707746220369/node_modules/@ethersproject-xdc/abstract-signer" + "@ethersproject-xdc/address" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-contracts-5.6.0-7cd30a41-9596-405e-8019-6d01651e414c-1707746220369/node_modules/@ethersproject-xdc/address" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-contracts-5.6.0-7cd30a41-9596-405e-8019-6d01651e414c-1707746220369/node_modules/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-contracts-5.6.0-7cd30a41-9596-405e-8019-6d01651e414c-1707746220369/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/constants" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-contracts-5.6.0-7cd30a41-9596-405e-8019-6d01651e414c-1707746220369/node_modules/@ethersproject-xdc/constants" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-contracts-5.6.0-7cd30a41-9596-405e-8019-6d01651e414c-1707746220369/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-contracts-5.6.0-7cd30a41-9596-405e-8019-6d01651e414c-1707746220369/node_modules/@ethersproject-xdc/properties" + "@ethersproject-xdc/transactions" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-contracts-5.6.0-7cd30a41-9596-405e-8019-6d01651e414c-1707746220369/node_modules/@ethersproject-xdc/transactions" "@ethersproject-xdc/hash@file:vendor/@ethersproject-xdc/hash": version "5.7.0" dependencies: - "@ethersproject-xdc/abstract-signer" "file:vendor/@ethersproject-xdc/abstract-signer" - "@ethersproject-xdc/address" "file:vendor/@ethersproject-xdc/address" - "@ethersproject-xdc/base64" "file:vendor/@ethersproject-xdc/base64" - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/keccak256" "file:vendor/@ethersproject-xdc/keccak256" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" - "@ethersproject-xdc/strings" "file:vendor/@ethersproject-xdc/strings" + "@ethersproject-xdc/abstract-signer" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hash-5.7.0-6ecadd7b-375d-40b7-bab4-4e06c441aa07-1707746220370/node_modules/@ethersproject-xdc/abstract-signer" + "@ethersproject-xdc/address" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hash-5.7.0-6ecadd7b-375d-40b7-bab4-4e06c441aa07-1707746220370/node_modules/@ethersproject-xdc/address" + "@ethersproject-xdc/base64" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hash-5.7.0-6ecadd7b-375d-40b7-bab4-4e06c441aa07-1707746220370/node_modules/@ethersproject-xdc/base64" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hash-5.7.0-6ecadd7b-375d-40b7-bab4-4e06c441aa07-1707746220370/node_modules/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hash-5.7.0-6ecadd7b-375d-40b7-bab4-4e06c441aa07-1707746220370/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/keccak256" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hash-5.7.0-6ecadd7b-375d-40b7-bab4-4e06c441aa07-1707746220370/node_modules/@ethersproject-xdc/keccak256" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hash-5.7.0-6ecadd7b-375d-40b7-bab4-4e06c441aa07-1707746220370/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hash-5.7.0-6ecadd7b-375d-40b7-bab4-4e06c441aa07-1707746220370/node_modules/@ethersproject-xdc/properties" + "@ethersproject-xdc/strings" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hash-5.7.0-6ecadd7b-375d-40b7-bab4-4e06c441aa07-1707746220370/node_modules/@ethersproject-xdc/strings" "@ethersproject-xdc/hdnode@file:vendor/@ethersproject-xdc/hdnode": version "5.7.0" dependencies: - "@ethersproject-xdc/abstract-signer" "file:vendor/@ethersproject-xdc/abstract-signer" - "@ethersproject-xdc/basex" "file:vendor/@ethersproject-xdc/basex" - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/pbkdf2" "file:vendor/@ethersproject-xdc/pbkdf2" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" - "@ethersproject-xdc/sha2" "file:vendor/@ethersproject-xdc/sha2" - "@ethersproject-xdc/signing-key" "file:vendor/@ethersproject-xdc/signing-key" - "@ethersproject-xdc/strings" "file:vendor/@ethersproject-xdc/strings" - "@ethersproject-xdc/transactions" "file:vendor/@ethersproject-xdc/transactions" - "@ethersproject-xdc/wordlists" "file:vendor/@ethersproject-xdc/wordlists" + "@ethersproject-xdc/abstract-signer" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hdnode-5.7.0-0e95dde4-d63c-4291-a92d-e045705ed812-1707746220376/node_modules/@ethersproject-xdc/abstract-signer" + "@ethersproject-xdc/basex" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hdnode-5.7.0-0e95dde4-d63c-4291-a92d-e045705ed812-1707746220376/node_modules/@ethersproject-xdc/basex" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hdnode-5.7.0-0e95dde4-d63c-4291-a92d-e045705ed812-1707746220376/node_modules/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hdnode-5.7.0-0e95dde4-d63c-4291-a92d-e045705ed812-1707746220376/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hdnode-5.7.0-0e95dde4-d63c-4291-a92d-e045705ed812-1707746220376/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/pbkdf2" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hdnode-5.7.0-0e95dde4-d63c-4291-a92d-e045705ed812-1707746220376/node_modules/@ethersproject-xdc/pbkdf2" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hdnode-5.7.0-0e95dde4-d63c-4291-a92d-e045705ed812-1707746220376/node_modules/@ethersproject-xdc/properties" + "@ethersproject-xdc/sha2" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hdnode-5.7.0-0e95dde4-d63c-4291-a92d-e045705ed812-1707746220376/node_modules/@ethersproject-xdc/sha2" + "@ethersproject-xdc/signing-key" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hdnode-5.7.0-0e95dde4-d63c-4291-a92d-e045705ed812-1707746220376/node_modules/@ethersproject-xdc/signing-key" + "@ethersproject-xdc/strings" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hdnode-5.7.0-0e95dde4-d63c-4291-a92d-e045705ed812-1707746220376/node_modules/@ethersproject-xdc/strings" + "@ethersproject-xdc/transactions" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hdnode-5.7.0-0e95dde4-d63c-4291-a92d-e045705ed812-1707746220376/node_modules/@ethersproject-xdc/transactions" + "@ethersproject-xdc/wordlists" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-hdnode-5.7.0-0e95dde4-d63c-4291-a92d-e045705ed812-1707746220376/node_modules/@ethersproject-xdc/wordlists" "@ethersproject-xdc/json-wallets@file:vendor/@ethersproject-xdc/json-wallets": version "5.6.0" dependencies: - "@ethersproject-xdc/abstract-signer" "file:vendor/@ethersproject-xdc/abstract-signer" - "@ethersproject-xdc/address" "file:vendor/@ethersproject-xdc/address" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/hdnode" "file:vendor/@ethersproject-xdc/hdnode" - "@ethersproject-xdc/keccak256" "file:vendor/@ethersproject-xdc/keccak256" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/pbkdf2" "file:vendor/@ethersproject-xdc/pbkdf2" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" - "@ethersproject-xdc/random" "file:vendor/@ethersproject-xdc/random" - "@ethersproject-xdc/strings" "file:vendor/@ethersproject-xdc/strings" - "@ethersproject-xdc/transactions" "file:vendor/@ethersproject-xdc/transactions" + "@ethersproject-xdc/abstract-signer" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-json-wallets-5.6.0-3ecef9c3-62c3-437f-912d-922b4d3b52fe-1707746220369/node_modules/@ethersproject-xdc/abstract-signer" + "@ethersproject-xdc/address" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-json-wallets-5.6.0-3ecef9c3-62c3-437f-912d-922b4d3b52fe-1707746220369/node_modules/@ethersproject-xdc/address" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-json-wallets-5.6.0-3ecef9c3-62c3-437f-912d-922b4d3b52fe-1707746220369/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/hdnode" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-json-wallets-5.6.0-3ecef9c3-62c3-437f-912d-922b4d3b52fe-1707746220369/node_modules/@ethersproject-xdc/hdnode" + "@ethersproject-xdc/keccak256" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-json-wallets-5.6.0-3ecef9c3-62c3-437f-912d-922b4d3b52fe-1707746220369/node_modules/@ethersproject-xdc/keccak256" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-json-wallets-5.6.0-3ecef9c3-62c3-437f-912d-922b4d3b52fe-1707746220369/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/pbkdf2" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-json-wallets-5.6.0-3ecef9c3-62c3-437f-912d-922b4d3b52fe-1707746220369/node_modules/@ethersproject-xdc/pbkdf2" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-json-wallets-5.6.0-3ecef9c3-62c3-437f-912d-922b4d3b52fe-1707746220369/node_modules/@ethersproject-xdc/properties" + "@ethersproject-xdc/random" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-json-wallets-5.6.0-3ecef9c3-62c3-437f-912d-922b4d3b52fe-1707746220369/node_modules/@ethersproject-xdc/random" + "@ethersproject-xdc/strings" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-json-wallets-5.6.0-3ecef9c3-62c3-437f-912d-922b4d3b52fe-1707746220369/node_modules/@ethersproject-xdc/strings" + "@ethersproject-xdc/transactions" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-json-wallets-5.6.0-3ecef9c3-62c3-437f-912d-922b4d3b52fe-1707746220369/node_modules/@ethersproject-xdc/transactions" aes-js "3.0.0" scrypt-js "3.0.1" "@ethersproject-xdc/keccak256@file:vendor/@ethersproject-xdc/keccak256": version "5.7.0" dependencies: - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-keccak256-5.7.0-d933a273-2b5e-4549-80eb-b73ea608cfc2-1707746220374/node_modules/@ethersproject-xdc/bytes" js-sha3 "0.8.0" "@ethersproject-xdc/logger@file:vendor/@ethersproject-xdc/logger": @@ -1313,67 +1313,67 @@ "@ethersproject-xdc/networks@file:vendor/@ethersproject-xdc/networks": version "5.7.1" dependencies: - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-networks-5.7.1-b99d9e84-87b1-452f-9ba0-af541322b53f-1707746220369/node_modules/@ethersproject-xdc/logger" "@ethersproject-xdc/pbkdf2@file:vendor/@ethersproject-xdc/pbkdf2": version "5.7.0" dependencies: - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/sha2" "file:vendor/@ethersproject-xdc/sha2" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-pbkdf2-5.7.0-10fb5c74-3e6a-4ab9-abd6-7d1b45bb193b-1707746220370/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/sha2" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-pbkdf2-5.7.0-10fb5c74-3e6a-4ab9-abd6-7d1b45bb193b-1707746220370/node_modules/@ethersproject-xdc/sha2" "@ethersproject-xdc/properties@file:vendor/@ethersproject-xdc/properties": version "5.7.0" dependencies: - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-properties-5.7.0-72c88612-fe94-4441-93a9-df7fe5582399-1707746220369/node_modules/@ethersproject-xdc/logger" "@ethersproject-xdc/providers@file:vendor/@ethersproject-xdc/providers": version "5.6.2" dependencies: - "@ethersproject-xdc/abstract-provider" "file:vendor/@ethersproject-xdc/abstract-provider" - "@ethersproject-xdc/abstract-signer" "file:vendor/@ethersproject-xdc/abstract-signer" - "@ethersproject-xdc/address" "file:vendor/@ethersproject-xdc/address" - "@ethersproject-xdc/basex" "file:vendor/@ethersproject-xdc/basex" - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/constants" "file:vendor/@ethersproject-xdc/constants" - "@ethersproject-xdc/hash" "file:vendor/@ethersproject-xdc/hash" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/networks" "file:vendor/@ethersproject-xdc/networks" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" - "@ethersproject-xdc/random" "file:vendor/@ethersproject-xdc/random" - "@ethersproject-xdc/rlp" "file:vendor/@ethersproject-xdc/rlp" - "@ethersproject-xdc/sha2" "file:vendor/@ethersproject-xdc/sha2" - "@ethersproject-xdc/strings" "file:vendor/@ethersproject-xdc/strings" - "@ethersproject-xdc/transactions" "file:vendor/@ethersproject-xdc/transactions" - "@ethersproject-xdc/web" "file:vendor/@ethersproject-xdc/web" + "@ethersproject-xdc/abstract-provider" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/abstract-provider" + "@ethersproject-xdc/abstract-signer" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/abstract-signer" + "@ethersproject-xdc/address" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/address" + "@ethersproject-xdc/basex" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/basex" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/constants" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/constants" + "@ethersproject-xdc/hash" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/hash" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/networks" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/networks" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/properties" + "@ethersproject-xdc/random" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/random" + "@ethersproject-xdc/rlp" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/rlp" + "@ethersproject-xdc/sha2" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/sha2" + "@ethersproject-xdc/strings" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/strings" + "@ethersproject-xdc/transactions" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/transactions" + "@ethersproject-xdc/web" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-providers-5.6.2-720be74c-b4fe-4b1f-a23a-4a16ab69a557-1707746220372/node_modules/@ethersproject-xdc/web" bech32 "1.1.4" ws "7.4.6" "@ethersproject-xdc/random@file:vendor/@ethersproject-xdc/random": version "5.7.0" dependencies: - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-random-5.7.0-30cabe5f-7588-453e-8a94-444f15e4c9d4-1707746220370/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-random-5.7.0-30cabe5f-7588-453e-8a94-444f15e4c9d4-1707746220370/node_modules/@ethersproject-xdc/logger" "@ethersproject-xdc/rlp@file:vendor/@ethersproject-xdc/rlp": version "5.7.0" dependencies: - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-rlp-5.7.0-f9c07616-1cb8-4a4e-b317-4b4e407b78f3-1707746220370/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-rlp-5.7.0-f9c07616-1cb8-4a4e-b317-4b4e407b78f3-1707746220370/node_modules/@ethersproject-xdc/logger" "@ethersproject-xdc/sha2@file:vendor/@ethersproject-xdc/sha2": version "5.7.0" dependencies: - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-sha2-5.7.0-9195cf3e-2f5e-4298-a931-ddcfd128d73c-1707746220371/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-sha2-5.7.0-9195cf3e-2f5e-4298-a931-ddcfd128d73c-1707746220371/node_modules/@ethersproject-xdc/logger" hash.js "1.1.7" "@ethersproject-xdc/signing-key@file:vendor/@ethersproject-xdc/signing-key": version "5.7.0" dependencies: - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-signing-key-5.7.0-38058fe6-de4a-46aa-99d5-01e4e1f17325-1707746220371/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-signing-key-5.7.0-38058fe6-de4a-46aa-99d5-01e4e1f17325-1707746220371/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-signing-key-5.7.0-38058fe6-de4a-46aa-99d5-01e4e1f17325-1707746220371/node_modules/@ethersproject-xdc/properties" bn.js "^5.2.1" elliptic "6.5.4" hash.js "1.1.7" @@ -1381,76 +1381,76 @@ "@ethersproject-xdc/solidity@file:vendor/@ethersproject-xdc/solidity": version "5.6.0" dependencies: - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/keccak256" "file:vendor/@ethersproject-xdc/keccak256" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/sha2" "file:vendor/@ethersproject-xdc/sha2" - "@ethersproject-xdc/strings" "file:vendor/@ethersproject-xdc/strings" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-solidity-5.6.0-f8bdd66a-813b-450f-8fdf-5001c4db924d-1707746220373/node_modules/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-solidity-5.6.0-f8bdd66a-813b-450f-8fdf-5001c4db924d-1707746220373/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/keccak256" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-solidity-5.6.0-f8bdd66a-813b-450f-8fdf-5001c4db924d-1707746220373/node_modules/@ethersproject-xdc/keccak256" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-solidity-5.6.0-f8bdd66a-813b-450f-8fdf-5001c4db924d-1707746220373/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/sha2" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-solidity-5.6.0-f8bdd66a-813b-450f-8fdf-5001c4db924d-1707746220373/node_modules/@ethersproject-xdc/sha2" + "@ethersproject-xdc/strings" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-solidity-5.6.0-f8bdd66a-813b-450f-8fdf-5001c4db924d-1707746220373/node_modules/@ethersproject-xdc/strings" "@ethersproject-xdc/strings@file:vendor/@ethersproject-xdc/strings": version "5.7.0" dependencies: - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/constants" "file:vendor/@ethersproject-xdc/constants" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-strings-5.7.0-6290db1a-50f2-4106-9d3b-ace409fab9d8-1707746220373/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/constants" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-strings-5.7.0-6290db1a-50f2-4106-9d3b-ace409fab9d8-1707746220373/node_modules/@ethersproject-xdc/constants" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-strings-5.7.0-6290db1a-50f2-4106-9d3b-ace409fab9d8-1707746220373/node_modules/@ethersproject-xdc/logger" "@ethersproject-xdc/transactions@file:vendor/@ethersproject-xdc/transactions": version "5.7.0" dependencies: - "@ethersproject-xdc/address" "file:vendor/@ethersproject-xdc/address" - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/constants" "file:vendor/@ethersproject-xdc/constants" - "@ethersproject-xdc/keccak256" "file:vendor/@ethersproject-xdc/keccak256" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" - "@ethersproject-xdc/rlp" "file:vendor/@ethersproject-xdc/rlp" - "@ethersproject-xdc/signing-key" "file:vendor/@ethersproject-xdc/signing-key" + "@ethersproject-xdc/address" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-transactions-5.7.0-dc7d6b77-752e-444d-ba26-6d7efb0a56f0-1707746220375/node_modules/@ethersproject-xdc/address" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-transactions-5.7.0-dc7d6b77-752e-444d-ba26-6d7efb0a56f0-1707746220375/node_modules/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-transactions-5.7.0-dc7d6b77-752e-444d-ba26-6d7efb0a56f0-1707746220375/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/constants" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-transactions-5.7.0-dc7d6b77-752e-444d-ba26-6d7efb0a56f0-1707746220375/node_modules/@ethersproject-xdc/constants" + "@ethersproject-xdc/keccak256" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-transactions-5.7.0-dc7d6b77-752e-444d-ba26-6d7efb0a56f0-1707746220375/node_modules/@ethersproject-xdc/keccak256" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-transactions-5.7.0-dc7d6b77-752e-444d-ba26-6d7efb0a56f0-1707746220375/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-transactions-5.7.0-dc7d6b77-752e-444d-ba26-6d7efb0a56f0-1707746220375/node_modules/@ethersproject-xdc/properties" + "@ethersproject-xdc/rlp" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-transactions-5.7.0-dc7d6b77-752e-444d-ba26-6d7efb0a56f0-1707746220375/node_modules/@ethersproject-xdc/rlp" + "@ethersproject-xdc/signing-key" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-transactions-5.7.0-dc7d6b77-752e-444d-ba26-6d7efb0a56f0-1707746220375/node_modules/@ethersproject-xdc/signing-key" "@ethersproject-xdc/units@file:vendor/@ethersproject-xdc/units": version "5.6.0" dependencies: - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" - "@ethersproject-xdc/constants" "file:vendor/@ethersproject-xdc/constants" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-units-5.6.0-514c593c-0809-476b-a3b1-4237333d3639-1707746220374/node_modules/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/constants" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-units-5.6.0-514c593c-0809-476b-a3b1-4237333d3639-1707746220374/node_modules/@ethersproject-xdc/constants" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-units-5.6.0-514c593c-0809-476b-a3b1-4237333d3639-1707746220374/node_modules/@ethersproject-xdc/logger" "@ethersproject-xdc/wallet@file:vendor/@ethersproject-xdc/wallet": version "5.6.0" dependencies: - "@ethersproject-xdc/abstract-provider" "file:vendor/@ethersproject-xdc/abstract-provider" - "@ethersproject-xdc/abstract-signer" "file:vendor/@ethersproject-xdc/abstract-signer" - "@ethersproject-xdc/address" "file:vendor/@ethersproject-xdc/address" - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/hash" "file:vendor/@ethersproject-xdc/hash" - "@ethersproject-xdc/hdnode" "file:vendor/@ethersproject-xdc/hdnode" - "@ethersproject-xdc/json-wallets" "file:vendor/@ethersproject-xdc/json-wallets" - "@ethersproject-xdc/keccak256" "file:vendor/@ethersproject-xdc/keccak256" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" - "@ethersproject-xdc/random" "file:vendor/@ethersproject-xdc/random" - "@ethersproject-xdc/signing-key" "file:vendor/@ethersproject-xdc/signing-key" - "@ethersproject-xdc/transactions" "file:vendor/@ethersproject-xdc/transactions" - "@ethersproject-xdc/wordlists" "file:vendor/@ethersproject-xdc/wordlists" + "@ethersproject-xdc/abstract-provider" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/abstract-provider" + "@ethersproject-xdc/abstract-signer" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/abstract-signer" + "@ethersproject-xdc/address" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/address" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/hash" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/hash" + "@ethersproject-xdc/hdnode" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/hdnode" + "@ethersproject-xdc/json-wallets" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/json-wallets" + "@ethersproject-xdc/keccak256" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/keccak256" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/properties" + "@ethersproject-xdc/random" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/random" + "@ethersproject-xdc/signing-key" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/signing-key" + "@ethersproject-xdc/transactions" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/transactions" + "@ethersproject-xdc/wordlists" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wallet-5.6.0-fc5769d5-fff3-4cc7-a16a-28f96f7ba315-1707746220374/node_modules/@ethersproject-xdc/wordlists" "@ethersproject-xdc/web@file:vendor/@ethersproject-xdc/web": version "5.7.1" dependencies: - "@ethersproject-xdc/base64" "file:vendor/@ethersproject-xdc/base64" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" - "@ethersproject-xdc/strings" "file:vendor/@ethersproject-xdc/strings" + "@ethersproject-xdc/base64" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-web-5.7.1-3861e2ee-bbca-4081-ae05-f6881a31c492-1707746220377/node_modules/@ethersproject-xdc/base64" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-web-5.7.1-3861e2ee-bbca-4081-ae05-f6881a31c492-1707746220377/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-web-5.7.1-3861e2ee-bbca-4081-ae05-f6881a31c492-1707746220377/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-web-5.7.1-3861e2ee-bbca-4081-ae05-f6881a31c492-1707746220377/node_modules/@ethersproject-xdc/properties" + "@ethersproject-xdc/strings" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-web-5.7.1-3861e2ee-bbca-4081-ae05-f6881a31c492-1707746220377/node_modules/@ethersproject-xdc/strings" "@ethersproject-xdc/wordlists@file:vendor/@ethersproject-xdc/wordlists": version "5.7.0" dependencies: - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/hash" "file:vendor/@ethersproject-xdc/hash" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" - "@ethersproject-xdc/strings" "file:vendor/@ethersproject-xdc/strings" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wordlists-5.7.0-5a7df4e3-9685-48ee-a435-82e237b3b004-1707746220376/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/hash" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wordlists-5.7.0-5a7df4e3-9685-48ee-a435-82e237b3b004-1707746220376/node_modules/@ethersproject-xdc/hash" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wordlists-5.7.0-5a7df4e3-9685-48ee-a435-82e237b3b004-1707746220376/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wordlists-5.7.0-5a7df4e3-9685-48ee-a435-82e237b3b004-1707746220376/node_modules/@ethersproject-xdc/properties" + "@ethersproject-xdc/strings" "file:../../../../Library/Caches/Yarn/v6/npm-@ethersproject-xdc-wordlists-5.7.0-5a7df4e3-9685-48ee-a435-82e237b3b004-1707746220376/node_modules/@ethersproject-xdc/strings" "@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.0.12", "@ethersproject/abi@^5.1.2", "@ethersproject/abi@^5.4.0", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.6.3", "@ethersproject/abi@^5.7.0": version "5.7.0" @@ -1540,7 +1540,7 @@ dependencies: "@ethersproject/bignumber" "^5.7.0" -"@ethersproject/contracts@5.7.0", "@ethersproject/contracts@^5.0.1", "@ethersproject/contracts@^5.4.0": +"@ethersproject/contracts@5.7.0", "@ethersproject/contracts@^5.0.1", "@ethersproject/contracts@^5.4.0", "@ethersproject/contracts@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== @@ -1685,7 +1685,7 @@ bech32 "1.1.4" ws "7.4.6" -"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.0.4", "@ethersproject/providers@^5.4.0": +"@ethersproject/providers@5.7.2", "@ethersproject/providers@^5.0.4", "@ethersproject/providers@^5.4.0", "@ethersproject/providers@^5.7.2": version "5.7.2" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== @@ -1784,7 +1784,7 @@ "@ethersproject/rlp" "^5.7.0" "@ethersproject/signing-key" "^5.7.0" -"@ethersproject/units@5.7.0": +"@ethersproject/units@5.7.0", "@ethersproject/units@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== @@ -7876,36 +7876,36 @@ ethereumjs-util@^6.0.0, ethereumjs-util@^6.2.1: "ethers-xdc@file:./vendor/ethers-xdc": version "5.7.2" dependencies: - "@ethersproject-xdc/abi" "file:vendor/@ethersproject-xdc/abi" - "@ethersproject-xdc/abstract-provider" "file:vendor/@ethersproject-xdc/abstract-provider" - "@ethersproject-xdc/abstract-signer" "file:vendor/@ethersproject-xdc/abstract-signer" - "@ethersproject-xdc/address" "file:vendor/@ethersproject-xdc/address" - "@ethersproject-xdc/base64" "file:vendor/@ethersproject-xdc/base64" - "@ethersproject-xdc/basex" "file:vendor/@ethersproject-xdc/basex" - "@ethersproject-xdc/bignumber" "file:vendor/@ethersproject-xdc/bignumber" - "@ethersproject-xdc/bytes" "file:vendor/@ethersproject-xdc/bytes" - "@ethersproject-xdc/constants" "file:vendor/@ethersproject-xdc/constants" - "@ethersproject-xdc/contracts" "file:vendor/@ethersproject-xdc/contracts" - "@ethersproject-xdc/hash" "file:vendor/@ethersproject-xdc/hash" - "@ethersproject-xdc/hdnode" "file:vendor/@ethersproject-xdc/hdnode" - "@ethersproject-xdc/json-wallets" "file:vendor/@ethersproject-xdc/json-wallets" - "@ethersproject-xdc/keccak256" "file:vendor/@ethersproject-xdc/keccak256" - "@ethersproject-xdc/logger" "file:vendor/@ethersproject-xdc/logger" - "@ethersproject-xdc/networks" "file:vendor/@ethersproject-xdc/networks" - "@ethersproject-xdc/pbkdf2" "file:vendor/@ethersproject-xdc/pbkdf2" - "@ethersproject-xdc/properties" "file:vendor/@ethersproject-xdc/properties" - "@ethersproject-xdc/providers" "file:vendor/@ethersproject-xdc/providers" - "@ethersproject-xdc/random" "file:vendor/@ethersproject-xdc/random" - "@ethersproject-xdc/rlp" "file:vendor/@ethersproject-xdc/rlp" - "@ethersproject-xdc/sha2" "file:vendor/@ethersproject-xdc/sha2" - "@ethersproject-xdc/signing-key" "file:vendor/@ethersproject-xdc/signing-key" - "@ethersproject-xdc/solidity" "file:vendor/@ethersproject-xdc/solidity" - "@ethersproject-xdc/strings" "file:vendor/@ethersproject-xdc/strings" - "@ethersproject-xdc/transactions" "file:vendor/@ethersproject-xdc/transactions" - "@ethersproject-xdc/units" "file:vendor/@ethersproject-xdc/units" - "@ethersproject-xdc/wallet" "file:vendor/@ethersproject-xdc/wallet" - "@ethersproject-xdc/web" "file:vendor/@ethersproject-xdc/web" - "@ethersproject-xdc/wordlists" "file:vendor/@ethersproject-xdc/wordlists" + "@ethersproject-xdc/abi" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/abi" + "@ethersproject-xdc/abstract-provider" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/abstract-provider" + "@ethersproject-xdc/abstract-signer" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/abstract-signer" + "@ethersproject-xdc/address" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/address" + "@ethersproject-xdc/base64" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/base64" + "@ethersproject-xdc/basex" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/basex" + "@ethersproject-xdc/bignumber" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/bignumber" + "@ethersproject-xdc/bytes" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/bytes" + "@ethersproject-xdc/constants" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/constants" + "@ethersproject-xdc/contracts" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/contracts" + "@ethersproject-xdc/hash" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/hash" + "@ethersproject-xdc/hdnode" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/hdnode" + "@ethersproject-xdc/json-wallets" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/json-wallets" + "@ethersproject-xdc/keccak256" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/keccak256" + "@ethersproject-xdc/logger" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/logger" + "@ethersproject-xdc/networks" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/networks" + "@ethersproject-xdc/pbkdf2" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/pbkdf2" + "@ethersproject-xdc/properties" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/properties" + "@ethersproject-xdc/providers" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/providers" + "@ethersproject-xdc/random" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/random" + "@ethersproject-xdc/rlp" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/rlp" + "@ethersproject-xdc/sha2" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/sha2" + "@ethersproject-xdc/signing-key" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/signing-key" + "@ethersproject-xdc/solidity" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/solidity" + "@ethersproject-xdc/strings" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/strings" + "@ethersproject-xdc/transactions" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/transactions" + "@ethersproject-xdc/units" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/units" + "@ethersproject-xdc/wallet" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/wallet" + "@ethersproject-xdc/web" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/web" + "@ethersproject-xdc/wordlists" "file:../../../../Library/Caches/Yarn/v6/npm-ethers-xdc-5.7.2-116f9596-15f0-41b7-b3dc-b958163dbaf9-1707746220349/node_modules/@ethersproject-xdc/wordlists" ethers@4.0.0-beta.3: version "4.0.0-beta.3" @@ -8010,6 +8010,11 @@ eventemitter3@^4.0.7: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +events@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" From 71efd7490ddee63ecdde3c2a80a5cf0342270d21 Mon Sep 17 00:00:00 2001 From: mlguys Date: Thu, 15 Feb 2024 13:53:20 +0700 Subject: [PATCH 33/35] Convert string to uppercase in convertStringToHex function --- src/connectors/xrpl/xrpl.utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connectors/xrpl/xrpl.utils.ts b/src/connectors/xrpl/xrpl.utils.ts index f1656c3c96..bc43127bf9 100644 --- a/src/connectors/xrpl/xrpl.utils.ts +++ b/src/connectors/xrpl/xrpl.utils.ts @@ -129,7 +129,7 @@ export function convertStringToHex(str: string): string { while (hex.length < 40) { hex += '00'; // pad with zeros to reach 160 bits (40 hex characters) } - return hex; + return hex.toUpperCase(); } return str; From 1c9a498484dfc0d81ce5e215f61d7ff7134818c2 Mon Sep 17 00:00:00 2001 From: mlguys Date: Sun, 18 Feb 2024 23:22:15 +0700 Subject: [PATCH 34/35] add retry and set submittx to submit and wait --- src/chains/xrpl/xrpl.ts | 32 ++++++++++++++++++++++++-------- src/connectors/xrpl/xrpl.ts | 2 +- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/chains/xrpl/xrpl.ts b/src/chains/xrpl/xrpl.ts index 408915b7b2..9a3463c7d0 100644 --- a/src/chains/xrpl/xrpl.ts +++ b/src/chains/xrpl/xrpl.ts @@ -21,7 +21,7 @@ import { rootPath } from '../../paths'; import { TokenListType, walletPath, MarketListType } from '../../services/base'; import { ConfigManagerCertPassphrase } from '../../services/config-manager-cert-passphrase'; import { getXRPLConfig } from './xrpl.config'; -// import { logger } from '../../services/logger'; +import { logger } from '../../services/logger'; import { TransactionResponseStatusCode, TokenBalance } from './xrpl.requests'; import { XRPLOrderStorage } from './xrpl.order-storage'; import { OrderTracker } from './xrpl.order-tracker'; @@ -54,6 +54,9 @@ export type Fee = { openLedger: string; }; +const MAX_POLL_RETRY = 5; +const POLL_RETRY_INTERVAL = 300; + export class XRPL implements XRPLish { private static _instances: { [name: string]: XRPL }; public rpcUrl; @@ -505,15 +508,28 @@ export class XRPL implements XRPLish { async getTransaction(txHash: string): Promise { await this.ensureConnection(); - const tx_resp = await this._client.request({ - command: 'tx', - transaction: txHash, - binary: false, - }); + let retryCount = 0; + let result: any = undefined; + while (retryCount < MAX_POLL_RETRY && result === undefined) { + try { + const tx_resp = await this._client.request({ + command: 'tx', + transaction: txHash, + binary: false, + }); - const result = tx_resp; + result = tx_resp; + } catch (error) { + retryCount++; + if (retryCount >= 5) { + throw new Error(`Transaction ${txHash} not found, error: ` + String(error)); + } + logger.info(`Transaction ${txHash} not found, retrying ${retryCount}/${MAX_POLL_RETRY}...`); + await new Promise(resolve => setTimeout(resolve, POLL_RETRY_INTERVAL)); // Add delay + } + } - return result; + return result as TxResponse; } async close() { diff --git a/src/connectors/xrpl/xrpl.ts b/src/connectors/xrpl/xrpl.ts index c1b574ad8f..bff3bb3f23 100644 --- a/src/connectors/xrpl/xrpl.ts +++ b/src/connectors/xrpl/xrpl.ts @@ -568,7 +568,7 @@ export class XRPLCLOB implements CLOBish { const prepared = await this._client.autofill(offer); const signed = wallet.sign(prepared); await this._xrpl.ensureConnection(); - await this._client.submit(signed.tx_blob); + await this._client.submitAndWait(signed.tx_blob); this._isSubmittingTxn = false; return { prepared, signed }; } From 527d8a559dc27017e69677b41e9e99ab17c39419 Mon Sep 17 00:00:00 2001 From: mlguys Date: Sun, 18 Feb 2024 23:56:13 +0700 Subject: [PATCH 35/35] Increase batch for fetching markets --- src/connectors/xrpl/xrpl.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/connectors/xrpl/xrpl.ts b/src/connectors/xrpl/xrpl.ts index bff3bb3f23..75c3de54dd 100644 --- a/src/connectors/xrpl/xrpl.ts +++ b/src/connectors/xrpl/xrpl.ts @@ -44,6 +44,7 @@ import LRUCache from 'lru-cache'; import { getXRPLConfig } from '../../chains/xrpl/xrpl.config'; import { isUndefined } from 'mathjs'; import { convertStringToHex } from './xrpl.utils'; +import { logger } from '../../services/logger'; // const XRP_FACTOR = 1000000; const ORDERBOOK_LIMIT = 50; @@ -145,7 +146,9 @@ export class XRPLCLOB implements CLOBish { loadedMarkets.push(processedMarket); }; - await promiseAllInBatches(getMarket, markets, 1, 1); + logger.info(`Fetching markets for ${this.chain} ${this.network}`); + + await promiseAllInBatches(getMarket, markets, 15, 300); return loadedMarkets; }