diff --git a/.changeset/lucky-candles-serve.md b/.changeset/lucky-candles-serve.md new file mode 100644 index 000000000..263cf46ac --- /dev/null +++ b/.changeset/lucky-candles-serve.md @@ -0,0 +1,5 @@ +--- +'@xchainjs/xchain-aggregator': minor +--- + +Chainflip protocol support diff --git a/packages/xchain-aggregator/__e2e__/aggregator.e2e.ts b/packages/xchain-aggregator/__e2e__/aggregator.e2e.ts index fa9a7f1be..42fae6d63 100644 --- a/packages/xchain-aggregator/__e2e__/aggregator.e2e.ts +++ b/packages/xchain-aggregator/__e2e__/aggregator.e2e.ts @@ -7,9 +7,9 @@ import { defaultBTCParams as defaultBtcParams, } from '@xchainjs/xchain-bitcoin' import { Network } from '@xchainjs/xchain-client' -import { AssetETH } from '@xchainjs/xchain-ethereum' +import { AssetETH, Client as EthClient, defaultEthParams } from '@xchainjs/xchain-ethereum' import { AssetKUJI } from '@xchainjs/xchain-kujira' -import { CryptoAmount, assetAmount, assetToBase, assetToString } from '@xchainjs/xchain-util' +import { CryptoAmount, assetAmount, assetFromStringEx, assetToBase, assetToString } from '@xchainjs/xchain-util' import { Wallet } from '@xchainjs/xchain-wallet' import { Aggregator, QuoteSwap } from '../src' @@ -50,6 +50,8 @@ function printQuoteSwap(quoteSwap: QuoteSwap) { }) } +jest.deepUnmock('@chainflip/sdk/swap') + describe('Aggregator', () => { let aggregator: Aggregator let wallet: Wallet @@ -58,6 +60,11 @@ describe('Aggregator', () => { const phrase = process.env.PHRASE_MAINNET wallet = new Wallet({ BTC: new BtcClient({ ...defaultBtcParams, phrase, network: Network.Mainnet }), + ETH: new EthClient({ + ...defaultEthParams, + phrase, + network: Network.Mainnet, + }), AVAX: new AvaxClient({ ...defaultAvaxParams, phrase, network: Network.Mainnet }), BNB: new BnbClient({ phrase, network: Network.Mainnet }), }) @@ -117,6 +124,33 @@ describe('Aggregator', () => { console.log(txSubmitted) }) + it('Should do ERC20 swap using chosen protocol', async () => { + const txEstimatedSwap = await aggregator.estimateSwap({ + fromAsset: assetFromStringEx('ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7'), + destinationAsset: AssetETH, + amount: new CryptoAmount( + assetToBase(assetAmount(20, 6)), + assetFromStringEx('ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7'), + ), + destinationAddress: await wallet.getAddress(AssetETH.chain), + }) + + printQuoteSwap(txEstimatedSwap) + + const txSubmitted = await aggregator.doSwap({ + protocol: txEstimatedSwap.protocol, + fromAsset: assetFromStringEx('ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7'), + destinationAsset: AssetETH, + amount: new CryptoAmount( + assetToBase(assetAmount(20, 6)), + assetFromStringEx('ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7'), + ), + destinationAddress: await wallet.getAddress(AssetETH.chain), + }) + + console.log(txSubmitted) + }) + it('Should do swap without selecting protocol', async () => { const txSubmitted = await aggregator.doSwap({ fromAsset: AssetBNB, diff --git a/packages/xchain-aggregator/__mocks__/@chainflip/sdk/swap.ts b/packages/xchain-aggregator/__mocks__/@chainflip/sdk/swap.ts new file mode 100644 index 000000000..bab1849d3 --- /dev/null +++ b/packages/xchain-aggregator/__mocks__/@chainflip/sdk/swap.ts @@ -0,0 +1,322 @@ +import { + Asset, + AssetData, + ChainData, + Chains, + DepositAddressRequest, + DepositAddressResponse, + QuoteRequest, + QuoteResponse, +} from '@chainflip/sdk/swap' + +class SwapSDK { + async getChains(): Promise { + return [ + { + chain: 'Ethereum', + name: 'Ethereum', + evmChainId: 11155111, + isMainnet: false, + requiredBlockConfirmations: 7, + }, + { + chain: 'Polkadot', + name: 'Polkadot', + evmChainId: undefined, + isMainnet: false, + requiredBlockConfirmations: undefined, + }, + { + chain: 'Bitcoin', + name: 'Bitcoin', + evmChainId: undefined, + isMainnet: false, + requiredBlockConfirmations: 6, + }, + ] + } + + async getAssets(): Promise { + return [ + { + chainflipId: 'Eth', + asset: 'ETH', + chain: 'Ethereum', + contractAddress: undefined, + decimals: 18, + name: 'Ether', + symbol: 'ETH', + isMainnet: true, + minimumSwapAmount: '10000000000000000', + maximumSwapAmount: null, + minimumEgressAmount: '1', + }, + { + chainflipId: 'Flip', + asset: 'FLIP', + chain: 'Ethereum', + contractAddress: '0x826180541412D574cf1336d22c0C0a287822678A', + decimals: 18, + name: 'FLIP', + symbol: 'FLIP', + isMainnet: true, + minimumSwapAmount: '4000000000000000000', + maximumSwapAmount: null, + minimumEgressAmount: '1', + }, + { + chainflipId: 'Usdc', + asset: 'USDC', + chain: 'Ethereum', + contractAddress: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', + decimals: 6, + name: 'USDC', + symbol: 'USDC', + isMainnet: true, + minimumSwapAmount: '20000000', + maximumSwapAmount: null, + minimumEgressAmount: '1', + }, + { + chainflipId: 'Usdt', + asset: 'USDT', + chain: 'Ethereum', + contractAddress: '0xdAC17F958D2ee523a2206206994597C13D831ec7', + decimals: 6, + name: 'USDT', + symbol: 'USDT', + isMainnet: true, + minimumSwapAmount: '20000000', + maximumSwapAmount: null, + minimumEgressAmount: '1', + }, + { + chainflipId: 'Dot', + asset: 'DOT', + chain: 'Polkadot', + contractAddress: undefined, + decimals: 10, + name: 'Polkadot', + symbol: 'DOT', + isMainnet: true, + minimumSwapAmount: '40000000000', + maximumSwapAmount: null, + minimumEgressAmount: '1', + }, + { + chainflipId: 'Btc', + asset: 'BTC', + chain: 'Bitcoin', + contractAddress: undefined, + decimals: 8, + name: 'Bitcoin', + symbol: 'BTC', + isMainnet: true, + minimumSwapAmount: '70000', + maximumSwapAmount: null, + minimumEgressAmount: '600', + }, + ] + } + + async requestDepositAddress(params: DepositAddressRequest): Promise { + if (params.srcChain === 'Bitcoin') + return { + ...params, + depositAddress: 'BITCOINfakeaddress', + depositChannelId: 'bitcoin-channel-id', + brokerCommissionBps: 0, + depositChannelExpiryBlock: BigInt(10000), + estimatedDepositChannelExpiryTime: 1716889354, + channelOpeningFee: BigInt(100), + } + if (params.srcChain === 'Ethereum' || params.srcChain === 'Arbitrum') + return { + ...params, + depositAddress: 'ETHEREUMfakeaddress', + depositChannelId: 'ethereum-channel-id', + brokerCommissionBps: 0, + depositChannelExpiryBlock: BigInt(20000), + estimatedDepositChannelExpiryTime: 1716889354, + channelOpeningFee: BigInt(100), + } + if (params.srcChain === 'Polkadot') + return { + ...params, + depositAddress: 'POLKADOTfakeaddress', + depositChannelId: 'polkadot-channel-id', + brokerCommissionBps: 0, + depositChannelExpiryBlock: BigInt(30000), + estimatedDepositChannelExpiryTime: 1716889354, + channelOpeningFee: BigInt(100), + } + throw Error('Can not get deposit address') + } + + async getQuote({ srcAsset, srcChain, destAsset, destChain, amount }: QuoteRequest): Promise { + if ( + srcChain === 'Ethereum' && + srcAsset === 'ETH' && + destChain === 'Bitcoin' && + destAsset === 'BTC' && + amount === '10000000000000000' + ) { + return { + srcAsset, + srcChain, + destAsset, + destChain, + amount, + quote: { + intermediateAmount: '36115119', + egressAmount: '51193', + includedFees: [ + { + type: 'INGRESS', + chain: 'Ethereum', + asset: 'ETH', + amount: '689176257450000', + }, + { + type: 'NETWORK', + chain: 'Ethereum', + asset: 'USDC', + amount: '36115', + }, + { + type: 'LIQUIDITY', + chain: 'Ethereum', + asset: 'ETH', + amount: '4655411871275', + }, + { + type: 'LIQUIDITY', + chain: 'Ethereum', + asset: 'USDC', + amount: '18057', + }, + { + type: 'EGRESS', + chain: 'Bitcoin', + asset: 'BTC', + amount: '1599', + }, + ], + lowLiquidityWarning: false, + estimatedDurationSeconds: 702, + }, + } + } + + if ( + srcChain === 'Ethereum' && + srcAsset === 'USDT' && + destChain === 'Ethereum' && + destAsset === 'ETH' && + amount === '20000000' + ) { + return { + srcAsset, + srcChain, + destAsset, + destChain, + amount, + quote: { + intermediateAmount: '13560635', + egressAmount: '2063188201000691', + includedFees: [ + { + type: 'INGRESS', + chain: 'Ethereum', + asset: 'USDT', + amount: '6433935', + }, + { + type: 'NETWORK', + chain: 'Ethereum', + asset: 'USDC', + amount: '13560', + }, + { + type: 'LIQUIDITY', + chain: 'Ethereum', + asset: 'USDT', + amount: '6783', + }, + { + type: 'LIQUIDITY', + chain: 'Ethereum', + asset: 'USDC', + amount: '6780', + }, + { + type: 'EGRESS', + chain: 'Ethereum', + asset: 'ETH', + amount: '1447621978320000', + }, + ], + lowLiquidityWarning: false, + estimatedDurationSeconds: 114, + }, + } + } + + if ( + srcChain === 'Ethereum' && + srcAsset === 'ETH' && + destChain === 'Ethereum' && + destAsset === 'USDT' && + amount === '10000000000000000' + ) { + return { + srcAsset, + srcChain, + destAsset, + destChain, + amount, + quote: { + intermediateAmount: '33919877', + egressAmount: '24884030', + includedFees: [ + { + type: 'INGRESS', + chain: 'Ethereum', + asset: 'ETH', + amount: '1284811335100000', + }, + { + type: 'NETWORK', + chain: 'Ethereum', + asset: 'USDC', + amount: '33919', + }, + { + type: 'LIQUIDITY', + chain: 'Ethereum', + asset: 'ETH', + amount: '4357594332450', + }, + { + type: 'LIQUIDITY', + chain: 'Ethereum', + asset: 'USDC', + amount: '16959', + }, + { + type: 'EGRESS', + chain: 'Ethereum', + asset: 'USDT', + amount: '8988369', + }, + ], + lowLiquidityWarning: false, + estimatedDurationSeconds: 114, + }, + } + } + throw Error('Quote not mocked') + } +} + +export { SwapSDK, Asset, AssetData, Chains } diff --git a/packages/xchain-aggregator/__tests__/chainflipProtocol.test.ts b/packages/xchain-aggregator/__tests__/chainflipProtocol.test.ts new file mode 100644 index 000000000..015737115 --- /dev/null +++ b/packages/xchain-aggregator/__tests__/chainflipProtocol.test.ts @@ -0,0 +1,170 @@ +import { AssetBTC, Client as BtcClient, defaultBTCParams as defaultBtcParams } from '@xchainjs/xchain-bitcoin' +import { Network } from '@xchainjs/xchain-client' +import { AssetETH, Client as EthClient, ETH_GAS_ASSET_DECIMAL, defaultEthParams } from '@xchainjs/xchain-ethereum' +import { AssetCacao } from '@xchainjs/xchain-mayachain' +import { AssetRuneNative } from '@xchainjs/xchain-thorchain' +import { CryptoAmount, assetAmount, assetFromStringEx, assetToBase, assetToString } from '@xchainjs/xchain-util' +import { Wallet } from '@xchainjs/xchain-wallet' + +import { ChainflipProtocol } from '../src/protocols' + +jest.setTimeout(60000) + +describe('Chainflip protocol', () => { + let protocol: ChainflipProtocol + let wallet: Wallet + + beforeAll(() => { + const phrase = process.env.PHRASE_MAINNET + + wallet = new Wallet({ + BTC: new BtcClient({ ...defaultBtcParams, phrase, network: Network.Mainnet }), + ETH: new EthClient({ + ...defaultEthParams, + phrase, + }), + }) + + protocol = new ChainflipProtocol(wallet) + }) + + it('Should get supported chains', async () => { + const chains = await protocol.getSupportedChains() + expect(chains.length).toBe(3) + }) + + it('Should check native assets are supported', async () => { + expect(await protocol.isAssetSupported(AssetBTC)).toBeTruthy() + expect(await protocol.isAssetSupported(AssetETH)).toBeTruthy() + }) + + it('Should check native assets are not supported', async () => { + expect(await protocol.isAssetSupported(AssetCacao)).toBeFalsy() + expect(await protocol.isAssetSupported(AssetRuneNative)).toBeFalsy() + }) + + it('Should check ERC20 assets are supported', async () => { + expect( + await protocol.isAssetSupported(assetFromStringEx('ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7')), + ).toBeTruthy() + expect( + await protocol.isAssetSupported(assetFromStringEx('ETH.USDT-0xdac17f958d2ee523a2206206994597c13d831ec7')), + ).toBeTruthy() + expect( + await protocol.isAssetSupported(assetFromStringEx('ETH.USDC-0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48')), + ).toBeTruthy() + expect( + await protocol.isAssetSupported(assetFromStringEx('ETH.FLIP-0x826180541412D574cf1336d22c0C0a287822678A')), + ).toBeTruthy() + }) + + it('Should check ERC20 assets are not supported', async () => { + expect( + await protocol.isAssetSupported(assetFromStringEx('ETH.BNB-0x826180541412D574cf1336d22c0C0a287822678A')), + ).toBeFalsy() + }) + + it('Should not get swap history', () => { + expect(async () => { + await protocol.getSwapHistory() + }).rejects.toThrowError('Method not implemented.') + }) + + it('Should estimate native swap', async () => { + const estimatedSwap = await protocol.estimateSwap({ + fromAsset: AssetETH, + destinationAsset: AssetBTC, + amount: new CryptoAmount(assetToBase(assetAmount(0.01, ETH_GAS_ASSET_DECIMAL)), AssetETH), + destinationAddress: 'BITCOINFakeAddress', + }) + expect(estimatedSwap.protocol).toBe('Chainflip') + expect(estimatedSwap.toAddress).toBe('ETHEREUMfakeaddress') + expect(estimatedSwap.memo).toBe('') + expect(assetToString(estimatedSwap.expectedAmount.asset)).toBe('BTC.BTC') + expect(estimatedSwap.expectedAmount.baseAmount.amount().toString()).toBe('51193') + expect(estimatedSwap.expectedAmount.baseAmount.decimal).toBe(8) + expect(assetToString(estimatedSwap.dustThreshold.asset)).toBe('ETH.ETH') + expect(estimatedSwap.dustThreshold.baseAmount.amount().toString()).toBe('10000000000000000') + expect(estimatedSwap.dustThreshold.baseAmount.decimal).toBe(18) + expect(assetToString(estimatedSwap.fees.asset)).toBe('BTC.BTC') + expect(assetToString(estimatedSwap.fees.affiliateFee.asset)).toBe('BTC.BTC') + expect(estimatedSwap.fees.affiliateFee.baseAmount.amount().toString()).toBe('0') + expect(estimatedSwap.fees.affiliateFee.baseAmount.decimal).toBe(8) + expect(assetToString(estimatedSwap.fees.outboundFee.asset)).toBe('BTC.BTC') + expect(estimatedSwap.fees.outboundFee.baseAmount.amount().toString()).toBe('1599') + expect(estimatedSwap.fees.outboundFee.baseAmount.decimal).toBe(8) + expect(estimatedSwap.totalSwapSeconds).toBe(702) + expect(estimatedSwap.slipBasisPoints).toBe(0) + expect(estimatedSwap.canSwap).toBe(true) + expect(estimatedSwap.errors.length).toBe(0) + expect(estimatedSwap.warning).toBe('Do not cache this response. Do not send funds after the expiry.') + }) + + it('Should estimate from ERC-20 swap', async () => { + const USDT = assetFromStringEx('ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7') + const estimatedSwap = await protocol.estimateSwap({ + fromAsset: USDT, + destinationAsset: AssetETH, + amount: new CryptoAmount(assetToBase(assetAmount(20, 6)), USDT), + destinationAddress: 'ETHEREUMfakeaddress', + }) + expect(estimatedSwap.protocol).toBe('Chainflip') + expect(estimatedSwap.toAddress).toBe('ETHEREUMfakeaddress') + expect(estimatedSwap.memo).toBe('') + expect(assetToString(estimatedSwap.expectedAmount.asset)).toBe('ETH.ETH') + expect(estimatedSwap.expectedAmount.baseAmount.amount().toString()).toBe('2063188201000691') + expect(estimatedSwap.expectedAmount.baseAmount.decimal).toBe(18) + expect(assetToString(estimatedSwap.dustThreshold.asset)).toBe('ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7') + expect(estimatedSwap.dustThreshold.baseAmount.amount().toString()).toBe('20000000') + expect(estimatedSwap.dustThreshold.baseAmount.decimal).toBe(6) + expect(assetToString(estimatedSwap.fees.asset)).toBe('ETH.ETH') + expect(assetToString(estimatedSwap.fees.affiliateFee.asset)).toBe('ETH.ETH') + expect(estimatedSwap.fees.affiliateFee.baseAmount.amount().toString()).toBe('0') + expect(estimatedSwap.fees.affiliateFee.baseAmount.decimal).toBe(18) + expect(assetToString(estimatedSwap.fees.outboundFee.asset)).toBe('ETH.ETH') + expect(estimatedSwap.fees.outboundFee.baseAmount.amount().toString()).toBe('1447621978320000') + expect(estimatedSwap.fees.outboundFee.baseAmount.decimal).toBe(18) + expect(estimatedSwap.totalSwapSeconds).toBe(114) + expect(estimatedSwap.slipBasisPoints).toBe(0) + expect(estimatedSwap.canSwap).toBe(true) + expect(estimatedSwap.errors.length).toBe(0) + expect(estimatedSwap.warning).toBe('Do not cache this response. Do not send funds after the expiry.') + }) + + it('Should estimate to ERC-20 swap', async () => { + const USDT = assetFromStringEx('ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7') + const estimatedSwap = await protocol.estimateSwap({ + fromAsset: AssetETH, + destinationAsset: USDT, + amount: new CryptoAmount(assetToBase(assetAmount(0.01, 18)), AssetETH), + destinationAddress: 'ETHEREUMfakeaddress', + }) + expect(estimatedSwap.protocol).toBe('Chainflip') + expect(estimatedSwap.toAddress).toBe('ETHEREUMfakeaddress') + expect(estimatedSwap.memo).toBe('') + expect(assetToString(estimatedSwap.expectedAmount.asset)).toBe( + 'ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7', + ) + expect(estimatedSwap.expectedAmount.baseAmount.amount().toString()).toBe('24884030') + expect(estimatedSwap.expectedAmount.baseAmount.decimal).toBe(6) + expect(assetToString(estimatedSwap.dustThreshold.asset)).toBe('ETH.ETH') + expect(estimatedSwap.dustThreshold.baseAmount.amount().toString()).toBe('10000000000000000') + expect(estimatedSwap.dustThreshold.baseAmount.decimal).toBe(18) + expect(assetToString(estimatedSwap.fees.asset)).toBe('ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7') + expect(assetToString(estimatedSwap.fees.affiliateFee.asset)).toBe( + 'ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7', + ) + expect(estimatedSwap.fees.affiliateFee.baseAmount.amount().toString()).toBe('0') + expect(estimatedSwap.fees.affiliateFee.baseAmount.decimal).toBe(6) + expect(assetToString(estimatedSwap.fees.outboundFee.asset)).toBe( + 'ETH.USDT-0xdAC17F958D2ee523a2206206994597C13D831ec7', + ) + expect(estimatedSwap.fees.outboundFee.baseAmount.amount().toString()).toBe('8988369') + expect(estimatedSwap.fees.outboundFee.baseAmount.decimal).toBe(6) + expect(estimatedSwap.totalSwapSeconds).toBe(114) + expect(estimatedSwap.slipBasisPoints).toBe(0) + expect(estimatedSwap.canSwap).toBe(true) + expect(estimatedSwap.errors.length).toBe(0) + expect(estimatedSwap.warning).toBe('Do not cache this response. Do not send funds after the expiry.') + }) +}) diff --git a/packages/xchain-aggregator/package.json b/packages/xchain-aggregator/package.json index 00c96563c..b84fbe4e1 100644 --- a/packages/xchain-aggregator/package.json +++ b/packages/xchain-aggregator/package.json @@ -29,6 +29,7 @@ "directory": "release/package" }, "dependencies": { + "@chainflip/sdk": "1.3.0", "@xchainjs/xchain-client": "workspace:*", "@xchainjs/xchain-mayachain": "workspace:*", "@xchainjs/xchain-mayachain-amm": "workspace:*", @@ -48,4 +49,4 @@ "axios": "1.3.6", "axios-mock-adapter": "1.20.0" } -} \ No newline at end of file +} diff --git a/packages/xchain-aggregator/rollup.config.js b/packages/xchain-aggregator/rollup.config.js index 97bf77e08..b0b3b61a5 100644 --- a/packages/xchain-aggregator/rollup.config.js +++ b/packages/xchain-aggregator/rollup.config.js @@ -13,12 +13,14 @@ export default { format: 'cjs', exports: 'named', sourcemap: true, + inlineDynamicImports: true, }, { file: pkg.module, format: 'es', exports: 'named', sourcemap: true, + inlineDynamicImports: true, }, ], plugins: [ diff --git a/packages/xchain-aggregator/src/aggregator.ts b/packages/xchain-aggregator/src/aggregator.ts index 3fa5f42fc..be635ec0e 100644 --- a/packages/xchain-aggregator/src/aggregator.ts +++ b/packages/xchain-aggregator/src/aggregator.ts @@ -1,7 +1,7 @@ import { assetToString } from '@xchainjs/xchain-util' import { Wallet } from '@xchainjs/xchain-wallet' -import { MayachainProtocol, ThorchainProtocol } from './protocols' +import { ChainflipProtocol, MayachainProtocol, ThorchainProtocol } from './protocols' import { IProtocol, Protocol, @@ -18,7 +18,7 @@ export class Aggregator { private protocols: IProtocol[] constructor(wallet?: Wallet) { - this.protocols = [new ThorchainProtocol(wallet), new MayachainProtocol(wallet)] + this.protocols = [new ThorchainProtocol(wallet), new MayachainProtocol(wallet), new ChainflipProtocol(wallet)] } /** @@ -51,7 +51,9 @@ export class Aggregator { }) if (!optimalSwap) - throw Error(`Can not estimate swap from ${assetToString(params.fromAsset)} to ${assetToString(params.fromAsset)}`) + throw Error( + `Can not estimate swap from ${assetToString(params.fromAsset)} to ${assetToString(params.destinationAsset)}`, + ) return optimalSwap } diff --git a/packages/xchain-aggregator/src/protocols/chainflip/chainflipProtocol.ts b/packages/xchain-aggregator/src/protocols/chainflip/chainflipProtocol.ts new file mode 100644 index 000000000..689c1fae5 --- /dev/null +++ b/packages/xchain-aggregator/src/protocols/chainflip/chainflipProtocol.ts @@ -0,0 +1,194 @@ +import { AssetData, SwapSDK } from '@chainflip/sdk/swap' +import { Asset, CachedValue, Chain, CryptoAmount, baseAmount, isSynthAsset } from '@xchainjs/xchain-util' +import { Wallet } from '@xchainjs/xchain-wallet' + +import { IProtocol, QuoteSwap, QuoteSwapParams, SwapHistory, TxSubmitted } from '../../types' + +import { cChainToXChain, xAssetToCAsset } from './utils' + +/** + * Chainflip protocol + */ +export class ChainflipProtocol implements IProtocol { + public readonly name = 'Chainflip' + private sdk: SwapSDK + private wallet: Wallet + private assetsData: CachedValue + + constructor(wallet: Wallet = new Wallet({})) { + this.sdk = new SwapSDK({ + network: 'mainnet', + }) + this.wallet = wallet + this.assetsData = new CachedValue(() => { + return this.sdk.getAssets() + }, 24 * 60 * 60 * 1000) + } + + /** + * Check if an asset is supported in the protocol + * @param {Asset} asset Asset to check if it is supported + * @returns {boolean} True if the asset is supported, otherwise false + */ + public async isAssetSupported(asset: Asset): Promise { + if (isSynthAsset(asset)) return false + try { + await this.getAssetData(asset) + return true + } catch { + return false + } + } + + /** + * Retrieve the supported chains by the protocol + * @returns {Chain[]} the supported chains by the protocol + */ + public async getSupportedChains(): Promise { + const chains = await this.sdk.getChains() + return chains.map((chain) => cChainToXChain(chain.chain)).filter((chain) => chain !== null) as Chain[] + } + + /** + * Estimate swap by validating the swap parameters. + * + * @param {QuoteSwapParams} quoteSwapParams Swap parameters. + * @returns {QuoteSwap} Quote swap result. If swap cannot be done, it returns an empty QuoteSwap with reasons. + */ + public async estimateSwap(params: QuoteSwapParams): Promise { + const srcAssetData = await this.getAssetData(params.fromAsset) + const destAssetData = await this.getAssetData(params.destinationAsset) + + try { + let toAddress = '' + if (params.destinationAddress) { + const { depositAddress } = await this.sdk.requestDepositAddress({ + srcChain: srcAssetData.chain, + srcAsset: srcAssetData.asset, + destChain: destAssetData.chain, + destAsset: destAssetData.asset, + destAddress: params.destinationAddress, + amount: params.amount.baseAmount.amount().toString(), + }) + + toAddress = depositAddress + } + + const { quote } = await this.sdk.getQuote({ + srcChain: srcAssetData.chain, + srcAsset: srcAssetData.asset, + destChain: destAssetData.chain, + destAsset: destAssetData.asset, + amount: params.amount.baseAmount.amount().toString(), + }) + + const outboundFee = quote.includedFees.find((fee) => fee.type === 'EGRESS') + const brokerFee = quote.includedFees.find((fee) => fee.type === 'BROKER') + + return { + protocol: this.name, + toAddress, + memo: '', + expectedAmount: new CryptoAmount( + baseAmount(quote.egressAmount, destAssetData.decimals), + params.destinationAsset, + ), + dustThreshold: new CryptoAmount( + baseAmount(srcAssetData.minimumSwapAmount, srcAssetData.decimals), + params.fromAsset, + ), + totalSwapSeconds: quote.estimatedDurationSeconds, + canSwap: toAddress !== '', + warning: quote.lowLiquidityWarning + ? 'Do not cache this response. Do not send funds after the expiry. The difference in the chainflip swap rate (excluding fees) is lower than the global index rate of the swap by more than a certain threshold (currently set to 5%)' + : 'Do not cache this response. Do not send funds after the expiry.', + errors: [], + slipBasisPoints: 0, + fees: { + asset: params.destinationAsset, + outboundFee: new CryptoAmount( + baseAmount(outboundFee ? outboundFee.amount : 0, destAssetData.decimals), + params.destinationAsset, + ), + affiliateFee: new CryptoAmount( + baseAmount(brokerFee ? brokerFee.amount : 0, destAssetData.decimals), + params.destinationAsset, + ), + }, + } + } catch (e) { + return { + protocol: this.name, + toAddress: '', + memo: '', + expectedAmount: new CryptoAmount(baseAmount(0, destAssetData.decimals), params.destinationAsset), + dustThreshold: new CryptoAmount( + baseAmount(srcAssetData.minimumSwapAmount, srcAssetData.decimals), + params.fromAsset, + ), + totalSwapSeconds: 0, + canSwap: false, + warning: '', + errors: [e instanceof Error ? e.message : 'Unknown error'], + slipBasisPoints: 0, + fees: { + asset: params.destinationAsset, + outboundFee: new CryptoAmount(baseAmount(0, destAssetData.decimals), params.destinationAsset), + affiliateFee: new CryptoAmount(baseAmount(0, destAssetData.decimals), params.destinationAsset), + }, + } + } + } + + /** + * Perform a swap operation between assets. + * @param {QuoteSwapParams} quoteSwapParams Swap parameters + * @returns {TxSubmitted} Transaction hash and URL of the swap + */ + public async doSwap(params: QuoteSwapParams): Promise { + const quoteSwap = await this.estimateSwap(params) + if (!quoteSwap.canSwap) { + throw Error(`Can not make swap. ${quoteSwap.errors.join('\n')}`) + } + + const hash = await this.wallet.transfer({ + recipient: quoteSwap.toAddress, + amount: params.amount.baseAmount, + asset: params.fromAsset, + memo: quoteSwap.memo, + }) + + return { + hash, + url: await this.wallet.getExplorerTxUrl(params.fromAsset.chain, hash), + } + } + + /** + * Get historical swaps + * @throws {Error} - Method not implemented. + * @returns the swap history + */ + public async getSwapHistory(): Promise { + throw new Error('Method not implemented.') + } + + /** + * Get asset data + * @param {Asset} asset - Asset of which return data + * @throws {Error} - If asset is not supported in Chainflip + * @returns the asset data + */ + private async getAssetData(asset: Asset): Promise { + const chainAssets = await this.assetsData.getValue() + const assetData = chainAssets.find((chainAsset) => { + const contractAddress = asset.symbol.split('-').length > 1 ? asset.symbol.split('-')[1] : undefined + return ( + chainAsset.asset === xAssetToCAsset(asset) && + chainAsset.contractAddress?.toLowerCase() === contractAddress?.toLowerCase() + ) + }) + if (!assetData) throw Error(`${asset.ticker} asset not supported in ${asset.chain} chain`) + return assetData + } +} diff --git a/packages/xchain-aggregator/src/protocols/chainflip/index.ts b/packages/xchain-aggregator/src/protocols/chainflip/index.ts new file mode 100644 index 000000000..59d6cefea --- /dev/null +++ b/packages/xchain-aggregator/src/protocols/chainflip/index.ts @@ -0,0 +1 @@ +export { ChainflipProtocol } from './chainflipProtocol' diff --git a/packages/xchain-aggregator/src/protocols/chainflip/utils.ts b/packages/xchain-aggregator/src/protocols/chainflip/utils.ts new file mode 100644 index 000000000..f9a9fb6b5 --- /dev/null +++ b/packages/xchain-aggregator/src/protocols/chainflip/utils.ts @@ -0,0 +1,43 @@ +import { Asset as CAsset, AssetData, Chain as CChain, Chains } from '@chainflip/sdk/swap' +import { Asset as XAsset, Chain as XChain } from '@xchainjs/xchain-util' + +export const cChainToXChain = (chain: CChain): XChain => { + switch (chain) { + case 'Bitcoin': + return 'BTC' + case 'Ethereum': + return 'ETH' + case 'Polkadot': + return 'POL' + default: + throw Error('Unsupported chain in XChainJS') + } +} + +export const xChainToCChain = (chain: XChain): CChain => { + switch (chain) { + case 'BTC': + return Chains.Bitcoin + case 'ETH': + return Chains.Ethereum + case 'POL': + return Chains.Polkadot + default: + throw Error('Unsupported chain in Chainflip') + } +} + +export const cAssetToXAsset = (asset: AssetData): XAsset => { + const chain = cChainToXChain(asset.chain) + if (!chain) throw Error() + return { + chain, + ticker: asset.contractAddress ? `${asset.symbol}-${asset.contractAddress}` : asset.symbol, + symbol: asset.symbol, + synth: false, + } +} + +export const xAssetToCAsset = (asset: XAsset): CAsset => { + return asset.ticker as CAsset +} diff --git a/packages/xchain-aggregator/src/protocols/index.ts b/packages/xchain-aggregator/src/protocols/index.ts index 9ab3d7bdd..6f8209165 100644 --- a/packages/xchain-aggregator/src/protocols/index.ts +++ b/packages/xchain-aggregator/src/protocols/index.ts @@ -1,2 +1,3 @@ export { MayachainProtocol } from './mayachain' export { ThorchainProtocol } from './thorchain' +export { ChainflipProtocol } from './chainflip' diff --git a/packages/xchain-aggregator/src/types.ts b/packages/xchain-aggregator/src/types.ts index 13d100425..60097d637 100644 --- a/packages/xchain-aggregator/src/types.ts +++ b/packages/xchain-aggregator/src/types.ts @@ -18,7 +18,7 @@ type Fees = { outboundFee: CryptoAmount // The outbound fee amount } -type Protocol = 'Thorchain' | 'Mayachain' +type Protocol = 'Thorchain' | 'Mayachain' | 'Chainflip' /** * Represents a quote for a swap operation. @@ -29,6 +29,7 @@ type QuoteSwap = { memo: string // The memo associated with the swap expectedAmount: CryptoAmount // The expected amount to be received after the swap dustThreshold: CryptoAmount // The dust threshold for the swap + // TODO: Update type to return an array of the fees fees: Fees // The fees associated with the swap totalSwapSeconds: number // The total time for the swap operation slipBasisPoints: number // The slip basis points for the swap diff --git a/yarn.lock b/yarn.lock index b041505fc..0b3d7ffb5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25,6 +25,13 @@ __metadata: languageName: node linkType: hard +"@adraffy/ens-normalize@npm:1.10.1": + version: 1.10.1 + resolution: "@adraffy/ens-normalize@npm:1.10.1" + checksum: 10c0/fdd647604e8fac6204921888aaf5a6bc65eabf0d2921bc5f93b64d01f4bc33ead167c1445f7de05468d05cd92ac31b74c68d2be840c62b79d73693308f885c06 + languageName: node + linkType: hard + "@ampproject/remapping@npm:^2.2.0": version: 2.2.1 resolution: "@ampproject/remapping@npm:2.2.1" @@ -676,6 +683,18 @@ __metadata: languageName: node linkType: hard +"@chainflip/sdk@npm:1.3.0": + version: 1.3.0 + resolution: "@chainflip/sdk@npm:1.3.0" + dependencies: + "@polkadot/util": "npm:^12.6.2" + "@polkadot/util-crypto": "npm:^12.6.2" + axios: "npm:^1.6.8" + ethers: "npm:^6.11.1" + checksum: 10c0/18690f1a2eb070339221bc7c1bd9fa9c05d8d69e5c04e156355ad138a89d6013f131e290398c9a8e766307644ee12f346d4e3cb1dae7e6b12f2f321c3f50d61f + languageName: node + linkType: hard + "@changesets/apply-release-plan@npm:^7.0.0": version: 7.0.0 resolution: "@changesets/apply-release-plan@npm:7.0.0" @@ -2419,13 +2438,38 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:^1, @noble/hashes@npm:^1.0.0": +"@noble/curves@npm:1.2.0": + version: 1.2.0 + resolution: "@noble/curves@npm:1.2.0" + dependencies: + "@noble/hashes": "npm:1.3.2" + checksum: 10c0/0bac7d1bbfb3c2286910b02598addd33243cb97c3f36f987ecc927a4be8d7d88e0fcb12b0f0ef8a044e7307d1844dd5c49bb724bfa0a79c8ec50ba60768c97f6 + languageName: node + linkType: hard + +"@noble/curves@npm:^1.3.0": + version: 1.4.0 + resolution: "@noble/curves@npm:1.4.0" + dependencies: + "@noble/hashes": "npm:1.4.0" + checksum: 10c0/31fbc370df91bcc5a920ca3f2ce69c8cf26dc94775a36124ed8a5a3faf0453badafd2ee4337061ffea1b43c623a90ee8b286a5a81604aaf9563bdad7ff795d18 + languageName: node + linkType: hard + +"@noble/hashes@npm:1.3.2, @noble/hashes@npm:^1, @noble/hashes@npm:^1.0.0": version: 1.3.2 resolution: "@noble/hashes@npm:1.3.2" checksum: 10c0/2482cce3bce6a596626f94ca296e21378e7a5d4c09597cbc46e65ffacc3d64c8df73111f2265444e36a3168208628258bbbaccba2ef24f65f58b2417638a20e7 languageName: node linkType: hard +"@noble/hashes@npm:1.4.0, @noble/hashes@npm:^1.3.3": + version: 1.4.0 + resolution: "@noble/hashes@npm:1.4.0" + checksum: 10c0/8c3f005ee72e7b8f9cff756dfae1241485187254e3f743873e22073d63906863df5d4f13d441b7530ea614b7a093f0d889309f28b59850f33b66cb26a779a4a5 + languageName: node + linkType: hard + "@noble/hashes@npm:^1.2.0": version: 1.3.0 resolution: "@noble/hashes@npm:1.3.0" @@ -2528,6 +2572,184 @@ __metadata: languageName: node linkType: hard +"@polkadot/networks@npm:12.6.2": + version: 12.6.2 + resolution: "@polkadot/networks@npm:12.6.2" + dependencies: + "@polkadot/util": "npm:12.6.2" + "@substrate/ss58-registry": "npm:^1.44.0" + tslib: "npm:^2.6.2" + checksum: 10c0/44a482c46900058e6d5b25110cb5396382036057240cd4a8e0dae325fab54e689ec81bc43b047570581f14ce456b67310c05c1fe34c4b7f7d4e064f095f4c276 + languageName: node + linkType: hard + +"@polkadot/util-crypto@npm:^12.6.2": + version: 12.6.2 + resolution: "@polkadot/util-crypto@npm:12.6.2" + dependencies: + "@noble/curves": "npm:^1.3.0" + "@noble/hashes": "npm:^1.3.3" + "@polkadot/networks": "npm:12.6.2" + "@polkadot/util": "npm:12.6.2" + "@polkadot/wasm-crypto": "npm:^7.3.2" + "@polkadot/wasm-util": "npm:^7.3.2" + "@polkadot/x-bigint": "npm:12.6.2" + "@polkadot/x-randomvalues": "npm:12.6.2" + "@scure/base": "npm:^1.1.5" + tslib: "npm:^2.6.2" + peerDependencies: + "@polkadot/util": 12.6.2 + checksum: 10c0/b25f1574a2d4298c32b7a3cf3fa9f1b1237af3cc9e4ac16e75840097e9bcea11c8188abd5c46522d46d350edceb1e3e54fe8cbb01111e4eb643df4040ff41e2a + languageName: node + linkType: hard + +"@polkadot/util@npm:12.6.2, @polkadot/util@npm:^12.6.2": + version: 12.6.2 + resolution: "@polkadot/util@npm:12.6.2" + dependencies: + "@polkadot/x-bigint": "npm:12.6.2" + "@polkadot/x-global": "npm:12.6.2" + "@polkadot/x-textdecoder": "npm:12.6.2" + "@polkadot/x-textencoder": "npm:12.6.2" + "@types/bn.js": "npm:^5.1.5" + bn.js: "npm:^5.2.1" + tslib: "npm:^2.6.2" + checksum: 10c0/e426d31f8a6b8e8c57b86c18b419312906c5a169e5b2d89c15b54a5d6cf297912250d336f81926e07511ce825d36222d9e6387a01240aa6a20b11aa25dc8226a + languageName: node + linkType: hard + +"@polkadot/wasm-bridge@npm:7.3.2": + version: 7.3.2 + resolution: "@polkadot/wasm-bridge@npm:7.3.2" + dependencies: + "@polkadot/wasm-util": "npm:7.3.2" + tslib: "npm:^2.6.2" + peerDependencies: + "@polkadot/util": "*" + "@polkadot/x-randomvalues": "*" + checksum: 10c0/8becfcd4efbabe8ea536c353164c8b767a5510d6d62e376813ab1dc0dd4560906f1dfdb1b349d56b4da657ba7c88bc9f074b658218dcae9b1edbd36f4508b710 + languageName: node + linkType: hard + +"@polkadot/wasm-crypto-asmjs@npm:7.3.2": + version: 7.3.2 + resolution: "@polkadot/wasm-crypto-asmjs@npm:7.3.2" + dependencies: + tslib: "npm:^2.6.2" + peerDependencies: + "@polkadot/util": "*" + checksum: 10c0/c4eb0b2c6bae2cd7b4ada5211c877a0f0cff4d4a4f2716817430c5aab74f4e8d37099add57c809a098033028378ed3e88ba1c56fd85b6fd0a80b181742f7a3f9 + languageName: node + linkType: hard + +"@polkadot/wasm-crypto-init@npm:7.3.2": + version: 7.3.2 + resolution: "@polkadot/wasm-crypto-init@npm:7.3.2" + dependencies: + "@polkadot/wasm-bridge": "npm:7.3.2" + "@polkadot/wasm-crypto-asmjs": "npm:7.3.2" + "@polkadot/wasm-crypto-wasm": "npm:7.3.2" + "@polkadot/wasm-util": "npm:7.3.2" + tslib: "npm:^2.6.2" + peerDependencies: + "@polkadot/util": "*" + "@polkadot/x-randomvalues": "*" + checksum: 10c0/4813a87bf44065d4ec7cdc29b00f37cc6859974969710c6a6fefba8e42f5bb0c7e102293a8418b1c6e1b5fd55540d13beebdff777200b69420ce50b8fad803ed + languageName: node + linkType: hard + +"@polkadot/wasm-crypto-wasm@npm:7.3.2": + version: 7.3.2 + resolution: "@polkadot/wasm-crypto-wasm@npm:7.3.2" + dependencies: + "@polkadot/wasm-util": "npm:7.3.2" + tslib: "npm:^2.6.2" + peerDependencies: + "@polkadot/util": "*" + checksum: 10c0/546ebc5c42929f2f37565190014ff26f6817024e087c56053c1d8c1dcffd1f02014c4638ca70c79145d540f760339699209bb1dc939c235085a7c78efd56bc60 + languageName: node + linkType: hard + +"@polkadot/wasm-crypto@npm:^7.3.2": + version: 7.3.2 + resolution: "@polkadot/wasm-crypto@npm:7.3.2" + dependencies: + "@polkadot/wasm-bridge": "npm:7.3.2" + "@polkadot/wasm-crypto-asmjs": "npm:7.3.2" + "@polkadot/wasm-crypto-init": "npm:7.3.2" + "@polkadot/wasm-crypto-wasm": "npm:7.3.2" + "@polkadot/wasm-util": "npm:7.3.2" + tslib: "npm:^2.6.2" + peerDependencies: + "@polkadot/util": "*" + "@polkadot/x-randomvalues": "*" + checksum: 10c0/ff3ef6a2a4dcbbdeb257e7a42f906f1bb7e31292600482c1acf9267406011ea75bd9d3d6ceaf4c011f986e25a2416768775ee59ccc7dbfa6c529b11b8ea91eb4 + languageName: node + linkType: hard + +"@polkadot/wasm-util@npm:7.3.2, @polkadot/wasm-util@npm:^7.3.2": + version: 7.3.2 + resolution: "@polkadot/wasm-util@npm:7.3.2" + dependencies: + tslib: "npm:^2.6.2" + peerDependencies: + "@polkadot/util": "*" + checksum: 10c0/58ef58d357e7983c3bb4008b0159262d5c588234d7be64155c031f452fc0daeb078ff0ac8bb4b0377dac307130b0b548c01fd466968869ed308d50e2c162d23b + languageName: node + linkType: hard + +"@polkadot/x-bigint@npm:12.6.2": + version: 12.6.2 + resolution: "@polkadot/x-bigint@npm:12.6.2" + dependencies: + "@polkadot/x-global": "npm:12.6.2" + tslib: "npm:^2.6.2" + checksum: 10c0/78123efa2a5fad7fccb79dbe0c44f5506b70405a2b9b1dc9db9450ddd2f01791b011a46c9fff31ed8b21aace6f676179c4b7746c97ca254e8822bcf543e4d779 + languageName: node + linkType: hard + +"@polkadot/x-global@npm:12.6.2": + version: 12.6.2 + resolution: "@polkadot/x-global@npm:12.6.2" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/63738eb46465e3e43151d746321c178131385a734e1d3865fc76667fec9d4b1fb8b35a0d8ee75834035b54a4047e0bae86c4f2e465b16c73d4fc15ec4426446f + languageName: node + linkType: hard + +"@polkadot/x-randomvalues@npm:12.6.2": + version: 12.6.2 + resolution: "@polkadot/x-randomvalues@npm:12.6.2" + dependencies: + "@polkadot/x-global": "npm:12.6.2" + tslib: "npm:^2.6.2" + peerDependencies: + "@polkadot/util": 12.6.2 + "@polkadot/wasm-util": "*" + checksum: 10c0/44920ec7a93ca0b5b0d2abae493fe5a9fb8cdb44b70029d431c1244a11dea0a9f14d216b4d14bde8b984199b9dd364a3ae68b51937784645343f686b3613c223 + languageName: node + linkType: hard + +"@polkadot/x-textdecoder@npm:12.6.2": + version: 12.6.2 + resolution: "@polkadot/x-textdecoder@npm:12.6.2" + dependencies: + "@polkadot/x-global": "npm:12.6.2" + tslib: "npm:^2.6.2" + checksum: 10c0/d1aa46dc0c4f88bce3cb7aaadbede99c2fb159c0fd317fb9fe5b54bdbb83da9cce3a5d628e25892028b34cc4eeef72669c344f0af12e21f05429142cc7b4732d + languageName: node + linkType: hard + +"@polkadot/x-textencoder@npm:12.6.2": + version: 12.6.2 + resolution: "@polkadot/x-textencoder@npm:12.6.2" + dependencies: + "@polkadot/x-global": "npm:12.6.2" + tslib: "npm:^2.6.2" + checksum: 10c0/fa234ce4d164991ea98f34e9eae2adf0c4d2b0806e2e30b11c41a52b432f8cbd91fb16945243809fd9433c513b8c7ab4c16d902b92faf7befaa523daae7459f4 + languageName: node + linkType: hard + "@protobufjs/aspromise@npm:^1.1.1, @protobufjs/aspromise@npm:^1.1.2": version: 1.1.2 resolution: "@protobufjs/aspromise@npm:1.1.2" @@ -2725,6 +2947,13 @@ __metadata: languageName: node linkType: hard +"@scure/base@npm:^1.1.5": + version: 1.1.6 + resolution: "@scure/base@npm:1.1.6" + checksum: 10c0/237a46a1f45391fc57719154f14295db936a0b1562ea3e182dd42d7aca082dbb7062a28d6c49af16a7e478b12dae8a0fe678d921ea5056bcc30238d29eb05c55 + languageName: node + linkType: hard + "@sinclair/typebox@npm:^0.25.16": version: 0.25.24 resolution: "@sinclair/typebox@npm:0.25.24" @@ -2750,6 +2979,13 @@ __metadata: languageName: node linkType: hard +"@substrate/ss58-registry@npm:^1.44.0": + version: 1.48.0 + resolution: "@substrate/ss58-registry@npm:1.48.0" + checksum: 10c0/b2ff1bd7688a3f72353f05dd47676a0127be346d0516a0dd2214118d4085637e35a37b115f4bd9101772a9723f6111e4e77dc008cf69ad45b64c806de419547c + languageName: node + linkType: hard + "@supercharge/promise-pool@npm:2.4.0": version: 2.4.0 resolution: "@supercharge/promise-pool@npm:2.4.0" @@ -2892,6 +3128,15 @@ __metadata: languageName: node linkType: hard +"@types/bn.js@npm:^5.1.5": + version: 5.1.5 + resolution: "@types/bn.js@npm:5.1.5" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/e9f375b43d8119ed82aed2090f83d4cda8afbb63ba13223afb02fa7550258ff90acd76d65cd7186838644048f085241cd98a3a512d8d187aa497c6039c746ac8 + languageName: node + linkType: hard + "@types/crypto-js@npm:^3.1.43": version: 3.1.47 resolution: "@types/crypto-js@npm:3.1.47" @@ -3055,6 +3300,13 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:18.15.13": + version: 18.15.13 + resolution: "@types/node@npm:18.15.13" + checksum: 10c0/6e5f61c559e60670a7a8fb88e31226ecc18a21be103297ca4cf9848f0a99049dae77f04b7ae677205f2af494f3701b113ba8734f4b636b355477a6534dbb8ada + languageName: node + linkType: hard + "@types/node@npm:20.11.28": version: 20.11.28 resolution: "@types/node@npm:20.11.28" @@ -3335,6 +3587,7 @@ __metadata: version: 0.0.0-use.local resolution: "@xchainjs/xchain-aggregator@workspace:packages/xchain-aggregator" dependencies: + "@chainflip/sdk": "npm:1.3.0" "@xchainjs/xchain-avax": "workspace:*" "@xchainjs/xchain-binance": "workspace:*" "@xchainjs/xchain-bitcoin": "workspace:*" @@ -3915,6 +4168,13 @@ __metadata: languageName: node linkType: hard +"aes-js@npm:4.0.0-beta.5": + version: 4.0.0-beta.5 + resolution: "aes-js@npm:4.0.0-beta.5" + checksum: 10c0/444f4eefa1e602cbc4f2a3c644bc990f93fd982b148425fee17634da510586fc09da940dcf8ace1b2d001453c07ff042e55f7a0482b3cc9372bf1ef75479090c + languageName: node + linkType: hard + "agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1": version: 7.1.1 resolution: "agent-base@npm:7.1.1" @@ -4278,6 +4538,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:^1.6.8": + version: 1.7.2 + resolution: "axios@npm:1.7.2" + dependencies: + follow-redirects: "npm:^1.15.6" + form-data: "npm:^4.0.0" + proxy-from-env: "npm:^1.1.0" + checksum: 10c0/cbd47ce380fe045313364e740bb03b936420b8b5558c7ea36a4563db1258c658f05e40feb5ddd41f6633fdd96d37ac2a76f884dad599c5b0224b4c451b3fa7ae + languageName: node + linkType: hard + "b4a@npm:^1.6.0": version: 1.6.3 resolution: "b4a@npm:1.6.3" @@ -6240,6 +6511,21 @@ __metadata: languageName: node linkType: hard +"ethers@npm:^6.11.1": + version: 6.12.1 + resolution: "ethers@npm:6.12.1" + dependencies: + "@adraffy/ens-normalize": "npm:1.10.1" + "@noble/curves": "npm:1.2.0" + "@noble/hashes": "npm:1.3.2" + "@types/node": "npm:18.15.13" + aes-js: "npm:4.0.0-beta.5" + tslib: "npm:2.4.0" + ws: "npm:8.5.0" + checksum: 10c0/7686e1efdb0a831578f35d69188783c225de5a6fbb1b422327bc45cee04d49a2707e73c9342a6a5eb2870ce35668c71372737439ec3993d31d83f4a0e2446cc7 + languageName: node + linkType: hard + "events@npm:^3.0.0, events@npm:^3.3.0": version: 3.3.0 resolution: "events@npm:3.3.0" @@ -11338,6 +11624,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:2.4.0": + version: 2.4.0 + resolution: "tslib@npm:2.4.0" + checksum: 10c0/eb19bda3ae545b03caea6a244b34593468e23d53b26bf8649fbc20fce43e9b21a71127fd6d2b9662c0fe48ee6ff668ead48fd00d3b88b2b716b1c12edae25b5d + languageName: node + linkType: hard + "tslib@npm:2.5.0, tslib@npm:^2.1.0, tslib@npm:^2.4.0, tslib@npm:^2.5.0": version: 2.5.0 resolution: "tslib@npm:2.5.0" @@ -11352,6 +11645,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.6.2": + version: 2.6.2 + resolution: "tslib@npm:2.6.2" + checksum: 10c0/e03a8a4271152c8b26604ed45535954c0a45296e32445b4b87f8a5abdb2421f40b59b4ca437c4346af0f28179780d604094eb64546bee2019d903d01c6c19bdb + languageName: node + linkType: hard + "tsutils@npm:^3.21.0": version: 3.21.0 resolution: "tsutils@npm:3.21.0" @@ -12087,6 +12387,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:8.5.0": + version: 8.5.0 + resolution: "ws@npm:8.5.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 10c0/0baeee03e97865accda8fad51e8e5fa17d19b8e264529efdf662bbba2acc1c7f1de8316287e6df5cb639231a96009e6d5234b57e6ff36ee2d04e49a0995fec2f + languageName: node + linkType: hard + "ws@npm:^3.2.0": version: 3.3.3 resolution: "ws@npm:3.3.3"