diff --git a/.changeset/witty-rats-arrive.md b/.changeset/witty-rats-arrive.md new file mode 100644 index 000000000..53b05168a --- /dev/null +++ b/.changeset/witty-rats-arrive.md @@ -0,0 +1,12 @@ +--- +"@swapkit/plugin-thorchain": minor +"@swapkit/helpers": minor +"@swapkit/tokens": minor +"@swapkit/wallet-ledger": minor +"@swapkit/plugin-radix": minor +"@swapkit/plugin-kado": minor +"@swapkit/core": minor +"@swapkit/sdk": minor +--- + +Adds Kado plugin diff --git a/bun.lockb b/bun.lockb index c81d7094a..60765b0a1 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/plugins/kado/CHANGELOG.md b/packages/plugins/kado/CHANGELOG.md new file mode 100644 index 000000000..e69de29bb diff --git a/packages/plugins/kado/build.ts b/packages/plugins/kado/build.ts new file mode 100644 index 000000000..e7817001f --- /dev/null +++ b/packages/plugins/kado/build.ts @@ -0,0 +1,4 @@ +import { buildPackage } from "../../../tools/builder"; +import { dependencies } from "./package.json"; + +buildPackage({ dependencies }); diff --git a/packages/plugins/kado/package.json b/packages/plugins/kado/package.json new file mode 100644 index 000000000..7ee55d96d --- /dev/null +++ b/packages/plugins/kado/package.json @@ -0,0 +1,32 @@ +{ + "author": "swapkit-oss", + "dependencies": { + "@swapkit/api": "workspace:*", + "@swapkit/helpers": "workspace:*" + }, + "description": "SwapKit Plugin - Kado", + "files": [ + "src/", + "dist/" + ], + "homepage": "https://github.com/thorswap/SwapKit", + "license": "Apache-2.0", + "main": "./dist/index.js", + "name": "@swapkit/plugin-kado", + "react-native": "./src/index.ts", + "repository": { + "type": "git", + "url": "git+https://github.com/thorswap/SwapKit.git" + }, + "scripts": { + "build": "bun run ./build.ts", + "clean": "rm -rf dist node_modules *.tsbuildinfo", + "lint": "biome check --write ./src", + "test": "echo 'bun test'", + "test:coverage": "bun test --coverage", + "type-check": "tsc --noEmit" + }, + "type": "module", + "types": "./src/index.ts", + "version": "0.0.0" +} diff --git a/packages/plugins/kado/src/helpers.ts b/packages/plugins/kado/src/helpers.ts new file mode 100644 index 000000000..19100ea14 --- /dev/null +++ b/packages/plugins/kado/src/helpers.ts @@ -0,0 +1,29 @@ +import { Chain } from "@swapkit/helpers"; + +export const SupportedKadoChain = { + thorchain: Chain.THORChain, + solana: Chain.Solana, + polygon: Chain.Polygon, + Optimism: Chain.Optimism, + litecoin: Chain.Litecoin, + kujira: Chain.Kujira, + ethereum: Chain.Ethereum, + "cosmos hub": Chain.Cosmos, + bitcoin: Chain.Bitcoin, + base: Chain.Base, + Avalanche: Chain.Avalanche, + Arbitrum: Chain.Arbitrum, +}; + +export const ChainToKadoChain = (chain: Chain) => { + const entries = Object.entries(SupportedKadoChain); + const found = entries.find(([_, value]) => value === chain); + if (!found) throw new Error(`Chain ${chain} not supported`); + return found[0]; +}; + +export const KadoChainToChain = (kadoChain: string) => { + const found = Object.keys(SupportedKadoChain).includes(kadoChain); + if (!found) throw new Error(`KadoChain ${kadoChain} not supported`); + return SupportedKadoChain[kadoChain as keyof typeof SupportedKadoChain]; +}; diff --git a/packages/plugins/kado/src/index.ts b/packages/plugins/kado/src/index.ts new file mode 100644 index 000000000..39b9a61b2 --- /dev/null +++ b/packages/plugins/kado/src/index.ts @@ -0,0 +1 @@ +export * from "./plugin"; diff --git a/packages/plugins/kado/src/plugin.ts b/packages/plugins/kado/src/plugin.ts new file mode 100644 index 000000000..d589e60bc --- /dev/null +++ b/packages/plugins/kado/src/plugin.ts @@ -0,0 +1,339 @@ +import type { QuoteResponse, QuoteResponseRoute } from "@swapkit/api"; +import { + type AssetValue, + Chain, + FeeTypeEnum, + ProviderName, + RequestClient, + blockTimes, +} from "@swapkit/helpers"; +import type { SwapKitPluginParams } from "@swapkit/helpers"; +import { ChainToKadoChain } from "./helpers"; +import type { + KadoFiatCurrency, + KadoFiatMethod, + KadoQuoteRequest, + KadoQuoteResponse, +} from "./types"; + +function mapKadoQuoteToQuoteResponse({ + quote, + sellAsset, + buyAsset, +}: { + quote: KadoQuoteResponse; + sellAsset: AssetValue; + buyAsset: AssetValue; +}): QuoteResponse { + const isOnRamp = sellAsset.chain === Chain.Fiat; + + const buyAssetAmount = buyAsset.set( + isOnRamp + ? quote.data.quote.receive.unitCount.toString() + : quote.data.quote.receive.amount.toString(), + ); + const totalSlippageBps = isOnRamp + ? Math.round((quote.data.quote.totalFee.amount / quote.data.quote.receive.amount) * 10_000) + : Math.round( + (quote.data.quote.totalFee.amount / + (quote.data.quote.price.price * quote.data.quote.baseAmount.amount)) * + 10_000, + ); + + const inboundChain = sellAsset.chain; + const outboundChain = buyAsset.chain; + + const inbound = Math.ceil(blockTimes[inboundChain] * 3); + const swap = Math.ceil(60); + const outbound = Math.ceil(blockTimes[outboundChain]); + const routes: QuoteResponseRoute[] = [ + { + providers: [ProviderName.KADO], + sellAsset: sellAsset.toString(), + sellAmount: sellAsset.getValue("string"), + buyAsset: buyAsset.toString(), + expectedBuyAmount: buyAssetAmount.getValue("string"), + expectedBuyAmountMaxSlippage: buyAssetAmount.getValue("string"), + sourceAddress: "{sourceAddress}", + destinationAddress: "{destinationAddress}", + fees: [ + { + asset: quote.data.quote.processingFee.currency, + amount: quote.data.quote.processingFee.amount.toString(), + type: FeeTypeEnum.LIQUIDITY, + protocol: ProviderName.KADO, + chain: Chain.Fiat, + }, + { + asset: quote.data.quote.networkFee.currency, + amount: quote.data.quote.networkFee.amount.toString(), + type: FeeTypeEnum.NETWORK, + protocol: ProviderName.KADO, + chain: buyAsset.chain, + }, + ], + totalSlippageBps, + legs: [ + { + provider: ProviderName.KADO, + sellAsset: sellAsset.toString(), + sellAmount: sellAsset.getValue("string"), + buyAsset: buyAsset.toString(), + buyAmount: quote.data.quote.receive.unitCount.toString(), + buyAmountMaxSlippage: quote.data.quote.receive.unitCount.toString(), + fees: [ + { + asset: quote.data.quote.processingFee.currency, + amount: quote.data.quote.processingFee.amount.toString(), + type: FeeTypeEnum.LIQUIDITY, + protocol: ProviderName.KADO, + chain: Chain.Fiat, + }, + { + asset: quote.data.quote.networkFee.currency, + amount: quote.data.quote.networkFee.amount.toString(), + type: FeeTypeEnum.NETWORK, + protocol: ProviderName.KADO, + chain: buyAsset.chain, + }, + ], + }, + ], + warnings: [], + meta: { + tags: [], + }, + estimatedTime: { + inbound, + swap, + outbound, + total: inbound + swap + outbound, + }, + }, + ]; + + return { + quoteId: crypto.randomUUID(), + routes, + error: quote.success ? undefined : quote.message, + }; +} + +export type KadoBlockchainsResponse = { + success: boolean; + message: string; + data: { + blockchains: { + _id: string; + supportedEnvironment: string; + network: string; + origin: string; + label: string; + associatedAssets: { + _id: string; + name: string; + description: string; + label: string; + supportedProviders: string[]; + stablecoin: boolean; + liveOnRamp: boolean; + createdAt: string; + updatedAt: string; + __v: number; + priority: number; + }; + avgTransactionTimeSeconds: number; + usesAvaxRouter: boolean; + liveOnRamp: boolean; + createdAt: string; + updatedAt: string; + __v: number; + priority: number; + }[]; + }; +}; + +export type KadoSupportedAssetsResponse = { + success: boolean; + message: string; + data: { + assets: { + _id: string; + name: string; + description: string; + label: string; + symbol: string; + supportedProviders: string[]; + stablecoin: boolean; + liveOnRamp: boolean; + createdAt: string; + updatedAt: string; + __v: number; + priority: number; + }[]; + }; +}; + +function plugin({ config: { kadoApiKey } }: SwapKitPluginParams<{ kadoApiKey: string }>) { + async function fetchProviderQuote({ + sellAsset, + buyAsset, + fiatMethod, + }: { + sellAsset: AssetValue; + buyAsset: AssetValue; + fiatMethod: KadoFiatMethod; + }): Promise { + try { + const isOnRamp = sellAsset.chain === Chain.Fiat; + + const transactionType = isOnRamp ? "buy" : "sell"; + + const currency = (isOnRamp ? sellAsset.symbol : buyAsset.symbol) as KadoFiatCurrency; + + const asset = isOnRamp ? buyAsset : sellAsset; + + const quoteRequest: KadoQuoteRequest = { + transactionType, + fiatMethod, + partner: "fortress", + amount: sellAsset.getValue("string"), + asset: asset.symbol, + blockchain: ChainToKadoChain(asset.chain), + currency, + }; + + const quote = await RequestClient.get( + "https://api.kado.money/v2/ramp/quote", + { + searchParams: quoteRequest, + headers: { + "X-Widget-Id": kadoApiKey, + }, + }, + ); + + if (!quote.success) { + throw new Error(quote.message); + } + + return mapKadoQuoteToQuoteResponse({ quote, sellAsset, buyAsset }); + } catch (_) { + throw new Error("core_swap_quote_error"); + } + } + + async function getBlockchains() { + const response = await RequestClient.get( + "https://api.kado.money/v1/ramp/blockchains", + ); + + if (!response.success) { + throw new Error(response.message); + } + + return response.data.blockchains; + } + + async function getAssets() { + const response = await RequestClient.get<{ + success: boolean; + message: string; + data: { + assets: { + _id: string; + name: string; + description: string; + label: string; + symbol: string; + supportedProviders: string[]; + stablecoin: boolean; + liveOnRamp: boolean; + createdAt: string; + updatedAt: string; + __v: number; + priority: number; + }[]; + }; + }>("https://api.kado.money/v1/ramp/supported-assets"); + + if (!response.success) { + throw new Error(response.message); + } + + return response.data.assets; + } + + async function getOrderStatus(orderId: string) { + try { + const response = await RequestClient.get<{ + success: boolean; + message: string; + data: { + order: { + status: string; + // Add other relevant fields from the API response + }; + }; + }>(`https://api.kado.money/v2/public/orders/${orderId}`, { + headers: { + "X-Widget-Id": kadoApiKey, + }, + }); + + if (!response.success) { + throw new Error(response.message); + } + + return response.data.order; + } catch (_error) { + throw new Error("Failed to get order status"); + } + } + + function getKadoWidgetUrl({ + sellAsset, + buyAsset, + supportedAssets, + recipient, + networkList, + type, + typeList, + widgetMode, + }: { + sellAsset: AssetValue; + buyAsset: AssetValue; + supportedAssets: AssetValue[]; + recipient: string; + networkList: Chain[]; + type: "BUY" | "SELL"; + typeList: "BUY" | "SELL"; + widgetMode: "minimal" | "full"; + }) { + const urlParams = new URLSearchParams({ + onPayAmount: sellAsset.getValue("string"), + onPayCurrency: sellAsset.symbol, + onRevCurrency: buyAsset.symbol, + cryptoList: supportedAssets.map((asset) => asset.symbol).join(","), + onToAddress: recipient, + network: ChainToKadoChain(buyAsset.chain).toUpperCase(), + networkList: networkList.map((chain) => ChainToKadoChain(chain).toUpperCase()).join(","), + product: type, + productList: typeList, + mode: widgetMode, + }); + + return `https://app.kado.money/?${urlParams.toString()}`; + } + + return { + fetchProviderQuote, + getBlockchains, + getAssets, + getOrderStatus, + getKadoWidgetUrl, + supportedSwapkitProviders: [ProviderName.KADO], + }; +} + +export const KadoPlugin = { kado: { plugin } } as const; diff --git a/packages/plugins/kado/src/types.ts b/packages/plugins/kado/src/types.ts new file mode 100644 index 000000000..ed3078fd1 --- /dev/null +++ b/packages/plugins/kado/src/types.ts @@ -0,0 +1,212 @@ +import type { SupportedKadoChain } from "./helpers"; + +export const KadoSupportedFiatCurrencies = [ + "USD", + "CAD", + "GBP", + "EUR", + "MXN", + "COP", + "INR", + "CHF", + "AUD", + "ARS", + "BRL", + "CLP", + "JPY", + "KRW", + "PEN", + "PHP", + "SGD", + "TRY", + "UYU", + "TWD", + "VND", + "CRC", + "SEK", + "PLN", + "DKK", + "NOK", + "NZD", +] as const; + +export type KadoFiatCurrency = (typeof KadoSupportedFiatCurrencies)[number]; + +export type KadoFiatMethod = + | "ach" + | "debit_card" + | "credit_card" + | "apple_pay_credit" + | "apple_pay_debit" + | "wire" + | "sepa" + | "pix" + | "koywe"; + +export type KadoQuoteRequest = { + transactionType: "buy" | "sell"; + fiatMethod: KadoFiatMethod; + partner: "fortress"; + amount: string; + asset: string; + blockchain: string; + currency: KadoFiatCurrency; +}; + +export type KadoAsset = { + _id: string; + name: string; + description: string; + label: string; + symbol: string; + supportedProviders: string[]; + stablecoin: boolean; + liveOnRamp: boolean; + createdAt: string; + updatedAt: string; + __v: number; + priority: number; + displayPrecision: number; + usesAvaxRouter: boolean; + squidChainId: string; + coingeckoId: string; + usesAxelarBridge: boolean; + squidAssetId: string; + address: string; + blockExplorerURI: string; + decimals: number; + officialChainId: keyof typeof SupportedKadoChain; + precision: number; + rampProducts: string[]; + wallets: string[]; + rpcURI: string; + usesPolygonFulfillment: boolean; + usesOsmoRouter: boolean; + ibcChannelIdOffRamp: number; + ibcChannelIdOnRamp: number; + osmoPfmChannel: number; + osmoPfmReceiver: string; + ibcDenom: string; + isNative: boolean; + avgOffRampTimeInSeconds: number; + avgOnRampTimeInSeconds: number; + providers: string[]; + trustekAssetId: string; + trustekNetworkId: string; + kycLevels: string[]; +}; + +export type KadoBlockchainsResponse = { + success: boolean; + message: string; + data: { + blockchains: { + _id: string; + supportedEnvironment: string; + network: string; + origin: string; + label: string; + associatedAssets: KadoAsset[]; + avgTransactionTimeSeconds: number; + usesAvaxRouter: boolean; + liveOnRamp: boolean; + createdAt: string; + updatedAt: string; + __v: number; + priority: number; + }[]; + }; +}; + +export type KadoSupportedAssetsResponse = { + success: boolean; + message: string; + data: { + assets: KadoAsset[]; + }; +}; + +export type KadoQuoteResponse = { + success: boolean; + message: string; + data: { + request: { + transactionType: string; + fiatMethod: KadoFiatMethod; + partner: string; + amount: number; + asset: string; + blockchain: keyof typeof SupportedKadoChain; + currency: KadoFiatCurrency; + reverse: false; + ipCountry: string; + }; + quote: { + asset: string; + baseAmount: { + amount: number; + currency: KadoFiatCurrency; + }; + price: { + amount: number; + price: number; + symbol: string; + unit: string; + }; + bridgeFee: { + amount: number; + currency: KadoFiatCurrency; + originalAmount: number; + promotionModifier: number; + }; + receiveAmountAfterFees: { + originalAmount: number; + amount: number; + currency: KadoFiatCurrency; + }; + receiveUnitCountAfterFees: { + amount: number; + currency: KadoFiatCurrency; + }; + feeType: string; + minValue: { + amount: number; + unit: string; + }; + maxValue: { + amount: number; + unit: string; + }; + receive: { + amount: number; + originalAmount: number; + symbol: string; + unit: string; + unitCount: number; + }; + networkFee: { + amount: number; + currency: KadoFiatCurrency; + originalAmount: number; + promotionModifier: number; + }; + processingFee: { + amount: number; + currency: KadoFiatCurrency; + originalAmount: number; + promotionModifier: number; + }; + totalFee: { + amount: number; + currency: KadoFiatCurrency; + originalAmount: number; + }; + smartContractFee: { + amount: number; + currency: KadoFiatCurrency; + originalAmount: number; + promotionModifier: number; + }; + }; + }; +}; diff --git a/packages/plugins/kado/tsconfig.json b/packages/plugins/kado/tsconfig.json new file mode 100644 index 000000000..46d921163 --- /dev/null +++ b/packages/plugins/kado/tsconfig.json @@ -0,0 +1,4 @@ +{ + "extends": "../../../tools/typescript/base.json", + "include": ["src", "./build.ts", "../../../tools/typescript/typings.d.ts"] +} diff --git a/packages/plugins/radix/src/index.ts b/packages/plugins/radix/src/index.ts index 800095539..f4f0d75f2 100644 --- a/packages/plugins/radix/src/index.ts +++ b/packages/plugins/radix/src/index.ts @@ -29,7 +29,7 @@ function plugin({ getWallet }: SwapKitPluginParams) { // await convertInstructionsToManifest({ network: RadixMainnet })( // route.transaction as Instructions, // ) - // ).value as string; + // ).value as string;c return wallet.signAndBroadcast({ manifest: route.tx as string, }); diff --git a/packages/plugins/thorchain/src/tcPlugin.ts b/packages/plugins/thorchain/src/tcPlugin.ts index b52076e3f..0dc4bc3e9 100644 --- a/packages/plugins/thorchain/src/tcPlugin.ts +++ b/packages/plugins/thorchain/src/tcPlugin.ts @@ -13,6 +13,7 @@ import { TCBscDepositABI, TCEthereumVaultAbi, type UTXOChain, + type WalletChain, getMemoForLoan, } from "@swapkit/helpers"; @@ -143,7 +144,7 @@ function plugin({ getWallet, stagenet = false }: SwapKitPluginParams) { getMemoForLoan(type === "open" ? MemoType.OPEN_LOAN : MemoType.CLOSE_LOAN, { asset: assetValue.toString(), minAmount: minAmount.toString(), - address: getWallet(assetValue.chain).address, + address: getWallet(assetValue.chain as WalletChain).address, }), }); } diff --git a/packages/swapkit/core/src/client.ts b/packages/swapkit/core/src/client.ts index 60d24e187..5e6bf5ea0 100644 --- a/packages/swapkit/core/src/client.ts +++ b/packages/swapkit/core/src/client.ts @@ -110,7 +110,7 @@ export function SwapKit< return plugin; } - function addChain(connectWallet: ChainWallet) { + function addChain(connectWallet: ChainWallet) { const currentWallet = getWallet(connectWallet.chain); connectedWallets[connectWallet.chain] = { ...currentWallet, ...connectWallet }; @@ -169,7 +169,7 @@ export function SwapKit< /** * @Public */ - function getWallet(chain: T) { + function getWallet(chain: T) { return connectedWallets[chain]; } @@ -177,7 +177,7 @@ export function SwapKit< return { ...connectedWallets }; } - function getAddress(chain: T) { + function getAddress(chain: T) { return getWallet(chain)?.address || ""; } @@ -189,7 +189,7 @@ export function SwapKit< return approve({ assetValue, contractAddress, type: ApproveMode.CheckOnly }); } - function disconnectChain(chain: T) { + function disconnectChain(chain: T) { const wallet = getWallet(chain); wallet?.disconnect?.(); delete connectedWallets[chain]; @@ -201,7 +201,7 @@ export function SwapKit< } } - function getBalance( + function getBalance( chain: T, refresh?: R, ): ConditionalAssetValueReturn { @@ -222,7 +222,7 @@ export function SwapKit< }); } - async function getWalletWithBalance(chain: T, potentialScamFilter = true) { + async function getWalletWithBalance(chain: T, potentialScamFilter = true) { const defaultBalance = [AssetValue.from({ chain })]; const wallet = getWallet(chain); @@ -260,14 +260,14 @@ export function SwapKit< assetValue, ...params }: UTXOTransferParams | EVMTransferParams | CosmosTransferParams) { - const chain = assetValue.chain as WalletChain; + const chain = assetValue.chain as Exclude; const wallet = getWallet(chain); if (!wallet) throw new SwapKitError("core_wallet_connection_not_found"); return wallet.transfer({ ...params, assetValue }); } - function signMessage({ chain, message }: { chain: Chain; message: string }) { + function signMessage({ chain, message }: { chain: WalletChain; message: string }) { const wallet = getWallet(chain); if (!wallet) throw new SwapKitError("core_wallet_connection_not_found"); @@ -321,7 +321,8 @@ export function SwapKit< const { assetValue } = params; const { chain } = assetValue; - if (!getWallet(chain)) throw new SwapKitError("core_wallet_connection_not_found"); + if (!getWallet(chain as WalletChain)) + throw new SwapKitError("core_wallet_connection_not_found"); const baseValue = AssetValue.from({ chain }); diff --git a/packages/swapkit/helpers/src/helpers/asset.ts b/packages/swapkit/helpers/src/helpers/asset.ts index 0de865fab..9b4875e4f 100644 --- a/packages/swapkit/helpers/src/helpers/asset.ts +++ b/packages/swapkit/helpers/src/helpers/asset.ts @@ -278,3 +278,27 @@ export async function findAssetBy( return; } + +export const blockTimes = { + [Chain.Arbitrum]: 1, + [Chain.Avalanche]: 3, + [Chain.Base]: 1, + [Chain.BinanceSmartChain]: 3, + [Chain.Bitcoin]: 600, + [Chain.BitcoinCash]: 600, + [Chain.Chainflip]: 5, + [Chain.Cosmos]: 1.5, + [Chain.Dash]: 150, + [Chain.Dogecoin]: 600, + [Chain.Ethereum]: 12.5, + [Chain.Fiat]: 60, + [Chain.Kujira]: 2.2, + [Chain.Litecoin]: 150, + [Chain.Maya]: 6, + [Chain.Optimism]: 1, + [Chain.Polkadot]: 6, + [Chain.Polygon]: 2.1, + [Chain.Radix]: 5, + [Chain.Solana]: 1, + [Chain.THORChain]: 6, +}; diff --git a/packages/swapkit/helpers/src/types/chains.ts b/packages/swapkit/helpers/src/types/chains.ts index 4cf0c0669..02866fb49 100644 --- a/packages/swapkit/helpers/src/types/chains.ts +++ b/packages/swapkit/helpers/src/types/chains.ts @@ -9,6 +9,7 @@ export enum Chain { Dash = "DASH", Dogecoin = "DOGE", Ethereum = "ETH", + Fiat = "FIAT", Kujira = "KUJI", Litecoin = "LTC", Maya = "MAYA", @@ -26,7 +27,7 @@ export enum StagenetChain { Maya = "MAYA_STAGENET", } -export type WalletChain = Exclude; +export type WalletChain = Exclude; export enum ChainId { Arbitrum = "42161", @@ -46,6 +47,7 @@ export enum ChainId { Kujira = "kaiyo-1", Ethereum = "1", EthereumHex = "0x1", + Fiat = "fiat", Litecoin = "litecoin", Maya = "mayachain-mainnet-v1", MayaStagenet = "mayachain-stagenet-v1", @@ -76,6 +78,7 @@ export const ChainIdToChain: Record = { [ChainId.Dash]: Chain.Dash, [ChainId.Dogecoin]: Chain.Dogecoin, [ChainId.EthereumHex]: Chain.Ethereum, + [ChainId.Fiat]: Chain.Fiat, [ChainId.Kujira]: Chain.Kujira, [ChainId.Ethereum]: Chain.Ethereum, [ChainId.Litecoin]: Chain.Litecoin, @@ -107,6 +110,7 @@ export const BaseDecimal: Record = { DOGE: 8, DOT: 10, ETH: 18, + FIAT: 2, FLIP: 18, GAIA: 6, KUJI: 6, @@ -131,6 +135,7 @@ export const BlockTimes: Record, number> = { [Chain.Dash]: 150, [Chain.Dogecoin]: 600, [Chain.Ethereum]: 12.5, + [Chain.Fiat]: 60, [Chain.Kujira]: 2.2, [Chain.Litecoin]: 150, [Chain.Maya]: 6, @@ -213,6 +218,7 @@ export const RPC_URLS: Record = { [Chain.Dash]: "https://dash-rpc.publicnode.com", [Chain.Dogecoin]: "https://node-router.thorswap.net/dogecoin", [Chain.Ethereum]: "https://ethereum-rpc.publicnode.com", + [Chain.Fiat]: "", [Chain.Kujira]: "https://rpc-kujira.synergynodes.com/", [Chain.Litecoin]: "https://node-router.thorswap.net/litecoin", [Chain.Maya]: "https://tendermint.mayachain.info", @@ -250,6 +256,7 @@ export const FALLBACK_URLS: Record = { [Chain.Dash]: ["https://dash-rpc.publicnode.com"], [Chain.Dogecoin]: ["https://doge.getblock.io/mainnet", "https://dogecoin.publicnode.com"], [Chain.Ethereum]: ["https://eth.llamarpc.com", "https://rpc.ankr.com/eth"], + [Chain.Fiat]: [], [Chain.Kujira]: ["https://kujira-rpc.polkachu.com", "https://kujira-rpc.ibs.team"], [Chain.Litecoin]: ["https://ltc.getblock.io/mainnet", "https://litecoin.publicnode.com"], [Chain.Maya]: ["https://tendermint.mayachain.info", "https://maya-tendermint.publicnode.com"], @@ -278,6 +285,7 @@ export const EXPLORER_URLS: Record = { [Chain.Dash]: "https://blockchair.com/dash", [Chain.Dogecoin]: "https://blockchair.com/dogecoin", [Chain.Ethereum]: "https://etherscan.io", + [Chain.Fiat]: "", [Chain.Kujira]: "https://finder.kujira.network/kaiyo-1", [Chain.Litecoin]: "https://blockchair.com/litecoin", [Chain.Maya]: "https://www.mayascan.org", diff --git a/packages/swapkit/helpers/src/types/commonTypes.ts b/packages/swapkit/helpers/src/types/commonTypes.ts index 69f014922..86af734be 100644 --- a/packages/swapkit/helpers/src/types/commonTypes.ts +++ b/packages/swapkit/helpers/src/types/commonTypes.ts @@ -1,5 +1,5 @@ import type { RadixNetwork } from "@swapkit/toolbox-radix"; -import type { Chain } from "./chains"; +import type { Chain, WalletChain } from "./chains"; import type { ChainApis } from "./sdk"; import type { ChainWallet } from "./wallet"; @@ -78,10 +78,15 @@ export type ConnectConfig = SwapkitConfig & { applicationVersion: string; network: RadixNetwork; }; + + /** + * @optional for setting the kado api key + */ + kadoApiKey?: string; }; export type ConnectWalletParams = { - addChain: (params: ChainWallet & M) => void; + addChain: (params: ChainWallet & M) => void; apis: ChainApis; config: ConnectConfig; rpcUrls: { [chain in Chain]?: string }; diff --git a/packages/swapkit/helpers/src/types/derivationPath.ts b/packages/swapkit/helpers/src/types/derivationPath.ts index 19f1ae2b4..4b8942f17 100644 --- a/packages/swapkit/helpers/src/types/derivationPath.ts +++ b/packages/swapkit/helpers/src/types/derivationPath.ts @@ -55,7 +55,8 @@ export const NetworkDerivationPath: Record = { THOR: [44, 931, 0, 0, 0], // Polkadot and related network derivation path is not number based - XRD: [0, 0, 0, 0, 0], DOT: [0, 0, 0, 0, 0], + FIAT: [0, 0, 0, 0, 0], FLIP: [0, 0, 0, 0, 0], + XRD: [0, 0, 0, 0, 0], }; diff --git a/packages/swapkit/helpers/src/types/quotes.ts b/packages/swapkit/helpers/src/types/quotes.ts index d8bac3854..c3c71785c 100644 --- a/packages/swapkit/helpers/src/types/quotes.ts +++ b/packages/swapkit/helpers/src/types/quotes.ts @@ -159,6 +159,7 @@ export enum ProviderName { TRADERJOE_V2 = "TRADERJOE_V2", UNISWAP_V2 = "UNISWAP_V2", UNISWAP_V3 = "UNISWAP_V3", + KADO = "KADO", } export enum FeeTypeEnum { diff --git a/packages/swapkit/helpers/src/types/wallet.ts b/packages/swapkit/helpers/src/types/wallet.ts index 6f68f6a3a..04fa797e4 100644 --- a/packages/swapkit/helpers/src/types/wallet.ts +++ b/packages/swapkit/helpers/src/types/wallet.ts @@ -7,7 +7,7 @@ import type { UTXOWallets } from "@swapkit/toolbox-utxo"; import type { Eip1193Provider } from "ethers"; import type { AssetValue } from "../modules/assetValue"; -import type { Chain } from "./chains"; +import type { Chain, WalletChain } from "./chains"; import type { ConnectWalletParams } from "./commonTypes"; declare global { @@ -66,9 +66,9 @@ export type ChainWallet = { signMessage?: (message: string) => Promise; }; -export type EmptyWallet = { [key in Chain]?: unknown }; +export type EmptyWallet = { [key in WalletChain]?: unknown }; export type BaseWallet> = { - [key in Chain]: ChainWallet & (T extends EmptyWallet ? T[key] : never); + [key in WalletChain]: ChainWallet & (T extends EmptyWallet ? T[key] : never); }; export type FullWallet = BaseWallet< @@ -91,7 +91,7 @@ export type SwapKitWallet = ( ) => (...connectParams: ConnectParams) => boolean | Promise; export type SwapKitPluginParams = { - getWallet: (chain: T) => FullWallet[T]; + getWallet: (chain: T) => FullWallet[T]; stagenet?: boolean; config: Config; }; diff --git a/packages/swapkit/sdk/package.json b/packages/swapkit/sdk/package.json index 299955105..02cb87a6e 100644 --- a/packages/swapkit/sdk/package.json +++ b/packages/swapkit/sdk/package.json @@ -5,6 +5,7 @@ "@swapkit/core": "workspace:*", "@swapkit/plugin-chainflip": "workspace:*", "@swapkit/plugin-evm": "workspace:*", + "@swapkit/plugin-kado": "workspace:*", "@swapkit/plugin-radix": "workspace:*", "@swapkit/plugin-thorchain": "workspace:*", "@swapkit/tokens": "workspace:*", diff --git a/packages/swapkit/sdk/src/index.ts b/packages/swapkit/sdk/src/index.ts index 578b22424..a9223c9a9 100644 --- a/packages/swapkit/sdk/src/index.ts +++ b/packages/swapkit/sdk/src/index.ts @@ -1,6 +1,7 @@ import { SwapKit, type SwapKitParams } from "@swapkit/core"; import { ChainflipPlugin } from "@swapkit/plugin-chainflip"; import { EVMPlugin } from "@swapkit/plugin-evm"; +import { KadoPlugin } from "@swapkit/plugin-kado"; import { RadixPlugin } from "@swapkit/plugin-radix"; import { MayachainPlugin, ThorchainPlugin } from "@swapkit/plugin-thorchain"; import { wallets as defaultWallets } from "@swapkit/wallets"; @@ -11,6 +12,7 @@ export * from "@swapkit/tokens"; const defaultPlugins = { ...ChainflipPlugin, ...EVMPlugin, + ...KadoPlugin, ...MayachainPlugin, ...ThorchainPlugin, ...RadixPlugin, diff --git a/packages/swapkit/tokens/src/index.ts b/packages/swapkit/tokens/src/index.ts index 58308be38..c8e3162fe 100644 --- a/packages/swapkit/tokens/src/index.ts +++ b/packages/swapkit/tokens/src/index.ts @@ -1,6 +1,7 @@ export { list as CaviarV1List } from "./tokenLists/caviar_v1"; export { list as ChainflipList } from "./tokenLists/chainflip"; export { list as JupiterList } from "./tokenLists/jupiter"; +export { list as KadoList } from "./tokenLists/kado"; export { list as MayaList } from "./tokenLists/mayachain"; // export { list as OciswapV1List } from "./tokenLists/ociswap_v1"; export { list as OneInchList } from "./tokenLists/oneinch"; diff --git a/packages/swapkit/tokens/src/tokenLists/kado.ts b/packages/swapkit/tokens/src/tokenLists/kado.ts new file mode 100644 index 000000000..86abc33b0 --- /dev/null +++ b/packages/swapkit/tokens/src/tokenLists/kado.ts @@ -0,0 +1,276 @@ +export const list = { + provider: "KADO", + chainId: "fiat", + name: "KADO", + timestamp: "2024-09-25T15:31:06.827Z", + version: { + major: 1, + minor: 0, + patch: 0, + }, + keywords: [], + count: 27, + tokens: [ + { + chain: "BTC", + chainId: "bitcoin", + decimals: 8, + identifier: "BTC.BTC", + logoURI: "", + symbol: "BTC", + ticker: "BTC", + }, + { + chain: "ETH", + chainId: "1", + decimals: 18, + identifier: "ETH.ETH", + logoURI: "", + symbol: "ETH", + ticker: "ETH", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.USD", + logoURI: "", + symbol: "USD", + ticker: "USD", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.CAD", + logoURI: "", + symbol: "CAD", + ticker: "CAD", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.GBP", + logoURI: "", + symbol: "GBP", + ticker: "GBP", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.EUR", + logoURI: "", + symbol: "EUR", + ticker: "EUR", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.MXN", + logoURI: "", + symbol: "MXN", + ticker: "MXN", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.COP", + logoURI: "", + symbol: "COP", + ticker: "COP", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.INR", + logoURI: "", + symbol: "INR", + ticker: "INR", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.CHF", + logoURI: "", + symbol: "CHF", + ticker: "CHF", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.AUD", + logoURI: "", + symbol: "AUD", + ticker: "AUD", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.ARS", + logoURI: "", + symbol: "ARS", + ticker: "ARS", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.BRL", + logoURI: "", + symbol: "BRL", + ticker: "BRL", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.CLP", + logoURI: "", + symbol: "CLP", + ticker: "CLP", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.JPY", + logoURI: "", + symbol: "JPY", + ticker: "JPY", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.KRW", + logoURI: "", + symbol: "KRW", + ticker: "KRW", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.PEN", + logoURI: "", + symbol: "PEN", + ticker: "PEN", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.PHP", + logoURI: "", + symbol: "PHP", + ticker: "PHP", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.SGD", + logoURI: "", + symbol: "SGD", + ticker: "SGD", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.TRY", + logoURI: "", + symbol: "TRY", + ticker: "TRY", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.UYU", + logoURI: "", + symbol: "UYU", + ticker: "UYU", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.TWD", + logoURI: "", + symbol: "TWD", + ticker: "TWD", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.VND", + logoURI: "", + symbol: "VND", + ticker: "VND", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.CRC", + logoURI: "", + symbol: "CRC", + ticker: "CRC", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.SEK", + logoURI: "", + symbol: "SEK", + ticker: "SEK", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.PLN", + logoURI: "", + symbol: "PLN", + ticker: "PLN", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.DKK", + logoURI: "", + symbol: "DKK", + ticker: "DKK", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.NOK", + logoURI: "", + symbol: "NOK", + ticker: "NOK", + }, + { + chain: "FIAT", + chainId: "fiat", + decimals: 2, + identifier: "FIAT.NZD", + logoURI: "", + symbol: "NZD", + ticker: "NZD", + }, + ], +} as const; diff --git a/packages/wallets/ledger/src/ledgerLive.ts b/packages/wallets/ledger/src/ledgerLive.ts index 51a333e9c..f13f541fb 100644 --- a/packages/wallets/ledger/src/ledgerLive.ts +++ b/packages/wallets/ledger/src/ledgerLive.ts @@ -14,9 +14,10 @@ import { FeeOption, SwapKitError, SwapKitNumber, + type WalletChain, WalletOption, setRequestClientConfig, -} from "@swapkit/sdk"; +} from "@swapkit/helpers"; import { ETHToolbox, getProvider } from "@swapkit/toolbox-evm"; import type { UTXOTransferParams } from "@swapkit/toolbox-utxo"; import { BigNumber as BigNumberJS } from "bignumber.js"; @@ -37,14 +38,14 @@ export enum LedgerLiveChain { ATOM = "cosmos", } -export const LEDGER_LIVE_SUPPORTED_CHAINS: Chain[] = [ +export const LEDGER_LIVE_SUPPORTED_CHAINS = [ Chain.Bitcoin, Chain.Ethereum, Chain.Cosmos, Chain.Litecoin, Chain.Dogecoin, Chain.BitcoinCash, -]; +] as WalletChain[]; export const ChainToLedgerLiveChain: Partial> = { [Chain.Arbitrum]: LedgerLiveChain.ARB, diff --git a/playgrounds/nextjs/package.json b/playgrounds/nextjs/package.json index 69dcccfb1..b16211bd0 100644 --- a/playgrounds/nextjs/package.json +++ b/playgrounds/nextjs/package.json @@ -35,6 +35,7 @@ "@swapkit/core": "workspace:*", "@swapkit/plugin-chainflip": "workspace:*", "@swapkit/plugin-evm": "workspace:*", + "@swapkit/plugin-kado": "workspace:*", "@swapkit/plugin-thorchain": "workspace:*", "@swapkit/tokens": "workspace:*", "@swapkit/wallets": "workspace:*", diff --git a/playgrounds/nextjs/src/app/offRamp/page.tsx b/playgrounds/nextjs/src/app/offRamp/page.tsx new file mode 100644 index 000000000..8c97cd905 --- /dev/null +++ b/playgrounds/nextjs/src/app/offRamp/page.tsx @@ -0,0 +1,17 @@ +"use client"; + +import { Card, CardDescription, CardTitle } from "~/components/ui/card"; +// import { useSwapKit } from "~/lib/swapKit"; + +export default function Send() { + // const { swapKit } = useSwapKit(); + + return ( +
+ + Send + Send + +
+ ); +} diff --git a/playgrounds/nextjs/src/lib/swapKit.ts b/playgrounds/nextjs/src/lib/swapKit.ts index 8103d4618..a0d70b3a5 100644 --- a/playgrounds/nextjs/src/lib/swapKit.ts +++ b/playgrounds/nextjs/src/lib/swapKit.ts @@ -19,6 +19,7 @@ export const useSwapKit = () => { const loadSwapKit = async () => { const { SwapKit } = await import("@swapkit/core"); const { ChainflipPlugin } = await import("@swapkit/plugin-chainflip"); + const { KadoPlugin } = await import("@swapkit/plugin-kado"); const { ThorchainPlugin, MayachainPlugin } = await import("@swapkit/plugin-thorchain"); const { wallets } = await import("@swapkit/wallets"); @@ -41,7 +42,7 @@ export const useSwapKit = () => { }, }, wallets, - plugins: { ...ThorchainPlugin, ...ChainflipPlugin, ...MayachainPlugin }, + plugins: { ...ThorchainPlugin, ...ChainflipPlugin, ...MayachainPlugin, ...KadoPlugin }, }); setSwapKit(swapKitClient); diff --git a/playgrounds/vite/src/App.tsx b/playgrounds/vite/src/App.tsx index ea1b6249b..4ba395a34 100644 --- a/playgrounds/vite/src/App.tsx +++ b/playgrounds/vite/src/App.tsx @@ -1,4 +1,4 @@ -import { AssetValue, type Chain, type FullWallet } from "@swapkit/core"; +import { AssetValue, type FullWallet, type WalletChain } from "@swapkit/core"; import { useCallback, useEffect, useMemo, useState } from "react"; import { WalletWidget } from "@swapkit/wallet-exodus"; @@ -14,7 +14,7 @@ import { getSwapKitClient } from "./swapKitClient"; const apiKeys = ["walletConnectProjectId"] as const; -type WalletDataType = FullWallet[Chain] | FullWallet[Chain][] | null; +type WalletDataType = FullWallet[WalletChain] | FullWallet[WalletChain][] | null; const App = () => { const [widgetType, setWidgetType] = useState<"swap" | "loan" | "earn">("swap"); @@ -62,7 +62,7 @@ const App = () => { [inputAsset, outputAsset], ); - const disconnectChain = (chain: Chain) => { + const disconnectChain = (chain: WalletChain) => { if (!skClient) return; skClient.disconnectChain(chain); setWallet(Object.values(skClient.getAllWallets())); @@ -154,15 +154,17 @@ const App = () => { key={`${walletData?.address}-${walletData?.balance?.[0]?.chain}`} setAsset={setAsset} walletData={walletData} - disconnect={() => disconnectChain(walletData?.balance?.[0]?.chain as Chain)} + disconnect={() => + disconnectChain(walletData?.balance?.[0]?.chain as WalletChain) + } /> )) ) : ( disconnectChain(wallet?.balance?.[0]?.chain as Chain)} + walletData={wallet as FullWallet[WalletChain]} + disconnect={() => disconnectChain(wallet?.balance?.[0]?.chain as WalletChain)} /> )} diff --git a/playgrounds/vite/src/Send/index.tsx b/playgrounds/vite/src/Send/index.tsx index ab8b51390..c5494519e 100644 --- a/playgrounds/vite/src/Send/index.tsx +++ b/playgrounds/vite/src/Send/index.tsx @@ -1,4 +1,4 @@ -import type { AssetValue, WalletChain } from "@swapkit/core"; +import type { AssetValue, Chain, WalletChain } from "@swapkit/core"; import { useCallback, useState } from "react"; import type { SwapKitClient } from "../swapKitClient"; @@ -22,13 +22,15 @@ export default function Send({ const handleSend = useCallback(async () => { if (!(inputAsset && inputAssetValue?.gt(0) && skClient)) return; - const from = skClient.getAddress(inputAsset.chain); - const txHash = await skClient.getWallet(inputAssetValue.chain as WalletChain).transfer({ - from, - assetValue: inputAssetValue, - memo: "", - recipient, - }); + const from = skClient.getAddress(inputAsset.chain as WalletChain); + const txHash = await skClient + .getWallet(inputAssetValue.chain as Exclude) + .transfer({ + from, + assetValue: inputAssetValue, + memo: "", + recipient, + }); window.open( `${skClient.getExplorerTxUrl({ chain: inputAssetValue.chain, txHash: txHash as string })}`, diff --git a/playgrounds/vite/src/Swap/SwapInputs.tsx b/playgrounds/vite/src/Swap/SwapInputs.tsx index aac6ad490..0d59ba60c 100644 --- a/playgrounds/vite/src/Swap/SwapInputs.tsx +++ b/playgrounds/vite/src/Swap/SwapInputs.tsx @@ -1,5 +1,11 @@ "use client"; -import type { AssetValue, EVMTransaction, QuoteResponseRoute, SwapKit } from "@swapkit/sdk"; +import type { + AssetValue, + EVMTransaction, + QuoteResponseRoute, + SwapKit, + WalletChain, +} from "@swapkit/sdk"; import { ProviderName, SwapKitApi, SwapKitNumber } from "@swapkit/sdk"; import { useCallback, useState } from "react"; @@ -34,8 +40,8 @@ export const SwapInputs = ({ skClient, inputAsset, outputAsset, handleSwap }: Pr setLoading(true); setRoutes([]); - const sourceAddress = skClient.getAddress(inputAsset.chain); - const destinationAddress = skClient.getAddress(outputAsset.chain); + const sourceAddress = skClient.getAddress(inputAsset.chain as WalletChain); + const destinationAddress = skClient.getAddress(outputAsset.chain as WalletChain); // const providers = Object.values(ProviderName); try { diff --git a/playgrounds/vite/src/TNS/index.tsx b/playgrounds/vite/src/TNS/index.tsx index 8f7e0a8c1..a2af1552e 100644 --- a/playgrounds/vite/src/TNS/index.tsx +++ b/playgrounds/vite/src/TNS/index.tsx @@ -1,9 +1,15 @@ -import { AssetValue, Chain, SwapKitApi, type THORNameDetails } from "@swapkit/sdk"; +import { + AssetValue, + Chain, + SwapKitApi, + type THORNameDetails, + type WalletChain, +} from "@swapkit/sdk"; import { useCallback, useState } from "react"; import type { SwapKitClient } from "../swapKitClient"; export default function TNS({ skClient }: { skClient: SwapKitClient }) { - const [selectedChain, setSelectedChain] = useState(Chain.THORChain); + const [selectedChain, setSelectedChain] = useState(Chain.THORChain); const [name, setName] = useState(""); const [tnsSearch, setTnsSearch] = useState(""); const [tnsDetail, setTnsDetail] = useState(); @@ -55,7 +61,7 @@ export default function TNS({ skClient }: { skClient: SwapKitClient }) { >
- setSelectedChain(e.target.value as WalletChain)}> {Object.values(Chain).map((chain) => (