-
Notifications
You must be signed in to change notification settings - Fork 177
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
[skip ci] wip: chainflip swapper #8049
base: develop
Are you sure you want to change the base?
Changes from 72 commits
f9cf7a9
b0fef83
6f4b2cd
ae0d083
56a4003
53cfa9f
8515f51
c4131d5
159650a
cca5688
09b820d
9f60594
8bab08e
09b9b02
e6a43ac
9c2f447
20216d6
d5b9fee
2861ba3
57e08d1
2207d5b
56697f1
9507b73
668856f
1765b86
7b3cc26
a6d42f9
dfa6a3a
ef79b52
fea992c
ee0d7bd
572135d
089fff0
1d25ce9
a1661cd
72f1c7e
3fd7b62
519546b
799320f
708a826
8a17ba6
12ae967
c090d43
34ea201
b253f4b
630f8ef
e931c5b
8f4b47a
8662c0d
3517d98
ae28709
1251fa3
15a0499
c3c1745
1e57f3d
4a227c5
22372ed
0c309d4
3997245
2cce1db
5626b31
5b4a64d
f69ae34
a906812
4924c16
1fd442b
dccd703
206a337
7cfe7c2
c2def52
5ef0169
2e77080
c32b370
9a10db4
19f5fcd
3111366
820283b
8c25692
ddd1339
1c1b711
3172d80
388b4dc
267eb4f
ae0dd35
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 |
---|---|---|
|
@@ -17,16 +17,22 @@ export const arbitrumNovaAssetId: AssetId = 'eip155:42170/slip44:60' | |
export const baseAssetId: AssetId = 'eip155:8453/slip44:60' | ||
export const solAssetId: AssetId = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/slip44:501' | ||
|
||
export const foxatarAssetId: AssetId = | ||
'eip155:137/erc721:0x2e727c425a11ce6b8819b3004db332c12d2af2a2' | ||
|
||
export const foxyAssetId: AssetId = 'eip155:1/erc20:0xdc49108ce5c57bc3408c3a5e95f3d864ec386ed3' | ||
export const foxOnGnosisAssetId: AssetId = | ||
'eip155:100/erc20:0x21a42669643f45bc0e086b8fc2ed70c23d67509d' | ||
export const foxOnArbitrumOneAssetId: AssetId = | ||
'eip155:42161/erc20:0xf929de51d91c77e42f5090069e0ad7a09e513c73' | ||
export const foxAssetId: AssetId = 'eip155:1/erc20:0xc770eefad204b5180df6a14ee197d99d808ee52d' | ||
export const foxatarAssetId: AssetId = | ||
'eip155:137/erc721:0x2e727c425a11ce6b8819b3004db332c12d2af2a2' | ||
export const foxyAssetId: AssetId = 'eip155:1/erc20:0xdc49108ce5c57bc3408c3a5e95f3d864ec386ed3' | ||
|
||
export const usdtAssetId: AssetId = 'eip155:1/erc20:0xdac17f958d2ee523a2206206994597c13d831ec7' | ||
export const usdcAssetId: AssetId = 'eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48' | ||
export const usdcOnArbitrumOneAssetId: AssetId = | ||
'eip155:42161/erc20:0xaf88d065e77c8cc2239327c5edb3a432268e5831' | ||
export const usdcOnSolanaAssetId: AssetId = | ||
'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp/token:EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v' | ||
export const flipAssetId: AssetId = 'eip155:1/erc20:0x826180541412d574cf1336d22c0c0a287822678a' | ||
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. all common AssetIds moved into one block + some additions |
||
|
||
export const cosmosAssetId: AssetId = 'cosmos:cosmoshub-4/slip44:118' | ||
export const thorchainAssetId: AssetId = 'cosmos:thorchain-1/slip44:931' | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import type { AssetId } from '@shapeshiftoss/caip' | ||
import type { BTCSignTx } from '@shapeshiftoss/hdwallet-core' | ||
import type { Asset } from '@shapeshiftoss/types' | ||
|
||
import type { BuyAssetBySellIdInput, Swapper, UtxoTransactionExecutionProps } from '../../types' | ||
import { executeEvmTransaction } from '../../utils' | ||
import { CHAINFLIP_SUPPORTED_CHAIN_IDS } from './constants' | ||
import { isSupportedAssetId } from './utils/helpers' | ||
|
||
export const chainflipSwapper: Swapper = { | ||
executeEvmTransaction, | ||
|
||
executeUtxoTransaction: async ( | ||
txToSign: BTCSignTx, | ||
{ signAndBroadcastTransaction }: UtxoTransactionExecutionProps, | ||
): Promise<string> => { | ||
return await signAndBroadcastTransaction(txToSign) | ||
}, | ||
|
||
filterAssetIdsBySellable: (assets: Asset[]): Promise<AssetId[]> => { | ||
return Promise.resolve( | ||
assets | ||
.filter(asset => CHAINFLIP_SUPPORTED_CHAIN_IDS.sell.includes(asset.chainId)) | ||
.filter(asset => isSupportedAssetId(asset.chainId, asset.assetId)) | ||
.map(asset => asset.assetId), | ||
) | ||
}, | ||
|
||
filterBuyAssetsBySellAssetId: (input: BuyAssetBySellIdInput): Promise<AssetId[]> => { | ||
return Promise.resolve( | ||
input.assets | ||
.filter(asset => CHAINFLIP_SUPPORTED_CHAIN_IDS.buy.includes(asset.chainId)) | ||
.filter(asset => isSupportedAssetId(asset.chainId, asset.assetId)) | ||
.map(asset => asset.assetId), | ||
) | ||
}, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import type { SwapperApi } from '../../types' | ||
import { checkTradeStatus } from './swapperApi/checkTradeStatus' | ||
import { getTradeQuote } from './swapperApi/getTradeQuote' | ||
import { getUnsignedEvmTransaction } from './swapperApi/getUnsignedEvmTransaction' | ||
import { getUnsignedUtxoTransaction } from './swapperApi/getUnsignedUtxoTransaction' | ||
|
||
export const chainflipApi: SwapperApi = { | ||
getTradeQuote, | ||
getUnsignedEvmTransaction, | ||
getUnsignedUtxoTransaction, | ||
checkTradeStatus, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { | ||
arbitrumAssetId, | ||
type AssetId, | ||
btcAssetId, | ||
type ChainId, | ||
ethAssetId, | ||
flipAssetId, | ||
solAssetId, | ||
usdcAssetId, | ||
usdcOnArbitrumOneAssetId, | ||
usdcOnSolanaAssetId, | ||
usdtAssetId, | ||
} from '@shapeshiftoss/caip' | ||
import type { Asset } from '@shapeshiftoss/types' | ||
import { KnownChainIds } from '@shapeshiftoss/types' | ||
|
||
import type { SupportedChainIds, SwapSource } from '../../types' | ||
import { SwapperName } from '../../types' | ||
|
||
export const CHAINFLIP_REGULAR_QUOTE = 'regular' | ||
export const CHAINFLIP_DCA_QUOTE = 'dca' | ||
export const CHAINFLIP_BAAS_COMMISSION = 5 | ||
|
||
export const ChainflipSupportedChainIds = [ | ||
KnownChainIds.EthereumMainnet, | ||
KnownChainIds.ArbitrumMainnet, | ||
KnownChainIds.BitcoinMainnet, | ||
KnownChainIds.SolanaMainnet, | ||
] as const | ||
|
||
export type ChainflipSupportedChainId = (typeof ChainflipSupportedChainIds)[number] | ||
|
||
export const ChainflipSupportedAssetIdsByChainId: Partial<Record<KnownChainIds, AssetId[]>> = { | ||
[KnownChainIds.EthereumMainnet]: [ethAssetId, flipAssetId, usdcAssetId, usdtAssetId], | ||
[KnownChainIds.ArbitrumMainnet]: [arbitrumAssetId, usdcOnArbitrumOneAssetId], | ||
[KnownChainIds.BitcoinMainnet]: [btcAssetId], | ||
[KnownChainIds.SolanaMainnet]: [solAssetId, usdcOnSolanaAssetId], | ||
} | ||
|
||
export const chainIdToChainflipNetwork: Partial<Record<KnownChainIds, string>> = { | ||
[KnownChainIds.EthereumMainnet]: 'eth', | ||
[KnownChainIds.ArbitrumMainnet]: 'arb', | ||
[KnownChainIds.BitcoinMainnet]: 'btc', | ||
[KnownChainIds.SolanaMainnet]: 'sol', | ||
} | ||
|
||
export const CHAINFLIP_SUPPORTED_CHAIN_IDS: SupportedChainIds = { | ||
sell: ChainflipSupportedChainIds as unknown as ChainId[], | ||
buy: ChainflipSupportedChainIds as unknown as ChainId[], | ||
} | ||
|
||
export const CHAINFLIP_SWAP_SOURCE: SwapSource = SwapperName.Chainflip | ||
export const CHAINFLIP_BOOST_SWAP_SOURCE: SwapSource = `${SwapperName.Chainflip} • Boost` | ||
export const CHAINFLIP_DCA_SWAP_SOURCE: SwapSource = `${SwapperName.Chainflip} • DCA` | ||
export const CHAINFLIP_DCA_BOOST_SWAP_SOURCE: SwapSource = `${SwapperName.Chainflip} • DCA • Boost` | ||
|
||
export const usdcAsset: Asset = { | ||
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. A fyi, the reason I copied this here is because the 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. That's absolutely fine @CumpsD! Asking because we may be able to simply use 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. Network fee is always USDC, it is the 0.10% buy & burn fee the chainflip network uses. Liquidity is the LP fee, which I would have to double check but I believe the second leg is always USDC. It's possible this Will double check! 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. Yep, so the second leg seems to be always usdc.eth for the liquidity fee. 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. Happy to keep the second branch for the sake of paranoia. Tyvm for disambiguating this! Looks like indeed, we need usdc as a static asset then, which means keeping it here as a const is absolutely fine. 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.
Pretty much the reason I added all branches too :D |
||
assetId: 'eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', | ||
chainId: KnownChainIds.EthereumMainnet, | ||
color: '#2373CB', | ||
explorer: 'https://etherscan.io', | ||
explorerAddressLink: 'https://etherscan.io/address/', | ||
explorerTxLink: 'https://etherscan.io/tx/', | ||
icon: 'https://rawcdn.githack.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png', | ||
name: 'USDC on Ethereum', | ||
precision: 6, | ||
relatedAssetKey: 'eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48', | ||
symbol: 'USDC', | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
// @ts-nocheck | ||
/* tslint:disable */ | ||
/* eslint-disable */ | ||
/** | ||
* Chainflip Broker as a Service | ||
* Run your own Chainflip Broker without any hassle. | ||
* | ||
* The version of the OpenAPI document: v1 | ||
* | ||
* | ||
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). | ||
* https://openapi-generator.tech | ||
* Do not edit the class manually. | ||
*/ | ||
|
||
import { exists, mapValues } from '../runtime'; | ||
import type { ChainflipBaasQuoteBoostQuote } from './ChainflipBaasQuoteBoostQuote'; | ||
import { | ||
ChainflipBaasQuoteBoostQuoteFromJSON, | ||
ChainflipBaasQuoteBoostQuoteFromJSONTyped, | ||
ChainflipBaasQuoteBoostQuoteToJSON, | ||
} from './ChainflipBaasQuoteBoostQuote'; | ||
import type { ChainflipBaasQuotePoolInfo } from './ChainflipBaasQuotePoolInfo'; | ||
import { | ||
ChainflipBaasQuotePoolInfoFromJSON, | ||
ChainflipBaasQuotePoolInfoFromJSONTyped, | ||
ChainflipBaasQuotePoolInfoToJSON, | ||
} from './ChainflipBaasQuotePoolInfo'; | ||
import type { ChainflipBaasQuoteQuoteFee } from './ChainflipBaasQuoteQuoteFee'; | ||
import { | ||
ChainflipBaasQuoteQuoteFeeFromJSON, | ||
ChainflipBaasQuoteQuoteFeeFromJSONTyped, | ||
ChainflipBaasQuoteQuoteFeeToJSON, | ||
} from './ChainflipBaasQuoteQuoteFee'; | ||
|
||
/** | ||
* | ||
* @export | ||
* @interface ChainflipBaasQuoteQuote | ||
*/ | ||
export interface ChainflipBaasQuoteQuote { | ||
/** | ||
* The type of quote. | ||
* @type {string} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
readonly type?: ChainflipBaasQuoteQuoteTypeEnum; | ||
/** | ||
* The asset to send. | ||
* @type {string} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
ingressAsset?: string; | ||
/** | ||
* The amount to send. | ||
* @type {number} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
ingressAmount?: number; | ||
/** | ||
* The amount to send in native units. | ||
* @type {string} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
readonly ingressAmountNative?: string; | ||
/** | ||
* The asset to receive. | ||
* @type {string} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
egressAsset?: string; | ||
/** | ||
* The amount to receive. | ||
* @type {number} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
readonly egressAmount?: number; | ||
/** | ||
* The amount to receive in native units. | ||
* @type {string} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
readonly egressAmountNative?: string; | ||
/** | ||
* The fee structure, this includes all fees. | ||
* @type {Array<ChainflipBaasQuoteQuoteFee>} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
readonly includedFees?: Array<ChainflipBaasQuoteQuoteFee>; | ||
/** | ||
* A warning in case liquidity is low and there is a risk of high slippage. | ||
* @type {boolean} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
readonly lowLiquidityWarning?: boolean; | ||
/** | ||
* Liquidity pools involved in the swap, as well as estimated liquidity provider fees. | ||
* @type {Array<ChainflipBaasQuotePoolInfo>} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
poolInfo?: Array<ChainflipBaasQuotePoolInfo>; | ||
/** | ||
* The estimated time the swap will take. | ||
* @type {number} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
readonly estimatedDurationSeconds?: number; | ||
/** | ||
* The number of "sub-swaps" to perform for a DCA swap. | ||
* @type {number} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
chunkIntervalBlocks?: number | null; | ||
/** | ||
* The delay between the "sub-swaps" of a DCA swap in number of blocks. | ||
* @type {number} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
numberOfChunks?: number | null; | ||
/** | ||
* | ||
* @type {ChainflipBaasQuoteBoostQuote} | ||
* @memberof ChainflipBaasQuoteQuote | ||
*/ | ||
boostQuote?: ChainflipBaasQuoteBoostQuote; | ||
} | ||
|
||
|
||
/** | ||
* @export | ||
*/ | ||
export const ChainflipBaasQuoteQuoteTypeEnum = { | ||
Regular: 'regular', | ||
Dca: 'dca' | ||
} as const; | ||
export type ChainflipBaasQuoteQuoteTypeEnum = typeof ChainflipBaasQuoteQuoteTypeEnum[keyof typeof ChainflipBaasQuoteQuoteTypeEnum]; | ||
|
||
|
||
/** | ||
* Check if a given object implements the ChainflipBaasQuoteQuote interface. | ||
*/ | ||
export function instanceOfChainflipBaasQuoteQuote(value: object): boolean { | ||
let isInstance = true; | ||
|
||
return isInstance; | ||
} | ||
|
||
export function ChainflipBaasQuoteQuoteFromJSON(json: any): ChainflipBaasQuoteQuote { | ||
return ChainflipBaasQuoteQuoteFromJSONTyped(json, false); | ||
} | ||
|
||
export function ChainflipBaasQuoteQuoteFromJSONTyped(json: any, ignoreDiscriminator: boolean): ChainflipBaasQuoteQuote { | ||
if ((json === undefined) || (json === null)) { | ||
return json; | ||
} | ||
return { | ||
|
||
'type': !exists(json, 'type') ? undefined : json['type'], | ||
'ingressAsset': !exists(json, 'ingressAsset') ? undefined : json['ingressAsset'], | ||
'ingressAmount': !exists(json, 'ingressAmount') ? undefined : json['ingressAmount'], | ||
'ingressAmountNative': !exists(json, 'ingressAmountNative') ? undefined : json['ingressAmountNative'], | ||
'egressAsset': !exists(json, 'egressAsset') ? undefined : json['egressAsset'], | ||
'egressAmount': !exists(json, 'egressAmount') ? undefined : json['egressAmount'], | ||
'egressAmountNative': !exists(json, 'egressAmountNative') ? undefined : json['egressAmountNative'], | ||
'includedFees': !exists(json, 'includedFees') ? undefined : ((json['includedFees'] as Array<any>).map(ChainflipBaasQuoteQuoteFeeFromJSON)), | ||
'lowLiquidityWarning': !exists(json, 'lowLiquidityWarning') ? undefined : json['lowLiquidityWarning'], | ||
'poolInfo': !exists(json, 'poolInfo') ? undefined : ((json['poolInfo'] as Array<any>).map(ChainflipBaasQuotePoolInfoFromJSON)), | ||
'estimatedDurationSeconds': !exists(json, 'estimatedDurationSeconds') ? undefined : json['estimatedDurationSeconds'], | ||
'chunkIntervalBlocks': !exists(json, 'chunkIntervalBlocks') ? undefined : json['chunkIntervalBlocks'], | ||
'numberOfChunks': !exists(json, 'numberOfChunks') ? undefined : json['numberOfChunks'], | ||
'boostQuote': !exists(json, 'boostQuote') ? undefined : ChainflipBaasQuoteBoostQuoteFromJSON(json['boostQuote']), | ||
}; | ||
} | ||
|
||
export function ChainflipBaasQuoteQuoteToJSON(value?: ChainflipBaasQuoteQuote | null): any { | ||
if (value === undefined) { | ||
return undefined; | ||
} | ||
if (value === null) { | ||
return null; | ||
} | ||
return { | ||
|
||
'ingressAsset': value.ingressAsset, | ||
'ingressAmount': value.ingressAmount, | ||
'egressAsset': value.egressAsset, | ||
'poolInfo': value.poolInfo === undefined ? undefined : ((value.poolInfo as Array<any>).map(ChainflipBaasQuotePoolInfoToJSON)), | ||
'chunkIntervalBlocks': value.chunkIntervalBlocks, | ||
'numberOfChunks': value.numberOfChunks, | ||
'boostQuote': ChainflipBaasQuoteBoostQuoteToJSON(value.boostQuote), | ||
}; | ||
} | ||
|
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.
Give me a ping to set up Shapeshift's API key when going forward, this is currently mine for testing! :) The best person to reach out to me for this would be the one who will be responsible for redeeming affiliate rewards in the future. The one controlling the wallet to sign Chainflip transactions that is (redeeming is a chainflip tx)
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.
Sounds good @CumpsD! Is there any reason we should wait for go-live to get an actual API key here, or can we already get one? Will get the convs going already
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.
The key in there right now is actually the one from my swapping Discord bot https://swappy.be/ :) It works, you can test with it, but it charges 0.15% fee (0.05 BaaS + 0.10 Swappy). I can make you a temp one that does only 0.05 (BaaS fee + 0% partner) if you want. You can go live with that as well and swap it out to the Shapeshift one afterwards. lmk
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.
@CumpsD just to be sure, we can parametrize fee bps right i.e this isn't tied to the API key used?
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.
Current one for dev is absolutely fine, so long as we go live on prod (and ideally on develop as a follow-up) with a proper API key 🙏🏽
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.
Yes, you can pass in
commissionBps
for both quote and opening a deposit channel (I think I added that in the PR already even! taking in affiliateBps from shapeshift)