Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
3 changes: 2 additions & 1 deletion demo/v4ledger-adapter/src/WalletV4R2LedgerAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,8 @@ export class WalletV4R2LedgerAdapter implements WalletAdapter {
* Get wallet's TON address
*/
getAddress(options?: { testnet?: boolean }): UserFriendlyAddress {
return formatWalletAddress(this.walletContract.address, options?.testnet);
// Convert to string to avoid Address type version mismatch between @ton/core versions
return formatWalletAddress(this.walletContract.address.toString(), options?.testnet);
}

getWalletId(): WalletId {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ export class AndroidStorageAdapter implements StorageAdapter {
async get<T>(key: string): Promise<T | null> {
try {
const value = this.androidBridge.storageGet(key);
log('[AndroidStorageAdapter] get:', key, '=', value ? `${value.substring(0, 100)}...` : 'null');
if (!value) {
return null;
}
Expand All @@ -53,7 +52,6 @@ export class AndroidStorageAdapter implements StorageAdapter {
async set<T>(key: string, value: T): Promise<void> {
try {
const serialized = JSON.stringify(value);
log('[AndroidStorageAdapter] set:', key, '=', serialized.substring(0, 100) + '...');
this.androidBridge.storageSet(key, serialized);
} catch (err) {
error('[AndroidStorageAdapter] Failed to set key:', key, err);
Expand Down
41 changes: 8 additions & 33 deletions packages/walletkit-android-bridge/src/api/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,24 @@
/**
* Internal browser events dispatched back to the native layer.
*/
import type { EmitBrowserPageArgs, EmitBrowserErrorArgs, EmitBrowserBridgeRequestArgs } from '../types';
import { emit } from '../transport/messaging';

/**
* Signals that the internal browser started loading a page.
*
* @param args - Page metadata.
*/
export function emitBrowserPageStarted(args: EmitBrowserPageArgs) {
emit('browserPageStarted', { url: args.url });
export function emitBrowserPageStarted(args: { url: string }) {
emit('browserPageStarted', args);
return { success: true };
}

/**
* Signals that the internal browser finished loading a page.
*
* @param args - Page metadata.
*/
export function emitBrowserPageFinished(args: EmitBrowserPageArgs) {
emit('browserPageFinished', { url: args.url });
export function emitBrowserPageFinished(args: { url: string }) {
emit('browserPageFinished', args);
return { success: true };
}

/**
* Reports a browser error to the native layer.
*
* @param args - Error details.
*/
export function emitBrowserError(args: EmitBrowserErrorArgs) {
emit('browserError', { message: args.message });
export function emitBrowserError(args: { message: string }) {
emit('browserError', args);
return { success: true };
}

/**
* Emits a TonConnect bridge request originating in the internal browser.
*
* @param args - Request metadata for analytics/UI.
*/
export function emitBrowserBridgeRequest(args: EmitBrowserBridgeRequestArgs) {
emit('browserBridgeRequest', {
messageId: args.messageId,
method: args.method,
request: args.request,
});
export function emitBrowserBridgeRequest(args: { messageId: string; method: string; request: unknown }) {
emit('browserBridgeRequest', args);
return { success: true };
}
63 changes: 16 additions & 47 deletions packages/walletkit-android-bridge/src/api/cryptography.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,64 +8,33 @@

/**
* Cryptographic helpers backed by WalletKit and custom signer coordination.
*
* Note: Array.from() conversions for Uint8Array → number[] are necessary glue code.
* JSON.stringify cannot serialize Uint8Array, so we must convert to number arrays
* for RPC communication between JS and Kotlin layers.
*/
import type { Hex } from '@ton/walletkit';

import type { MnemonicToKeyPairArgs, SignArgs, CreateTonMnemonicArgs } from '../types';
import { CreateTonMnemonic, MnemonicToKeyPair, DefaultSignature } from '../core/moduleLoader';
import { callBridge } from '../utils/bridgeWrapper';

/**
* Signs data using a custom signer stored in Kotlin.
* This is called by custom signer wrappers created in createAdapter.
*/
export async function signWithCustomSigner(signerId: string, bytes: Uint8Array): Promise<Hex> {
const result = await callBridge('signWithCustomSigner', async () => {
// Call back to Kotlin's SignerManager
return window.WalletKitNative?.signWithCustomSigner?.(signerId, Array.from(bytes));
});
const result = await window.WalletKitNative?.signWithCustomSigner?.(signerId, Array.from(bytes));
return result as Hex;
}

/**
* Converts a mnemonic phrase to a key pair (public + secret keys).
* Returns raw keyPair object - Kotlin handles Uint8Array to ByteArray conversion.
*
* @param args - Mnemonic words and optional type ('ton' or 'bip39').
*/
export async function mnemonicToKeyPair(args: MnemonicToKeyPairArgs) {
return callBridge('mnemonicToKeyPair', async () => {
return await MnemonicToKeyPair!(args.mnemonic, args.mnemonicType ?? 'ton');
});
export async function mnemonicToKeyPair(args: { mnemonic: string[]; mnemonicType?: string }) {
if (!MnemonicToKeyPair) {
throw new Error('MnemonicToKeyPair module not loaded');
}
return MnemonicToKeyPair(args.mnemonic, args.mnemonicType ?? 'ton');
}

/**
* Signs arbitrary data using a secret key.
* Returns signature hex string directly.
*
* @param args - Data bytes and secret key bytes.
*/
export async function sign(args: SignArgs) {
return callBridge('sign', async () => {
const dataBytes = Uint8Array.from(args.data);
const secretKeyBytes = Uint8Array.from(args.secretKey);
return DefaultSignature!(dataBytes, secretKeyBytes);
});
export async function sign(args: { data: number[]; secretKey: number[] }) {
if (!DefaultSignature) {
throw new Error('DefaultSignature module not loaded');
}
return DefaultSignature(Uint8Array.from(args.data), Uint8Array.from(args.secretKey));
}

/**
* Generates a TON mnemonic phrase.
* Returns array of words directly.
*
* @param _args - Optional generation parameters.
*/
export async function createTonMnemonic(_args: CreateTonMnemonicArgs = { count: 24 }) {
return callBridge('createTonMnemonic', async () => {
const mnemonicResult = await CreateTonMnemonic!();
return Array.isArray(mnemonicResult) ? mnemonicResult : `${mnemonicResult}`.split(' ').filter(Boolean);
});
export async function createTonMnemonic() {
if (!CreateTonMnemonic) {
throw new Error('CreateTonMnemonic module not loaded');
}
return CreateTonMnemonic();
}
38 changes: 32 additions & 6 deletions packages/walletkit-android-bridge/src/api/eventListeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,41 @@
*
*/

import type {
ConnectionRequestEvent,
DisconnectionEvent,
RequestErrorEvent,
SendTransactionRequestEvent,
SignDataRequestEvent,
IntentEvent,
} from '@ton/walletkit';

/**
* Shared event listener references used to manage WalletKit callbacks.
*/
export type BridgeEventListener = ((event: unknown) => void) | null;
export type ConnectEventListener = ((event: ConnectionRequestEvent) => void) | null;
export type TransactionEventListener = ((event: SendTransactionRequestEvent) => void) | null;
export type SignDataEventListener = ((event: SignDataRequestEvent) => void) | null;
export type DisconnectEventListener = ((event: DisconnectionEvent) => void) | null;
export type ErrorEventListener = ((event: RequestErrorEvent) => void) | null;
export type IntentEventListener = ((event: IntentEvent) => void) | null;

/**
* Union type for all bridge event listeners.
*/
export type BridgeEventListener =
| ConnectEventListener
| TransactionEventListener
| SignDataEventListener
| DisconnectEventListener
| ErrorEventListener
| IntentEventListener;

export const eventListeners = {
onConnectListener: null as BridgeEventListener,
onTransactionListener: null as BridgeEventListener,
onSignDataListener: null as BridgeEventListener,
onDisconnectListener: null as BridgeEventListener,
onErrorListener: null as BridgeEventListener,
onConnectListener: null as ConnectEventListener,
onTransactionListener: null as TransactionEventListener,
onSignDataListener: null as SignDataEventListener,
onDisconnectListener: null as DisconnectEventListener,
onErrorListener: null as ErrorEventListener,
onIntentListener: null as IntentEventListener,
};
19 changes: 14 additions & 5 deletions packages/walletkit-android-bridge/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ import * as wallets from './wallets';
import * as transactions from './transactions';
import * as requests from './requests';
import * as tonconnect from './tonconnect';
import * as intents from './intents';
import * as nft from './nft';
import * as jettons from './jettons';
import * as browser from './browser';
import { eventListeners } from './eventListeners';

export { eventListeners };

const apiImpl: WalletKitBridgeApi = {
export const api: WalletKitBridgeApi = {
// Initialization
init: initialization.init,
setEventsListeners: initialization.setEventsListeners,
Expand All @@ -40,7 +41,7 @@ const apiImpl: WalletKitBridgeApi = {
getAdapterAddress: wallets.getAdapterAddress,
addWallet: wallets.addWallet,
getWallets: wallets.getWallets,
getWallet: wallets.getWallet,
getWallet: wallets.getWalletById,
getWalletAddress: wallets.getWalletAddress,
removeWallet: wallets.removeWallet,
getBalance: wallets.getBalance,
Expand All @@ -67,6 +68,16 @@ const apiImpl: WalletKitBridgeApi = {
disconnectSession: tonconnect.disconnectSession,
processInternalBrowserRequest: tonconnect.processInternalBrowserRequest,

// Intents
handleIntentUrl: intents.handleIntentUrl,
isIntentUrl: intents.isIntentUrl,
intentItemsToTransactionRequest: intents.intentItemsToTransactionRequest,
approveTransactionIntent: intents.approveTransactionIntent,
approveSignDataIntent: intents.approveSignDataIntent,
approveActionIntent: intents.approveActionIntent,
rejectIntent: intents.rejectIntent,
processConnectAfterIntent: intents.processConnectAfterIntent,

// NFTs
getNfts: nft.getNfts,
getNft: nft.getNft,
Expand All @@ -84,8 +95,6 @@ const apiImpl: WalletKitBridgeApi = {
emitBrowserPageFinished: browser.emitBrowserPageFinished,
emitBrowserError: browser.emitBrowserError,
emitBrowserBridgeRequest: browser.emitBrowserBridgeRequest,
};

export const api = apiImpl;
} as unknown as WalletKitBridgeApi;

export type { BridgeEventListener } from './eventListeners';
46 changes: 36 additions & 10 deletions packages/walletkit-android-bridge/src/api/initialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,19 @@
* Simplified bridge for WalletKit initialization and event listener management.
*/

import type {
ConnectionRequestEvent,
DisconnectionEvent,
IntentEvent,
RequestErrorEvent,
SendTransactionRequestEvent,
SignDataRequestEvent,
} from '@ton/walletkit';

import type { WalletKitBridgeInitConfig, SetEventsListenersArgs, WalletKitBridgeEventCallback } from '../types';
import { ensureWalletKitLoaded } from '../core/moduleLoader';
import { initTonWalletKit, requireWalletKit } from '../core/initialization';
import { initTonWalletKit } from '../core/initialization';
import { getKit } from '../utils/bridge';
import { emit } from '../transport/messaging';
import { postToNative } from '../transport/nativeBridge';
import { eventListeners } from './eventListeners';
Expand All @@ -36,8 +46,8 @@ export async function init(config?: WalletKitBridgeInitConfig) {
/**
* Registers bridge event listeners, proxying WalletKit events to the native layer.
*/
export function setEventsListeners(args?: SetEventsListenersArgs): { ok: true } {
const kit = requireWalletKit();
export async function setEventsListeners(args?: SetEventsListenersArgs): Promise<{ ok: true }> {
const kit = await getKit();

const callback: WalletKitBridgeEventCallback =
args?.callback ??
Expand All @@ -49,7 +59,7 @@ export function setEventsListeners(args?: SetEventsListenersArgs): { ok: true }
kit.removeConnectRequestCallback();
}

eventListeners.onConnectListener = (event: unknown) => {
eventListeners.onConnectListener = (event: ConnectionRequestEvent) => {
callback('connectRequest', event);
};

Expand All @@ -59,7 +69,7 @@ export function setEventsListeners(args?: SetEventsListenersArgs): { ok: true }
kit.removeTransactionRequestCallback();
}

eventListeners.onTransactionListener = (event: unknown) => {
eventListeners.onTransactionListener = (event: SendTransactionRequestEvent) => {
callback('transactionRequest', event);
};

Expand All @@ -69,7 +79,7 @@ export function setEventsListeners(args?: SetEventsListenersArgs): { ok: true }
kit.removeSignDataRequestCallback();
}

eventListeners.onSignDataListener = (event: unknown) => {
eventListeners.onSignDataListener = (event: SignDataRequestEvent) => {
callback('signDataRequest', event);
};

Expand All @@ -79,7 +89,7 @@ export function setEventsListeners(args?: SetEventsListenersArgs): { ok: true }
kit.removeDisconnectCallback();
}

eventListeners.onDisconnectListener = (event: unknown) => {
eventListeners.onDisconnectListener = (event: DisconnectionEvent) => {
callback('disconnect', event);
};

Expand All @@ -90,20 +100,31 @@ export function setEventsListeners(args?: SetEventsListenersArgs): { ok: true }
kit.removeErrorCallback();
}

eventListeners.onErrorListener = (event: unknown) => {
eventListeners.onErrorListener = (event: RequestErrorEvent) => {
callback('requestError', event);
};

kit.onRequestError(eventListeners.onErrorListener);

// Register intent listener - forwards IntentEvent for wallet UI
if (eventListeners.onIntentListener) {
kit.removeIntentRequestCallback?.();
}

eventListeners.onIntentListener = (event: IntentEvent) => {
callback('intentRequest', event);
};

kit.onIntentRequest?.(eventListeners.onIntentListener);

return { ok: true };
}

/**
* Removes all previously registered bridge event listeners.
*/
export function removeEventListeners(): { ok: true } {
const kit = requireWalletKit();
export async function removeEventListeners(): Promise<{ ok: true }> {
const kit = await getKit();

if (eventListeners.onConnectListener) {
kit.removeConnectRequestCallback();
Expand All @@ -130,5 +151,10 @@ export function removeEventListeners(): { ok: true } {
eventListeners.onErrorListener = null;
}

if (eventListeners.onIntentListener) {
kit.removeIntentRequestCallback?.();
eventListeners.onIntentListener = null;
}

return { ok: true };
}
Loading
Loading