diff --git a/src/connector/TonhubConnector.ts b/src/connector/TonhubConnector.ts index eaf40df..f7761fc 100644 --- a/src/connector/TonhubConnector.ts +++ b/src/connector/TonhubConnector.ts @@ -1,4 +1,3 @@ -import axios, { AxiosRequestConfig } from "axios"; import { getSecureRandomBytes, keyPairFromSeed } from "ton-crypto"; import { backoff } from "../utils/backoff"; import { toUrlSafe } from "../utils/toURLsafe"; @@ -8,7 +7,6 @@ import { Cell, Address, beginCell, CommentMessage, safeSign, contractAddress, sa import BN from 'bn.js'; import { WalletV4Source } from 'ton-contracts'; import { TonhubHttpTransport } from '../transport/TonhubHttpTransport'; -import { TonhubEmbeddedTransport } from '../transport/TonhubEmbeddedTransport'; const sessionStateCodec = t.union([ t.type({ @@ -168,15 +166,6 @@ function textToCell(src: string) { return res; } -export function autodiscoverTransport() { - if (TonhubEmbeddedTransport.isAvailable()) { - return new TonhubEmbeddedTransport(); - } - return new TonhubHttpTransport({ - endpoint: 'https://connect.tonhubapi.com' - }); -} - export class TonhubConnector { static extractPublicKey(config: { @@ -277,7 +266,7 @@ export class TonhubConnector { readonly network: 'mainnet' | 'sandbox'; readonly transport: Transport; - + constructor(args?: { network?: 'mainnet' | 'sandbox', transport?: Transport }) { let network: 'mainnet' | 'sandbox' = 'mainnet'; @@ -288,7 +277,7 @@ export class TonhubConnector { } this.network = network; - this.transport = args?.transport || autodiscoverTransport(); + this.transport = args?.transport || new TonhubHttpTransport(); } createNewSession = async (args: { name: string, url: string }): Promise => { @@ -306,8 +295,8 @@ export class TonhubConnector { name: args.name, url: args.url, }); - - + + if (!session.ok) { throw Error('Unable to create state'); } @@ -589,7 +578,7 @@ export class TonhubConnector { return { type: 'expired' }; } if (res.job !== boc) { - return { type: 'rejected' }; + return { type: 'rejected' }; } if (res.state === 'expired') { return { type: 'expired' }; diff --git a/src/connector/TonhubLocalConnector.ts b/src/connector/TonhubLocalConnector.ts new file mode 100644 index 0000000..7b17845 --- /dev/null +++ b/src/connector/TonhubLocalConnector.ts @@ -0,0 +1,140 @@ +import * as t from 'io-ts'; + +const configCodec = t.type({ + version: t.literal(1), + platform: t.union([t.literal('iOS'), t.literal('Android')]), + platformVersion: t.union([t.string, t.number]), + network: t.union([t.literal('sandbox'), t.literal('mainnet')]), + address: t.string, + publicKey: t.string, + walletConfig: t.string, + walletType: t.string, + signature: t.string, + time: t.number, + subkey: t.type({ + domain: t.string, + publicKey: t.string, + time: t.number, + signature: t.string + }) +}); + +export type TonhubLocalConfig = { + version: number, + network: 'sandbox' | 'mainnet', + address: string, + publicKey: string, + walletConfig: string, + walletType: string, + signature: string, + time: number, + subkey: { + domain: string, + publicKey: string, + time: number, + signature: string + } +}; + +export type TonhubLocalTransactionRequest = { + to: string, + value: string, + stateInit?: string | null | undefined, + text?: string | null | undefined, + payload?: string | null | undefined +}; + +export type TonhubLocalTransactionResponse = { + type: 'success', + response: string +} | { + type: 'rejected' +}; + +export class TonhubLocalConnector { + + static isAvailable() { + if (typeof window === 'undefined') { + return false; + } + if (!((window as any)['ton-x'])) { + return false; + } + let tx = ((window as any)['ton-x']); + if (tx.__IS_TON_X !== true) { + return false; + } + if (!configCodec.is(tx)) { + return false; + } + return true; + } + + readonly network: 'mainnet' | 'sandbox'; + readonly config: TonhubLocalConfig; + + #provider: (name: string, args: any) => Promise + + constructor(network: 'mainnet' | 'sandbox') { + if (typeof window === 'undefined') { + throw Error('Not running in browser'); + } + if (!((window as any)['ton-x'])) { + throw Error('Not running in dApp browser'); + } + let tx = ((window as any)['ton-x']); + if (tx.__IS_TON_X !== true) { + throw Error('Not running in dApp browser'); + } + let cfg = tx.config; + if (!configCodec.is(cfg)) { + throw Error('Not running in dApp browser'); + } + if (cfg.network !== network) { + throw Error('Invalid network'); + } + this.network = network; + this.config = { + version: cfg.version, + network: cfg.network, + address: cfg.address, + publicKey: cfg.publicKey, + walletConfig: cfg.walletConfig, + walletType: cfg.walletType, + signature: cfg.signature, + time: cfg.time, + subkey: { + domain: cfg.subkey.domain, + publicKey: cfg.subkey.publicKey, + time: cfg.subkey.time, + signature: cfg.subkey.signature + } + }; + this.#provider = tx.call; + Object.freeze(this.config.subkey); + Object.freeze(this.config); + Object.freeze(this); + } + + async requestTransaction(request: TonhubLocalTransactionRequest): Promise { + let res = await this.#provider('tx', { + network: this.network, + to: request.to, + value: request.value, + stateInit: request.stateInit ? request.stateInit : null, + text: request.text ? request.text : null, + payload: request.payload ? request.payload : null, + }); + if (res.type === 'ok') { + let d = res.data; + if (d.state === 'rejected') { + return { type: 'rejected' }; + } + if (d.state === 'sent') { + return { type: 'success', response: d.result }; + } + throw Error('Unknown reponse'); + } + throw Error(res.message); + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index f7a3b57..8c41fea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,4 +12,11 @@ export { TonhubSignRequest, TonhubSignResponse, TonhubWalletConfig -} from './connector/TonhubConnector'; \ No newline at end of file +} from './connector/TonhubConnector'; + +export { + TonhubLocalConnector, + TonhubLocalConfig, + TonhubLocalTransactionRequest, + TonhubLocalTransactionResponse +} from './connector/TonhubLocalConnector'; \ No newline at end of file diff --git a/src/transport/TonhubEmbeddedTransport.ts b/src/transport/TonhubEmbeddedTransport.ts deleted file mode 100644 index 79bb971..0000000 --- a/src/transport/TonhubEmbeddedTransport.ts +++ /dev/null @@ -1,25 +0,0 @@ - -export class TonhubEmbeddedTransport implements Transport { - call(method: string, args: TArgs): Promise { - let tonX = TonhubEmbeddedTransport.get(); - if (!tonX) { - throw new Error('ton-x not found'); - } - - return tonX.call(method, args); - } - - private static get() { - if (typeof window === 'undefined' || !(window as any)?.tonX) { - return null; - } - - return window && (window as any).tonX as { - call: (type: string, payload: TPayload) => Promise - }; - } - - static isAvailable() { - return this.get() !== null; - } -} \ No newline at end of file diff --git a/src/transport/TonhubHttpTransport.ts b/src/transport/TonhubHttpTransport.ts index c59c463..d36cf30 100644 --- a/src/transport/TonhubHttpTransport.ts +++ b/src/transport/TonhubHttpTransport.ts @@ -1,5 +1,4 @@ import axios from 'axios'; -import { string } from 'fp-ts'; export class TonhubHttpTransport implements Transport { private readonly _endpoint: string;