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
11 changes: 11 additions & 0 deletions .changeset/swapkit-sdk-15fcrq2iqxhgc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
"@swapkit/sdk": patch
"@swapkit/wallet-extensions": patch
"@swapkit/wallet-hardware": patch
"@swapkit/wallet-mobile": patch
"@swapkit/wallets": patch
---

Update SwapKit SDK dependencies:

- @swapkit/helpers: 4.20.1 → 0.0.0-rc-20260728115044
13 changes: 13 additions & 0 deletions .changeset/ton-connect-wallet.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@swapkit/wallets": minor
---

Add TON Connect as a wallet option (`@swapkit/wallets/tonconnect`).

TON Connect is the standard wallet connection protocol for the TON blockchain and supports only `Chain.Ton`, including its native token and Jettons. The integration wraps `@tonconnect/ui` for the connect modal, universal links, and bridge sessions, and delegates message building, balances, and fee estimation to the signerless `@swapkit/toolboxes/ton` toolbox. Signing and broadcasting are routed through the connected wallet's `sendTransaction`.

Notes:

- Requires a publicly hosted `tonconnect-manifest.json` (`manifestUrl`), or an existing `TonConnectUI` instance can be injected.
- Sweep transfers (`CARRY_ALL_REMAINING_BALANCE`) are rejected: the TON Connect protocol does not carry a send mode, the wallet chooses it.
- Detached signing (`sign`) is not supported by the protocol; use `signAndBroadcastTransaction` / `transfer`.
53 changes: 36 additions & 17 deletions bun.lock

Large diffs are not rendered by default.

13 changes: 10 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"@cosmjs/stargate": "0.37.0",
"@scure/bip32": "2.2.0",
"@swapkit/core": "^4.6.4",
"@swapkit/helpers": "^4.20.1",
"@swapkit/helpers": "0.0.0-rc-20260728115044",
"@swapkit/plugins": "^4.7.1",
"@swapkit/server": "^4.4.2",
"@swapkit/toolboxes": "^4.28.0",
Expand All @@ -21,7 +21,10 @@
"zod": "4.4.3"
},
"name": "swapkit-wallets",
"overrides": { "nuqs": "2.8.9" },
"overrides": {
"nuqs": "2.8.9",
"@swapkit/helpers": "0.0.0-rc-20260728115044"
},
"packageManager": "bun@1.3.13",
"private": true,
"scripts": {
Expand All @@ -45,5 +48,9 @@
"type-check:ci": "bun run --filter './packages/*' type-check",
"version-bump": "bunx changeset version && bun run enrich:changelogs && bun install"
},
"workspaces": ["packages/*", "playgrounds/*", "tools/*"]
"workspaces": [
"packages/*",
"playgrounds/*",
"tools/*"
]
}
2 changes: 1 addition & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"dependencies": {
"@stricahq/typhonjs": "~3.0.1",
"@swapkit/core": "^4.6.4",
"@swapkit/helpers": "^4.20.1",
"@swapkit/helpers": "0.0.0-rc-20260728115044",
"@swapkit/plugins": "^4.7.1",
"@swapkit/server": "^4.4.2",
"@swapkit/toolboxes": "^4.28.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/wallet-extensions/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"@near-js/transactions": "~2.5.0",
"@scure/base": "~2.2.0",
"@solana/web3.js": "~1.98.4",
"@swapkit/helpers": "^4.20.1",
"@swapkit/helpers": "0.0.0-rc-20260728115044",
"@swapkit/toolboxes": "^4.28.0",
"@swapkit/utxo-signer": "^2.3.0",
"@swapkit/wallet-core": "^4.3.19",
Expand Down
20 changes: 10 additions & 10 deletions packages/wallet-extensions/src/evm-extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,19 @@ type Eip1193EventProvider = Eip1193Provider & {
};

export type EVMWalletOptions =
| WalletOption.BRAVE
| WalletOption.OKX_MOBILE
| WalletOption.METAMASK
| WalletOption.COINBASE_WEB
| WalletOption.EIP6963;
| typeof WalletOption.BRAVE
| typeof WalletOption.OKX_MOBILE
| typeof WalletOption.METAMASK
| typeof WalletOption.COINBASE_WEB
| typeof WalletOption.EIP6963;

const getWalletForType = (
walletType:
| WalletOption.BRAVE
| WalletOption.OKX_MOBILE
| WalletOption.METAMASK
| WalletOption.COINBASE_WEB
| WalletOption.EIP6963,
| typeof WalletOption.BRAVE
| typeof WalletOption.OKX_MOBILE
| typeof WalletOption.METAMASK
| typeof WalletOption.COINBASE_WEB
| typeof WalletOption.EIP6963,
) => {
switch (walletType) {
case WalletOption.COINBASE_WEB:
Expand Down
4 changes: 2 additions & 2 deletions packages/wallet-extensions/src/keplr/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ const keplrSupportedChainIds = [ChainId.Cosmos, ChainId.Kujira, ChainId.Noble, C
export const keplrWallet: ExtensionWallet<
"connectKeplr",
Chain[],
[chains: Chain[], walletType?: WalletOption.KEPLR | WalletOption.LEAP]
[chains: Chain[], walletType?: typeof WalletOption.KEPLR | typeof WalletOption.LEAP]
> = createWallet({
connect: ({ addChain, supportedChains }) =>
async function connectKeplr(
chains: Chain[],
walletType: WalletOption.KEPLR | WalletOption.LEAP = WalletOption.KEPLR,
walletType: typeof WalletOption.KEPLR | typeof WalletOption.LEAP = WalletOption.KEPLR,
) {
const extensionKey = walletType === WalletOption.LEAP ? "leap" : "keplr";
const filteredChains = filterSupportedChains({ chains, supportedChains, walletType });
Expand Down
2 changes: 1 addition & 1 deletion packages/wallet-hardware/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@near-js/transactions": "~2.5.0",
"@scure/base": "2.2.0",
"@scure/bip32": "2.2.0",
"@swapkit/helpers": "^4.20.1",
"@swapkit/helpers": "0.0.0-rc-20260728115044",
"@swapkit/toolboxes": "^4.28.0",
"@swapkit/utxo-signer": "^2.3.0",
"@swapkit/wallet-core": "^4.3.19",
Expand Down
2 changes: 1 addition & 1 deletion packages/wallet-mobile/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"author": "swapkit",
"dependencies": {
"@swapkit/helpers": "^4.20.1",
"@swapkit/helpers": "0.0.0-rc-20260728115044",
"@swapkit/toolboxes": "^4.28.0",
"@swapkit/wallet-core": "^4.3.19"
},
Expand Down
27 changes: 14 additions & 13 deletions packages/wallets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
"@scure/base": "~2.2.0",
"@scure/bip39": "~2.2.0",
"@solana/web3.js": "~1.98.4",
"@swapkit/helpers": "^4.20.1",
"@swapkit/helpers": "0.0.0-rc-20260728115044",
"@swapkit/toolboxes": "^4.28.0",
"@swapkit/utxo-signer": "^2.3.0",
"@swapkit/wallet-core": "^4.3.19",
"@swapkit/wallet-extensions": "workspace:*",
"@swapkit/wallet-hardware": "workspace:*",
"@ton/core": "~0.63.1",
"@tonconnect/ui": "~3.0.0",
"@walletconnect/modal": "~2.7.0",
"@walletconnect/sign-client": "~2.23.9",
"cosmjs-types": "~0.10.1",
Expand All @@ -46,6 +48,8 @@
"@radixdlt/radix-dapp-toolkit": "2.3.0",
"@scure/base": "2.2.0",
"@solana/web3.js": "1.98.4",
"@ton/core": "0.63.1",
"@tonconnect/ui": "3.0.0",
"@walletconnect/logger": "3.0.2",
"@walletconnect/modal": "2.7.0",
"@walletconnect/sign-client": "2.23.9",
Expand Down Expand Up @@ -184,6 +188,12 @@
"require": "./dist/src/talisman.cjs",
"types": "./dist/types/talisman.d.ts"
},
"./tonconnect": {
"bun": "./src/tonconnect/index.ts",
"default": "./dist/src/tonconnect/index.js",
"require": "./dist/src/tonconnect/index.cjs",
"types": "./dist/types/tonconnect/index.d.ts"
},
"./trezor": {
"bun": "./src/trezor.ts",
"default": "./dist/src/trezor.js",
Expand Down Expand Up @@ -215,21 +225,12 @@
"types": "./dist/types/xaman/index.d.ts"
}
},
"files": [
"dist/",
"src/"
],
"files": ["dist/", "src/"],
"homepage": "https://github.com/swapkit/wallets",
"license": "SEE LICENSE IN LICENSE",
"name": "@swapkit/wallets",
"publishConfig": {
"access": "public"
},
"repository": {
"directory": "packages/wallets",
"type": "git",
"url": "git+https://github.com/swapkit/wallets.git"
},
"publishConfig": { "access": "public" },
"repository": { "directory": "packages/wallets", "type": "git", "url": "git+https://github.com/swapkit/wallets.git" },
"scripts": {
"build": "bun run ./build.ts",
"build:clean": "rm -rf dist && bun run ./build.ts",
Expand Down
55 changes: 55 additions & 0 deletions packages/wallets/src/tonconnect/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { AssetValue, Chain, type GenericTransferParams, SwapKitError } from "@swapkit/helpers";
import type { TONTransactionInput } from "@swapkit/toolboxes/ton";
import type { TonConnectUI } from "@tonconnect/ui";
import { match } from "ts-pattern";
import type { TonConnectConfig } from "./types";
import { sendTonConnectTransaction } from "./walletMethods";

interface GetWalletForChainParams {
address: string;
chain: Chain;
config: TonConnectConfig;
tonConnectUI: TonConnectUI;
}

export function getWalletForChain({ address, chain, config, tonConnectUI }: GetWalletForChainParams) {
return match(chain)
.with(Chain.Ton, async () => {
const { getTONToolbox } = await import("@swapkit/toolboxes/ton");
// Signerless toolbox: message building, balances, and fee estimation happen
// locally; signing and broadcasting are delegated to the connected wallet.
const toolbox = getTONToolbox();

const signAndBroadcastTransaction = (transaction: TONTransactionInput) =>
sendTonConnectTransaction({ tonConnectUI, transaction, validSeconds: config.requestTimeoutSeconds });

const transfer = async ({ assetValue, recipient, memo }: GenericTransferParams) => {
const transaction = await toolbox.createTransaction({ assetValue, memo, recipient, sender: address });
return signAndBroadcastTransaction(transaction);
};

const estimateTransactionFee = ({ assetValue }: GenericTransferParams) => {
// The signerless toolbox's estimateTransactionFee needs a configured wallet
// and silently falls back to a flat 0.01 TON when getWallet() throws — badly
// underestimating jettons. Mirror the toolbox's own transfer budget instead:
// jettons attach ~0.05 TON gas + 0.01 TON forward, native transfers ~0.01 TON.
return Promise.resolve(AssetValue.from({ chain: Chain.Ton, value: assetValue.isGasAsset ? "0.01" : "0.06" }));
};

return {
...toolbox,
estimateTransactionFee,
getAddress: () => address,
// TON Connect signs and broadcasts atomically; a detached external-message
// signature is not part of the protocol.
sign: () => {
throw new SwapKitError("core_wallet_sign_message_not_supported", { wallet: "TON Connect" });
},
signAndBroadcastTransaction,
transfer,
};
})
.otherwise(() => {
throw new SwapKitError("wallet_chain_not_supported", { chain, wallet: "TON Connect" });
});
}
43 changes: 43 additions & 0 deletions packages/wallets/src/tonconnect/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Chain, filterSupportedChains, WalletOption } from "@swapkit/helpers";
import { createWallet, getWalletSupportedChains } from "@swapkit/wallet-core";
// Deep importers of @swapkit/wallets/tonconnect need the registration too;
// the module cache keeps this idempotent with the eager import in utils.ts.
import "./register";
import { getWalletForChain } from "./helpers";
import type { TonConnectConfig } from "./types";
import { connectTonConnect, getTonConnectInstance } from "./walletMethods";

export const tonconnectWallet = createWallet({
connect: ({ addChain, supportedChains: walletSupportedChains, walletType }) =>
async function connectTonConnectWallet(chains: Chain[], config: TonConnectConfig = {}) {
const supportedChains = filterSupportedChains({ chains, supportedChains: walletSupportedChains, walletType });

const tonConnectUI = await getTonConnectInstance(config);
const address = await connectTonConnect(tonConnectUI);

const promises = supportedChains.map(async (chain) => {
const walletMethods = await getWalletForChain({ address, chain, config, tonConnectUI });

addChain({
...walletMethods,
address,
balance: [],
chain,
disconnect: () => tonConnectUI.disconnect(),
walletType: WalletOption.TON_CONNECT,
});
});

await Promise.all(promises);
return true;
},
directSigningSupport: { [Chain.Ton]: true },
name: "connectTonConnect",
supportedChains: [Chain.Ton],
walletType: WalletOption.TON_CONNECT,
});

export const TON_CONNECT_SUPPORTED_CHAINS = getWalletSupportedChains(tonconnectWallet);
export type TonConnectSupportedChain = (typeof TON_CONNECT_SUPPORTED_CHAINS)[number];

export type { TonConnectConfig } from "./types";
9 changes: 9 additions & 0 deletions packages/wallets/src/tonconnect/register.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { registerWalletOption } from "@swapkit/helpers";

declare module "@swapkit/helpers" {
interface WalletOptionRegistry {
TON_CONNECT: "TON_CONNECT";
}
}

registerWalletOption("TON_CONNECT", "TON_CONNECT");
26 changes: 26 additions & 0 deletions packages/wallets/src/tonconnect/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { TonConnectUI, TonConnectUiCreateOptions } from "@tonconnect/ui";

export type TonConnectConfig = {
/**
* Reuse an existing TonConnectUI instance (e.g. the one created by
* `@tonconnect/ui-react`'s TonConnectUIProvider). When provided, `manifestUrl`
* and `uiOptions` are ignored.
*/
instance?: TonConnectUI;
/**
* URL of the dApp's publicly hosted tonconnect-manifest.json.
* Required when no `instance` is provided.
* @see https://docs.ton.org/applications/ton-connect/get-started
*/
manifestUrl?: string;
/**
* Extra TonConnectUI creation options (theme, wallets list configuration, etc.)
* forwarded when SwapKit creates the instance.
*/
uiOptions?: Omit<TonConnectUiCreateOptions, "manifestUrl">;
/**
* How long (in seconds) a sendTransaction request stays valid before the
* wallet refuses it (`validUntil`). Defaults to 300 seconds.
*/
requestTimeoutSeconds?: number;
};
Loading
Loading