From 2cb786719ebdfc576c787bbc14f376b7ee78424b Mon Sep 17 00:00:00 2001 From: Daniel Liu Date: Wed, 22 Mar 2023 14:40:28 +0800 Subject: [PATCH] refactor: create globiance connector --- package.json | 1 + src/app.ts | 2 + src/chains/ethereum/ethereum.validators.ts | 1 + src/chains/xdc/xdc.ts | 3 + src/chains/xdc/xdc.validators.ts | 4 +- src/connectors/connectors.routes.ts | 6 + src/connectors/globiance/globiance.config.ts | 22 + src/connectors/globiance/globiance.ts | 234 +++++ .../globiance/globiance_v2_router_abi.json | 975 ++++++++++++++++++ src/services/common-interfaces.ts | 10 + src/services/connection-manager.ts | 3 + src/services/schema/globiance-schema.json | 30 + src/templates/globiance.yml | 17 + src/templates/root.yml | 4 + yarn.lock | 13 + 15 files changed, 1323 insertions(+), 2 deletions(-) create mode 100644 src/connectors/globiance/globiance.config.ts create mode 100644 src/connectors/globiance/globiance.ts create mode 100644 src/connectors/globiance/globiance_v2_router_abi.json create mode 100644 src/services/schema/globiance-schema.json create mode 100644 src/templates/globiance.yml diff --git a/package.json b/package.json index e8c60a2208..0d4763d929 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,7 @@ "express": "^4.17.1", "express-winston": "^4.1.0", "fs-extra": "^10.0.0", + "globiance-sdk": "^1.0.0-beta", "js-yaml": "^4.1.0", "level": "^8.0.0", "lodash": "^4.17.21", diff --git a/src/app.ts b/src/app.ts index 210037d779..4b596d6ece 100644 --- a/src/app.ts +++ b/src/app.ts @@ -22,6 +22,7 @@ import { MadMeerkatConfig } from './connectors/mad_meerkat/mad_meerkat.config'; import { PangolinConfig } from './connectors/pangolin/pangolin.config'; import { QuickswapConfig } from './connectors/quickswap/quickswap.config'; import { XsswapConfig } from './connectors/xsswap/xsswap.config'; +import { GlobianceConfig } from './connectors/globiance/globiance.config'; import { TraderjoeConfig } from './connectors/traderjoe/traderjoe.config'; import { UniswapConfig } from './connectors/uniswap/uniswap.config'; import { OpenoceanConfig } from './connectors/openocean/openocean.config'; @@ -85,6 +86,7 @@ gatewayApp.get( pangolin: PangolinConfig.config.availableNetworks, quickswap: QuickswapConfig.config.availableNetworks, xsswap: XsswapConfig.config.availableNetworks, + globiance: GlobianceConfig.config.availableNetworks, sushiswap: SushiswapConfig.config.availableNetworks, openocean: OpenoceanConfig.config.availableNetworks, traderjoe: TraderjoeConfig.config.availableNetworks, diff --git a/src/chains/ethereum/ethereum.validators.ts b/src/chains/ethereum/ethereum.validators.ts index 0c050d722d..755314acfa 100644 --- a/src/chains/ethereum/ethereum.validators.ts +++ b/src/chains/ethereum/ethereum.validators.ts @@ -58,6 +58,7 @@ export const validateSpender: Validator = mkValidator( val === 'openocean' || val === 'quickswap' || val === 'xsswap' || + val === 'globiance' || val === 'defikingdoms' || val === 'defira' || val === 'mad_meerkat' || diff --git a/src/chains/xdc/xdc.ts b/src/chains/xdc/xdc.ts index 102bf18972..34156b09b1 100644 --- a/src/chains/xdc/xdc.ts +++ b/src/chains/xdc/xdc.ts @@ -5,6 +5,7 @@ import { EthereumBase } from '../ethereum/ethereum-base'; import { getEthereumConfig as getPolygonConfig } from '../ethereum/ethereum.config'; import { Provider } from '@ethersproject/abstract-provider'; import { XsswapConfig } from '../../connectors/xsswap/xsswap.config'; +import { GlobianceConfig } from '../../connectors/globiance/globiance.config'; import { Ethereumish } from '../../services/common-interfaces'; import { ConfigManagerV2 } from '../../services/config-manager-v2'; @@ -67,6 +68,8 @@ export class Xdc extends EthereumBase implements Ethereumish { let spender: string; if (reqSpender === 'xsswap') { spender = XsswapConfig.config.routerAddress(this._chain); + } else if (reqSpender === 'globiance') { + spender = GlobianceConfig.config.routerAddress(this._chain); } else { spender = reqSpender; } diff --git a/src/chains/xdc/xdc.validators.ts b/src/chains/xdc/xdc.validators.ts index 614d61d2ed..b4a875bec7 100644 --- a/src/chains/xdc/xdc.validators.ts +++ b/src/chains/xdc/xdc.validators.ts @@ -16,11 +16,11 @@ import { export const invalidSpenderError: string = 'The spender param is not a valid xdc address (0x followed by 40 hexidecimal characters).'; -// given a request, look for a key called spender that is 'xsswap' or an Ethereum address +// given a request, look for a key called spender that is 'xsswap' or 'globiance' or an Ethereum address export const validateSpender: Validator = mkValidator( 'spender', invalidSpenderError, - (val) => typeof val === 'string' && (val === 'xsswap' || isAddress(val)) + (val) => typeof val === 'string' && (val === 'xsswap' || val === 'globiance' || isAddress(val)) ); export const validateXdcApproveRequest: RequestValidator = diff --git a/src/connectors/connectors.routes.ts b/src/connectors/connectors.routes.ts index 1308f8ff29..9000134c78 100644 --- a/src/connectors/connectors.routes.ts +++ b/src/connectors/connectors.routes.ts @@ -8,6 +8,7 @@ import { PangolinConfig } from './pangolin/pangolin.config'; import { PerpConfig } from './perp/perp.config'; import { QuickswapConfig } from './quickswap/quickswap.config'; import { XsswapConfig } from './xsswap/xsswap.config'; +import { GlobianceConfig } from './globiance/globiance.config'; import { SushiswapConfig } from './sushiswap/sushiswap.config'; import { TraderjoeConfig } from './traderjoe/traderjoe.config'; import { UniswapConfig } from './uniswap/uniswap.config'; @@ -56,6 +57,11 @@ export namespace ConnectorsRoutes { trading_type: XsswapConfig.config.tradingTypes, available_networks: XsswapConfig.config.availableNetworks, }, + { + name: 'globiance', + trading_type: GlobianceConfig.config.tradingTypes, + available_networks: GlobianceConfig.config.availableNetworks, + }, { name: 'perp', trading_type: PerpConfig.config.tradingTypes('perp'), diff --git a/src/connectors/globiance/globiance.config.ts b/src/connectors/globiance/globiance.config.ts new file mode 100644 index 0000000000..29c183c2f8 --- /dev/null +++ b/src/connectors/globiance/globiance.config.ts @@ -0,0 +1,22 @@ +import { ConfigManagerV2 } from '../../services/config-manager-v2'; +import { AvailableNetworks } from '../../services/config-manager-types'; + +export namespace GlobianceConfig { + export interface NetworkConfig { + allowedSlippage: string; + gasLimitEstimate: number; + ttl: number; + routerAddress: (network: string) => string; + tradingTypes: Array; + availableNetworks: Array; + } + + export const config: NetworkConfig = { + allowedSlippage: ConfigManagerV2.getInstance().get('globiance.allowedSlippage'), + gasLimitEstimate: ConfigManagerV2.getInstance().get('globiance.gasLimitEstimate'), + ttl: ConfigManagerV2.getInstance().get('globiance.ttl'), + routerAddress: (network: string) => ConfigManagerV2.getInstance().get('globiance.contractAddresses.' + network + '.routerAddress'), + tradingTypes: ['EVM_AMM'], + availableNetworks: [{ chain: 'xdc', networks: ['xinfin', 'apothem'] }], + }; +} \ No newline at end of file diff --git a/src/connectors/globiance/globiance.ts b/src/connectors/globiance/globiance.ts new file mode 100644 index 0000000000..a9ea7ca149 --- /dev/null +++ b/src/connectors/globiance/globiance.ts @@ -0,0 +1,234 @@ +import { percentRegexp } from '../../services/config-manager-v2'; +import { UniswapishPriceError } from '../../services/error-handler'; +import { + BigNumber, + Contract, + ContractInterface, + Transaction, + Wallet, +} from 'ethers'; +import { isFractionString } from '../../services/validators'; +import { GlobianceConfig } from './globiance.config'; +import routerAbi from './globiance_v2_router_abi.json'; +import { + Fetcher, + Percent, + Router, + Token, + TokenAmount, + Trade, + Pair +} from 'globiance-sdk'; +import { logger } from '../../services/logger'; +import { Xdc } from '../../chains/xdc/xdc'; +import { ExpectedTrade, Uniswapish } from '../../services/common-interfaces'; + +export class Globiance implements Uniswapish { + private static _instances: { [name: string]: Globiance }; + private xdc: Xdc; + private _router: string; + private _routerAbi: ContractInterface; + private _gasLimitEstimate: number; + private _ttl: number; + private chainId; + private tokenList: Record = {}; + private _ready: boolean = false; + + private constructor(network: string) { + const config = GlobianceConfig.config; + this.xdc = Xdc.getInstance(network); + this.chainId = this.xdc.chainId; + this._router = config.routerAddress(network); + this._ttl = config.ttl; + this._routerAbi = routerAbi.abi; + this._gasLimitEstimate = config.gasLimitEstimate; + } + + public static getInstance(chain: string, network: string): Globiance { + if (Globiance._instances === undefined) { + Globiance._instances = {}; + } + if (!(chain + network in Globiance._instances)) { + Globiance._instances[chain + network] = new Globiance(network); + } + + return Globiance._instances[chain + network]; + } + + /** + * Given a token's address, return the connector's native representation of + * the token. + * + * @param address Token address + */ + public getTokenByAddress(address: string): Token { + return this.tokenList[address]; + } + + public async init() { + if (!this.xdc.ready()) { + await this.xdc.init(); + } + for (const token of this.xdc.storedTokenList) { + this.tokenList[token.address] = new Token(this.chainId, token.address, token.decimals, token.symbol, token.name); + } + this._ready = true; + } + + public ready(): boolean { + return this._ready; + } + + /** + * Router address. + */ + public get router(): string { + return this._router; + } + + /** + * Router smart contract ABI. + */ + public get routerAbi(): ContractInterface { + return this._routerAbi; + } + + /** + * Default gas limit used to estimate cost for swap transactions. + */ + public get gasLimitEstimate(): number { + return this._gasLimitEstimate; + } + + /** + * Default time-to-live for swap transactions, in seconds. + */ + public get ttl(): number { + return this._ttl; + } + + /** + * 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): Percent { + if (allowedSlippageStr != null && isFractionString(allowedSlippageStr)) { + const fractionSplit = allowedSlippageStr.split('/'); + return new Percent(fractionSplit[0], fractionSplit[1]); + } + + const allowedSlippage = GlobianceConfig.config.allowedSlippage; + const nd = allowedSlippage.match(percentRegexp); + if (nd) return new Percent(nd[1], nd[2]); + throw new Error('Encountered a malformed percent string in the config for ALLOWED_SLIPPAGE.'); + } + + /** + * 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, allowedSlippage?: string): Promise { + const nativeTokenAmount: TokenAmount = new TokenAmount(baseToken, amount.toString()); + logger.info(`Fetching pair data for ${baseToken.address}-${quoteToken.address}.`); + const pair: Pair = await Fetcher.fetchPairData(baseToken, quoteToken, this.xdc.provider); + const trades: Trade[] = Trade.bestTradeExactIn([pair], nativeTokenAmount, quoteToken, { maxHops: 1 }); + if (!trades || trades.length === 0) { + throw new UniswapishPriceError(`priceSwapIn: no trade pair found for ${baseToken} to ${quoteToken}.`); + } + logger.info(`Best trade for ${baseToken.address}-${quoteToken.address}: ${trades[0]}`); + const expectedAmount = trades[0].minimumAmountOut(this.getAllowedSlippage(allowedSlippage)); + return { trade: trades[0], expectedAmount }; + } + + /** + * 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 + */ + async estimateBuyTrade(quoteToken: Token, baseToken: Token, amount: BigNumber, allowedSlippage?: string): Promise { + const nativeTokenAmount: TokenAmount = new TokenAmount(baseToken, amount.toString()); + logger.info(`Fetching pair data for ${quoteToken.address}-${baseToken.address}.`); + const pair: Pair = await Fetcher.fetchPairData(quoteToken, baseToken, this.xdc.provider); + const trades: Trade[] = Trade.bestTradeExactOut([pair], quoteToken, nativeTokenAmount, { maxHops: 1 }); + if (!trades || trades.length === 0) { + throw new UniswapishPriceError(`priceSwapOut: no trade pair found for ${quoteToken.address} to ${baseToken.address}.`); + } + logger.info(`Best trade for ${quoteToken.address}-${baseToken.address}: ${trades[0]}`); + + const expectedAmount = trades[0].maximumAmountIn(this.getAllowedSlippage(allowedSlippage)); + return { trade: trades[0], 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 globianceRouter 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: Trade, + gasPrice: number, + globianceRouter: string, + ttl: number, + abi: ContractInterface, + gasLimit: number, + nonce?: number, + maxFeePerGas?: BigNumber, + maxPriorityFeePerGas?: BigNumber, + allowedSlippage?: string + ): Promise { + const result = Router.swapCallParameters(trade, { + ttl, + recipient: wallet.address, + allowedSlippage: this.getAllowedSlippage(allowedSlippage), + }); + + const contract = new Contract(globianceRouter, abi, wallet); + if (!nonce) { + nonce = await this.xdc.nonceManager.getNextNonce(wallet.address); + } + let tx; + if (maxFeePerGas || maxPriorityFeePerGas) { + tx = await contract[result.methodName](...result.args, { + gasLimit: gasLimit.toFixed(0), + value: result.value, + nonce: nonce, + maxFeePerGas, + maxPriorityFeePerGas, + }); + } else { + tx = await contract[result.methodName](...result.args, { + gasPrice: (gasPrice * 1e9).toFixed(0), + gasLimit: gasLimit.toFixed(0), + value: result.value, + nonce: nonce, + }); + } + + logger.info(tx); + await this.xdc.nonceManager.commitNonce(wallet.address, nonce); + return tx; + } +} \ No newline at end of file diff --git a/src/connectors/globiance/globiance_v2_router_abi.json b/src/connectors/globiance/globiance_v2_router_abi.json new file mode 100644 index 0000000000..96c20e112e --- /dev/null +++ b/src/connectors/globiance/globiance_v2_router_abi.json @@ -0,0 +1,975 @@ +{ + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_factory", + "type": "address" + }, + { + "internalType": "address", + "name": "_WETH", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "WETH", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountADesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBDesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "addLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amountTokenDesired", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "addLiquidityETH", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "factory", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveOut", + "type": "uint256" + } + ], + "name": "getAmountIn", + "outputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveOut", + "type": "uint256" + } + ], + "name": "getAmountOut", + "outputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + } + ], + "name": "getAmountsIn", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + } + ], + "name": "getAmountsOut", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reserveB", + "type": "uint256" + } + ], + "name": "quote", + "outputs": [ + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidity", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidityETH", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "removeLiquidityETHSupportingFeeOnTransferTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityETHWithPermit", + "outputs": [ + { + "internalType": "uint256", + "name": "amountToken", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountTokenMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountETHMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityETHWithPermitSupportingFeeOnTransferTokens", + "outputs": [ + { + "internalType": "uint256", + "name": "amountETH", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "liquidity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountAMin", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountBMin", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "approveMax", + "type": "bool" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "removeLiquidityWithPermit", + "outputs": [ + { + "internalType": "uint256", + "name": "amountA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountB", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapETHForExactTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactETHForTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactETHForTokensSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForETH", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForETHSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountIn", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountOutMin", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapExactTokensForTokensSupportingFeeOnTransferTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMax", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapTokensForExactETH", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amountOut", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amountInMax", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "path", + "type": "address[]" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + } + ], + "name": "swapTokensForExactTokens", + "outputs": [ + { + "internalType": "uint256[]", + "name": "amounts", + "type": "uint256[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } + ] +} diff --git a/src/services/common-interfaces.ts b/src/services/common-interfaces.ts index a80e6943b5..b01809919e 100644 --- a/src/services/common-interfaces.ts +++ b/src/services/common-interfaces.ts @@ -45,6 +45,12 @@ import { Trade as TradeXsswap, Fraction as XsswapFraction, } from 'xsswap-sdk'; +import { + Token as TokenGlobiance, + CurrencyAmount as CurrencyAmountGlobiance, + Trade as TradeGlobiance, + Fraction as GlobianceFraction +} from 'globiance-sdk'; import { Trade as SushiswapTrade, Token as SushiToken, @@ -106,6 +112,7 @@ export type Tokenish = | UniswapCoreToken | TokenQuickswap | TokenXsswap + | TokenGlobiance | TokenTraderjoe | UniswapCoreToken | SushiToken @@ -128,6 +135,7 @@ export type UniswapishTrade = | UniswapV3Trade | TradeQuickswap | TradeXsswap + | TradeGlobiance | TradeTraderjoe | SushiswapTrade | UniswapV3Trade @@ -151,6 +159,7 @@ export type UniswapishAmount = | CurrencyAmountPangolin | CurrencyAmountQuickswap | CurrencyAmountXsswap + | CurrencyAmountGlobiance | UniswapCoreCurrencyAmount | CurrencyAmountTraderjoe | SushiCurrencyAmount @@ -164,6 +173,7 @@ export type Fractionish = | PangolinFraction | QuickswapFraction | XsswapFraction + | GlobianceFraction | TraderjoeFraction | SushiFraction | DefikingdomsFraction diff --git a/src/services/connection-manager.ts b/src/services/connection-manager.ts index 3af48cb655..7f21ee8082 100644 --- a/src/services/connection-manager.ts +++ b/src/services/connection-manager.ts @@ -11,6 +11,7 @@ import { Pangolin } from '../connectors/pangolin/pangolin'; import { Perp } from '../connectors/perp/perp'; import { Quickswap } from '../connectors/quickswap/quickswap'; import { Xsswap } from '../connectors/xsswap/xsswap'; +import { Globiance } from '../connectors/globiance/globiance'; import { PancakeSwap } from '../connectors/pancakeswap/pancakeswap'; import { Uniswap } from '../connectors/uniswap/uniswap'; import { UniswapLP } from '../connectors/uniswap/uniswap.lp'; @@ -92,6 +93,8 @@ export async function getConnector( connectorInstance = Quickswap.getInstance(chain, network); } else if (chain === 'xdc' && connector === 'xsswap') { connectorInstance = Xsswap.getInstance(chain, network); + } else if (chain === 'xdc' && connector === 'globiance') { + connectorInstance = Globiance.getInstance(chain, network); } else if ( (chain === 'ethereum' || chain === 'polygon') && connector === 'uniswapLP' diff --git a/src/services/schema/globiance-schema.json b/src/services/schema/globiance-schema.json new file mode 100644 index 0000000000..9505b66fe2 --- /dev/null +++ b/src/services/schema/globiance-schema.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "allowedSlippage": { "type": "string" }, + "gasLimitEstimate": { "type": "integer" }, + "ttl": { "type": "integer" }, + "contractAddresses": { + "type": "object", + "patternProperties": { + "^\\w+$": { + "type": "object", + "properties": { + "routerAddress": { "type": "string" } + }, + "required": ["routerAddress"], + "additionalProperties": false + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": [ + "allowedSlippage", + "gasLimitEstimate", + "ttl", + "contractAddresses" + ] +} diff --git a/src/templates/globiance.yml b/src/templates/globiance.yml new file mode 100644 index 0000000000..2fbb549d24 --- /dev/null +++ b/src/templates/globiance.yml @@ -0,0 +1,17 @@ +# allowedSlippage: how much the execution price is allowed to move unfavorably +# from the trade execution price. It uses a rational number for precision. +allowedSlippage: '2/100' + +# the maximum gas used to estimate cost of a globiance trade. +gasLimitEstimate: 300000 + +# ttl: how long a trade is valid in seconds. After this time passes +# globiance will not perform the trade, but the gas will still be sent. +ttl: 600 + +contractAddresses: + # constant used for each supported network + xinfin: + routerAddress: '0x90055EdC794e839567a5631d42752dB732E10C8F' + apothem: + routerAddress: '0x35Ea4174c56cf87961875994d8bc7D5EcF2F8d45' \ No newline at end of file diff --git a/src/templates/root.yml b/src/templates/root.yml index 52c434af01..cd1d1e53db 100644 --- a/src/templates/root.yml +++ b/src/templates/root.yml @@ -44,6 +44,10 @@ configurations: configurationPath: xsswap.yml schemaPath: xsswap-schema.json + $namespace globiance: + configurationPath: globiance.yml + schemaPath: globiance-schema.json + $namespace perp: configurationPath: perp.yml schemaPath: perp-schema.json diff --git a/yarn.lock b/yarn.lock index 75b75bf5e7..309f51aa1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7735,6 +7735,19 @@ globby@^11.0.3: merge2 "^1.4.1" slash "^3.0.0" +globiance-sdk@^1.0.0-beta: + version "1.0.0-beta" + resolved "https://registry.yarnpkg.com/globiance-sdk/-/globiance-sdk-1.0.0-beta.tgz#4ca2f2700c2a55e190852150496e251c8ebdbded" + integrity sha512-2BObozQeWjZpW4TePe9PPe1AzAQehgSl8+XVcylJk7zWjtH2FVt+3wcAy8XL/etCNUpg9S0lPa3SeAn2EvkJeA== + dependencies: + "@uniswap/v2-core" "^1.0.0" + big.js "^5.2.2" + decimal.js-light "^2.5.0" + jsbi "^3.1.1" + tiny-invariant "^1.1.0" + tiny-warning "^1.0.3" + toformat "^2.0.0" + golang-cover-parse@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/golang-cover-parse/-/golang-cover-parse-2.0.0.tgz#6bac2e5a88c1fb26468fa8d605aa52b4b8d12119"