method.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.Method = void 0;
  4. const tslib_1 = require("tslib");
  5. /* eslint-disable no-control-regex */
  6. const index_js_1 = tslib_1.__importDefault(require("../../utils/index.js"));
  7. const abi_js_1 = require("../../utils/abi.js");
  8. const crypto_js_1 = require("../../utils/crypto.js");
  9. const getFunctionSelector = (abi) => {
  10. if ('stateMutability' in abi) {
  11. abi.stateMutability = abi.stateMutability ? abi.stateMutability.toLowerCase() : 'nonpayable';
  12. }
  13. abi.type = abi.type ? abi.type.toLowerCase() : '';
  14. if (abi.type === 'fallback' || abi.type === 'receive')
  15. return '0x';
  16. const iface = new index_js_1.default.ethersUtils.Interface([abi]);
  17. let obj;
  18. if (abi.type === 'event') {
  19. obj = iface.getEvent(abi.name);
  20. }
  21. else {
  22. obj = iface.getFunction(abi.name);
  23. }
  24. if (obj) {
  25. return obj.format('sighash');
  26. }
  27. throw new Error('unknown function');
  28. };
  29. const decodeOutput = (abi, output) => {
  30. return (0, abi_js_1.decodeParamsV2ByABI)(abi, output);
  31. };
  32. class Method {
  33. tronWeb;
  34. contract;
  35. abi;
  36. name;
  37. inputs;
  38. outputs;
  39. functionSelector;
  40. signature;
  41. defaultOptions;
  42. constructor(contract, abi) {
  43. this.tronWeb = contract.tronWeb;
  44. this.contract = contract;
  45. this.abi = abi;
  46. this.name = abi.name || abi.type;
  47. this.inputs = abi.inputs || [];
  48. this.outputs = [];
  49. if ('outputs' in abi && abi.outputs) {
  50. this.outputs = abi.outputs;
  51. }
  52. this.functionSelector = getFunctionSelector(abi);
  53. this.signature = (0, crypto_js_1.sha3)(this.functionSelector, false).slice(0, 8);
  54. this.defaultOptions = {
  55. feeLimit: this.tronWeb.feeLimit,
  56. callValue: 0,
  57. userFeePercentage: 100,
  58. shouldPollResponse: false, // Only used for sign()
  59. };
  60. }
  61. decodeInput(data) {
  62. const abi = JSON.parse(JSON.stringify(this.abi));
  63. abi.outputs = abi.inputs;
  64. return decodeOutput(abi, '0x' + data);
  65. }
  66. onMethod(...args) {
  67. let rawParameter = '';
  68. if (this.abi && !/event/i.test(this.abi.type)) {
  69. rawParameter = (0, abi_js_1.encodeParamsV2ByABI)(this.abi, args);
  70. }
  71. return {
  72. call: async (options = {}) => {
  73. options = {
  74. ...options,
  75. rawParameter,
  76. };
  77. return await this._call([], [], options);
  78. },
  79. send: async (options = {}, privateKey = this.tronWeb.defaultPrivateKey) => {
  80. options = {
  81. ...options,
  82. rawParameter,
  83. };
  84. return await this._send([], [], options, privateKey);
  85. },
  86. };
  87. }
  88. async _call(types, args, options = {}) {
  89. if (types.length !== args.length) {
  90. throw new Error('Invalid argument count provided');
  91. }
  92. if (!this.contract.address) {
  93. throw new Error('Smart contract is missing address');
  94. }
  95. if (!this.contract.deployed) {
  96. throw new Error('Calling smart contracts requires you to load the contract first');
  97. }
  98. if ('stateMutability' in this.abi) {
  99. const { stateMutability } = this.abi;
  100. if (stateMutability && !['pure', 'view'].includes(stateMutability.toLowerCase())) {
  101. throw new Error(`Methods with state mutability "${stateMutability}" must use send()`);
  102. }
  103. }
  104. options = {
  105. ...this.defaultOptions,
  106. from: this.tronWeb.defaultAddress.hex,
  107. ...options,
  108. _isConstant: true,
  109. };
  110. const parameters = args.map((value, index) => ({
  111. type: types[index],
  112. value,
  113. }));
  114. const transaction = await this.tronWeb.transactionBuilder.triggerSmartContract(this.contract.address, this.functionSelector, options, parameters, options.from ? this.tronWeb.address.toHex(options.from) : undefined);
  115. if (!index_js_1.default.hasProperty(transaction, 'constant_result')) {
  116. throw new Error('Failed to execute');
  117. }
  118. const len = transaction.constant_result[0].length;
  119. if (len === 0 || len % 64 === 8) {
  120. let msg = 'The call has been reverted or has thrown an error.';
  121. if (len !== 0) {
  122. msg += ' Error message: ';
  123. let msg2 = '';
  124. const chunk = transaction.constant_result[0].substring(8);
  125. for (let i = 0; i < len - 8; i += 64) {
  126. msg2 += this.tronWeb.toUtf8(chunk.substring(i, i + 64));
  127. }
  128. msg += msg2
  129. .replace(/(\u0000|\u000b|\f)+/g, ' ')
  130. .replace(/ +/g, ' ')
  131. .replace(/\s+$/g, '');
  132. }
  133. throw new Error(msg);
  134. }
  135. let output = decodeOutput(this.abi, '0x' + transaction.constant_result[0]);
  136. if (output.length === 1 && Object.keys(output).length === 1) {
  137. output = output[0];
  138. }
  139. return output;
  140. }
  141. async _send(types, args, options = {}, privateKey = this.tronWeb.defaultPrivateKey) {
  142. if (types.length !== args.length) {
  143. throw new Error('Invalid argument count provided');
  144. }
  145. if (!this.contract.address) {
  146. throw new Error('Smart contract is missing address');
  147. }
  148. if (!this.contract.deployed) {
  149. throw new Error('Calling smart contracts requires you to load the contract first');
  150. }
  151. const { stateMutability } = this.abi;
  152. if (['pure', 'view'].includes(stateMutability.toLowerCase())) {
  153. throw new Error(`Methods with state mutability "${stateMutability}" must use call()`);
  154. }
  155. // If a function isn't payable, dont provide a callValue.
  156. if (!['payable'].includes(stateMutability.toLowerCase())) {
  157. options.callValue = 0;
  158. }
  159. options = {
  160. ...this.defaultOptions,
  161. from: this.tronWeb.defaultAddress.hex,
  162. ...options,
  163. };
  164. const parameters = args.map((value, index) => ({
  165. type: types[index],
  166. value,
  167. }));
  168. const address = privateKey ? this.tronWeb.address.fromPrivateKey(privateKey) : this.tronWeb.defaultAddress.base58;
  169. const transaction = await this.tronWeb.transactionBuilder.triggerSmartContract(this.contract.address, this.functionSelector, options, parameters, this.tronWeb.address.toHex(address));
  170. if (!transaction.result || !transaction.result.result) {
  171. throw new Error('Unknown error: ' + JSON.stringify(transaction, null, 2));
  172. }
  173. // If privateKey is false, this won't be signed here. We assume sign functionality will be replaced.
  174. const signedTransaction = await this.tronWeb.trx.sign(transaction.transaction, privateKey);
  175. if (!signedTransaction.signature) {
  176. if (!privateKey) {
  177. throw new Error('Transaction was not signed properly');
  178. }
  179. throw new Error('Invalid private key provided');
  180. }
  181. const broadcast = await this.tronWeb.trx.sendRawTransaction(signedTransaction);
  182. if (broadcast.code) {
  183. const err = {
  184. error: broadcast.code,
  185. message: broadcast.code,
  186. };
  187. if (broadcast.message)
  188. err.message = this.tronWeb.toUtf8(broadcast.message);
  189. const error = new Error(err.message);
  190. error.error = broadcast.code;
  191. throw error;
  192. }
  193. if (!options.shouldPollResponse) {
  194. return signedTransaction.txID;
  195. }
  196. const checkResult = async (index) => {
  197. if (index === (options.pollTimes || 20)) {
  198. const error = new Error('Cannot find result in solidity node');
  199. error.error = 'Cannot find result in solidity node';
  200. error.transaction = signedTransaction;
  201. throw error;
  202. }
  203. const output = await this.tronWeb.trx.getTransactionInfo(signedTransaction.txID);
  204. if (!Object.keys(output).length) {
  205. await new Promise((r) => setTimeout(r, 3000));
  206. return checkResult(index + 1);
  207. }
  208. if (output.result && output.result === 'FAILED') {
  209. const error = new Error(this.tronWeb.toUtf8(output.resMessage));
  210. error.error = this.tronWeb.toUtf8(output.resMessage);
  211. error.transaction = signedTransaction;
  212. error.output = output;
  213. throw error;
  214. }
  215. if (!index_js_1.default.hasProperty(output, 'contractResult')) {
  216. const error = new Error('Failed to execute: ' + JSON.stringify(output, null, 2));
  217. error.error = 'Failed to execute: ' + JSON.stringify(output, null, 2);
  218. error.transaction = signedTransaction;
  219. error.output = output;
  220. throw error;
  221. }
  222. if (options.rawResponse) {
  223. return output;
  224. }
  225. let decoded = decodeOutput(this.abi, '0x' + output.contractResult[0]);
  226. if (decoded.length === 1 && Object.keys(decoded).length === 1) {
  227. decoded = decoded[0];
  228. }
  229. if (options.keepTxID) {
  230. return [signedTransaction.txID, decoded];
  231. }
  232. return decoded;
  233. };
  234. return checkResult(0);
  235. }
  236. }
  237. exports.Method = Method;
  238. //# sourceMappingURL=method.js.map