tronweb.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. import { HttpProvider, providers } from './lib/providers/index.js';
  2. import type { Providers } from './lib/providers/index.js';
  3. import utils from './utils/index.js';
  4. import { BigNumber } from 'bignumber.js';
  5. import EventEmitter from 'eventemitter3';
  6. import semver from 'semver';
  7. import { TransactionBuilder } from './lib/TransactionBuilder/TransactionBuilder.js';
  8. import { Trx } from './lib/trx.js';
  9. import { Contract } from './lib/contract/index.js';
  10. import { Plugin } from './lib/plugin.js';
  11. import { Event } from './lib/event.js';
  12. import { keccak256 } from './utils/ethersUtils.js';
  13. import { fromHex, fromPrivateKey, isAddress, toHex, toChecksumAddress, isChecksumAddress } from './utils/address.js';
  14. import { HeadersType } from './types/Providers.js';
  15. import { isString } from './utils/validations.js';
  16. import { DefaultAddress, NodeProvider, TronWebOptions, IBigNumber } from './types/TronWeb.js';
  17. import { ContractAbiInterface } from './types/ABI.js';
  18. import { Address } from './types/Trx.js';
  19. const DEFAULT_VERSION = '4.7.1';
  20. const FEE_LIMIT = 150000000;
  21. const version = '6.0.3';
  22. function isValidOptions(options: unknown): options is TronWebOptions {
  23. return (
  24. !!options &&
  25. typeof options === 'object' &&
  26. (!!(options as TronWebOptions).fullNode || !!(options as TronWebOptions).fullHost)
  27. );
  28. }
  29. export class TronWeb extends EventEmitter {
  30. providers: Providers;
  31. BigNumber: typeof BigNumber;
  32. transactionBuilder: TransactionBuilder;
  33. trx: Trx;
  34. plugin: Plugin;
  35. event: Event;
  36. version: typeof TronWeb.version;
  37. static version = version;
  38. utils: typeof utils;
  39. defaultBlock: number | false | 'earliest' | 'latest';
  40. defaultPrivateKey: string | false;
  41. defaultAddress: DefaultAddress;
  42. fullnodeVersion: string;
  43. feeLimit: number;
  44. fullNode!: HttpProvider;
  45. solidityNode!: HttpProvider;
  46. eventServer?: HttpProvider;
  47. constructor(options: TronWebOptions);
  48. constructor(fullNode: NodeProvider, solidityNode: NodeProvider, eventServer?: NodeProvider, privateKey?: string);
  49. /* prettier-ignore */
  50. constructor(fullNode: NodeProvider, solidityNode: NodeProvider, eventServer: NodeProvider, privateKey?: string);
  51. constructor(
  52. options: TronWebOptions | NodeProvider,
  53. solidityNode: NodeProvider = '',
  54. eventServer?: NodeProvider,
  55. privateKey = ''
  56. ) {
  57. super();
  58. let fullNode;
  59. let headers: HeadersType | false = false;
  60. let eventHeaders: HeadersType | false = false;
  61. if (isValidOptions(options)) {
  62. fullNode = options.fullNode || options.fullHost;
  63. solidityNode = (options.solidityNode || options.fullHost)!;
  64. eventServer = (options.eventServer || options.fullHost)!;
  65. headers = options.headers || false;
  66. eventHeaders = options.eventHeaders || headers;
  67. privateKey = options.privateKey!;
  68. } else {
  69. fullNode = options;
  70. }
  71. if (utils.isString(fullNode)) fullNode = new providers.HttpProvider(fullNode);
  72. if (utils.isString(solidityNode)) solidityNode = new providers.HttpProvider(solidityNode);
  73. if (utils.isString(eventServer)) eventServer = new providers.HttpProvider(eventServer);
  74. this.event = new Event(this);
  75. this.transactionBuilder = new TransactionBuilder(this);
  76. this.trx = new Trx(this);
  77. this.plugin = new Plugin(this, {
  78. disablePlugins: isValidOptions(options) ? options.disablePlugins : false,
  79. });
  80. this.utils = utils;
  81. this.setFullNode(fullNode as HttpProvider);
  82. this.setSolidityNode(solidityNode as HttpProvider);
  83. this.setEventServer(eventServer!);
  84. this.providers = providers;
  85. this.BigNumber = BigNumber;
  86. this.defaultBlock = false;
  87. this.defaultPrivateKey = false;
  88. this.defaultAddress = {
  89. hex: false,
  90. base58: false,
  91. };
  92. this.version = TronWeb.version;
  93. this.sha3 = TronWeb.sha3;
  94. this.fromUtf8 = TronWeb.fromUtf8;
  95. this.address = TronWeb.address;
  96. this.toAscii = TronWeb.toAscii;
  97. this.toUtf8 = TronWeb.toUtf8;
  98. this.isAddress = TronWeb.isAddress;
  99. this.fromAscii = TronWeb.fromAscii;
  100. this.toHex = TronWeb.toHex;
  101. this.toBigNumber = TronWeb.toBigNumber;
  102. this.toDecimal = TronWeb.toDecimal;
  103. this.fromDecimal = TronWeb.fromDecimal;
  104. this.toSun = TronWeb.toSun;
  105. this.fromSun = TronWeb.fromSun;
  106. this.createAccount = TronWeb.createAccount;
  107. this.createRandom = TronWeb.createRandom;
  108. this.fromMnemonic = TronWeb.fromMnemonic;
  109. if (privateKey) this.setPrivateKey(privateKey);
  110. this.fullnodeVersion = DEFAULT_VERSION;
  111. this.feeLimit = FEE_LIMIT;
  112. if (headers) {
  113. this.setFullNodeHeader(headers);
  114. }
  115. if (eventHeaders) {
  116. this.setEventHeader(eventHeaders);
  117. }
  118. }
  119. async getFullnodeVersion() {
  120. try {
  121. const nodeInfo = await this.trx.getNodeInfo();
  122. this.fullnodeVersion = nodeInfo.configNodeInfo.codeVersion;
  123. if (this.fullnodeVersion.split('.').length === 2) {
  124. this.fullnodeVersion += '.0';
  125. }
  126. } catch (err) {
  127. this.fullnodeVersion = DEFAULT_VERSION;
  128. }
  129. }
  130. setDefaultBlock(blockID: false | 'latest' | 'earliest' | number = false) {
  131. if ([false, 'latest', 'earliest', 0].includes(blockID)) {
  132. return (this.defaultBlock = blockID);
  133. }
  134. if (!utils.isInteger(blockID) || !blockID) throw new Error('Invalid block ID provided');
  135. return (this.defaultBlock = Math.abs(blockID));
  136. }
  137. setPrivateKey(privateKey: string) {
  138. try {
  139. this.setAddress(TronWeb.address.fromPrivateKey(privateKey) as string);
  140. } catch {
  141. throw new Error('Invalid private key provided');
  142. }
  143. this.defaultPrivateKey = privateKey;
  144. this.emit('privateKeyChanged', privateKey);
  145. }
  146. setAddress(address: string) {
  147. if (!TronWeb.isAddress(address)) throw new Error('Invalid address provided');
  148. const hex = TronWeb.address.toHex(address);
  149. const base58 = TronWeb.address.fromHex(address);
  150. if (this.defaultPrivateKey && TronWeb.address.fromPrivateKey(this.defaultPrivateKey) !== base58)
  151. this.defaultPrivateKey = false;
  152. this.defaultAddress = {
  153. hex,
  154. base58,
  155. };
  156. this.emit('addressChanged', { hex, base58 });
  157. }
  158. fullnodeSatisfies(version: string) {
  159. return semver.satisfies(this.fullnodeVersion, version);
  160. }
  161. isValidProvider(provider: unknown) {
  162. return Object.values(providers).some((knownProvider) => provider instanceof knownProvider);
  163. }
  164. setFullNode(fullNode: HttpProvider | string) {
  165. if (isString(fullNode)) fullNode = new providers.HttpProvider(fullNode);
  166. if (!this.isValidProvider(fullNode)) throw new Error('Invalid full node provided');
  167. this.fullNode = fullNode;
  168. this.fullNode.setStatusPage('wallet/getnowblock');
  169. }
  170. setSolidityNode(solidityNode: HttpProvider | string) {
  171. if (utils.isString(solidityNode)) solidityNode = new providers.HttpProvider(solidityNode);
  172. if (!this.isValidProvider(solidityNode)) throw new Error('Invalid solidity node provided');
  173. this.solidityNode = solidityNode;
  174. this.solidityNode.setStatusPage('walletsolidity/getnowblock');
  175. }
  176. setEventServer(eventServer: NodeProvider, healthcheck?: string) {
  177. this.event.setServer(eventServer, healthcheck);
  178. }
  179. setHeader(headers = {}) {
  180. const fullNode = new providers.HttpProvider(this.fullNode.host, 30000, '', '', headers);
  181. const solidityNode = new providers.HttpProvider(this.solidityNode.host, 30000, '', '', headers);
  182. const eventServer = new providers.HttpProvider(this.eventServer!.host, 30000, '', '', headers);
  183. this.setFullNode(fullNode);
  184. this.setSolidityNode(solidityNode);
  185. this.setEventServer(eventServer);
  186. }
  187. setFullNodeHeader(headers = {}) {
  188. const fullNode = new providers.HttpProvider(this.fullNode.host, 30000, '', '', headers);
  189. const solidityNode = new providers.HttpProvider(this.solidityNode.host, 30000, '', '', headers);
  190. this.setFullNode(fullNode);
  191. this.setSolidityNode(solidityNode);
  192. }
  193. setEventHeader(headers = {}) {
  194. const eventServer = new providers.HttpProvider(this.eventServer!.host, 30000, '', '', headers);
  195. this.setEventServer(eventServer);
  196. }
  197. currentProviders() {
  198. return {
  199. fullNode: this.fullNode,
  200. solidityNode: this.solidityNode,
  201. eventServer: this.eventServer,
  202. };
  203. }
  204. currentProvider() {
  205. return this.currentProviders();
  206. }
  207. getEventResult(...params: Parameters<Event['getEventsByContractAddress']>): ReturnType<Event['getEventsByContractAddress']> {
  208. return this.event.getEventsByContractAddress(...params);
  209. }
  210. getEventByTransactionID(
  211. ...params: Parameters<Event['getEventsByTransactionID']>
  212. ): ReturnType<Event['getEventsByTransactionID']> {
  213. return this.event.getEventsByTransactionID(...params);
  214. }
  215. contract(abi: ContractAbiInterface = [], address?: Address) {
  216. return new Contract(this, abi, address!);
  217. }
  218. address: typeof TronWeb.address;
  219. static get address() {
  220. return {
  221. fromHex(address: string) {
  222. return fromHex(address);
  223. },
  224. toHex(address: string) {
  225. return toHex(address);
  226. },
  227. toChecksumAddress(address: string) {
  228. return toChecksumAddress(address);
  229. },
  230. isChecksumAddress(address: string) {
  231. return isChecksumAddress(address);
  232. },
  233. fromPrivateKey(privateKey: string, strict = false) {
  234. return fromPrivateKey(privateKey, strict);
  235. },
  236. };
  237. }
  238. sha3: typeof TronWeb.sha3;
  239. static sha3(string: string, prefix = true) {
  240. return (prefix ? '0x' : '') + keccak256(Buffer.from(string, 'utf-8')).toString().substring(2);
  241. }
  242. toHex: typeof TronWeb.toHex;
  243. static toHex(val: string | number | boolean | Record<string | number | symbol, unknown> | unknown[] | IBigNumber) {
  244. if (utils.isBoolean(val)) return TronWeb.fromDecimal(+val);
  245. if (utils.isBigNumber(val)) return TronWeb.fromDecimal(val);
  246. if (typeof val === 'object') return TronWeb.fromUtf8(JSON.stringify(val));
  247. if (utils.isString(val)) {
  248. if (/^(-|)0x/.test(val)) return val;
  249. // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  250. // @ts-ignore
  251. if (!isFinite(val) || /^\s*$/.test(val)) return TronWeb.fromUtf8(val);
  252. }
  253. const result = TronWeb.fromDecimal(val as number);
  254. if (result === '0xNaN') {
  255. throw new Error('The passed value is not convertible to a hex string');
  256. } else {
  257. return result;
  258. }
  259. }
  260. toUtf8: typeof TronWeb.toUtf8;
  261. static toUtf8(hex: string) {
  262. if (utils.isHex(hex)) {
  263. hex = hex.replace(/^0x/, '');
  264. return Buffer.from(hex, 'hex').toString('utf8');
  265. } else {
  266. throw new Error('The passed value is not a valid hex string');
  267. }
  268. }
  269. fromUtf8: typeof TronWeb.fromUtf8;
  270. static fromUtf8(string: string) {
  271. if (!utils.isString(string)) {
  272. throw new Error('The passed value is not a valid utf-8 string');
  273. }
  274. return '0x' + Buffer.from(string, 'utf8').toString('hex');
  275. }
  276. toAscii: typeof TronWeb.toAscii;
  277. static toAscii(hex: string) {
  278. if (utils.isHex(hex)) {
  279. let str = '';
  280. let i = 0;
  281. const l = hex.length;
  282. if (hex.substring(0, 2) === '0x') {
  283. i = 2;
  284. }
  285. for (; i < l; i += 2) {
  286. const code = parseInt(hex.substr(i, 2), 16);
  287. str += String.fromCharCode(code);
  288. }
  289. return str;
  290. } else {
  291. throw new Error('The passed value is not a valid hex string');
  292. }
  293. }
  294. fromAscii: typeof TronWeb.fromAscii;
  295. static fromAscii(string: string, padding?: number) {
  296. if (!utils.isString(string)) {
  297. throw new Error('The passed value is not a valid utf-8 string');
  298. }
  299. return '0x' + Buffer.from(string, 'ascii').toString('hex').padEnd(padding!, '0');
  300. }
  301. toDecimal: typeof TronWeb.toDecimal;
  302. static toDecimal(value: string | number | IBigNumber) {
  303. return TronWeb.toBigNumber(value).toNumber();
  304. }
  305. fromDecimal: typeof TronWeb.fromDecimal;
  306. static fromDecimal(value: number | IBigNumber) {
  307. const number = TronWeb.toBigNumber(value);
  308. const result = number.toString(16);
  309. return number.isLessThan(0) ? '-0x' + result.substr(1) : '0x' + result;
  310. }
  311. fromSun: typeof TronWeb.fromSun;
  312. static fromSun(sun: number): string | IBigNumber {
  313. const trx = TronWeb.toBigNumber(sun).div(1_000_000);
  314. return utils.isBigNumber(sun) ? trx : trx.toString(10);
  315. }
  316. toSun: typeof TronWeb.toSun;
  317. static toSun(trx: number): string | IBigNumber {
  318. const sun = TronWeb.toBigNumber(trx).times(1_000_000);
  319. return utils.isBigNumber(trx) ? sun : sun.toString(10);
  320. }
  321. toBigNumber: typeof TronWeb.toBigNumber;
  322. static toBigNumber(amount: string | number | IBigNumber = 0): IBigNumber {
  323. if (utils.isBigNumber(amount)) return amount;
  324. if (utils.isString(amount) && /^(-|)0x/.test(amount)) return new BigNumber(amount.replace('0x', ''), 16);
  325. return new BigNumber(amount.toString(10), 10);
  326. }
  327. isAddress: typeof TronWeb.isAddress;
  328. static isAddress(address: unknown = ''): boolean {
  329. return isAddress(address);
  330. }
  331. createAccount: typeof TronWeb.createAccount;
  332. static async createAccount() {
  333. const account = utils.accounts.generateAccount();
  334. return account;
  335. }
  336. createRandom: typeof TronWeb.createRandom;
  337. static createRandom(
  338. ...params: Parameters<(typeof utils)['accounts']['generateRandom']>
  339. ): ReturnType<(typeof utils)['accounts']['generateRandom']> {
  340. const account = utils.accounts.generateRandom(...params);
  341. return account;
  342. }
  343. fromMnemonic: typeof TronWeb.fromMnemonic;
  344. static fromMnemonic(
  345. ...params: Parameters<(typeof utils)['accounts']['generateAccountWithMnemonic']>
  346. ): ReturnType<(typeof utils)['accounts']['generateAccountWithMnemonic']> {
  347. const account = utils.accounts.generateAccountWithMnemonic(...params);
  348. return account;
  349. }
  350. async isConnected() {
  351. return {
  352. fullNode: await this.fullNode.isConnected(),
  353. solidityNode: await this.solidityNode.isConnected(),
  354. eventServer: this.eventServer && (await this.eventServer.isConnected()),
  355. };
  356. }
  357. }
  358. export default TronWeb;