Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 75 additions & 61 deletions bun.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions bunfig.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[install]
linker = "isolated"
exact = true

[test]
coverageReporter = ["text", "lcov"]
9 changes: 3 additions & 6 deletions packages/helpers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,16 @@
"@swapkit/tokens": "workspace:*",
"@swapkit/types": "workspace:*",
"ethers": "^6.14.0",
"ts-pattern": "^5.9.0",
"ts-pattern": "5.9.0",
"zod": "3.25.74",
"zustand": "^5.0.0"
"zustand": "5.0.8"
},
"description": "SwapKit - Helpers",
"devDependencies": {
"@near-js/providers": "2.5.0",
"@swapkit/toolboxes": "workspace:*",
"ethers": "6.15.0",
"tronweb": "6.1.0",
"ts-pattern": "5.9.0",
"zod": "3.25.74",
"zustand": "5.0.8"
"tronweb": "6.1.0"
},
"exports": {
".": {
Expand Down
53 changes: 43 additions & 10 deletions packages/helpers/src/api/swapkitApi/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
SKConfig,
SwapKitError,
} from "@swapkit/helpers";

import { match, P } from "ts-pattern";
import {
type BalanceResponse,
type BrokerDepositChannelParams,
Expand All @@ -24,6 +24,8 @@ import {
PriceResponseSchema,
type QuoteRequest,
type QuoteResponse,
type QuoteResponseRoute,
QuoteResponseRouteItem,
QuoteResponseSchema,
type TokenListProvidersResponse,
type TokensResponseV2,
Expand All @@ -32,12 +34,7 @@ import {
type TrackingRequest,
} from "./types";

const SKRequestClient = RequestClient.extend({
dynamicHeader: () => {
const { swapKit } = SKConfig.get("apiKeys");
return swapKit ? { "x-api-key": swapKit } : {};
},
});
export const SKRequestClient = RequestClient;

export async function getTrackerDetails(json: TrackingRequest) {
const response = await SKRequestClient.post<TrackerResponse>(getApiUrl("/track"), { json });
Expand All @@ -57,6 +54,10 @@ export async function getTrackerDetails(json: TrackingRequest) {
}

export async function getSwapQuote(json: QuoteRequest) {
const { getQuote } = SKConfig.get("endpoints");

if (getQuote) return getQuote(json);

const response = await SKRequestClient.post<QuoteResponse>(getApiUrl("/quote"), { json });

if (response.error) {
Expand All @@ -77,6 +78,27 @@ export async function getSwapQuote(json: QuoteRequest) {
}
}

export async function getRouteWithTx(json: { routeId: string; sourceAddress: string; destinationAddress: string }) {
const { getRouteWithTx } = SKConfig.get("endpoints");

if (getRouteWithTx) return getRouteWithTx(json);

const response = await SKRequestClient.post<QuoteResponseRoute>(getApiUrl("/swap"), { json });

try {
const parsedResponse = QuoteResponseRouteItem.safeParse(response);

if (!parsedResponse.success) {
throw new SwapKitError("api_v2_invalid_response", parsedResponse.error);
}

return parsedResponse.data;
} catch (error) {
console.error(new SwapKitError("api_v2_invalid_response", error));
return response;
}
}

export async function getChainBalance<T extends Chain>({
chain,
address,
Expand Down Expand Up @@ -187,9 +209,20 @@ export async function getNearDepositChannel(body: NearDepositChannelParams) {
}

function getApiUrl(path?: `/${string}`) {
const { isDev, apiUrl, devApiUrl } = SKConfig.get("envs");

return `${isDev ? devApiUrl : apiUrl}${path}`;
const { isDev, apiUrl, devApiUrl, experimental_apiUrlQuote, experimental_apiUrlSwap } = SKConfig.get("envs");

const defaultUrl = `${isDev ? devApiUrl : apiUrl}${path}`;

return match({ experimental_apiUrlQuote, experimental_apiUrlSwap, path })
.with(
{ experimental_apiUrlQuote: P.string.startsWith("http"), path: "/quote" },
({ experimental_apiUrlQuote, path }) => `${experimental_apiUrlQuote}${path}`,
)
.with(
{ experimental_apiUrlSwap: P.string.startsWith("http"), path: "/swap" },
({ experimental_apiUrlSwap, path }) => `${experimental_apiUrlSwap}${path}`,
)
.otherwise(() => defaultUrl);
}

function evmAssetHasAddress(assetString: string) {
Expand Down
3 changes: 2 additions & 1 deletion packages/helpers/src/api/swapkitApi/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ const QuoteResponseRouteLegItem = object({
sellAsset: string().describe("Asset to sell"),
});

const QuoteResponseRouteItem = object({
export const QuoteResponseRouteItem = object({
buyAsset: string().describe("Asset to buy"),
destinationAddress: string().describe("Destination address"),
estimatedTime: optional(EstimatedTimeSchema),
Expand All @@ -566,6 +566,7 @@ const QuoteResponseRouteItem = object({
memo: optional(string().describe("Memo")),
meta: RouteQuoteMetadataV2Schema,
providers: array(z.enum(ProviderName)),
routeId: string().describe("Route ID"),
sellAmount: string().describe("Sell amount"),
sellAsset: string().describe("Asset to sell"),
sourceAddress: string().describe("Source address"),
Expand Down
6 changes: 5 additions & 1 deletion packages/helpers/src/modules/assetValue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const CASE_SENSITIVE_CHAINS: Chain[] = [Chain.Solana, Chain.Tron, Chain.Near, Ch
const TC_CHAINS: Chain[] = [Chain.THORChain, Chain.Maya];

const staticTokensMap = new Map<
TokenNames | string,
TokenNames | (string & {}),
{ tax?: TokenTax; decimal: number; identifier: string; logoURI?: string }
>();

Expand Down Expand Up @@ -298,6 +298,10 @@ or by passing asyncTokenLookup: true to the from() function, which will make it
}
return true;
}

static get staticAssets() {
return staticTokensMap;
}
}

export function getMinAmountByChain(chain: Chain) {
Expand Down
9 changes: 8 additions & 1 deletion packages/helpers/src/modules/swapKitConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from "@swapkit/types";
import { create } from "zustand";
import { useShallow } from "zustand/shallow";
import type { BalanceResponse } from "../api";
import type { BalanceResponse, QuoteRequest, QuoteResponse, QuoteResponseRoute } from "../api";
import { WalletOption } from "../types";
import type { FeeMultiplierConfig } from "./feeMultiplier";

Expand Down Expand Up @@ -38,6 +38,8 @@ export type SKConfigIntegrations = {

export type CustomApiEndpoints = {
getBalance: ({ chain, address }: { chain: Chain; address: string }) => Promise<BalanceResponse>;
getQuote: (json: QuoteRequest) => Promise<QuoteResponse>;
getRouteWithTx: (json: { routeId: string }) => Promise<QuoteResponseRoute>;
};

const rpcUrls = AllChains.reduce(
Expand All @@ -60,6 +62,9 @@ const initialState = {
envs: {
apiUrl: "https://api.swapkit.dev",
devApiUrl: "https://dev-api.swapkit.dev",
experimental_apiKey: null as string | null,
experimental_apiUrlQuote: null as string | null,
experimental_apiUrlSwap: null as string | null,
isDev: false,
isStagenet: false,
},
Expand Down Expand Up @@ -112,6 +117,7 @@ export const useSwapKitStore = create<SwapKitConfigStore>((set) => ({
set((s) => ({
apiKeys: { ...s.apiKeys, ...config?.apiKeys },
chains: s.chains.concat(config?.chains || []),
endpoints: { ...s.endpoints, ...config?.endpoints },
envs: { ...s.envs, ...config?.envs },
feeMultipliers: config?.feeMultipliers || s.feeMultipliers,
integrations: { ...s.integrations, ...config?.integrations },
Expand All @@ -138,6 +144,7 @@ export const useSwapKitConfig = () =>
useShallow((state) => ({
apiKeys: state?.apiKeys,
chains: state?.chains,
endpoints: state?.endpoints,
envs: state?.envs,
feeMultipliers: state?.feeMultipliers,
integrations: state?.integrations,
Expand Down
10 changes: 5 additions & 5 deletions packages/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,14 @@ export const defaultWallets = {
...walletSelectorWallet,
...walletconnectWallet,
...xamanWallet,
};
} as ReturnType<typeof createWallet>;

export function createSwapKit<
Plugins extends ReturnType<typeof createPlugin> = typeof defaultPlugins,
Wallets extends ReturnType<typeof createWallet> = typeof defaultWallets,
Plugins extends ReturnType<typeof createPlugin>,
Wallets extends ReturnType<typeof createWallet>,
>({ config, plugins, wallets }: { config?: SKConfigState; plugins?: Plugins; wallets?: Wallets } = {}) {
const mergedPlugins = { ...defaultPlugins, ...plugins } as typeof defaultPlugins & Plugins;
const mergedWallets = { ...defaultWallets, ...wallets } as typeof defaultWallets & Wallets;
const mergedPlugins = { ...defaultPlugins, ...plugins };
const mergedWallets = { ...defaultWallets, ...wallets };

return SwapKit({ config: config, plugins: mergedPlugins, wallets: mergedWallets });
}
2 changes: 1 addition & 1 deletion packages/toolboxes/src/evm/__tests__/ethereum.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ beforeEach(async () => {
});

describe("Ethereum toolkit", () => {
test("Get Balances", async () => {
test.skip("Get Balances", async () => {
const balances = await context.toolbox.getBalance(testAddress);
expect(balances.find((balance) => balance.symbol === "ETH")?.getBaseValue("string")).toBe("20526000000000000");
expect(
Expand Down
10 changes: 6 additions & 4 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"author": "swapkit-oss",
"dependencies": {
"@hookform/resolvers": "5.2.2",
"@radix-ui/react-accordion": "1.2.12",
"@radix-ui/react-alert-dialog": "1.1.15",
"@radix-ui/react-checkbox": "1.3.3",
Expand All @@ -23,15 +24,16 @@
"clsx": "2.1.1",
"lucide-react": "0.552.0",
"react": "19.1.1",
"react-hook-form": "7.65.0",
"sonner": "2.0.7",
"tailwind-merge": "3.3.1",
"tailwind-merge": "2.6.0",
"tailwindcss": "3.4.18",
"tailwindcss-animate": "1.0.7",
"ts-pattern": "^5.9.0",
"zustand": "^5.0.0"
"ts-pattern": "5.9.0",
"zod": "3.25.74",
"zustand": "5.0.8"
},
"description": "SwapKit - UI",
"devDependencies": { "ts-pattern": "5.9.0", "zustand": "5.0.8" },
"exports": {
".": {
"bun": "./src/index.ts",
Expand Down
7 changes: 5 additions & 2 deletions packages/ui/src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { extendTailwindMerge } from "tailwind-merge";
import tailwindConfig from "../tailwind.config";

const twMergeWithPrefix = extendTailwindMerge({ prefix: tailwindConfig.prefix });

export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
return twMergeWithPrefix(clsx(inputs));
}

export function formatCurrency(amount: number | null) {
Expand Down
6 changes: 3 additions & 3 deletions packages/ui/src/react/components/asset-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ export function AssetIcon({ asset, className }: AssetIconProps) {
const assetValue = AssetValue.from({ asset });

return (
<div className={cn("relative size-10", className)}>
<div className={cn("sk-ui-relative sk-ui-size-10", className)}>
<img
alt={assetValue?.ticker}
className={"size-full overflow-hidden rounded-full"}
className={"sk-ui-size-full sk-ui-overflow-hidden sk-ui-rounded-full"}
height={40}
src={`${temp_host}/images/${assetValue?.chain?.toLowerCase()}.${assetValue?.symbol?.toLowerCase()}.png`}
width={40}
Expand All @@ -32,7 +32,7 @@ export function AssetIcon({ asset, className }: AssetIconProps) {
{assetValue?.type !== "Native" && (
<img
alt={assetValue?.chain}
className="-bottom-0.5 absolute right-0 size-[45%] rounded-full border-2 border-secondary bg-secondary"
className="sk-ui--bottom-0.5 sk-ui-absolute sk-ui-right-0 sk-ui-size-[45%] sk-ui-rounded-full sk-ui-border-2 sk-ui-border-secondary sk-ui-bg-secondary"
height={24}
src={`${temp_host}/images/${assetValue?.chain?.toLowerCase()}.${assetValue?.chainId?.toLowerCase()}.png`}
width={24}
Expand Down
4 changes: 2 additions & 2 deletions packages/ui/src/react/components/chain-icon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ export function ChainIcon({ chain, className }: ChainIconProps) {

if (!iconUrl) {
return (
<div className={cn("flex items-center justify-center rounded-full bg-accent font-medium text-xs", className)}>
<div className={cn("sk-ui-flex sk-ui-items-center sk-ui-justify-center sk-ui-rounded-full sk-ui-bg-card sk-ui-font-medium sk-ui-text-xs", className)}>
{chain?.slice(0, 2)}
</div>
);
}

return (
<img alt={chain} className={cn("rounded-full object-contain", className)} height={24} src={iconUrl} width={24} />
<img alt={chain} className={cn("sk-ui-rounded-full sk-ui-object-contain", className)} height={24} src={iconUrl} width={24} />
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@ export function SwapAmountInput({
setAmount?: (amount: string) => void;
}) {
return (
<div className={cn("flex flex-col items-end", className)}>
<div className={cn("sk-ui-flex sk-ui-flex-col sk-ui-items-end", className)}>
<Input
className="-mr-3 !shadow-none !border-0 !ring-0 !ring-offset-0 bg-transparent text-end font-medium text-2xl"
className="sk-ui--mr-3 !sk-ui-shadow-none !sk-ui-border-0 !sk-ui-ring-0 !sk-ui-ring-offset-0 sk-ui-bg-transparent sk-ui-text-end sk-ui-font-medium sk-ui-text-2xl"
disabled={disabled}
onChange={(e) => setAmount?.(e.target.value)}
placeholder="0.00"
type="text"
value={amount ?? "0.00"}
/>

<div className="flex items-center gap-1">
{isLoading && <Loader2Icon className="size-3.5 animate-spin" />}
<div className="sk-ui-flex sk-ui-items-center sk-ui-gap-1">
{isLoading && <Loader2Icon className="sk-ui-size-3.5 sk-ui-animate-spin" />}

<span className="text-muted-foreground text-sm">{formattedAmountUSD}</span>
<span className="sk-ui-text-muted-foreground sk-ui-text-sm">{formattedAmountUSD}</span>
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ export function SwapAssetItem({ asset }: { asset: string | null | undefined }) {
const assetValue = AssetValue.from({ asset });

return (
<div className="flex items-center gap-3">
<div className="sk-ui-flex sk-ui-min-w-0 sk-ui-items-center sk-ui-gap-3">
<AssetIcon asset={asset} />

<div className="flex flex-col items-start">
<span className="font-medium text-base text-foreground">{assetValue?.ticker}</span>
<div className="sk-ui-flex sk-ui-min-w-0 sk-ui-flex-col sk-ui-items-start">
<span className="sk-ui-max-w-full sk-ui-truncate sk-ui-font-medium sk-ui-text-base sk-ui-text-foreground">{assetValue?.ticker}</span>

<span className="-mt-0.5 text-muted-foreground text-sm">{assetValue?.chain}</span>
<span className="sk-ui--mt-0.5 sk-ui-text-muted-foreground sk-ui-text-sm">{assetValue?.chain}</span>
</div>
</div>
);
Expand Down
Loading
Loading