UNPKG

@ton/ton

Version:

[![Version npm](https://img.shields.io/npm/v/ton.svg?logo=npm)](https://www.npmjs.com/package/ton)

455 lines (454 loc) 15.3 kB
"use strict"; /** * Copyright (c) Whales Corp. * All Rights Reserved. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.TonClient = void 0; const HttpApi_1 = require("./api/HttpApi"); const core_1 = require("@ton/core"); class TonClient { constructor(parameters) { this.parameters = { endpoint: parameters.endpoint }; this.api = new HttpApi_1.HttpApi(this.parameters.endpoint, { timeout: parameters.timeout, apiKey: parameters.apiKey, adapter: parameters.httpAdapter }); } /** * Get Address Balance * @param address address for balance check * @returns balance */ async getBalance(address) { return (await this.getContractState(address)).balance; } /** * Invoke get method * @param address contract address * @param name name of method * @param params optional parameters * @returns stack and gas_used field */ async runMethod(address, name, stack = []) { let res = await this.api.callGetMethod(address, name, stack); if (res.exit_code !== 0) { throw Error('Unable to execute get method. Got exit_code: ' + res.exit_code); } return { gas_used: res.gas_used, stack: parseStack(res.stack) }; } /** * Invoke get method * @param address contract address * @param name name of method * @param params optional parameters * @returns stack and gas_used field * @deprecated use runMethod instead */ async callGetMethod(address, name, stack = []) { return this.runMethod(address, name, stack); } /** * Invoke get method that returns error code instead of throwing error * @param address contract address * @param name name of method * @param params optional parameters * @returns stack and gas_used field */ async runMethodWithError(address, name, params = []) { let res = await this.api.callGetMethod(address, name, params); return { gas_used: res.gas_used, stack: parseStack(res.stack), exit_code: res.exit_code }; } /** * Invoke get method that returns error code instead of throwing error * @param address contract address * @param name name of method * @param params optional parameters * @returns stack and gas_used field * @deprecated use runMethodWithError instead */ async callGetMethodWithError(address, name, stack = []) { return this.runMethodWithError(address, name, stack); } /** * Get transactions * @param address address */ async getTransactions(address, opts) { // Fetch transactions let tx = await this.api.getTransactions(address, opts); let res = []; for (let r of tx) { res.push((0, core_1.loadTransaction)(core_1.Cell.fromBoc(Buffer.from(r.data, 'base64'))[0].beginParse())); } return res; } /** * Get transaction by it's id * @param address address * @param lt logical time * @param hash transaction hash * @returns transaction or null if not exist */ async getTransaction(address, lt, hash) { let res = await this.api.getTransaction(address, lt, hash); if (res) { return (0, core_1.loadTransaction)(core_1.Cell.fromBoc(Buffer.from(res.data, 'base64'))[0].beginParse()); } else { return null; } } /** * Locate outcoming transaction of destination address by incoming message * @param source message source address * @param destination message destination address * @param created_lt message's created lt * @returns transaction */ async tryLocateResultTx(source, destination, created_lt) { let res = await this.api.tryLocateResultTx(source, destination, created_lt); return (0, core_1.loadTransaction)(core_1.Cell.fromBase64(res.data).beginParse()); } /** * Locate incoming transaction of source address by outcoming message * @param source message source address * @param destination message destination address * @param created_lt message's created lt * @returns transaction */ async tryLocateSourceTx(source, destination, created_lt) { let res = await this.api.tryLocateSourceTx(source, destination, created_lt); return (0, core_1.loadTransaction)(core_1.Cell.fromBase64(res.data).beginParse()); } /** * Fetch latest masterchain info * @returns masterchain info */ async getMasterchainInfo() { let r = await this.api.getMasterchainInfo(); return { workchain: r.init.workchain, shard: r.last.shard, initSeqno: r.init.seqno, latestSeqno: r.last.seqno }; } /** * Fetch latest workchain shards * @param seqno masterchain seqno */ async getWorkchainShards(seqno) { let r = await this.api.getShards(seqno); return r.map((m) => ({ workchain: m.workchain, shard: m.shard, seqno: m.seqno })); } /** * Fetch transactions inf shards * @param workchain * @param seqno * @param shard */ async getShardTransactions(workchain, seqno, shard) { let tx = await this.api.getBlockTransactions(workchain, seqno, shard); if (tx.incomplete) { throw Error('Unsupported'); } return tx.transactions.map((v) => ({ account: core_1.Address.parseRaw(v.account), lt: v.lt, hash: v.hash })); } /** * Send message to a network * @param src source message */ async sendMessage(src) { const boc = (0, core_1.beginCell)() .store((0, core_1.storeMessage)(src)) .endCell() .toBoc(); await this.api.sendBoc(boc); } /** * Send file to a network * @param src source file */ async sendFile(src) { await this.api.sendBoc(src); } /** * Estimate fees for external message * @param address target address * @returns */ async estimateExternalMessageFee(address, args) { return await this.api.estimateFee(address, { body: args.body, initCode: args.initCode, initData: args.initData, ignoreSignature: args.ignoreSignature }); } /** * Send external message to contract * @param contract contract to send message * @param src message body */ async sendExternalMessage(contract, src) { if (await this.isContractDeployed(contract.address) || !contract.init) { const message = (0, core_1.external)({ to: contract.address, body: src }); await this.sendMessage(message); } else { const message = (0, core_1.external)({ to: contract.address, init: contract.init, body: src }); await this.sendMessage(message); } } /** * Check if contract is deployed * @param address addres to check * @returns true if contract is in active state */ async isContractDeployed(address) { return (await this.getContractState(address)).state === 'active'; } /** * Resolves contract state * @param address contract address */ async getContractState(address) { let info = await this.api.getAddressInformation(address); let balance = BigInt(info.balance); let state = info.state; return { balance, extra_currencies: info.extra_currencies, state, code: info.code !== '' ? Buffer.from(info.code, 'base64') : null, data: info.data !== '' ? Buffer.from(info.data, 'base64') : null, lastTransaction: info.last_transaction_id.lt !== '0' ? { lt: info.last_transaction_id.lt, hash: info.last_transaction_id.hash, } : null, blockId: { workchain: info.block_id.workchain, shard: info.block_id.shard, seqno: info.block_id.seqno }, timestampt: info.sync_utime }; } /** * Open contract * @param src source contract * @returns contract */ open(src) { return (0, core_1.openContract)(src, (args) => createProvider(this, args.address, args.init)); } /** * Create a provider * @param address address * @param init optional init * @returns provider */ provider(address, init) { return createProvider(this, address, init ?? null); } } exports.TonClient = TonClient; function parseStackEntry(x) { const typeName = x['@type']; switch (typeName) { case 'tvm.list': case 'tvm.tuple': return x.elements.map(parseStackEntry); case 'tvm.cell': return core_1.Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0]; case 'tvm.slice': return core_1.Cell.fromBoc(Buffer.from(x.bytes, 'base64'))[0]; case 'tvm.stackEntryCell': return parseStackEntry(x.cell); case 'tvm.stackEntrySlice': return parseStackEntry(x.slice); case 'tvm.stackEntryTuple': return parseStackEntry(x.tuple); case 'tvm.stackEntryList': return parseStackEntry(x.list); case 'tvm.stackEntryNumber': return parseStackEntry(x.number); case 'tvm.numberDecimal': return BigInt(x.number); default: throw Error('Unsupported item type: ' + typeName); } } function parseStackItem(s) { if (s[0] === 'num') { let val = s[1]; if (val.startsWith('-')) { return { type: 'int', value: -BigInt(val.slice(1)) }; } else { return { type: 'int', value: BigInt(val) }; } } else if (s[0] === 'null') { return { type: 'null' }; } else if (s[0] === 'cell') { return { type: 'cell', cell: core_1.Cell.fromBoc(Buffer.from(s[1].bytes, 'base64'))[0] }; } else if (s[0] === 'slice') { return { type: 'slice', cell: core_1.Cell.fromBoc(Buffer.from(s[1].bytes, 'base64'))[0] }; } else if (s[0] === 'builder') { return { type: 'builder', cell: core_1.Cell.fromBoc(Buffer.from(s[1].bytes, 'base64'))[0] }; } else if (s[0] === 'tuple' || s[0] === 'list') { if (s[1].elements.length === 0) { return { type: 'null' }; } return { type: 'tuple', items: s[1].elements.map(parseStackEntry) }; } else { throw Error('Unsupported stack item type: ' + s[0]); } } function parseStack(src) { let stack = []; for (let s of src) { stack.push(parseStackItem(s)); } return new core_1.TupleReader(stack); } function createProvider(client, address, init) { return { async getState() { let state = await client.getContractState(address); let balance = state.balance; let last = state.lastTransaction ? { lt: BigInt(state.lastTransaction.lt), hash: Buffer.from(state.lastTransaction.hash, 'base64') } : null; let ecMap = null; let storage; if (state.state === 'active') { storage = { type: 'active', code: state.code ? state.code : null, data: state.data ? state.data : null, }; } else if (state.state === 'uninitialized') { storage = { type: 'uninit', }; } else if (state.state === 'frozen') { storage = { type: 'frozen', stateHash: Buffer.alloc(0), }; } else { throw Error('Unsupported state'); } if (state.extra_currencies && state.extra_currencies.length > 0) { ecMap = {}; for (let ec of state.extra_currencies) { ecMap[ec.id] = BigInt(ec.amount); } } return { balance, extracurrency: ecMap, last, state: storage, }; }, async get(name, args) { if (typeof name !== 'string') { throw new Error('Method name must be a string for TonClient provider'); } let method = await client.runMethod(address, name, args); return { stack: method.stack }; }, async external(message) { // // Resolve init // let neededInit = null; if (init && !await client.isContractDeployed(address)) { neededInit = init; } // // Send package // const ext = (0, core_1.external)({ to: address, init: neededInit, body: message }); let boc = (0, core_1.beginCell)() .store((0, core_1.storeMessage)(ext)) .endCell() .toBoc(); await client.sendFile(boc); }, async internal(via, message) { // Resolve init let neededInit = null; if (init && (!await client.isContractDeployed(address))) { neededInit = init; } // Resolve bounce let bounce = true; if (message.bounce !== null && message.bounce !== undefined) { bounce = message.bounce; } // Resolve value let value; if (typeof message.value === 'string') { value = (0, core_1.toNano)(message.value); } else { value = message.value; } // Resolve body let body = null; if (typeof message.body === 'string') { body = (0, core_1.comment)(message.body); } else if (message.body) { body = message.body; } // Send internal message await via.send({ to: address, value, bounce, sendMode: message.sendMode, extracurrency: message.extracurrency, init: neededInit, body }); }, open(contract) { return (0, core_1.openContract)(contract, (args) => createProvider(client, args.address, args.init ?? null)); }, getTransactions(address, lt, hash, limit) { return client.getTransactions(address, { limit: limit ?? 100, lt: lt.toString(), hash: hash.toString('base64'), inclusive: true }); } }; }