Skip to content

Commit

Permalink
Merge pull request #350 from SwaprHQ/develop
Browse files Browse the repository at this point in the history
release: 1.11.4
  • Loading branch information
ElRodrigote authored Mar 22, 2024
2 parents 1679546 + 212a542 commit ac2c007
Show file tree
Hide file tree
Showing 11 changed files with 386 additions and 8 deletions.
7 changes: 3 additions & 4 deletions graphql-codegen.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@


overwrite: true
schema: "https://api.thegraph.com/subgraphs/name/dxgraphs/swapr-xdai-v2"
schema: 'https://api.thegraph.com/subgraphs/name/dxgraphs/swapr-xdai-v2'
documents: 'src/**/!(*.d).{ts,tsx}'
generates:
./src/generated/graphql/index.ts:
plugins:
- typescript
- typescript-operations
- typescript-graphql-request
- typescript-graphql-request
hooks: { afterOneFileWrite: ['sed -i -e"s|graphql-request/dist/types\.dom|graphql-request/src/types.dom|g"'] }
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@swapr/sdk",
"license": "AGPL-3.0-or-later",
"version": "1.11.3",
"version": "1.11.4",
"description": "An SDK for building applications on top of Swapr",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
Expand Down
1 change: 1 addition & 0 deletions src/entities/trades/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from './gnosis-protocol'
export * from './interfaces/trade'
export * from './interfaces/trade-options'
export * from './OneInch'
export * from './openocean'
export { BaseRoutablePlatform, RoutablePlatform, UniswapV2RoutablePlatform } from './routable-platform'
export * from './sushiswap'
export * from './swapr-v3'
Expand Down
233 changes: 233 additions & 0 deletions src/entities/trades/openocean/Openocean.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
import { BaseProvider } from '@ethersproject/providers'
import { UnsignedTransaction } from '@ethersproject/transactions'
import { parseUnits } from '@ethersproject/units'
import fetch from 'node-fetch'
import invariant from 'tiny-invariant'

import { ChainId, ONE, TradeType } from '../../../constants'
import { Currency } from '../../currency'
import { CurrencyAmount, Fraction, Percent, Price, TokenAmount } from '../../fractions'
import { maximumSlippage as defaultMaximumSlippage } from '../constants'
import { Trade } from '../interfaces/trade'
import { TradeOptions } from '../interfaces/trade-options'
import { RoutablePlatform } from '../routable-platform'
import { getProvider, tryGetChainId, wrappedCurrency } from '../utils'
import { getBaseUrlWithChainCode, MainnetChainIds, OO_API_ENDPOINTS, OO_API_SWAPR_REFERRER } from './api'
import { OO_CONTRACT_ADDRESS_BY_CHAIN } from './constants'

export interface OpenoceanQuoteTypes {
amount: CurrencyAmount
quoteCurrency: Currency
tradeType: TradeType
maximumSlippage?: Percent
recipient?: string
}

interface OpenoceanConstructorParams {
maximumSlippage: Percent
inputAmount: CurrencyAmount
outputAmount: CurrencyAmount
tradeType: TradeType
chainId: ChainId
approveAddress: string
priceImpact: Percent
}

export class OpenoceanTrade extends Trade {
public constructor({
maximumSlippage,
inputAmount,
outputAmount,
tradeType,
chainId,
approveAddress,
priceImpact,
}: OpenoceanConstructorParams) {
super({
details: undefined,
type: tradeType,
inputAmount,
outputAmount,
maximumSlippage,
platform: RoutablePlatform.OPENOCEAN,
chainId,
executionPrice: new Price({
baseCurrency: inputAmount.currency,
quoteCurrency: outputAmount.currency,
denominator: inputAmount.raw,
numerator: outputAmount.raw,
}),
priceImpact,
approveAddress,
})
}

private static async getGas(chainId: MainnetChainIds) {
const baseUrl = getBaseUrlWithChainCode(chainId)
const gasResponse = await fetch(`${baseUrl}/${OO_API_ENDPOINTS.GET_GAS}`)

if (!gasResponse.ok) throw new Error(`OpenoceanTrade.getQuote: failed to get gasPrice`)

const gasData = await gasResponse.json()

return gasData.without_decimals.standard
}

static async getQuote(
{ amount, quoteCurrency, maximumSlippage = defaultMaximumSlippage, tradeType }: OpenoceanQuoteTypes,
provider?: BaseProvider,
): Promise<OpenoceanTrade | null> {
const chainId = tryGetChainId(amount, quoteCurrency)

if (!chainId) {
throw new Error('OpenoceanTrade.getQuote: chainId is required')
}

provider = provider || getProvider(chainId)

// Ensure the provider's chainId matches the provided currencies
invariant(
(await provider.getNetwork()).chainId == chainId,
`OpenoceanTrade.getQuote: currencies chainId does not match provider's chainId`,
)

const currencyIn = amount.currency
const currencyOut = quoteCurrency

// Ensure that the currencies are present
invariant(currencyIn.address && currencyOut.address, `getQuote: Currency address is required`)

try {
const baseUrl = getBaseUrlWithChainCode(chainId as MainnetChainIds)
const gasPrice = await this.getGas(chainId as MainnetChainIds)
const params = new URL(`${baseUrl}/${OO_API_ENDPOINTS.QUOTE}`)

params.searchParams.set(
'inTokenAddress',
`${Currency.isNative(currencyIn) ? '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' : currencyIn.address}`,
)
params.searchParams.set(
'outTokenAddress',
`${Currency.isNative(currencyOut) ? '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' : currencyOut.address}`,
)
params.searchParams.set('amount', `${parseUnits(amount.toSignificant(), 0).toString()}`)
params.searchParams.set('gasPrice', chainId === ChainId.MAINNET ? gasPrice.maxFeePerGas : gasPrice)
params.searchParams.set(
'slippage',
`${new Fraction(maximumSlippage.numerator, maximumSlippage.denominator).toSignificant(1)}`,
)

const res = await fetch(params.toString())
const data = await res.json()

if (data && amount) {
const approveAddress = OO_CONTRACT_ADDRESS_BY_CHAIN[chainId as MainnetChainIds]
const currencyAmountIn = Currency.isNative(currencyIn)
? CurrencyAmount.nativeCurrency(data.data.inAmount, chainId)
: new TokenAmount(wrappedCurrency(currencyIn, chainId), data.data.inAmount)

const currencyAmountOut = Currency.isNative(currencyOut)
? CurrencyAmount.nativeCurrency(data.data.outAmount, chainId)
: new TokenAmount(wrappedCurrency(currencyOut, chainId), data.data.outAmount)

return new OpenoceanTrade({
maximumSlippage,
inputAmount: currencyAmountIn,
outputAmount: currencyAmountOut,
tradeType,
chainId,
approveAddress,
priceImpact: new Percent('0', '100'),
})
}
} catch (error) {
console.error('Openocean.getQuote: Error fetching the quote:', error.message)
return null
}

return null
}

public minimumAmountOut(): CurrencyAmount {
if (this.tradeType === TradeType.EXACT_OUTPUT) {
return this.outputAmount
} else {
const slippageAdjustedAmountOut = new Fraction(ONE)
.add(this.maximumSlippage)
.invert()
.multiply(this.outputAmount.raw).quotient
return this.outputAmount instanceof TokenAmount
? new TokenAmount(this.outputAmount.token, slippageAdjustedAmountOut)
: CurrencyAmount.nativeCurrency(slippageAdjustedAmountOut, this.chainId)
}
}

public maximumAmountIn(): CurrencyAmount {
if (this.tradeType === TradeType.EXACT_INPUT) {
return this.inputAmount
} else {
const slippageAdjustedAmountIn = new Fraction(ONE)
.add(this.maximumSlippage)
.multiply(this.inputAmount.raw).quotient
return this.inputAmount instanceof TokenAmount
? new TokenAmount(this.inputAmount.token, slippageAdjustedAmountIn)
: CurrencyAmount.nativeCurrency(slippageAdjustedAmountIn, this.chainId)
}
}

/**
* Returns unsigned transaction for the trade
* @returns the unsigned transaction
*/
public async swapTransaction(options: TradeOptions): Promise<UnsignedTransaction> {
invariant(options, 'OpenoceanTrade.swapTransaction: Currency address is required')

/**
* @see https://docs.openocean.finance/dev/aggregator-api-and-sdk/aggregator-api/best-practice
*/

const inToken = this.inputAmount.currency
const outToken = this.outputAmount.currency
const amount = this.inputAmount
const maximumSlippage = this.maximumSlippage

const receivedSlippage = new Fraction(maximumSlippage.numerator, maximumSlippage.denominator).toSignificant(1)
const slippage = +receivedSlippage < 0.05 ? 0.05 : receivedSlippage

try {
// Ensure that the currencies are present
invariant(inToken.address && outToken.address, `getQuote: Currency address is required`)

const baseUrl = getBaseUrlWithChainCode(this.chainId as MainnetChainIds)
const quoteGasPrice = await OpenoceanTrade.getGas(this.chainId as MainnetChainIds)
const params = new URL(`${baseUrl}/${OO_API_ENDPOINTS.SWAP_QUOTE}`)

params.searchParams.set(
'inTokenAddress',
`${Currency.isNative(inToken) ? '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' : inToken.address}`,
)
params.searchParams.set(
'outTokenAddress',
`${Currency.isNative(outToken) ? '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' : outToken.address}`,
)
params.searchParams.set('amount', `${parseUnits(amount.toSignificant(), 0).toString()}`)
params.searchParams.set('referrer', `${OO_API_SWAPR_REFERRER}`)
params.searchParams.set('account', options.recipient)
params.searchParams.set('gasPrice', this.chainId === ChainId.MAINNET ? quoteGasPrice.maxFeePerGas : quoteGasPrice)
params.searchParams.set('slippage', `${slippage}`)

const res = await fetch(params.toString())
const swapQuoteData = await res.json()
const { data, gasPrice, to, value } = swapQuoteData?.data

return {
to,
gasPrice,
data,
value,
}
} catch (error) {
throw new Error(`Openocean.getQuote: Error fetching the trade: ${error.message}`)
}
}
}
37 changes: 37 additions & 0 deletions src/entities/trades/openocean/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ChainId } from '../../../constants'

export const OO_API_BASE_URL = 'https://open-api.openocean.finance/v3'
export const OO_API_SWAPR_REFERRER = '0xdaF6CABd165Fd44c037575a97cF3562339295Ea3'

export enum OO_API_ENDPOINTS {
GET_GAS = 'gasPrice',
QUOTE = 'quote',
SWAP_QUOTE = 'swap_quote',
}

export type TestChainIds =
| ChainId.ARBITRUM_GOERLI
| ChainId.ARBITRUM_RINKEBY
| ChainId.BSC_TESTNET
| ChainId.GOERLI
| ChainId.OPTIMISM_GOERLI
| ChainId.ZK_SYNC_ERA_TESTNET
| ChainId.RINKEBY

export type MainnetChainIds = Exclude<ChainId, TestChainIds>

/**
* @see https://docs.openocean.finance/dev/supported-chains
*/
const OO_API_CHAIN_CODE = {
[ChainId.ARBITRUM_ONE]: 'arbitrum',
[ChainId.BSC_MAINNET]: 'bsc',
[ChainId.GNOSIS]: 'xdai',
[ChainId.MAINNET]: 'eth',
[ChainId.OPTIMISM_MAINNET]: 'optimism',
[ChainId.POLYGON]: 'polygon',
[ChainId.SCROLL_MAINNET]: 'scroll',
[ChainId.ZK_SYNC_ERA_MAINNET]: 'zksync',
}

export const getBaseUrlWithChainCode = (chainId: MainnetChainIds) => `${OO_API_BASE_URL}/${OO_API_CHAIN_CODE[chainId]}`
15 changes: 15 additions & 0 deletions src/entities/trades/openocean/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ChainId } from '../../../constants'

/**
* @see https://docs.openocean.finance/dev/contracts-of-chains
*/
export const OO_CONTRACT_ADDRESS_BY_CHAIN = {
[ChainId.ARBITRUM_ONE]: '0x6352a56caadc4f1e25cd6c75970fa768a3304e64',
[ChainId.BSC_MAINNET]: '0x6352a56caadc4f1e25cd6c75970fa768a3304e64',
[ChainId.GNOSIS]: '0x6352a56caadc4f1e25cd6c75970fa768a3304e64',
[ChainId.MAINNET]: '0x6352a56caadc4f1e25cd6c75970fa768a3304e64',
[ChainId.OPTIMISM_MAINNET]: '0x6352a56caadc4f1e25cd6c75970fa768a3304e64',
[ChainId.POLYGON]: '0x6352a56caadc4f1e25cd6c75970fa768a3304e64',
[ChainId.SCROLL_MAINNET]: '0x6352a56caadc4f1e25cd6c75970fa768a3304e64',
[ChainId.ZK_SYNC_ERA_MAINNET]: '0x36A1aCbbCAfca2468b85011DDD16E7Cb4d673230',
}
1 change: 1 addition & 0 deletions src/entities/trades/openocean/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Openocean'
Loading

0 comments on commit ac2c007

Please sign in to comment.