Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/v6.0.0 beta.3 #511

5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
Change Log
=========

__6.0.0-beta.3__
- Support recovering transaction signer address by `trx.ecRecover`.
- Support both base58 format and hex format address field in keys of `updateAccountPermissions` params.
- Support type for contract instance.

__6.0.0-beta.2__
- Bump ethers from 6.8.0 to 6.11.1
- Bump ethereum-cryptography from 2.1.2 to 2.1.3
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,14 @@ docker run -it --rm \

## Creating an Instance

First off, in your javascript file, define TronWeb:
First of all, in your typescript file, define TronWeb:

```js
const TronWeb = require('tronweb')
```typescript
import { TronWeb, utils as TronWebUtils, Trx, TransactionBuilder, Contract, Event, Plugin } from 'tronweb';
```

Please note that this is not the same as v5.x. If you want to dive into more differences, check out [migration guide](https://tronweb.network/docu/docs/6.0.0-beta.3/Migrating%20from%20v5)

When you instantiate TronWeb you can define

* fullNode
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tronweb",
"version": "6.0.0-beta.2",
"version": "6.0.0-beta.3",
"description": "JavaScript SDK that encapsulates the TRON HTTP API",
"main": "./lib/commonjs/index.js",
"module": "./lib/esm/index.js",
Expand Down
14 changes: 14 additions & 0 deletions src/lib/TransactionBuilder/TransactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2113,6 +2113,10 @@ export class TransactionBuilder {
if ('type' in _ownerPermissions) {
delete _ownerPermissions.type;
}
_ownerPermissions.keys = _ownerPermissions.keys?.map(({ address, weight }) => ({
address: this.tronWeb.address.toHex(address),
weight,
}));
data.owner = _ownerPermissions as Permission;
}
if (witnessPermission) {
Expand All @@ -2121,6 +2125,10 @@ export class TransactionBuilder {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
_witnessPermissions.type = 'Witness';
_witnessPermissions.keys = _witnessPermissions.keys.map(({ address, weight }) => ({
address: this.tronWeb.address.toHex(address),
weight,
}));
data.witness = _witnessPermissions;
}
if (activesPermissions) {
Expand All @@ -2131,6 +2139,12 @@ export class TransactionBuilder {
// @ts-ignore
activePermissions.type = 'Active';
});
_activesPermissions.forEach((_activesPermission) => {
_activesPermission.keys = _activesPermission.keys.map(({ address, weight }) => ({
address: this.tronWeb.address.toHex(address),
weight,
}));
});
data.actives = _activesPermissions as Permission[];
}

Expand Down
18 changes: 8 additions & 10 deletions src/lib/contract/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@ import type { ContractAbiInterface } from '../../types/ABI.js';
import { Address } from '../../types/Trx.js';
import { CreateSmartContractOptions } from '../../types/TransactionBuilder.js';

export interface IContract {
[key: string | number | symbol]: (...args: any[]) => ReturnType<Method['onMethod']>;
}

export class Contract {
tronWeb: TronWeb;
Expand All @@ -17,9 +14,10 @@ export class Contract {
bytecode?: false | string;
deployed?: boolean;
lastBlock?: false | number;
methods: Record<any, any>;
methodInstances: Record<any, any>;
methods: Record<string | number | symbol, (...args: any) => ReturnType<Method['onMethod']>>;
methodInstances: Record<string | number | symbol, Method>;
props: any[];
[key: string | number | symbol]: any;

constructor(tronWeb: TronWeb, abi: ContractAbiInterface = [], address: Address) {
if (!tronWeb || !(tronWeb instanceof TronWeb)) throw new Error('Expected instance of TronWeb');
Expand Down Expand Up @@ -49,14 +47,14 @@ export class Contract {

hasProperty(property: number | string | symbol) {
// eslint-disable-next-line no-prototype-builtins
return this.hasOwnProperty(property) || (this as unknown as IContract).__proto__.hasOwnProperty(property);
return this.hasOwnProperty(property) || (this as any).__proto__.hasOwnProperty(property);
}

loadAbi(abi: ContractAbiInterface) {
this.abi = abi;
this.methods = {};

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

abi.forEach((func) => {
// Don't build a method for constructor function. That's handled through contract create.
Expand All @@ -77,17 +75,17 @@ export class Contract {
this.methodInstances[signature] = method;

if (!this.hasProperty(name)) {
(this as unknown as IContract)[name] = methodCall;
(this as any)[name] = methodCall;
this.props.push(name);
}

if (!this.hasProperty(functionSelector!)) {
(this as unknown as IContract)[functionSelector!] = methodCall;
(this as any)[functionSelector!] = methodCall;
this.props.push(functionSelector);
}

if (!this.hasProperty(signature)) {
(this as unknown as IContract)[signature] = methodCall;
(this as any)[signature] = methodCall;
this.props.push(signature);
}
});
Expand Down
22 changes: 22 additions & 0 deletions src/lib/trx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { keccak256, toUtf8Bytes, recoverAddress, SigningKey, Signature } from '.
import { ADDRESS_PREFIX } from '../utils/address.js';
import { Validator } from '../paramValidator/index.js';
import { txCheck } from '../utils/transaction.js';
import { ecRecover } from '../utils/crypto.js';
import { Block } from '../types/APIResponse.js';
import {
Token,
Expand Down Expand Up @@ -508,6 +509,27 @@ export class Trx {
return contract;
}

ecRecover(transaction: SignedTransaction) {
return Trx.ecRecover(transaction);
}

static ecRecover(transaction: SignedTransaction): Address | Address[] {
if (!txCheck(transaction)) {
throw new Error('Invalid transaction');
}
if (!transaction.signature?.length) {
throw new Error('Transaction is not signed');
}
if (transaction.signature.length === 1) {
const tronAddress = ecRecover(transaction.txID, transaction.signature[0]);
return TronWeb.address.fromHex(tronAddress);
}
return transaction.signature.map((sig) => {
const tronAddress = ecRecover(transaction.txID, sig);
return TronWeb.address.fromHex(tronAddress);
});
}

async verifyMessage(message: string, signature: string, address = this.tronWeb.defaultAddress.base58, useTronHeader = true) {
if (!utils.isHex(message)) {
throw new Error('Expected hex message input');
Expand Down
2 changes: 1 addition & 1 deletion src/tronweb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const DEFAULT_VERSION = '4.7.1';

const FEE_LIMIT = 150000000;

const version = '6.0.0-beta.2';
const version = '6.0.0-beta.3';

function isValidOptions(options: unknown): options is TronWebOptions {
return (
Expand Down
11 changes: 10 additions & 1 deletion src/utils/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ADDRESS_PREFIX, ADDRESS_PREFIX_BYTE, ADDRESS_SIZE } from './address.js'
import { base64EncodeToString, base64DecodeFromString, hexStr2byteArray } from './code.js';
import { encode58, decode58 } from './base58.js';
import { BytesLike, byte2hexStr, byteArray2hexStr } from './bytes.js';
import { keccak256, sha256, SigningKey } from './ethersUtils.js';
import { keccak256, sha256, SigningKey, recoverAddress, arrayify, Signature } from './ethersUtils.js';
import { TypedDataEncoder } from './typedData.js';
import { secp256k1 as secp } from 'ethereum-cryptography/secp256k1';
import type { TypedDataDomain, TypedDataField } from 'ethers';
Expand Down Expand Up @@ -66,6 +66,15 @@ export function signTransaction(priKeyBytes: string | BytesLike, transaction: an
return transaction;
}

export function ecRecover(signedData: string, signature: string) {
signedData = '0x' + signedData.replace(/^0x/, '');
signature = '0x' + signature.replace(/^0x/, '');

const recovered = recoverAddress(arrayify(signedData), Signature.from(signature));
const tronAddress = ADDRESS_PREFIX + recovered.substring(2);
return tronAddress;
}

export function arrayToBase64String(a: number[]) {
return btoa(String.fromCharCode(...a));
}
Expand Down
15 changes: 7 additions & 8 deletions test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { assert } from 'chai';
import Config from './helpers/config.js';
import { TronWeb, providers } from './setup/TronWeb.js';
import { Contract, TronWeb, providers } from './setup/TronWeb.js';
import tronWebBuilder from './helpers/tronWebBuilder.js';
import BigNumber from 'bignumber.js';
import broadcaster from './helpers/broadcaster.js';
import wait from './helpers/wait.js';
import { Address } from '../src/types/Trx.js';
import { IContract } from '../src/lib/contract/index.js';
import { RawAxiosRequestHeaders } from 'axios';

const HttpProvider = providers.HttpProvider;
Expand Down Expand Up @@ -754,7 +753,7 @@ describe('TronWeb Instance', function () {
};
let tronWeb: TronWeb;
let contractAddress;
let contract: IContract;
let contract: Contract;

before(async function () {
tronWeb = tronWebBuilder.createInstance();
Expand Down Expand Up @@ -814,13 +813,13 @@ describe('TronWeb Instance', function () {
);

contractAddress = result.receipt.transaction.contract_address!;
contract = (await tronWeb.contract().at(contractAddress)) as unknown as IContract;
contract = await tronWeb.contract().at(contractAddress);
});

it('should emit an unconfirmed event and get it', async function () {
this.timeout(60000);
tronWeb.setPrivateKey(accounts.pks[1]);
let txId = await contract.emitNow(accounts.hex[2], 2000).send({
let txId = await contract.methods.emitNow(accounts.hex[2], 2000).send({
from: accounts.hex[1],
});
let events;
Expand All @@ -845,7 +844,7 @@ describe('TronWeb Instance', function () {
};
let tronWeb: TronWeb;
let contractAddress: Address;
let contract: IContract;
let contract: Contract;
let eventLength = 0;

before(async function () {
Expand Down Expand Up @@ -906,15 +905,15 @@ describe('TronWeb Instance', function () {
);

contractAddress = result.receipt.transaction.contract_address!;
contract = (await tronWeb.contract().at(contractAddress)) as unknown as IContract;
contract = await tronWeb.contract().at(contractAddress);
});

// available on trongrid
it.skip('should emit an event and wait for it', async function () {
this.timeout(60000);
await wait(120); // wait for abi syncing.
tronWeb.setPrivateKey(accounts.pks[3]);
await contract.emitNow(accounts.hex[4], 4000).send({
await contract.methods.emitNow(accounts.hex[4], 4000).send({
from: accounts.hex[3],
});
eventLength++;
Expand Down
4 changes: 2 additions & 2 deletions test/lib/contract/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import broadcaster from '../../helpers/broadcaster.js';
import tronWebBuilder from '../../helpers/tronWebBuilder.js';
import { TronWeb } from '../../setup/TronWeb.js';
import contracts from '../../fixtures/contracts';
import { IContract, Contract } from '../../../src/lib/contract';
import { Contract } from '../../../src/lib/contract';

const testCustomError = contracts.testCustomError;

Expand Down Expand Up @@ -41,7 +41,7 @@ describe('#contract.index', function () {
});

it('should revert with custom error', async () => {
const txid = await (customError as unknown as IContract).test(111).send();
const txid = await customError.test(111).send();
await wait(10);
const data = await tronWeb.trx.getTransactionInfo(txid);
const errorData = data.contractResult;
Expand Down
15 changes: 7 additions & 8 deletions test/lib/contract/method.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { IContract } from '../../../src/lib/contract/index.js';
import { Address } from '../../../src/types/Trx.js';
import { assert } from 'chai';
import assertThrow from '../../helpers/assertThrow.js';
import broadcaster from '../../helpers/broadcaster.js';
import tronWebBuilder from '../../helpers/tronWebBuilder.js';
import { TronWeb } from '../../setup/TronWeb.js';
import { Contract, TronWeb } from '../../setup/TronWeb.js';
import contracts from '../../fixtures/contracts.js';
const testRevertContract = contracts.testRevert;
const testSetValContract = contracts.testSetVal;
Expand All @@ -24,8 +23,8 @@ describe('#contract.method', function () {
});

describe('#send()', function () {
let testRevert: IContract;
let testSetVal: IContract;
let testRevert: Contract;
let testSetVal: Contract;

before(async function () {
const tx = await broadcaster(
Expand All @@ -38,7 +37,7 @@ describe('#contract.method', function () {
),
accounts.pks[0]
);
testRevert = (await tronWeb.contract().at(tx.transaction.contract_address)) as unknown as IContract;
testRevert = await tronWeb.contract().at(tx.transaction.contract_address);

const tx2 = await broadcaster(
tronWeb.transactionBuilder.createSmartContract(
Expand All @@ -50,7 +49,7 @@ describe('#contract.method', function () {
),
accounts.pks[0]
);
testSetVal = (await tronWeb.contract().at(tx2.transaction.contract_address)) as unknown as IContract;
testSetVal = await tronWeb.contract().at(tx2.transaction.contract_address);
});

it('should set accounts[2] as the owner and check it with getOwner(1)', async function () {
Expand Down Expand Up @@ -79,7 +78,7 @@ describe('#contract.method', function () {
});

describe('#call()', function () {
let testRevert: IContract;
let testRevert: Contract;

before(async function () {
const tx = await broadcaster(
Expand All @@ -92,7 +91,7 @@ describe('#contract.method', function () {
),
accounts.pks[0]
);
testRevert = (await tronWeb.contract().at(tx.transaction.contract_address)) as unknown as IContract;
testRevert = await tronWeb.contract().at(tx.transaction.contract_address);
await testRevert.setOwner(accounts.b58[2]).send();
});

Expand Down