trx.js 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.Trx = void 0;
  4. const tslib_1 = require("tslib");
  5. const tronweb_js_1 = require("../tronweb.js");
  6. const index_js_1 = tslib_1.__importDefault(require("../utils/index.js"));
  7. const ethersUtils_js_1 = require("../utils/ethersUtils.js");
  8. const address_js_1 = require("../utils/address.js");
  9. const index_js_2 = require("../paramValidator/index.js");
  10. const transaction_js_1 = require("../utils/transaction.js");
  11. const crypto_js_1 = require("../utils/crypto.js");
  12. const TRX_MESSAGE_HEADER = '\x19TRON Signed Message:\n32';
  13. // it should be: '\x15TRON Signed Message:\n32';
  14. const ETH_MESSAGE_HEADER = '\x19Ethereum Signed Message:\n32';
  15. function toHex(value) {
  16. return tronweb_js_1.TronWeb.address.toHex(value);
  17. }
  18. class Trx {
  19. tronWeb;
  20. cache;
  21. validator;
  22. signMessage;
  23. sendAsset;
  24. send;
  25. sendTrx;
  26. broadcast;
  27. broadcastHex;
  28. signTransaction;
  29. constructor(tronWeb) {
  30. this.tronWeb = tronWeb;
  31. this.cache = {
  32. contracts: {},
  33. };
  34. this.validator = new index_js_2.Validator();
  35. this.signMessage = this.sign;
  36. this.sendAsset = this.sendToken;
  37. this.send = this.sendTransaction;
  38. this.sendTrx = this.sendTransaction;
  39. this.broadcast = this.sendRawTransaction;
  40. this.broadcastHex = this.sendHexTransaction;
  41. this.signTransaction = this.sign;
  42. }
  43. _parseToken(token) {
  44. return {
  45. ...token,
  46. name: this.tronWeb.toUtf8(token.name),
  47. abbr: token.abbr && this.tronWeb.toUtf8(token.abbr),
  48. description: token.description && this.tronWeb.toUtf8(token.description),
  49. url: token.url && this.tronWeb.toUtf8(token.url),
  50. };
  51. }
  52. getCurrentBlock() {
  53. return this.tronWeb.fullNode.request('wallet/getnowblock');
  54. }
  55. getConfirmedCurrentBlock() {
  56. return this.tronWeb.solidityNode.request('walletsolidity/getnowblock');
  57. }
  58. async getBlock(block = this.tronWeb.defaultBlock) {
  59. if (block === false) {
  60. throw new Error('No block identifier provided');
  61. }
  62. if (block == 'earliest')
  63. block = 0;
  64. if (block == 'latest')
  65. return this.getCurrentBlock();
  66. if (isNaN(+block) && index_js_1.default.isHex(block.toString()))
  67. return this.getBlockByHash(block);
  68. return this.getBlockByNumber(block);
  69. }
  70. async getBlockByHash(blockHash) {
  71. const block = await this.tronWeb.fullNode.request('wallet/getblockbyid', {
  72. value: blockHash,
  73. }, 'post');
  74. if (!Object.keys(block).length) {
  75. throw new Error('Block not found');
  76. }
  77. return block;
  78. }
  79. async getBlockByNumber(blockID) {
  80. if (!index_js_1.default.isInteger(blockID) || blockID < 0) {
  81. throw new Error('Invalid block number provided');
  82. }
  83. return this.tronWeb.fullNode
  84. .request('wallet/getblockbynum', {
  85. num: parseInt(blockID),
  86. }, 'post')
  87. .then((block) => {
  88. if (!Object.keys(block).length) {
  89. throw new Error('Block not found');
  90. }
  91. return block;
  92. });
  93. }
  94. async getBlockTransactionCount(block = this.tronWeb.defaultBlock) {
  95. const { transactions = [] } = await this.getBlock(block);
  96. return transactions.length;
  97. }
  98. async getTransactionFromBlock(block = this.tronWeb.defaultBlock, index) {
  99. const { transactions } = await this.getBlock(block);
  100. if (!transactions) {
  101. throw new Error('Transaction not found in block');
  102. }
  103. if (index >= 0 && index < transactions.length)
  104. return transactions[index];
  105. else
  106. throw new Error('Invalid transaction index provided');
  107. }
  108. async getTransactionsFromBlock(block = this.tronWeb.defaultBlock) {
  109. const { transactions } = await this.getBlock(block);
  110. if (!transactions) {
  111. throw new Error('Transaction not found in block');
  112. }
  113. return transactions;
  114. }
  115. async getTransaction(transactionID) {
  116. const transaction = await this.tronWeb.fullNode.request('wallet/gettransactionbyid', {
  117. value: transactionID,
  118. }, 'post');
  119. if (!Object.keys(transaction).length) {
  120. throw new Error('Transaction not found');
  121. }
  122. return transaction;
  123. }
  124. async getConfirmedTransaction(transactionID) {
  125. const transaction = await this.tronWeb.solidityNode.request('walletsolidity/gettransactionbyid', {
  126. value: transactionID,
  127. }, 'post');
  128. if (!Object.keys(transaction).length) {
  129. throw new Error('Transaction not found');
  130. }
  131. return transaction;
  132. }
  133. getUnconfirmedTransactionInfo(transactionID) {
  134. return this.tronWeb.fullNode.request('wallet/gettransactioninfobyid', { value: transactionID }, 'post');
  135. }
  136. getTransactionInfo(transactionID) {
  137. return this.tronWeb.solidityNode.request('walletsolidity/gettransactioninfobyid', { value: transactionID }, 'post');
  138. }
  139. getTransactionsToAddress(address = this.tronWeb.defaultAddress.hex, limit = 30, offset = 0) {
  140. return this.getTransactionsRelated(this.tronWeb.address.toHex(address), 'to', limit, offset);
  141. }
  142. getTransactionsFromAddress(address = this.tronWeb.defaultAddress.hex, limit = 30, offset = 0) {
  143. return this.getTransactionsRelated(this.tronWeb.address.toHex(address), 'from', limit, offset);
  144. }
  145. async getTransactionsRelated(address = this.tronWeb.defaultAddress.hex, direction = 'all', limit = 30, offset = 0) {
  146. if (this.tronWeb.fullnodeSatisfies('>=4.1.1')) {
  147. throw new Error('This api is not supported any more');
  148. }
  149. if (!['to', 'from', 'all'].includes(direction)) {
  150. throw new Error('Invalid direction provided: Expected "to", "from" or "all"');
  151. }
  152. if (direction == 'all') {
  153. const [from, to] = await Promise.all([
  154. this.getTransactionsRelated(address, 'from', limit, offset),
  155. this.getTransactionsRelated(address, 'to', limit, offset),
  156. ]);
  157. return [
  158. ...from.map((tx) => ((tx.direction = 'from'), tx)),
  159. ...to.map((tx) => ((tx.direction = 'to'), tx)),
  160. ].sort((a, b) => {
  161. return b.raw_data.timestamp - a.raw_data.timestamp;
  162. });
  163. }
  164. if (!this.tronWeb.isAddress(address)) {
  165. throw new Error('Invalid address provided');
  166. }
  167. if (!index_js_1.default.isInteger(limit) || limit < 0 || (offset && limit < 1)) {
  168. throw new Error('Invalid limit provided');
  169. }
  170. if (!index_js_1.default.isInteger(offset) || offset < 0) {
  171. throw new Error('Invalid offset provided');
  172. }
  173. address = this.tronWeb.address.toHex(address);
  174. return this.tronWeb.solidityNode
  175. .request(`walletextension/gettransactions${direction}this`, {
  176. account: {
  177. address,
  178. },
  179. offset,
  180. limit,
  181. }, 'post')
  182. .then(({ transaction }) => {
  183. return transaction;
  184. });
  185. }
  186. async getAccount(address = this.tronWeb.defaultAddress.hex) {
  187. if (!this.tronWeb.isAddress(address)) {
  188. throw new Error('Invalid address provided');
  189. }
  190. address = this.tronWeb.address.toHex(address);
  191. return this.tronWeb.solidityNode.request('walletsolidity/getaccount', {
  192. address,
  193. }, 'post');
  194. }
  195. getAccountById(id) {
  196. return this.getAccountInfoById(id, { confirmed: true });
  197. }
  198. async getAccountInfoById(id, options) {
  199. this.validator.notValid([
  200. {
  201. name: 'accountId',
  202. type: 'hex',
  203. value: id,
  204. },
  205. {
  206. name: 'accountId',
  207. type: 'string',
  208. lte: 32,
  209. gte: 8,
  210. value: id,
  211. },
  212. ]);
  213. if (id.startsWith('0x')) {
  214. id = id.slice(2);
  215. }
  216. return this.tronWeb[options.confirmed ? 'solidityNode' : 'fullNode'].request(`wallet${options.confirmed ? 'solidity' : ''}/getaccountbyid`, {
  217. account_id: id,
  218. }, 'post');
  219. }
  220. async getBalance(address = this.tronWeb.defaultAddress.hex) {
  221. const { balance = 0 } = await this.getAccount(address);
  222. return balance;
  223. }
  224. async getUnconfirmedAccount(address = this.tronWeb.defaultAddress.hex) {
  225. if (!this.tronWeb.isAddress(address)) {
  226. throw new Error('Invalid address provided');
  227. }
  228. address = this.tronWeb.address.toHex(address);
  229. return this.tronWeb.fullNode.request('wallet/getaccount', {
  230. address,
  231. }, 'post');
  232. }
  233. getUnconfirmedAccountById(id) {
  234. return this.getAccountInfoById(id, { confirmed: false });
  235. }
  236. async getUnconfirmedBalance(address = this.tronWeb.defaultAddress.hex) {
  237. const { balance = 0 } = await this.getUnconfirmedAccount(address);
  238. return balance;
  239. }
  240. async getBandwidth(address = this.tronWeb.defaultAddress.hex) {
  241. if (!this.tronWeb.isAddress(address)) {
  242. throw new Error('Invalid address provided');
  243. }
  244. address = this.tronWeb.address.toHex(address);
  245. return this.tronWeb.fullNode
  246. .request('wallet/getaccountnet', {
  247. address,
  248. }, 'post')
  249. .then(({ freeNetUsed = 0, freeNetLimit = 0, NetUsed = 0, NetLimit = 0 }) => {
  250. return freeNetLimit - freeNetUsed + (NetLimit - NetUsed);
  251. });
  252. }
  253. async getTokensIssuedByAddress(address = this.tronWeb.defaultAddress.hex) {
  254. if (!this.tronWeb.isAddress(address)) {
  255. throw new Error('Invalid address provided');
  256. }
  257. address = this.tronWeb.address.toHex(address);
  258. return this.tronWeb.fullNode
  259. .request('wallet/getassetissuebyaccount', {
  260. address,
  261. }, 'post')
  262. .then(({ assetIssue }) => {
  263. if (!assetIssue)
  264. return {};
  265. const tokens = assetIssue
  266. .map((token) => {
  267. return this._parseToken(token);
  268. })
  269. .reduce((tokens, token) => {
  270. return (tokens[token.name] = token), tokens;
  271. }, {});
  272. return tokens;
  273. });
  274. }
  275. async getTokenFromID(tokenID) {
  276. if (index_js_1.default.isInteger(tokenID))
  277. tokenID = tokenID.toString();
  278. if (!index_js_1.default.isString(tokenID) || !tokenID.length) {
  279. throw new Error('Invalid token ID provided');
  280. }
  281. return this.tronWeb.fullNode
  282. .request('wallet/getassetissuebyname', {
  283. value: this.tronWeb.fromUtf8(tokenID),
  284. }, 'post')
  285. .then((token) => {
  286. if (!token.name) {
  287. throw new Error('Token does not exist');
  288. }
  289. return this._parseToken(token);
  290. });
  291. }
  292. async listNodes() {
  293. const { nodes = [] } = await this.tronWeb.fullNode.request('wallet/listnodes');
  294. return nodes.map(({ address: { host, port } }) => `${this.tronWeb.toUtf8(host)}:${port}`);
  295. }
  296. async getBlockRange(start = 0, end = 30) {
  297. if (!index_js_1.default.isInteger(start) || start < 0) {
  298. throw new Error('Invalid start of range provided');
  299. }
  300. if (!index_js_1.default.isInteger(end) || end < start) {
  301. throw new Error('Invalid end of range provided');
  302. }
  303. if (end + 1 - start > 100) {
  304. throw new Error('Invalid range size, which should be no more than 100.');
  305. }
  306. return this.tronWeb.fullNode
  307. .request('wallet/getblockbylimitnext', {
  308. startNum: parseInt(start),
  309. endNum: parseInt(end) + 1,
  310. }, 'post')
  311. .then(({ block = [] }) => block);
  312. }
  313. async listSuperRepresentatives() {
  314. const { witnesses = [] } = await this.tronWeb.fullNode.request('wallet/listwitnesses');
  315. return witnesses;
  316. }
  317. async listTokens(limit = 0, offset = 0) {
  318. if (!index_js_1.default.isInteger(limit) || limit < 0 || (offset && limit < 1)) {
  319. throw new Error('Invalid limit provided');
  320. }
  321. if (!index_js_1.default.isInteger(offset) || offset < 0) {
  322. throw new Error('Invalid offset provided');
  323. }
  324. if (!limit) {
  325. return this.tronWeb.fullNode
  326. .request('wallet/getassetissuelist')
  327. .then(({ assetIssue = [] }) => assetIssue.map((token) => this._parseToken(token)));
  328. }
  329. return this.tronWeb.fullNode
  330. .request('wallet/getpaginatedassetissuelist', {
  331. offset: parseInt(offset),
  332. limit: parseInt(limit),
  333. }, 'post')
  334. .then(({ assetIssue = [] }) => assetIssue.map((token) => this._parseToken(token)));
  335. }
  336. async timeUntilNextVoteCycle() {
  337. const { num = -1 } = await this.tronWeb.fullNode.request('wallet/getnextmaintenancetime');
  338. if (num == -1) {
  339. throw new Error('Failed to get time until next vote cycle');
  340. }
  341. return Math.floor(num / 1000);
  342. }
  343. async getContract(contractAddress) {
  344. if (!this.tronWeb.isAddress(contractAddress)) {
  345. throw new Error('Invalid contract address provided');
  346. }
  347. if (this.cache.contracts[contractAddress]) {
  348. return this.cache.contracts[contractAddress];
  349. }
  350. contractAddress = this.tronWeb.address.toHex(contractAddress);
  351. const contract = await this.tronWeb.fullNode.request('wallet/getcontract', {
  352. value: contractAddress,
  353. });
  354. if (contract.Error) {
  355. throw new Error('Contract does not exist');
  356. }
  357. this.cache.contracts[contractAddress] = contract;
  358. return contract;
  359. }
  360. ecRecover(transaction) {
  361. return Trx.ecRecover(transaction);
  362. }
  363. static ecRecover(transaction) {
  364. if (!(0, transaction_js_1.txCheck)(transaction)) {
  365. throw new Error('Invalid transaction');
  366. }
  367. if (!transaction.signature?.length) {
  368. throw new Error('Transaction is not signed');
  369. }
  370. if (transaction.signature.length === 1) {
  371. const tronAddress = (0, crypto_js_1.ecRecover)(transaction.txID, transaction.signature[0]);
  372. return tronweb_js_1.TronWeb.address.fromHex(tronAddress);
  373. }
  374. return transaction.signature.map((sig) => {
  375. const tronAddress = (0, crypto_js_1.ecRecover)(transaction.txID, sig);
  376. return tronweb_js_1.TronWeb.address.fromHex(tronAddress);
  377. });
  378. }
  379. async verifyMessage(message, signature, address = this.tronWeb.defaultAddress.base58, useTronHeader = true) {
  380. if (!index_js_1.default.isHex(message)) {
  381. throw new Error('Expected hex message input');
  382. }
  383. if (Trx.verifySignature(message, address, signature, useTronHeader)) {
  384. return true;
  385. }
  386. throw new Error('Signature does not match');
  387. }
  388. static verifySignature(message, address, signature, useTronHeader = true) {
  389. message = message.replace(/^0x/, '');
  390. const messageBytes = [
  391. ...(0, ethersUtils_js_1.toUtf8Bytes)(useTronHeader ? TRX_MESSAGE_HEADER : ETH_MESSAGE_HEADER),
  392. ...index_js_1.default.code.hexStr2byteArray(message),
  393. ];
  394. const messageDigest = (0, ethersUtils_js_1.keccak256)(new Uint8Array(messageBytes));
  395. const recovered = (0, ethersUtils_js_1.recoverAddress)(messageDigest, ethersUtils_js_1.Signature.from(`0x${signature.replace(/^0x/, '')}`));
  396. const tronAddress = address_js_1.ADDRESS_PREFIX + recovered.substr(2);
  397. const base58Address = tronweb_js_1.TronWeb.address.fromHex(tronAddress);
  398. return base58Address == tronweb_js_1.TronWeb.address.fromHex(address);
  399. }
  400. async verifyMessageV2(message, signature) {
  401. return Trx.verifyMessageV2(message, signature);
  402. }
  403. static verifyMessageV2(message, signature) {
  404. return index_js_1.default.message.verifyMessage(message, signature);
  405. }
  406. verifyTypedData(domain, types, value, signature, address = this.tronWeb.defaultAddress.base58) {
  407. if (Trx.verifyTypedData(domain, types, value, signature, address))
  408. return true;
  409. throw new Error('Signature does not match');
  410. }
  411. static verifyTypedData(domain, types, value, signature, address) {
  412. const messageDigest = index_js_1.default._TypedDataEncoder.hash(domain, types, value);
  413. const recovered = (0, ethersUtils_js_1.recoverAddress)(messageDigest, ethersUtils_js_1.Signature.from(`0x${signature.replace(/^0x/, '')}`));
  414. const tronAddress = address_js_1.ADDRESS_PREFIX + recovered.substr(2);
  415. const base58Address = tronweb_js_1.TronWeb.address.fromHex(tronAddress);
  416. return base58Address == tronweb_js_1.TronWeb.address.fromHex(address);
  417. }
  418. async sign(transaction, privateKey = this.tronWeb.defaultPrivateKey, useTronHeader = true, multisig = false) {
  419. // Message signing
  420. if (index_js_1.default.isString(transaction)) {
  421. if (!index_js_1.default.isHex(transaction)) {
  422. throw new Error('Expected hex message input');
  423. }
  424. return Trx.signString(transaction, privateKey, useTronHeader);
  425. }
  426. if (!index_js_1.default.isObject(transaction)) {
  427. throw new Error('Invalid transaction provided');
  428. }
  429. if (!multisig && transaction.signature) {
  430. throw new Error('Transaction is already signed');
  431. }
  432. if (!multisig) {
  433. const address = this.tronWeb.address
  434. .toHex(this.tronWeb.address.fromPrivateKey(privateKey))
  435. .toLowerCase();
  436. if (address !== this.tronWeb.address.toHex(transaction.raw_data.contract[0].parameter.value.owner_address)) {
  437. throw new Error('Private key does not match address in transaction');
  438. }
  439. if (!(0, transaction_js_1.txCheck)(transaction)) {
  440. throw new Error('Invalid transaction');
  441. }
  442. }
  443. return index_js_1.default.crypto.signTransaction(privateKey, transaction);
  444. }
  445. static signString(message, privateKey, useTronHeader = true) {
  446. message = message.replace(/^0x/, '');
  447. const value = `0x${privateKey.replace(/^0x/, '')}`;
  448. const signingKey = new ethersUtils_js_1.SigningKey(value);
  449. const messageBytes = [
  450. ...(0, ethersUtils_js_1.toUtf8Bytes)(useTronHeader ? TRX_MESSAGE_HEADER : ETH_MESSAGE_HEADER),
  451. ...index_js_1.default.code.hexStr2byteArray(message),
  452. ];
  453. const messageDigest = (0, ethersUtils_js_1.keccak256)(new Uint8Array(messageBytes));
  454. const signature = signingKey.sign(messageDigest);
  455. const signatureHex = ['0x', signature.r.substring(2), signature.s.substring(2), Number(signature.v).toString(16)].join('');
  456. return signatureHex;
  457. }
  458. /**
  459. * sign message v2 for verified header length
  460. *
  461. * @param {message to be signed, should be Bytes or string} message
  462. * @param {privateKey for signature} privateKey
  463. * @param {reserved} options
  464. */
  465. signMessageV2(message, privateKey = this.tronWeb.defaultPrivateKey) {
  466. return Trx.signMessageV2(message, privateKey);
  467. }
  468. static signMessageV2(message, privateKey) {
  469. return index_js_1.default.message.signMessage(message, privateKey);
  470. }
  471. _signTypedData(domain, types, value, privateKey = this.tronWeb.defaultPrivateKey) {
  472. return Trx._signTypedData(domain, types, value, privateKey);
  473. }
  474. static _signTypedData(domain, types, value, privateKey) {
  475. return index_js_1.default.crypto._signTypedData(domain, types, value, privateKey);
  476. }
  477. async multiSign(transaction, privateKey = this.tronWeb.defaultPrivateKey, permissionId = 0) {
  478. if (!index_js_1.default.isObject(transaction) || !transaction.raw_data || !transaction.raw_data.contract) {
  479. throw new Error('Invalid transaction provided');
  480. }
  481. // If owner permission or permission id exists in transaction, do sign directly
  482. // If no permission id inside transaction or user passes permission id, use old way to reset permission id
  483. if (!transaction.raw_data.contract[0].Permission_id && permissionId > 0) {
  484. // set permission id
  485. transaction.raw_data.contract[0].Permission_id = permissionId;
  486. // check if private key insides permission list
  487. const address = this.tronWeb.address
  488. .toHex(this.tronWeb.address.fromPrivateKey(privateKey))
  489. .toLowerCase();
  490. const signWeight = await this.getSignWeight(transaction, permissionId);
  491. if (signWeight.result.code === 'PERMISSION_ERROR') {
  492. throw new Error(signWeight.result.message);
  493. }
  494. let foundKey = false;
  495. signWeight.permission.keys.map((key) => {
  496. if (key.address === address)
  497. foundKey = true;
  498. });
  499. if (!foundKey) {
  500. throw new Error(privateKey + ' has no permission to sign');
  501. }
  502. if (signWeight.approved_list && signWeight.approved_list.indexOf(address) != -1) {
  503. throw new Error(privateKey + ' already sign transaction');
  504. }
  505. // reset transaction
  506. if (signWeight.transaction && signWeight.transaction.transaction) {
  507. transaction = signWeight.transaction.transaction;
  508. if (permissionId > 0) {
  509. transaction.raw_data.contract[0].Permission_id = permissionId;
  510. }
  511. }
  512. else {
  513. throw new Error('Invalid transaction provided');
  514. }
  515. }
  516. // sign
  517. if (!(0, transaction_js_1.txCheck)(transaction)) {
  518. throw new Error('Invalid transaction');
  519. }
  520. return index_js_1.default.crypto.signTransaction(privateKey, transaction);
  521. }
  522. async getApprovedList(transaction) {
  523. if (!index_js_1.default.isObject(transaction)) {
  524. throw new Error('Invalid transaction provided');
  525. }
  526. return this.tronWeb.fullNode.request('wallet/getapprovedlist', transaction, 'post');
  527. }
  528. async getSignWeight(transaction, permissionId) {
  529. if (!index_js_1.default.isObject(transaction) || !transaction.raw_data || !transaction.raw_data.contract)
  530. throw new Error('Invalid transaction provided');
  531. if (index_js_1.default.isInteger(permissionId)) {
  532. transaction.raw_data.contract[0].Permission_id = parseInt(permissionId);
  533. }
  534. else if (typeof transaction.raw_data.contract[0].Permission_id !== 'number') {
  535. transaction.raw_data.contract[0].Permission_id = 0;
  536. }
  537. return this.tronWeb.fullNode.request('wallet/getsignweight', transaction, 'post');
  538. }
  539. async sendRawTransaction(signedTransaction) {
  540. if (!index_js_1.default.isObject(signedTransaction)) {
  541. throw new Error('Invalid transaction provided');
  542. }
  543. if (!signedTransaction.signature || !index_js_1.default.isArray(signedTransaction.signature)) {
  544. throw new Error('Transaction is not signed');
  545. }
  546. const result = await this.tronWeb.fullNode.request('wallet/broadcasttransaction', signedTransaction, 'post');
  547. return {
  548. ...result,
  549. transaction: signedTransaction,
  550. };
  551. }
  552. async sendHexTransaction(signedHexTransaction) {
  553. if (!index_js_1.default.isHex(signedHexTransaction)) {
  554. throw new Error('Invalid hex transaction provided');
  555. }
  556. const params = {
  557. transaction: signedHexTransaction,
  558. };
  559. const result = await this.tronWeb.fullNode.request('wallet/broadcasthex', params, 'post');
  560. if (result.result) {
  561. return {
  562. ...result,
  563. transaction: JSON.parse(result.transaction),
  564. hexTransaction: signedHexTransaction,
  565. };
  566. }
  567. return result;
  568. }
  569. async sendTransaction(to, amount, options = {}) {
  570. if (typeof options === 'string')
  571. options = { privateKey: options };
  572. if (!this.tronWeb.isAddress(to)) {
  573. throw new Error('Invalid recipient provided');
  574. }
  575. if (!index_js_1.default.isInteger(amount) || amount <= 0) {
  576. throw new Error('Invalid amount provided');
  577. }
  578. options = {
  579. privateKey: this.tronWeb.defaultPrivateKey,
  580. address: this.tronWeb.defaultAddress.hex,
  581. ...options,
  582. };
  583. if (!options.privateKey && !options.address) {
  584. throw new Error('Function requires either a private key or address to be set');
  585. }
  586. const address = options.privateKey ? this.tronWeb.address.fromPrivateKey(options.privateKey) : options.address;
  587. const transaction = await this.tronWeb.transactionBuilder.sendTrx(to, amount, address);
  588. const signedTransaction = await this.sign(transaction, options.privateKey);
  589. const result = await this.sendRawTransaction(signedTransaction);
  590. return result;
  591. }
  592. async sendToken(to, amount, tokenID, options = {}) {
  593. if (typeof options === 'string')
  594. options = { privateKey: options };
  595. if (!this.tronWeb.isAddress(to)) {
  596. throw new Error('Invalid recipient provided');
  597. }
  598. if (!index_js_1.default.isInteger(amount) || amount <= 0) {
  599. throw new Error('Invalid amount provided');
  600. }
  601. if (index_js_1.default.isInteger(tokenID))
  602. tokenID = tokenID.toString();
  603. if (!index_js_1.default.isString(tokenID)) {
  604. throw new Error('Invalid token ID provided');
  605. }
  606. options = {
  607. privateKey: this.tronWeb.defaultPrivateKey,
  608. address: this.tronWeb.defaultAddress.hex,
  609. ...options,
  610. };
  611. if (!options.privateKey && !options.address) {
  612. throw new Error('Function requires either a private key or address to be set');
  613. }
  614. const address = options.privateKey ? this.tronWeb.address.fromPrivateKey(options.privateKey) : options.address;
  615. const transaction = await this.tronWeb.transactionBuilder.sendToken(to, amount, tokenID, address);
  616. const signedTransaction = await this.sign(transaction, options.privateKey);
  617. const result = await this.sendRawTransaction(signedTransaction);
  618. return result;
  619. }
  620. /**
  621. * Freezes an amount of TRX.
  622. * Will give bandwidth OR Energy and TRON Power(voting rights)
  623. * to the owner of the frozen tokens.
  624. *
  625. * @param amount - is the number of frozen trx
  626. * @param duration - is the duration in days to be frozen
  627. * @param resource - is the type, must be either "ENERGY" or "BANDWIDTH"
  628. * @param options
  629. */
  630. async freezeBalance(amount = 0, duration = 3, resource = 'BANDWIDTH', options = {}, receiverAddress) {
  631. if (typeof options === 'string')
  632. options = { privateKey: options };
  633. if (!['BANDWIDTH', 'ENERGY'].includes(resource)) {
  634. throw new Error('Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"');
  635. }
  636. if (!index_js_1.default.isInteger(amount) || amount <= 0) {
  637. throw new Error('Invalid amount provided');
  638. }
  639. if (!index_js_1.default.isInteger(duration) || duration < 3) {
  640. throw new Error('Invalid duration provided, minimum of 3 days');
  641. }
  642. options = {
  643. privateKey: this.tronWeb.defaultPrivateKey,
  644. address: this.tronWeb.defaultAddress.hex,
  645. ...options,
  646. };
  647. if (!options.privateKey && !options.address) {
  648. throw new Error('Function requires either a private key or address to be set');
  649. }
  650. const address = options.privateKey ? this.tronWeb.address.fromPrivateKey(options.privateKey) : options.address;
  651. const freezeBalance = await this.tronWeb.transactionBuilder.freezeBalance(amount, duration, resource, address, receiverAddress);
  652. const signedTransaction = await this.sign(freezeBalance, options.privateKey);
  653. const result = await this.sendRawTransaction(signedTransaction);
  654. return result;
  655. }
  656. /**
  657. * Unfreeze TRX that has passed the minimum freeze duration.
  658. * Unfreezing will remove bandwidth and TRON Power.
  659. *
  660. * @param resource - is the type, must be either "ENERGY" or "BANDWIDTH"
  661. * @param options
  662. */
  663. async unfreezeBalance(resource = 'BANDWIDTH', options = {}, receiverAddress) {
  664. if (typeof options === 'string')
  665. options = { privateKey: options };
  666. if (!['BANDWIDTH', 'ENERGY'].includes(resource)) {
  667. throw new Error('Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"');
  668. }
  669. options = {
  670. privateKey: this.tronWeb.defaultPrivateKey,
  671. address: this.tronWeb.defaultAddress.hex,
  672. ...options,
  673. };
  674. if (!options.privateKey && !options.address) {
  675. throw new Error('Function requires either a private key or address to be set');
  676. }
  677. const address = options.privateKey ? this.tronWeb.address.fromPrivateKey(options.privateKey) : options.address;
  678. const unfreezeBalance = await this.tronWeb.transactionBuilder.unfreezeBalance(resource, address, receiverAddress);
  679. const signedTransaction = await this.sign(unfreezeBalance, options.privateKey);
  680. const result = await this.sendRawTransaction(signedTransaction);
  681. return result;
  682. }
  683. /**
  684. * Modify account name
  685. * Note: Username is allowed to edit only once.
  686. *
  687. * @param privateKey - Account private Key
  688. * @param accountName - name of the account
  689. *
  690. * @return modified Transaction Object
  691. */
  692. async updateAccount(accountName, options = {}) {
  693. if (typeof options === 'string')
  694. options = { privateKey: options };
  695. if (!index_js_1.default.isString(accountName) || !accountName.length) {
  696. throw new Error('Name must be a string');
  697. }
  698. options = {
  699. privateKey: this.tronWeb.defaultPrivateKey,
  700. address: this.tronWeb.defaultAddress.hex,
  701. ...options,
  702. };
  703. if (!options.privateKey && !options.address)
  704. throw Error('Function requires either a private key or address to be set');
  705. const address = options.privateKey ? this.tronWeb.address.fromPrivateKey(options.privateKey) : options.address;
  706. const updateAccount = await this.tronWeb.transactionBuilder.updateAccount(accountName, address);
  707. const signedTransaction = await this.sign(updateAccount, options.privateKey);
  708. const result = await this.sendRawTransaction(signedTransaction);
  709. return result;
  710. }
  711. /**
  712. * Gets a network modification proposal by ID.
  713. */
  714. async getProposal(proposalID) {
  715. if (!index_js_1.default.isInteger(proposalID) || proposalID < 0) {
  716. throw new Error('Invalid proposalID provided');
  717. }
  718. return this.tronWeb.fullNode.request('wallet/getproposalbyid', {
  719. id: parseInt(proposalID),
  720. }, 'post');
  721. }
  722. /**
  723. * Lists all network modification proposals.
  724. */
  725. async listProposals() {
  726. const { proposals = [] } = await this.tronWeb.fullNode.request('wallet/listproposals', {}, 'post');
  727. return proposals;
  728. }
  729. /**
  730. * Lists all parameters available for network modification proposals.
  731. */
  732. async getChainParameters() {
  733. const { chainParameter = [] } = await this.tronWeb.fullNode.request('wallet/getchainparameters', {}, 'post');
  734. return chainParameter;
  735. }
  736. /**
  737. * Get the account resources
  738. */
  739. async getAccountResources(address = this.tronWeb.defaultAddress.hex) {
  740. if (!this.tronWeb.isAddress(address)) {
  741. throw new Error('Invalid address provided');
  742. }
  743. return this.tronWeb.fullNode.request('wallet/getaccountresource', {
  744. address: this.tronWeb.address.toHex(address),
  745. }, 'post');
  746. }
  747. /**
  748. * Query the amount of resources of a specific resourceType delegated by fromAddress to toAddress
  749. */
  750. async getDelegatedResourceV2(fromAddress = this.tronWeb.defaultAddress.hex, toAddress = this.tronWeb.defaultAddress.hex, options = { confirmed: true }) {
  751. if (!this.tronWeb.isAddress(fromAddress)) {
  752. throw new Error('Invalid address provided');
  753. }
  754. if (!this.tronWeb.isAddress(toAddress)) {
  755. throw new Error('Invalid address provided');
  756. }
  757. return this.tronWeb[options.confirmed ? 'solidityNode' : 'fullNode'].request(`wallet${options.confirmed ? 'solidity' : ''}/getdelegatedresourcev2`, {
  758. fromAddress: toHex(fromAddress),
  759. toAddress: toHex(toAddress),
  760. }, 'post');
  761. }
  762. /**
  763. * Query the resource delegation index by an account
  764. */
  765. async getDelegatedResourceAccountIndexV2(address = this.tronWeb.defaultAddress.hex, options = { confirmed: true }) {
  766. if (!this.tronWeb.isAddress(address)) {
  767. throw new Error('Invalid address provided');
  768. }
  769. return this.tronWeb[options.confirmed ? 'solidityNode' : 'fullNode'].request(`wallet${options.confirmed ? 'solidity' : ''}/getdelegatedresourceaccountindexv2`, {
  770. value: toHex(address),
  771. }, 'post');
  772. }
  773. /**
  774. * Query the amount of delegatable resources of the specified resource Type for target address, unit is sun.
  775. */
  776. async getCanDelegatedMaxSize(address = this.tronWeb.defaultAddress.hex, resource = 'BANDWIDTH', options = { confirmed: true }) {
  777. if (!this.tronWeb.isAddress(address)) {
  778. throw new Error('Invalid address provided');
  779. }
  780. this.validator.notValid([
  781. {
  782. name: 'resource',
  783. type: 'resource',
  784. value: resource,
  785. msg: 'Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"',
  786. },
  787. ]);
  788. return this.tronWeb[options.confirmed ? 'solidityNode' : 'fullNode'].request(`wallet${options.confirmed ? 'solidity' : ''}/getcandelegatedmaxsize`, {
  789. owner_address: toHex(address),
  790. type: resource === 'ENERGY' ? 1 : 0,
  791. }, 'post');
  792. }
  793. /**
  794. * Remaining times of available unstaking API
  795. */
  796. async getAvailableUnfreezeCount(address = this.tronWeb.defaultAddress.hex, options = { confirmed: true }) {
  797. if (!this.tronWeb.isAddress(address)) {
  798. throw new Error('Invalid address provided');
  799. }
  800. return this.tronWeb[options.confirmed ? 'solidityNode' : 'fullNode'].request(`wallet${options.confirmed ? 'solidity' : ''}/getavailableunfreezecount`, {
  801. owner_address: toHex(address),
  802. }, 'post');
  803. }
  804. /**
  805. * Query the withdrawable balance at the specified timestamp
  806. */
  807. async getCanWithdrawUnfreezeAmount(address = this.tronWeb.defaultAddress.hex, timestamp = Date.now(), options = { confirmed: true }) {
  808. if (!this.tronWeb.isAddress(address)) {
  809. throw new Error('Invalid address provided');
  810. }
  811. if (!index_js_1.default.isInteger(timestamp) || timestamp < 0) {
  812. throw new Error('Invalid timestamp provided');
  813. }
  814. return this.tronWeb[options.confirmed ? 'solidityNode' : 'fullNode'].request(`wallet${options.confirmed ? 'solidity' : ''}/getcanwithdrawunfreezeamount`, {
  815. owner_address: toHex(address),
  816. timestamp: timestamp,
  817. }, 'post');
  818. }
  819. /**
  820. * Get the exchange ID.
  821. */
  822. async getExchangeByID(exchangeID) {
  823. if (!index_js_1.default.isInteger(exchangeID) || exchangeID < 0) {
  824. throw new Error('Invalid exchangeID provided');
  825. }
  826. return this.tronWeb.fullNode.request('wallet/getexchangebyid', {
  827. id: exchangeID,
  828. }, 'post');
  829. }
  830. /**
  831. * Lists the exchanges
  832. */
  833. async listExchanges() {
  834. return this.tronWeb.fullNode
  835. .request('wallet/listexchanges', {}, 'post')
  836. .then(({ exchanges = [] }) => exchanges);
  837. }
  838. /**
  839. * Lists all network modification proposals.
  840. */
  841. async listExchangesPaginated(limit = 10, offset = 0) {
  842. return this.tronWeb.fullNode
  843. .request('wallet/getpaginatedexchangelist', {
  844. limit,
  845. offset,
  846. }, 'post')
  847. .then(({ exchanges = [] }) => exchanges);
  848. }
  849. /**
  850. * Get info about thre node
  851. */
  852. async getNodeInfo() {
  853. return this.tronWeb.fullNode.request('wallet/getnodeinfo', {}, 'post');
  854. }
  855. async getTokenListByName(tokenID) {
  856. if (index_js_1.default.isInteger(tokenID))
  857. tokenID = tokenID.toString();
  858. if (!index_js_1.default.isString(tokenID) || !tokenID.length) {
  859. throw new Error('Invalid token ID provided');
  860. }
  861. return this.tronWeb.fullNode
  862. .request('wallet/getassetissuelistbyname', {
  863. value: this.tronWeb.fromUtf8(tokenID),
  864. }, 'post')
  865. .then((token) => {
  866. if (Array.isArray(token.assetIssue)) {
  867. return token.assetIssue.map((t) => this._parseToken(t));
  868. }
  869. else if (!token.name) {
  870. throw new Error('Token does not exist');
  871. }
  872. return this._parseToken(token);
  873. });
  874. }
  875. getTokenByID(tokenID) {
  876. if (index_js_1.default.isInteger(tokenID))
  877. tokenID = tokenID.toString();
  878. if (!index_js_1.default.isString(tokenID) || !tokenID.length) {
  879. throw new Error('Invalid token ID provided');
  880. }
  881. return this.tronWeb.fullNode
  882. .request('wallet/getassetissuebyid', {
  883. value: tokenID,
  884. }, 'post')
  885. .then((token) => {
  886. if (!token.name) {
  887. throw new Error('Token does not exist');
  888. }
  889. return this._parseToken(token);
  890. });
  891. }
  892. async getReward(address, options = {}) {
  893. options.confirmed = true;
  894. return this._getReward(address, options);
  895. }
  896. async getUnconfirmedReward(address, options = {}) {
  897. options.confirmed = false;
  898. return this._getReward(address, options);
  899. }
  900. async getBrokerage(address, options = {}) {
  901. options.confirmed = true;
  902. return this._getBrokerage(address, options);
  903. }
  904. async getUnconfirmedBrokerage(address, options = {}) {
  905. options.confirmed = false;
  906. return this._getBrokerage(address, options);
  907. }
  908. async _getReward(address = this.tronWeb.defaultAddress.hex, options) {
  909. this.validator.notValid([
  910. {
  911. name: 'origin',
  912. type: 'address',
  913. value: address,
  914. },
  915. ]);
  916. const data = {
  917. address: toHex(address),
  918. };
  919. return this.tronWeb[options.confirmed ? 'solidityNode' : 'fullNode']
  920. .request(`wallet${options.confirmed ? 'solidity' : ''}/getReward`, data, 'post')
  921. .then((result = { reward: undefined }) => {
  922. if (typeof result.reward === 'undefined') {
  923. throw new Error('Not found.');
  924. }
  925. return result.reward;
  926. });
  927. }
  928. async _getBrokerage(address = this.tronWeb.defaultAddress.hex, options) {
  929. this.validator.notValid([
  930. {
  931. name: 'origin',
  932. type: 'address',
  933. value: address,
  934. },
  935. ]);
  936. const data = {
  937. address: toHex(address),
  938. };
  939. return this.tronWeb[options.confirmed ? 'solidityNode' : 'fullNode']
  940. .request(`wallet${options.confirmed ? 'solidity' : ''}/getBrokerage`, data, 'post')
  941. .then((result = {}) => {
  942. if (typeof result.brokerage === 'undefined') {
  943. throw new Error('Not found.');
  944. }
  945. return result.brokerage;
  946. });
  947. }
  948. async getBandwidthPrices() {
  949. return this.tronWeb.fullNode.request('wallet/getbandwidthprices', {}, 'post')
  950. .then((result = {}) => {
  951. if (typeof result.prices === 'undefined') {
  952. throw new Error('Not found.');
  953. }
  954. return result.prices;
  955. });
  956. }
  957. async getEnergyPrices() {
  958. return this.tronWeb.fullNode.request('wallet/getenergyprices', {}, 'post')
  959. .then((result = {}) => {
  960. if (typeof result.prices === 'undefined') {
  961. throw new Error('Not found.');
  962. }
  963. return result.prices;
  964. });
  965. }
  966. }
  967. exports.Trx = Trx;
  968. //# sourceMappingURL=trx.js.map