@custonomy/ton-js-bridge
Version:
TON Connect JS Bridge for Custonomy
375 lines (374 loc) • 21.6 kB
JavaScript
"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;