Skip to content

Commit 3c23529

Browse files
authored
Merge pull request #709 from tronprotocol/release/v6.4.0
Release/v6.4.0
2 parents 2b87775 + 7f1f71e commit 3c23529

25 files changed

Lines changed: 2494 additions & 567 deletions

CHANGELOG.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,45 @@
11
Change Log
22
=========
33

4+
__6.4.0__
5+
6+
## New Features
7+
8+
- Typed `read` / `write` contract namespaces
9+
10+
Added contract.read and contract.write namespaces for type-safe, ergonomic access to contract methods. Functions are split by state mutability: view / pure (constant call) functions are exposed under contract.read, while state-changing functions are exposed under contract.write. When an ABI is declared as const, function names and argument types are checked at compile time.
11+
12+
- Call as contract.read.methodName([arg0, arg1, ...], options) / contract.write.methodName([arg0, arg1, ...], options).
13+
- The write `account` option is a private key: its derived address owns and signs the transaction, so a write can be issued from a non-default signer — including on an instance with no default key. A value that is not a private key is rejected. Treat this value as secret. For reads, a `from` option sets the caller address directly (validated as an address).
14+
- Validation errors (argument count, signer, call value, account) are surfaced as promise rejections.
15+
- ABI functions literally named read or write remain callable via the legacy flat surface (handled through a proxy).
16+
- Each function is additionally exposed under its full selector, e.g. contract.read['balanceOf(address)'] / contract.write['transfer(address,uint256)']. The selector form pins that exact overload, so same-arity overloads can be addressed unambiguously. Note: namespaces now enumerate each method under both its bare name and its selector, so Object.keys / for…in over contract.read / contract.write return two entries per function.
17+
18+
- Added `utils.abi.encodeFunctionData`
19+
20+
Added utils.abi.encodeFunctionData(funcABI, args) to build smart contract calldata (function selector + encoded parameters). Validates argument count and supports TRON-specific handling of trcToken (kept in the selector, encoded as `uint256`).
21+
22+
- Added `transactionBuilder.updateWitness`
23+
24+
Added transactionBuilder.updateWitness(address, url, options) to create a WitnessUpdateContract transaction that updates a witness node URL. Validates the address and the URL format / length (≤ 256).
25+
26+
## Improvements
27+
28+
- Dropped `Buffer` in favor of `TextEncoder` / `TextDecoder`
29+
30+
Replaced Buffer`-based encoding in `tronweb, utils/crypto, utils/abi and utils/ethersUtils with TextEncoder / TextDecoder for better cross-environment (browser) compatibility. The conversion is byte-for-byte equivalent to the previous behavior.
31+
32+
- `address.fromPrivateKey` accepts `0x`-prefixed private keys
33+
34+
TronWeb.address.fromPrivateKey now strips a leading 0x from the private key before deriving the address, so a private key works whether or not it carries the 0x prefix.
35+
36+
## Changes
37+
38+
- Bump axios from 1.15.0 to 1.18.0.
39+
- Bump dev dependency nyc to 18.
40+
- Add overrides for ws (8.21.0) and serialize-javascript (7.0.5) to pin security-patched versions.
41+
- build now runs the full build:all (esm, cjs, dist).
42+
443
__6.3.0__
544

645
## New Features

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ In order to contribute you can
202202
* run the tests — `npm run test`
203203
* push your changes and open a pull request
204204

205-
Contact the team at https://cn.developers.tron.network/docs/online-technical-support
205+
Contact the team at [here](https://developers.tron.network/docs/online-technical-support)
206206

207207

208208
## Licence

package-lock.json

Lines changed: 542 additions & 383 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tronweb",
3-
"version": "6.3.0",
3+
"version": "6.4.0",
44
"description": "JavaScript SDK that encapsulates the TRON HTTP API",
55
"main": "./lib/commonjs/index.js",
66
"module": "./lib/esm/index.js",
@@ -41,7 +41,7 @@
4141
"copy-protocol:cjs": "cp -R ./src/protocol ./lib/commonjs/",
4242
"copy-protocol:esm": "cp -R ./src/protocol ./lib/esm/",
4343
"prepare": "npm run build",
44-
"build": "npm run clean && webpack --config webpack.config.js --progress --color",
44+
"build": "npm run build:all",
4545
"build:dev": "NODE_ENV=development npm run build",
4646
"clean:all": "rimraf dist lib",
4747
"clean": "rimraf dist",
@@ -60,7 +60,7 @@
6060
},
6161
"dependencies": {
6262
"@babel/runtime": "7.26.10",
63-
"axios": "1.15.0",
63+
"axios": "1.18.0",
6464
"bignumber.js": "9.1.2",
6565
"ethereum-cryptography": "2.2.1",
6666
"ethers": "6.13.5",
@@ -70,7 +70,7 @@
7070
"validator": "13.15.23"
7171
},
7272
"devDependencies": {
73-
"@babel/core": "7.26.10",
73+
"@babel/core": "7.29.7",
7474
"@babel/plugin-transform-class-properties": "7.25.9",
7575
"@babel/plugin-transform-numeric-separator": "7.25.9",
7676
"@babel/plugin-transform-object-rest-spread": "7.25.9",
@@ -92,7 +92,6 @@
9292
"babel-loader": "10.0.0",
9393
"babel-plugin-istanbul": "7.0.0",
9494
"babel-plugin-source-map-support": "2.2.0",
95-
"buffer": "6.0.3",
9695
"chai": "4.5.0",
9796
"chalk": "2.4.2",
9897
"eslint": "9.31.0",
@@ -113,7 +112,7 @@
113112
"karma-spec-reporter": "0.0.36",
114113
"karma-webpack": "5.0.1",
115114
"mocha": "11.1.0",
116-
"nyc": "17.1.0",
115+
"nyc": "18.0.0",
117116
"prettier": "3.5.3",
118117
"puppeteer": "24.23.0",
119118
"querystring-es3": "0.2.1",
@@ -133,6 +132,8 @@
133132
"license": "MIT",
134133
"repository": "https://github.com/tronprotocol/tronweb.git",
135134
"overrides": {
135+
"js-yaml": "4.2.0",
136+
"ws": "8.21.0",
136137
"serialize-javascript": "7.0.5"
137138
}
138139
}

src/lib/TransactionBuilder/TransactionBuilder.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ import {
4545
WithdrawBalanceContract,
4646
WithdrawExpireUnfreezeContract,
4747
WitnessCreateContract,
48+
WitnessUpdateContract,
4849
} from '../../types/Contract.js';
4950
import {
5051
createTransaction,
@@ -686,6 +687,47 @@ export class TransactionBuilder {
686687
);
687688
}
688689

690+
async updateWitness(
691+
address: string = this.tronWeb.defaultAddress.hex as string,
692+
url = '',
693+
options: TransactionCommonOptions = {}
694+
): Promise<Transaction<WitnessUpdateContract>> {
695+
this.validator.notValid([
696+
{
697+
name: 'origin',
698+
type: 'address',
699+
value: address,
700+
},
701+
{
702+
name: 'url',
703+
type: 'url',
704+
value: url as string,
705+
msg: 'Invalid url provided',
706+
},
707+
{
708+
name: 'url',
709+
type: 'string',
710+
value: url as string,
711+
lte: 256,
712+
msg: 'Invalid url provided',
713+
},
714+
]);
715+
716+
const data: WitnessUpdateContract = {
717+
owner_address: toHex(address as string),
718+
update_url: fromUtf8(url as string),
719+
};
720+
721+
const transactionOptions = getTransactionOptions(options);
722+
return createTransaction<WitnessUpdateContract>(
723+
this.tronWeb,
724+
ContractType.WitnessUpdateContract,
725+
data,
726+
options?.permissionId,
727+
transactionOptions
728+
);
729+
}
730+
689731
async vote(
690732
votes: VoteInfo = {},
691733
voterAddress: string = this.tronWeb.defaultAddress.hex as string,
@@ -1248,7 +1290,7 @@ export class TransactionBuilder {
12481290
);
12491291

12501292
if (args.function_selector) {
1251-
args.data = keccak256(Buffer.from(args.function_selector, 'utf-8')).toString().substring(2, 10) + args.parameter;
1293+
args.data = keccak256(new TextEncoder().encode(args.function_selector)).toString().substring(2, 10) + args.parameter;
12521294
}
12531295
const value: TriggerSmartContract = {
12541296
data: args.data,

src/lib/TransactionBuilder/helper.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { TronWeb } from '../../tronweb.js';
22
import { Transaction, TransactionWrapper } from '../../types/Transaction.js';
33
import { txCheckWithArgs, txJsonToPb, txPbToTxID, txPbToRawDataHex } from '../../utils/transaction.js';
44
import { keccak256 } from '../../utils/ethersUtils.js';
5+
import { hexStr2byteArray } from '../../utils/code.js';
56
import { ContractParamter, ContractType } from '../../types/Contract.js';
67
import { TriggerConstantContractOptions } from '../../types/TransactionBuilder.js';
78

@@ -50,7 +51,7 @@ export function resultManagerTriggerSmartContract(
5051
export function genContractAddress(ownerAddress: string, txID: string) {
5152
return (
5253
'41' +
53-
keccak256(Buffer.from(txID + ownerAddress, 'hex'))
54+
keccak256(new Uint8Array(hexStr2byteArray(txID + ownerAddress)))
5455
.toString()
5556
.substring(2)
5657
.slice(24)

src/lib/contract/index.ts

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
11
import { TronWeb } from '../../tronweb.js';
22
import utils from '../../utils/index.js';
33
import { Method, AbiFragmentNoErrConstructor } from './method.js';
4+
import { buildReadNamespace, buildWriteNamespace } from './readWrite.js';
5+
import type { ContractReadNamespace, ContractWriteNamespace } from './readWrite.js';
46
import type { ContractAbiInterface, GetMethodsTypeFromAbi, GetOnMethodTypeFromAbi, AnyOnMethodType } from '../../types/ABI.js';
57
import type { Address } from '../../types/Trx.js';
68
import type { CreateSmartContractOptions } from '../../types/TransactionBuilder.js';
79

10+
// When the ABI itself defines a function named `read`/`write`, the legacy flat
11+
// call surface must stay reachable as `contract.read(...)`/`contract.write(...)`.
12+
// Wrap the legacy call in a Proxy whose property access and enumeration are
13+
// served entirely by the namespace, so both surfaces share the one getter.
14+
function wrapReservedNamespace<T extends object>(namespace: T, legacyCall: unknown): T {
15+
if (typeof legacyCall !== 'function') return namespace;
16+
return new Proxy(legacyCall, {
17+
get: (_target, prop) => namespace[prop as keyof T],
18+
has: (_target, prop) => prop in namespace,
19+
ownKeys: () => Reflect.ownKeys(namespace),
20+
getOwnPropertyDescriptor: (_target, prop) => Object.getOwnPropertyDescriptor(namespace, prop),
21+
}) as T;
22+
}
23+
824
export class Contract<Abi extends ContractAbiInterface = ContractAbiInterface> {
925
tronWeb: TronWeb;
1026
abi: Abi;
@@ -16,6 +32,8 @@ export class Contract<Abi extends ContractAbiInterface = ContractAbiInterface> {
1632
methods: GetOnMethodTypeFromAbi<Abi> & AnyOnMethodType;
1733
methodInstances: GetMethodsTypeFromAbi<Abi>;
1834
props: any[];
35+
private _readNamespace?: { abi: Abi; namespace: ContractReadNamespace<Abi> };
36+
private _writeNamespace?: { abi: Abi; namespace: ContractWriteNamespace<Abi> };
1937
[key: string | number | symbol]: any;
2038

2139
constructor(tronWeb: TronWeb, abi: Abi = [] as any, address: Address) {
@@ -31,8 +49,8 @@ export class Contract<Abi extends ContractAbiInterface = ContractAbiInterface> {
3149
this.deployed = false;
3250
this.lastBlock = false;
3351

34-
this.methods = {} as GetOnMethodTypeFromAbi<Abi> & AnyOnMethodType;
35-
this.methodInstances = {} as GetMethodsTypeFromAbi<Abi>;
52+
this.methods = Object.create(null) as GetOnMethodTypeFromAbi<Abi> & AnyOnMethodType;
53+
this.methodInstances = Object.create(null) as GetMethodsTypeFromAbi<Abi>;
3654
this.props = [];
3755

3856
if (utils.address.isAddress(address)) {
@@ -44,14 +62,54 @@ export class Contract<Abi extends ContractAbiInterface = ContractAbiInterface> {
4462
this.loadAbi(abi);
4563
}
4664

65+
/**
66+
* Type-safe read namespace:
67+
* every `view`/`pure` ABI function is callable as
68+
* `contract.read.fn([args], { from, value })` and resolves to the decoded
69+
* result directly. Rebuilt lazily whenever the contract ABI is replaced.
70+
*/
71+
get read(): ContractReadNamespace<Abi> {
72+
if (!this._readNamespace || this._readNamespace.abi !== this.abi) {
73+
this._readNamespace = {
74+
abi: this.abi,
75+
namespace: wrapReservedNamespace(
76+
buildReadNamespace(this) as object,
77+
this.methods['read']
78+
) as ContractReadNamespace<Abi>,
79+
};
80+
}
81+
return this._readNamespace.namespace;
82+
}
83+
84+
/**
85+
* Type-safe write namespace:
86+
* every state-changing ABI function is callable as
87+
* `contract.write.fn([args], { account, value, feeLimit, tokenId, tokenValue, permissionId })`,
88+
* signed with the instance's default private key, broadcast, and resolved to
89+
* the transaction ID. Rebuilt lazily whenever the contract ABI is replaced.
90+
*/
91+
get write(): ContractWriteNamespace<Abi> {
92+
if (!this._writeNamespace || this._writeNamespace.abi !== this.abi) {
93+
this._writeNamespace = {
94+
abi: this.abi,
95+
namespace: wrapReservedNamespace(
96+
buildWriteNamespace(this) as object,
97+
this.methods['write']
98+
) as ContractWriteNamespace<Abi>,
99+
};
100+
}
101+
return this._writeNamespace.namespace;
102+
}
103+
47104
hasProperty(property: number | string | symbol) {
48105
// eslint-disable-next-line no-prototype-builtins
49106
return this.hasOwnProperty(property) || (this as any).__proto__.hasOwnProperty(property);
50107
}
51108

52109
loadAbi(abi: Abi) {
53110
this.abi = abi;
54-
this.methods = {} as GetOnMethodTypeFromAbi<Abi> & AnyOnMethodType;
111+
this.methods = Object.create(null) as GetOnMethodTypeFromAbi<Abi> & AnyOnMethodType;
112+
this.methodInstances = Object.create(null) as GetMethodsTypeFromAbi<Abi>;
55113

56114
this.props.forEach((prop: string) => delete (this as any)[prop]);
57115

@@ -151,3 +209,17 @@ export class Contract<Abi extends ContractAbiInterface = ContractAbiInterface> {
151209

152210
export type { CallOptions, SendOptions, AbiFragmentNoErrConstructor } from './method.js';
153211
export { Method } from './method.js';
212+
export type {
213+
ContractReadNamespace,
214+
ContractWriteNamespace,
215+
ReadContractFunctionName,
216+
WriteContractFunctionName,
217+
ReadContractParameters,
218+
ReadContractReturnType,
219+
WriteContractParameters,
220+
WriteContractReturnType,
221+
ReadOptions,
222+
WriteOptions,
223+
CollapseSingleItemTuple,
224+
} from './readWrite.js';
225+
export { buildReadNamespace, buildWriteNamespace } from './readWrite.js';

src/lib/contract/method.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ const decodeOutput = <T extends Readonly<AbiFragmentNoErrConstructor>>(abi: T, o
9090

9191
export class Method<AbiFrag extends Readonly<AbiFragmentNoErrConstructor>> {
9292
tronWeb: TronWeb;
93-
contract: Contract;
93+
contract: Contract<any>;
9494
abi: AbiFrag;
9595
name: string;
9696
inputs: AbiInputsType;
@@ -103,7 +103,7 @@ export class Method<AbiFrag extends Readonly<AbiFragmentNoErrConstructor>> {
103103
shouldPollResponse: boolean;
104104
};
105105

106-
constructor(contract: Contract, abi: AbiFrag) {
106+
constructor(contract: Contract<any>, abi: AbiFrag) {
107107
this.tronWeb = contract.tronWeb;
108108
this.contract = contract;
109109

0 commit comments

Comments
 (0)