Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c822a5a
chore: keystore transaction signing
towanTG Oct 10, 2025
caaa9c2
chore: correct quoteresponserouteitem
towanTG Oct 10, 2025
ac9823f
chore: clean up
towanTG Oct 10, 2025
831a14e
Merge branch 'develop' into chore/keystore-signing-api-provided-tx
towanTG Oct 16, 2025
8ba9336
Merge branch 'develop' into chore/keystore-signing-api-provided-tx
towanTG Oct 21, 2025
767cc24
Merge branch 'develop' into chore/keystore-signing-api-provided-tx
towanTG Oct 21, 2025
f32cf05
chore: move nightly changes into branch
towanTG Oct 21, 2025
1f1e59e
chore: uses approve endpoint for plugins
towanTG Oct 22, 2025
62923a7
Merge branch 'develop' into chore/keystore-signing-api-provided-tx
towanTG Nov 21, 2025
9cdee0b
Merge branch 'develop' into chore/keystore-signing-api-provided-tx
towanTG Nov 21, 2025
8d30bfd
chore: fix branch
towanTG Nov 21, 2025
e702fb6
Merge branch 'develop' into chore/keystore-signing-api-provided-tx
towanTG Nov 24, 2025
d232666
Merge branch 'develop' into chore/keystore-signing-api-provided-tx
towanTG Nov 25, 2025
b7689ed
fix: approval endpoint changes
towanTG Nov 25, 2025
3cefbb4
chore: cleans up ts errors
towanTG Nov 25, 2025
777dd5b
chore: update signing decision logic + adds btc to phantom
towanTG Nov 26, 2025
1de8bb3
Merge branch 'develop' into chore/keystore-signing-api-provided-tx
towanTG Nov 26, 2025
f783b25
Merge branch 'develop' into chore/keystore-signing-api-provided-tx
towanTG Nov 30, 2025
fc155f9
chore: adds sui sign and broadcast and support in trading plugin
towanTG Dec 1, 2025
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
14 changes: 13 additions & 1 deletion packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
SwapKitError,
type SwapParams,
UTXOChains,
WalletOption,
} from "@swapkit/helpers";
import type { EVMTransaction, QuoteResponseRoute } from "@swapkit/helpers/api";
import type { createPlugin } from "@swapkit/plugins";
Expand Down Expand Up @@ -201,7 +202,18 @@ export function SwapKit<
return wallet;
}

function swap<T extends PluginName>({ route, pluginName, ...rest }: SwapParams<T, QuoteResponseRoute>) {
function swap<T extends PluginName>({ route, pluginName, useApiTx, ...rest }: SwapParams<T, QuoteResponseRoute>) {
const fromChain = AssetValue.from({ asset: route.sellAsset }).chain;

// only keystore supports straight signing of all chains
if (useApiTx && getWallet(fromChain)?.walletType === WalletOption.KEYSTORE) {
const plugin = getSwapKitPlugin("swap");
if ("swap" in plugin) {
// @ts-expect-error TODO: fix this
return plugin.swap({ ...rest, route });
}
}

const plugin = getSwapKitPlugin(pluginName || route.providers[0]);

if ("swap" in plugin) {
Expand Down
48 changes: 46 additions & 2 deletions packages/helpers/src/api/swapkitApi/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,21 @@ export const EVMTransactionSchema = object({

export type EVMTransaction = z.infer<typeof EVMTransactionSchema>;

export const TronTransactionSchema = z.object({
raw_data: z.object({
contract: z.any(),
expiration: z.number(),
ref_block_bytes: z.string(),
ref_block_hash: z.string(),
timestamp: z.number(),
}),
raw_data_hex: z.string(),
txID: z.string(),
visible: z.boolean(),
});

export type TronTransaction = z.infer<typeof TronTransactionSchema>;

export const EVMTransactionDetailsParamsSchema = array(
union([
string(),
Expand All @@ -468,7 +483,36 @@ export const EVMTransactionDetailsSchema = object({

export type EVMTransactionDetails = z.infer<typeof EVMTransactionDetailsSchema>;

const EncodeObjectSchema = object({ typeUrl: string(), value: unknown() });
const ThorchainDepositMsgSchema = object({
typeUrl: string("/types.MsgDeposit"),
value: object({
coins: array(
object({
amount: string(),
asset: object({ chain: string(), symbol: string(), synth: boolean(), ticker: string() }),
}),
),
memo: string(),
signer: string(),
}),
});

export type ThorchainDepositMsg = z.infer<typeof ThorchainDepositMsgSchema>;

const CosmosSendMsgSchema = object({
typeUrl: string("/types.MsgSend"),
value: object({
amount: array(object({ amount: string(), denom: string() })),
fromAddress: string(),
toAddress: string(),
}),
});

export type CosmosSendMsg = z.infer<typeof CosmosSendMsgSchema>;

const EncodeObjectSchema = object({ typeUrl: string(), value: CosmosSendMsgSchema.or(ThorchainDepositMsgSchema) });

export type APICosmosEncodedObject = z.infer<typeof EncodeObjectSchema>;

const FeeSchema = object({ amount: array(object({ amount: string(), denom: string() })), gas: string() });

Expand Down Expand Up @@ -571,7 +615,7 @@ const QuoteResponseRouteItem = object({
sourceAddress: string().describe("Source address"),
targetAddress: optional(string().describe("Target address")),
totalSlippageBps: number().describe("Total slippage in bps"),
tx: optional(union([EVMTransactionSchema, CosmosTransactionSchema, string()])),
tx: optional(union([EVMTransactionSchema, CosmosTransactionSchema, TronTransactionSchema, string()])),
txType: optional(z.enum(RouteQuoteTxType)),
warnings: RouteQuoteWarningSchema,
});
Expand Down
4 changes: 4 additions & 0 deletions packages/helpers/src/modules/swapKitError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,10 @@ const errorCodes = {
* Garden Plugin
*/
plugin_garden_missing_data: 42001,
/**
* Swap Plugin
*/
plugin_generic_swap_invalid_data: 43001,
/**
* SwapKit API
*/
Expand Down
5 changes: 4 additions & 1 deletion packages/helpers/src/types/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ export type GenericSwapParams<T = unknown> = {
route: T;
};

export type SwapParams<PluginNames = string, R = unknown> = GenericSwapParams<R> & { pluginName?: PluginNames };
export type SwapParams<PluginNames = string, R = unknown> = GenericSwapParams<R> & {
pluginName?: PluginNames;
useApiTx?: boolean;
};

export enum FeeOption {
Average = "average",
Expand Down
5 changes: 5 additions & 0 deletions packages/plugins/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
"require": "./dist/solana/index.cjs",
"types": "./dist/types/solana/index.d.ts"
},
"./swap": {
"default": "./dist/swap/index.js",
"require": "./dist/swap/index.cjs",
"types": "./dist/types/swap/index.d.ts"
},
"./thorchain": {
"default": "./dist/thorchain/index.js",
"require": "./dist/thorchain/index.cjs",
Expand Down
1 change: 1 addition & 0 deletions packages/plugins/src/swap/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SwapPlugin } from "./plugin";
98 changes: 98 additions & 0 deletions packages/plugins/src/swap/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import type { ZcashPsbt } from "@bitgo/utxo-lib/dist/src/bitgo";
import { AssetValue, Chain, CosmosChains, EVMChains, SwapKitError, type SwapParams } from "@swapkit/helpers";
import {
CosmosTransactionSchema,
EVMTransactionSchema,
type QuoteResponseRoute,
TronTransactionSchema,
} from "@swapkit/helpers/api";
import { match, P } from "ts-pattern";
import { createPlugin } from "../utils";

const isEVMTransaction = (tx: unknown) => EVMTransactionSchema.safeParse(tx).success;
const isTronTransaction = (tx: unknown) => TronTransactionSchema.safeParse(tx).success;
const isCosmosTransaction = (tx: unknown) => CosmosTransactionSchema.safeParse(tx).success;

export const SwapPlugin = createPlugin({
methods: ({ getWallet }) => ({
swap: function swap({ route }: SwapParams<"swap", QuoteResponseRoute>) {
const { sellAsset, tx } = route;
const sellAssetValue = AssetValue.from({ asset: sellAsset });
const chain = sellAssetValue.chain;

return match({ chain, tx })
.returnType<Promise<string>>()
.with(
{ chain: P.union(Chain.Bitcoin, Chain.Dogecoin, Chain.Litecoin, Chain.Dash), tx: P.string },
async ({ chain, tx }) => {
const { Psbt } = await import("bitcoinjs-lib");
const wallet = await getWallet(chain);
const psbt = Psbt.fromBase64(tx);
if (chain === Chain.Dogecoin) psbt.setMaximumFeeRate(650000000);

return wallet.signAndBroadcastTransaction(psbt);
},
)
.with({ chain: Chain.BitcoinCash, tx: P.string }, async ({ chain, tx }) => {
const { UtxoPsbt } = await import("@bitgo/utxo-lib/dist/src/bitgo");
const { networks } = await import("@bitgo/utxo-lib");
const wallet = await getWallet(chain);
const psbt = UtxoPsbt.fromBuffer(Buffer.from(tx, "base64"), { network: networks.bitcoincash });

return wallet.signAndBroadcastTransaction(psbt);
})
.with({ chain: Chain.Zcash, tx: P.string }, async ({ chain, tx }) => {
const { ZcashPsbt } = await import("@bitgo/utxo-lib/dist/src/bitgo");
const { networks } = await import("@bitgo/utxo-lib");
const wallet = await getWallet(chain);

const psbt = ZcashPsbt.fromBuffer(Buffer.from(tx, "base64"), { network: networks.zcash });

return wallet.signAndBroadcastTransaction(psbt as ZcashPsbt);
})
.with({ chain: P.union(...EVMChains), tx: P.when(isEVMTransaction) }, async ({ chain, tx }) => {
const wallet = await getWallet(chain);
const transaction = EVMTransactionSchema.parse(tx);

return wallet.sendTransaction({ ...transaction, value: BigInt(transaction.value || "0") });
})
.with({ chain: Chain.Solana, tx: P.string }, async ({ chain, tx }) => {
const { VersionedTransaction } = await import("@solana/web3.js");
const wallet = await getWallet(chain);

const transaction = VersionedTransaction.deserialize(Buffer.from(tx, "base64"));
return wallet.signAndBroadcastTransaction(transaction);
})
.with({ chain: P.union(...CosmosChains), tx: P.when(isCosmosTransaction) }, async ({ chain, tx }) => {
const wallet = await getWallet(chain);
const transaction = CosmosTransactionSchema.parse(tx);

return wallet.signAndBroadcastTransaction(transaction);
})
.with({ chain: Chain.Near, tx: P.string }, async ({ chain, tx }) => {
const { Transaction } = await import("@near-js/transactions");
const wallet = await getWallet(chain);

const transaction = Transaction.decode(Buffer.from(tx, "base64"));

return wallet.signAndBroadcastTransaction(transaction);
})
.with({ chain: Chain.Ripple, tx: P.string }, async ({ chain, tx }) => {
const wallet = await getWallet(chain);

return wallet.signAndBroadcastTransaction(JSON.parse(tx));
})
.with({ chain: Chain.Tron, tx: P.when(isTronTransaction) }, async ({ chain, tx }) => {
const wallet = await getWallet(chain);
const transaction = TronTransactionSchema.parse(tx);

return wallet.signAndBroadcastTransaction(transaction);
})
.otherwise(() => {
throw new SwapKitError("plugin_generic_swap_invalid_data", { chain, tx });
});
},
}),
name: "swap",
properties: { supportedSwapkitProviders: [] },
});
2 changes: 2 additions & 0 deletions packages/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { GardenPlugin } from "@swapkit/plugins/garden";
import { NearPlugin } from "@swapkit/plugins/near";
import { RadixPlugin } from "@swapkit/plugins/radix";
import { SolanaPlugin } from "@swapkit/plugins/solana";
import { SwapPlugin } from "@swapkit/plugins/swap";
import { MayachainPlugin, ThorchainPlugin } from "@swapkit/plugins/thorchain";

import { bitgetWallet } from "@swapkit/wallets/bitget";
Expand Down Expand Up @@ -85,6 +86,7 @@ export const defaultPlugins = {
...SolanaPlugin,
...NearPlugin,
...GardenPlugin,
...SwapPlugin,
};

export const defaultWallets = {
Expand Down
43 changes: 42 additions & 1 deletion packages/toolboxes/src/cosmos/toolbox/cosmos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import {
type TCLikeChain,
updateDerivationPath,
} from "@swapkit/helpers";
import { SwapKitApi } from "@swapkit/helpers/api";
import { type CosmosTransaction, SwapKitApi } from "@swapkit/helpers/api";
import type { TxRaw } from "cosmjs-types/cosmos/tx/v1beta1/tx";
import { match, P } from "ts-pattern";
import type { CosmosToolboxParams } from "../types";
import {
Expand Down Expand Up @@ -136,6 +137,42 @@ export async function createCosmosToolbox({ chain, ...toolboxParams }: CosmosToo
return base64.encode(account?.pubkey);
}

async function signTransaction(transaction: CosmosTransaction): Promise<TxRaw> {
const from = await getAddress();

if (!(signer && from)) {
throw new SwapKitError("toolbox_cosmos_signer_not_defined");
}

const signingClient = await createSigningStargateClient(rpcUrl, signer);

const txRaw = await signingClient.sign(from, transaction.msgs, transaction.fee, transaction.memo, {
accountNumber: transaction.accountNumber,
chainId: transaction.chainId,
sequence: transaction.sequence,
});

return txRaw;
}

async function signAndBroadcastTransaction(transaction: CosmosTransaction) {
const from = await getAddress();

if (!(signer && from)) {
throw new SwapKitError("toolbox_cosmos_signer_not_defined");
}

const signingClient = await createSigningStargateClient(rpcUrl, signer);

const result = await signingClient.signAndBroadcast(from, transaction.msgs, transaction.fee, transaction.memo);

if (result.code !== 0) {
throw new SwapKitError("core_swap_transaction_error", { code: result.code, message: result.rawLog });
}

return result.transactionHash;
}

async function transfer({
recipient,
assetValue,
Expand Down Expand Up @@ -207,6 +244,10 @@ export async function createCosmosToolbox({ chain, ...toolboxParams }: CosmosToo
importedSigning.DirectSecp256k1Wallet ?? importedSigning.default?.DirectSecp256k1Wallet;
return DirectSecp256k1Wallet.fromKey(privateKey, chainPrefix);
},

signAndBroadcastTransaction,
signer,
signTransaction,
transfer,
validateAddress: getCosmosValidateAddress(chainPrefix),
verifySignature: verifySignature(getAccount),
Expand Down
1 change: 1 addition & 0 deletions packages/toolboxes/src/cosmos/toolbox/thorchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ export async function createThorchainToolbox({ chain, ...toolboxParams }: Cosmos
derivationPath: derivationPathToString(derivationPath),
prefix: chainPrefix,
}),
signer,
signMultisigTx: signMultisigTx(chain),
signWithPrivateKey,
transfer,
Expand Down
12 changes: 12 additions & 0 deletions packages/toolboxes/src/near/toolbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,16 @@ export async function getNearToolbox(toolboxParams?: NearToolboxParams): Promise
return result.transaction.hash;
}

async function signAndBroadcastTransaction(transaction: Transaction) {
try {
const signedTransaction = await signTransaction(transaction);

return await broadcastTransaction(signedTransaction);
} catch (error) {
throw new SwapKitError("toolbox_near_transfer_failed", { error });
}
}

async function estimateTransactionFee(params: NearTransferParams | NearGasEstimateParams) {
if ("assetValue" in params) {
const baseTransferCost = "115123062500";
Expand Down Expand Up @@ -364,6 +374,8 @@ export async function getNearToolbox(toolboxParams?: NearToolboxParams): Promise
nep141,
provider,
serializeTransaction,
signAndBroadcastTransaction,
signer,
signTransaction,
transfer,
validateAddress: await getValidateNearAddress(),
Expand Down
49 changes: 0 additions & 49 deletions packages/toolboxes/src/near/types.ts

This file was deleted.

Loading