Remote Connector

Connecting to Tonhub wallet from your dApp or bot

TonhubConnector allows you to connect to a wallet app from a web browser, bot or backend.

Creating a connector

Before connecting to an app you need to create a connector:

import { TonhubConnector} from "ton-x";
const connector = new TonhubConnector({network: "mainnet"}); //Set network "sandbox" for testnet

Starting a new session

Before requesting a connection to a wallet you need to create session and persist it's data somewhere in your app.

📘

You can generate session anywhere

It could be web app, bot, console app. ton-x doesn't depend on specific environment.

🚧

Do not forget to persist session

At this step you need to persist and reuse this session values even in the case of app restart until session was revoked (see later)

let session: TonhubCreatedSession = await connector.createNewSession({
    name: 'Your app name',
    url: 'Your app url'
});

// Session ID, Seed and Auth Link
const sessionId = session.id;
const sessionSeed = session.seed;
const sessionLink = session.link;

Present a session link to the user

After session was created and persisted you can present user a link to connect to your app. On desktop you need to show a QR Code and on mobile you need to show it as a normal link that user will need to press.

❗️

Do not try to navigate your user automatically

This is almost never works on mobile

Await session confirmation

After presenting a link the user you can await session confirmation or rejection.

🚧

Revoked session couldn't be restarted

If user rejected connection or backend revoked a token you can't use it again and have to create a new one.

const session: TonhubSessionAwaited = await connector.awaitSessionReady(sessionId, 5 * 60 * 1000); // 5 min timeout

if (session.state === 'revoked' || session.state === 'expired') {
    // Handle revoked or expired session
} else if (session.state === 'ready') {
    
    // Handle session
    const walletConfig: TonhubWalletConfig = session.walletConfig;
    
    // You need to persist this values to work with this connection:
    // * sessionId
    // * sessionSeed
    // * walletConfig

    // You can check signed wallet config on backend using TonhubConnector.verifyWalletConfig.
    // walletConfig is cryptographically signed for specific session and other parameters
    // you can safely use it as authentication proof without the need to sign something.
    const correctConfig: boolean = TonhubConnector.verifyWalletConfig(sessionId, walletConfig);

    // ...

} else {
    throw new Error('Impossible');
}

Requesting Transaction

To request a transaction you can call requestTransaction in connector after successful session.

// Request body
const request: TonhubTransactionRequest = {
    seed: sessionSeed, // Session Seed
    appPublicKey: walletConfig.appPublicKey, // Wallet's app public key
    to: 'EQCkR1cGmnsE45N4K0otPl5EnxnRakmGqeJUNua5fkWhales', // Destination
    value: '10000000000', // Amount in nano-tons
    timeout: 5 * 60 * 1000, // 5 minut timeout
    stateInit: '....', // Optional serialized to base64 string state_init cell
    text: 'Hello world', // Optional comment. If no payload specified - sends actual content, if payload is provided this text is used as UI-only hint
    payload: '....' // Optional serialized to base64 string payload cell
};
const response: TonhubTransactionResponse = await connector.requestTransaction(request);
if (response.type === 'rejected') {
    // Handle rejection
} else if (response.type === 'expired') {
    // Handle expiration
} else if (response.type === 'invalid_session') {
    // Handle expired or invalid session
} else if (response.type === 'success') {
    // Handle successful transaction
    const externalMessage = response.response; // Signed body of external message that was sent to the network
} else {
    throw new Error('Impossible');
}

Requesting Signature

To request a transaction you can call requestTransaction in connector after successful session.

const payloadToSign = Buffer.concat([Buffer.from([0, 0, 0, 0]), Buffer.from('Some random string')]);
const payload = beginCell()
    .storeBuffer(payloadToSign)
    .endCell()
    .toBoc({idx:false})
    .toString('base64');
const text = 'Please, sign our terms or service and privacy policy';

// Request body
const request: TonhubSignRequest = {
    seed: sessionSeed, // Session Seed
    appPublicKey: walletConfig.appPublicKey, // Wallet's app public key
    timeout: 5 * 60 * 1000, // 5 minut timeout
    text: 'Hello world', // Text to sign, presented to the user.
    payload: payload // Optional serialized to base64 string payload cell
};
const response: TonhubSignResponse = await connector.requestSign(request);
if (response.type === 'rejected') {
    // Handle rejection
} else if (response.type === 'expired') {
    // Handle expiration
} else if (response.type === 'invalid_session') {
    // Handle expired or invalid session
} else if (response.type === 'success') {
    // Handle successful transaction
    const signature = response.signature;

    // You can check signature on the backend with TonhubConnector.verifySignatureResponse
    let correctSignature = TonhubConnector.verifySignatureResponse({ signature: signature, config: walletConfig });
} else {
    throw new Error('Impossible');
}

Check session state

If you need to check from time to time that your session is a valid you can call getSessionState method.

While it is possible to track session state we are NOT recommending to do so since you can always re-authenticate user if transaction or signature request requested and session became expired.

const session: TonhubSessionState = await connector.getSessionState(sessionId);