UNPKG

@custonomy/ton-js-bridge

Version:

TON Connect JS Bridge for Custonomy

375 lines (374 loc) 21.6 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const protocol_1 = require("@tonconnect/protocol"); const types_1 = require("./types"); const community_sdk_1 = require("@custonomy/community-sdk"); const events_1 = __importDefault(require("events")); const core_1 = require("@ton/core"); const ton_1 = require("@ton/ton"); const core_2 = require("@ton/core"); class CustonomyTonJsBridge { constructor(config, mode = types_1.WEB3ASY_MODE.WIDGET) { this.mode = mode; this.deviceInfo = { platform: 'browser', appName: 'Custonomy Web3asy', appVersion: '1.0.2', maxProtocolVersion: 2, features: ['SendTransaction'] }; this.protocolVersion = 2; this.isWalletBrowser = true; const { callback } = config; this._config = config; this._callback = callback; this._window = config.window; this._address = null; // Check if config contains API endpoint and API key if (!config.rpcEndPoint || !config.rpcEndPointApiKey) { throw new Error('API endpoint and API enpoint key are required in the configuration'); } // Initialize TonClient with the provided endpoint and API key this._client = new ton_1.TonClient({ endpoint: config.rpcEndPoint, apiKey: config.rpcEndPointApiKey, }); // this._pubKey = null; if (mode === types_1.WEB3ASY_MODE.NOWIDGET && !config.callback) throw new Error("callback is required for no widget mode"); if (mode === types_1.WEB3ASY_MODE.BACKEND && !config.session && !config.apiSecret) throw new Error("Either session or apiSecret must be provided for backend mode"); if (mode === types_1.WEB3ASY_MODE.WIDGET && !config.session) throw new Error("session must be provided for Widget mode"); if (mode === types_1.WEB3ASY_MODE.WIDGET && !config.window) throw new Error("window must be provided for Widget mode"); this._community = new community_sdk_1.Community({ endPoint: this._config.endPoint, apiKey: this._config.apiKey, apiSecret: this._config.apiSecret, eventDrivenMode: true }); this._event = new events_1.default(); this._community.on("completed", (id, txnId) => __awaiter(this, void 0, void 0, function* () { this._event.emit(`${id}-completed`, id, txnId); })); this._community.on("sent", (id, txnId) => __awaiter(this, void 0, void 0, function* () { this._event.emit(`${id}-sent`, id, txnId); })); this._community.on("error", (id, message) => { this._event.emit(`${id}-error`, id, message); }); this._community.on("cancelled", (id) => { this._event.emit(`${id}-cancelled`, id); }); this._community.on("new_address_generated", (id, newAddress) => __awaiter(this, void 0, void 0, function* () { this._event.emit(`${id}-new_address_generated`, id, newAddress); })); } connect(protocolVersion, message) { return __awaiter(this, void 0, void 0, function* () { switch (this.mode) { case types_1.WEB3ASY_MODE.WIDGET: return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; try { if (!((_a = this._window) === null || _a === void 0 ? void 0 : _a.custonomy)) throw new Error("Custonomy widget not found"); const result = yield ((_c = (_b = this._window) === null || _b === void 0 ? void 0 : _b.custonomy) === null || _c === void 0 ? void 0 : _c.connect(this._config.network, this._config.projectId, (_d = this._config.session) !== null && _d !== void 0 ? _d : "", this._config.endPoint, this._config.apiKey, this._config.callback, "TON" /* CustonomyCHAIN.TON */)); const { accountExisted } = result !== null && result !== void 0 ? result : {}; if (accountExisted) { const address = yield ((_f = (_e = this._window) === null || _e === void 0 ? void 0 : _e.custonomy) === null || _f === void 0 ? void 0 : _f.getAddress(this._config.projectId, (_g = this._config.session) !== null && _g !== void 0 ? _g : "", "TON" /* CustonomyCHAIN.TON */)); console.log("address", address); if (address === null) { const error = { event: 'connect_error', id: Date.now().valueOf(), payload: { code: protocol_1.CONNECT_EVENT_ERROR_CODES.BAD_REQUEST_ERROR, message: "Failed to get address" } }; reject(error); return; } this._address = convertTonAddressToRawAddress(address); // this._pubKey = address.publicKey; const result = { event: 'connect', id: Date.now().valueOf(), payload: { items: [{ name: 'ton_addr', address: this._address, network: this._config.network === types_1.WEB3ASY_NETWORK.TON_MAINNET ? protocol_1.CHAIN.MAINNET : protocol_1.CHAIN.TESTNET, walletStateInit: '', publicKey: '' }], device: this.deviceInfo } }; resolve(result); } else { const address = yield ((_j = (_h = this._window) === null || _h === void 0 ? void 0 : _h.custonomy) === null || _j === void 0 ? void 0 : _j.createUser(this._config.projectId, (_k = this._config.session) !== null && _k !== void 0 ? _k : "", "TON" /* CustonomyCHAIN.TON */)); if (address === null) { const error = { event: 'connect_error', id: Date.now().valueOf(), payload: { code: protocol_1.CONNECT_EVENT_ERROR_CODES.BAD_REQUEST_ERROR, message: "Failed to create user" } }; reject(error); return; } this._address = convertTonAddressToRawAddress(address); const result = { event: 'connect', id: Date.now().valueOf(), payload: { items: [{ name: 'ton_addr', address: this._address, network: this._config.network === types_1.WEB3ASY_NETWORK.TON_MAINNET ? protocol_1.CHAIN.MAINNET : protocol_1.CHAIN.TESTNET, walletStateInit: '', publicKey: '' }], device: this.deviceInfo } }; resolve(result); } } catch (ex) { const result = { event: 'connect_error', id: Date.now().valueOf(), payload: { code: protocol_1.CONNECT_EVENT_ERROR_CODES.BAD_REQUEST_ERROR, message: ex instanceof Error ? ex.message : String(ex) } }; reject(result); } })); break; case types_1.WEB3ASY_MODE.NOWIDGET: case types_1.WEB3ASY_MODE.BACKEND: return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { var _l, _m, _o; try { this._community.once('address_generated', (id, address) => { this._address = convertTonAddressToRawAddress(address); const result = { event: 'connect', id: Date.now().valueOf(), payload: { items: [{ name: 'ton_addr', address: this._address, network: this._config.network === types_1.WEB3ASY_NETWORK.TON_MAINNET ? protocol_1.CHAIN.MAINNET : protocol_1.CHAIN.TESTNET, walletStateInit: '', publicKey: '' }], device: this.deviceInfo } }; resolve(result); }); const addr = yield this._community.getAddress(this._config.projectId, (_l = this._config.session) !== null && _l !== void 0 ? _l : "", "TON" /* CustonomyCHAIN.TON */); if (addr == null) { const response = yield this._community.createUser(this._config.projectId, (_m = this._config.session) !== null && _m !== void 0 ? _m : "", "TON" /* CustonomyCHAIN.TON */); (_o = this._callback) === null || _o === void 0 ? void 0 : _o.call(this, Object.assign(Object.assign({}, response), { callbackURL: concatenateURL(this._config.endPoint, response === null || response === void 0 ? void 0 : response.callbackURL) })); } else { this._address = convertTonAddressToRawAddress(addr); const result = { event: 'connect', id: Date.now().valueOf(), payload: { items: [{ name: 'ton_addr', address: this._address, network: this._config.network === types_1.WEB3ASY_NETWORK.TON_MAINNET ? protocol_1.CHAIN.MAINNET : protocol_1.CHAIN.TESTNET, walletStateInit: '', publicKey: '' }], device: this.deviceInfo // Add this line } }; resolve(result); } } catch (ex) { const result = { event: 'connect_error', id: Date.now().valueOf(), payload: { code: protocol_1.CONNECT_EVENT_ERROR_CODES.BAD_REQUEST_ERROR, message: ex instanceof Error ? ex.message : String(ex) } }; resolve(result); } })); break; default: const defaultResult = { event: 'connect_error', id: Date.now().valueOf(), payload: { code: protocol_1.CONNECT_EVENT_ERROR_CODES.BAD_REQUEST_ERROR, message: "Unsupported mode" } }; return Promise.reject(defaultResult); } }); } disconnect() { } restoreConnection(session = null) { return __awaiter(this, void 0, void 0, function* () { // Attempt to restore the connection by calling connect try { // Create a dummy ConnectRequest object const dummyConnectRequest = { manifestUrl: '', items: [] }; if (session) { this._config.session = session; } return yield this.connect(this.protocolVersion, dummyConnectRequest); } catch (error) { // If restoration fails, return a connect error event throw error; } }); } send(message) { return __awaiter(this, void 0, void 0, function* () { // Check if this._address is available if (!this._address) { throw new Error('No address available. Please connect first.'); } //console.log("Sending message:", message); const mockResponse = { event: 'response', id: Date.now(), payload: { // Add appropriate payload based on the message type // This is just a placeholder success: true, data: 'Mock response data' } }; // return Promise.resolve(mockResponse); // Implementation for sending messages return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g; switch (message.method) { case 'sendTransaction': const messages = JSON.parse(message.params[0]).messages; for (const msg of messages) { const txnDetail = { type: 0, chainId: parseInt(this._config.network), from: (_a = this._address) !== null && _a !== void 0 ? _a : "", to: msg.address, // @ts-ignore value: (0, core_1.fromNano)(msg.amount), method: 'ton_signTransaction', data: (_b = msg.payload) !== null && _b !== void 0 ? _b : '', }; switch (this.mode) { case types_1.WEB3ASY_MODE.WIDGET: if (!((_c = this._window) === null || _c === void 0 ? void 0 : _c.custonomy)) throw new Error("Custonomy widget not found"); console.log("txnDetail", txnDetail, parseInt(this._config.network)); try { const api_result = yield ((_e = (_d = this._window) === null || _d === void 0 ? void 0 : _d.custonomy) === null || _e === void 0 ? void 0 : _e.requestTransaction({ method: 'eth_sendTransaction', params: [txnDetail], chainId: parseInt(this._config.network) })); console.log("api_result", api_result); const boc = Buffer.from(api_result.txnId, 'hex'); yield this._client.sendFile(boc); const bocCell = core_2.Cell.fromBoc(boc)[0]; const response = { event: 'response', id: Date.now(), payload: { success: true, data: { txnId: bocCell.hash().toString('hex') } } }; resolve(response); } catch (ex) { console.log("Error", ex); throw ex; } break; case types_1.WEB3ASY_MODE.NOWIDGET: case types_1.WEB3ASY_MODE.BACKEND: const api_response = yield this._community.submitTransaction(this._config.projectId, (_f = this._config.session) !== null && _f !== void 0 ? _f : "", txnDetail, false); this._event.once(`${api_response.id}-completed`, (id, txnId) => __awaiter(this, void 0, void 0, function* () { const boc = Buffer.from(txnId, 'hex'); yield this._client.sendFile(boc); const bocCell = core_2.Cell.fromBoc(boc)[0]; const response = { event: 'response', id: Date.now(), payload: { success: true, data: { txnId: bocCell.hash().toString('hex') } } }; resolve(response); })); (_g = this._callback) === null || _g === void 0 ? void 0 : _g.call(this, Object.assign(Object.assign({}, api_response), { callbackURL: concatenateURL(this._config.endPoint, api_response === null || api_response === void 0 ? void 0 : api_response.callbackURL) })); break; default: throw new Error('Unsupported mode'); } } break; default: throw new Error('Unsupported method'); } })); }); } listen(callback) { // Implementation for event listening return () => { // Cleanup logic }; } } // export declare const window: Web3asyWindow; function concatenateURL(baseURL, path) { if (!path) { return baseURL; } // Ensure the base URL ends with a slash const slashEndedBaseURL = baseURL.endsWith('/') ? baseURL : `${baseURL}/`; // Remove the starting slash from the path if it exists const slashStartedPath = path.startsWith('/') ? path.substring(1) : path; // Concatenate the two parts return slashEndedBaseURL + slashStartedPath; } function convertTonAddressToRawAddress(address) { const addr = core_1.Address.parse(address); console.log("addr", addr.toRawString()); return addr.toRawString(); } exports.default = CustonomyTonJsBridge;