forked from hummingbot/gateway
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: create globiance connector
- Loading branch information
Showing
15 changed files
with
1,323 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<string>; | ||
availableNetworks: Array<AvailableNetworks>; | ||
} | ||
|
||
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'] }], | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<string, Token> = {}; | ||
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<ExpectedTrade> { | ||
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<ExpectedTrade> { | ||
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<Transaction> { | ||
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; | ||
} | ||
} |
Oops, something went wrong.