-
Notifications
You must be signed in to change notification settings - Fork 15
Add trade quote utility #60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
05f9027
d85ae3d
eb873b7
3548d1c
eb01164
4e8a0f1
d7c7de7
d317dc3
9ccac88
45b0b6b
9231a41
42a7b20
d8a7256
c7c5ac6
b5b52f0
3940d82
8d76606
e4a87aa
2c360f0
140edb3
c0b8941
184406f
324f6a4
4c81300
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -23,8 +23,23 @@ import { TransactionOverrides } from '@setprotocol/set-protocol-v2/dist/typechai | |||
import { BigNumber } from 'ethers/lib/ethers'; | ||||
|
||||
import TradeModuleWrapper from '../wrappers/set-protocol-v2/TradeModuleWrapper'; | ||||
import SetTokenAPI from './SetTokenAPI'; | ||||
import Assertions from '../assertions'; | ||||
|
||||
import { | ||||
TradeQuoter, | ||||
CoinGeckoDataService, | ||||
GasOracleService | ||||
} from './utils'; | ||||
|
||||
import { | ||||
TradeQuote, | ||||
CoinGeckoTokenData, | ||||
CoinGeckoTokenMap, | ||||
GasOracleSpeed, | ||||
CoinGeckoCoinPrices | ||||
} from '../types'; | ||||
|
||||
/** | ||||
* @title TradeAPI | ||||
* @author Set Protocol | ||||
|
@@ -36,14 +51,20 @@ import Assertions from '../assertions'; | |||
export default class TradeAPI { | ||||
private tradeModuleWrapper: TradeModuleWrapper; | ||||
private assert: Assertions; | ||||
private provider: Provider; | ||||
private tradeQuoter: TradeQuoter; | ||||
private coinGecko: CoinGeckoDataService; | ||||
private chainId: number; | ||||
|
||||
public constructor( | ||||
provider: Provider, | ||||
tradeModuleAddress: Address, | ||||
assertions?: Assertions | ||||
zeroExApiKey?: string, | ||||
) { | ||||
this.provider = provider; | ||||
this.tradeModuleWrapper = new TradeModuleWrapper(provider, tradeModuleAddress); | ||||
this.assert = assertions || new Assertions(); | ||||
this.assert = new Assertions(); | ||||
this.tradeQuoter = new TradeQuoter(zeroExApiKey); | ||||
} | ||||
|
||||
/** | ||||
|
@@ -113,4 +134,130 @@ export default class TradeAPI { | |||
txOpts | ||||
); | ||||
} | ||||
|
||||
/** | ||||
* Call 0x API to generate a trade quote for two SetToken components. | ||||
* | ||||
* @param fromToken Address of token being sold | ||||
* @param toToken Address of token being bought | ||||
* @param fromTokenDecimals Token decimals of token being sold (ex: 18) | ||||
* @param toTokenDecimals Token decimals of token being bought (ex: 18) | ||||
* @param rawAmount String quantity of token to sell (ex: "0.5") | ||||
* @param fromAddress SetToken address which holds the buy / sell components | ||||
* @param setToken SetTokenAPI instance | ||||
* @param gasPrice (Optional) gasPrice to calculate gas costs with (Default: fetched from GasNow) | ||||
* @param slippagePercentage (Optional) maximum slippage, determines min receive quantity. (Default: 2%) | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you grab these defaults from the service? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes... from heroku, here: https://dashboard.heroku.com/apps/set-core-production/settings The whole number percentage is then divided by 100 before being passed to Zero Ex here: set.js/src/api/utils/tradeQuoter.ts Line 231 in 2c360f0
|
||||
* @param isFirmQuote (Optional) Whether quote request is indicative or firm | ||||
* @param feePercentage (Optional) Default: 0 | ||||
* @param feeRecipient (Optional) Default: 0xD3D555Bb655AcBA9452bfC6D7cEa8cC7b3628C55 | ||||
* @param excludedSources (Optional) Exchanges to exclude (Default: ['Kyber', 'Eth2Dai', 'Uniswap', 'Mesh']) | ||||
* | ||||
* @return {Promise<TradeQuote>} | ||||
*/ | ||||
public async fetchTradeQuoteAsync( | ||||
fromToken: Address, | ||||
toToken: Address, | ||||
fromTokenDecimals: number, | ||||
toTokenDecimals: number, | ||||
rawAmount: string, | ||||
fromAddress: Address, | ||||
setToken: SetTokenAPI, | ||||
gasPrice?: number, | ||||
slippagePercentage?: number, | ||||
isFirmQuote?: boolean, | ||||
feePercentage?: number, | ||||
feeRecipient?: Address, | ||||
excludedSources?: string[], | ||||
): Promise<TradeQuote> { | ||||
this.assert.schema.isValidAddress('fromToken', fromToken); | ||||
this.assert.schema.isValidAddress('toToken', toToken); | ||||
this.assert.schema.isValidAddress('fromAddress', fromAddress); | ||||
this.assert.schema.isValidJsNumber('fromTokenDecimals', fromTokenDecimals); | ||||
this.assert.schema.isValidJsNumber('toTokenDecimals', toTokenDecimals); | ||||
this.assert.schema.isValidString('rawAmount', rawAmount); | ||||
|
||||
const chainId = (await this.provider.getNetwork()).chainId; | ||||
|
||||
return this.tradeQuoter.generate({ | ||||
fromToken, | ||||
toToken, | ||||
fromTokenDecimals, | ||||
toTokenDecimals, | ||||
rawAmount, | ||||
fromAddress, | ||||
chainId, | ||||
tradeModule: this.tradeModuleWrapper, | ||||
provider: this.provider, | ||||
setToken, | ||||
gasPrice, | ||||
slippagePercentage, | ||||
isFirmQuote, | ||||
feePercentage, | ||||
feeRecipient, | ||||
excludedSources, | ||||
}); | ||||
} | ||||
|
||||
/** | ||||
* Fetches a list of tokens and their metadata from CoinGecko. Each entry includes | ||||
* the token's address, proper name, decimals, exchange symbol and a logo URI if available. | ||||
* For Ethereum, this is a list of tokens tradeable on Uniswap, for Polygon it's a list of | ||||
* tokens tradeable on Sushiswap's Polygon exchange. Method is useful for acquiring token decimals | ||||
* necessary to generate a trade quote and images for representing available tokens in a UI. | ||||
* | ||||
* @return List of tradeable tokens for chain platform | ||||
*/ | ||||
public async fetchTokenListAsync(): Promise<CoinGeckoTokenData[]> { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just want to confirm this is not currently being consumed? It was part of the old flow for grabbing the decimals? This might belong in a different utils type of API There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're using this stuff in the admin UI. This API is helpful I think. |
||||
await this.initializeForChain(); | ||||
return this.coinGecko.fetchTokenList(); | ||||
} | ||||
|
||||
/** | ||||
* Fetches the same info as `fetchTokenList` in the form of a map indexed by address. Method is | ||||
* useful if you're cacheing the token list and want quick lookups for a variety of trades. | ||||
* | ||||
* @return Map of token addresses to token metadata | ||||
*/ | ||||
public async fetchTokenMapAsync(): Promise<CoinGeckoTokenMap> { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same |
||||
await this.initializeForChain(); | ||||
return this.coinGecko.fetchTokenMap(); | ||||
} | ||||
|
||||
/** | ||||
* Fetches a list of prices vs currencies for the specified inputs from CoinGecko | ||||
* | ||||
* @param contractAddresses String array of contract addresses | ||||
* @param vsCurrencies String array of currency codes (see CoinGecko api for a complete list) | ||||
* | ||||
* @return List of prices vs currencies | ||||
*/ | ||||
public async fetchCoinPricesAsync( | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think most of these below belong in a different API, perhaps ERC20 API or something of the sort |
||||
contractAddresses: string[], | ||||
vsCurrencies: string[] | ||||
): Promise<CoinGeckoCoinPrices> { | ||||
await this.initializeForChain(); | ||||
return this.coinGecko.fetchCoinPrices({contractAddresses, vsCurrencies}); | ||||
} | ||||
|
||||
/** | ||||
* Fetches the recommended gas price for a specified execution speed. | ||||
* | ||||
* @param speed (Optional) string value: "average" | "fast" | "fastest" (Default: fast) | ||||
* | ||||
* @return Number: gas price | ||||
*/ | ||||
public async fetchGasPriceAsync(speed?: GasOracleSpeed): Promise<number> { | ||||
await this.initializeForChain(); | ||||
const oracle = new GasOracleService(this.chainId); | ||||
return oracle.fetchGasPrice(speed); | ||||
} | ||||
|
||||
|
||||
private async initializeForChain() { | ||||
if (this.coinGecko === undefined) { | ||||
const network = await this.provider.getNetwork(); | ||||
this.chainId = network.chainId; | ||||
this.coinGecko = new CoinGeckoDataService(network.chainId); | ||||
} | ||||
} | ||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if people can generate their own API keys for 0x. We may have to supply it for it to be used by anyone else
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@asoong based on the statement from their docs it's not possible to request one. If I understand correctly it will still works but I just may not get the best deal available, right?