tronweb-proxy
Version:
Modified tronweb with proxy support => (JavaScript SDK that encapsulates the TRON HTTP API)
1,318 lines • 68 kB
JavaScript
import { TronWeb } from '../../tronweb.js';
import { AbiCoder, keccak256 } from '../../utils/ethersUtils.js';
import { ADDRESS_PREFIX_REGEX, toHex } from '../../utils/address.js';
import { encodeParamsV2ByABI } from '../../utils/abi.js';
import { Validator } from '../../paramValidator/index.js';
import { isArray, isInteger, isNotNullOrUndefined, isObject, isString } from '../../utils/validations.js';
import { ContractType, } from '../../types/Contract.js';
import { createTransaction, deepCopyJson, fromUtf8, genContractAddress, resultManager, resultManagerTriggerSmartContract, getTransactionOptions, } from './helper.js';
export class TransactionBuilder {
tronWeb;
validator;
constructor(tronWeb) {
if (!tronWeb || !(tronWeb instanceof TronWeb)) {
throw new Error('Expected instance of TronWeb');
}
this.tronWeb = tronWeb;
this.validator = new Validator();
}
async sendTrx(to, amount = 0, from = this.tronWeb.defaultAddress.hex, options = {}) {
// accept amounts passed as strings
amount = parseInt(amount);
this.validator.notValid([
{
name: 'recipient',
type: 'address',
value: to,
},
{
name: 'origin',
type: 'address',
value: from,
},
{
names: ['recipient', 'origin'],
type: 'notEqual',
msg: 'Cannot transfer TRX to the same account',
},
{
name: 'amount',
type: 'integer',
gt: 0,
value: amount,
},
]);
const data = {
to_address: toHex(to),
owner_address: toHex(from),
amount: amount,
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.TransferContract, data, options?.permissionId, transactionOptions);
}
async sendToken(to, amount = 0, tokenId, from = this.tronWeb.defaultAddress.hex, options = {}) {
amount = parseInt(amount);
this.validator.notValid([
{
name: 'recipient',
type: 'address',
value: to,
},
{
name: 'origin',
type: 'address',
value: from,
},
{
names: ['recipient', 'origin'],
type: 'notEqual',
msg: 'Cannot transfer tokens to the same account',
},
{
name: 'amount',
type: 'integer',
gt: 0,
value: amount,
},
{
name: 'token ID',
type: 'tokenId',
value: tokenId,
},
]);
const data = {
to_address: toHex(to),
owner_address: toHex(from),
asset_name: fromUtf8(tokenId),
amount,
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.TransferAssetContract, data, options?.permissionId, transactionOptions);
}
async purchaseToken(issuerAddress, tokenId, amount = 0, buyer = this.tronWeb.defaultAddress.hex, options = {}) {
this.validator.notValid([
{
name: 'buyer',
type: 'address',
value: buyer,
},
{
name: 'issuer',
type: 'address',
value: issuerAddress,
},
{
names: ['buyer', 'issuer'],
type: 'notEqual',
msg: 'Cannot purchase tokens from same account',
},
{
name: 'amount',
type: 'integer',
gt: 0,
value: amount,
},
{
name: 'token ID',
type: 'tokenId',
value: tokenId,
},
]);
const data = {
to_address: toHex(issuerAddress),
owner_address: toHex(buyer),
asset_name: fromUtf8(tokenId),
amount: parseInt(amount),
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.ParticipateAssetIssueContract, data, options?.permissionId, transactionOptions);
}
async freezeBalance(amount = 0, duration = 3, resource = 'BANDWIDTH', ownerAddress = this.tronWeb.defaultAddress.hex, receiverAddress, options = {}) {
this.validator.notValid([
{
name: 'origin',
type: 'address',
value: ownerAddress,
},
{
name: 'receiver',
type: 'address',
value: receiverAddress,
optional: true,
},
{
name: 'amount',
type: 'integer',
gt: 0,
value: amount,
},
{
name: 'duration',
type: 'integer',
gte: 3,
value: duration,
},
{
name: 'resource',
type: 'resource',
value: resource,
msg: 'Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"',
},
]);
const data = {
owner_address: toHex(ownerAddress),
frozen_balance: parseInt(amount),
frozen_duration: parseInt(String(duration)),
};
if (resource !== 'BANDWIDTH') {
data.resource = resource;
}
if (isNotNullOrUndefined(receiverAddress) && toHex(receiverAddress) !== toHex(ownerAddress)) {
data.receiver_address = toHex(receiverAddress);
}
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.FreezeBalanceContract, data, options?.permissionId, transactionOptions);
}
async unfreezeBalance(resource = 'BANDWIDTH', address = this.tronWeb.defaultAddress.hex, receiverAddress, options = {}) {
this.validator.notValid([
{
name: 'origin',
type: 'address',
value: address,
},
{
name: 'receiver',
type: 'address',
value: receiverAddress,
optional: true,
},
{
name: 'resource',
type: 'resource',
value: resource,
msg: 'Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"',
},
]);
const data = {
owner_address: toHex(address),
};
if (resource !== 'BANDWIDTH') {
data.resource = resource;
}
if (isNotNullOrUndefined(receiverAddress) && toHex(receiverAddress) !== toHex(address)) {
data.receiver_address = toHex(receiverAddress);
}
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.UnfreezeBalanceContract, data, options?.permissionId, transactionOptions);
}
async freezeBalanceV2(amount = 0, resource = 'BANDWIDTH', address = this.tronWeb.defaultAddress.hex, options = {}) {
this.validator.notValid([
{
name: 'origin',
type: 'address',
value: address,
},
{
name: 'amount',
type: 'integer',
gt: 0,
value: amount,
},
{
name: 'resource',
type: 'resource',
value: resource,
msg: 'Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"',
},
]);
const data = {
owner_address: toHex(address),
frozen_balance: parseInt(amount),
};
if (resource !== 'BANDWIDTH') {
data.resource = resource;
}
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.FreezeBalanceV2Contract, data, options?.permissionId, transactionOptions);
}
async unfreezeBalanceV2(amount = 0, resource = 'BANDWIDTH', address = this.tronWeb.defaultAddress.hex, options = {}) {
this.validator.notValid([
{
name: 'origin',
type: 'address',
value: address,
},
{
name: 'amount',
type: 'integer',
gt: 0,
value: amount,
},
{
name: 'resource',
type: 'resource',
value: resource,
msg: 'Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"',
},
]);
const data = {
owner_address: toHex(address),
unfreeze_balance: parseInt(amount),
};
if (resource !== 'BANDWIDTH') {
data.resource = resource;
}
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.UnfreezeBalanceV2Contract, data, options?.permissionId, transactionOptions);
}
async cancelUnfreezeBalanceV2(address = this.tronWeb.defaultAddress.hex, options = {}) {
this.validator.notValid([
{
name: 'origin',
type: 'address',
value: address,
},
]);
const data = {
owner_address: toHex(address),
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.CancelAllUnfreezeV2Contract, data, options?.permissionId, transactionOptions);
}
async delegateResource(amount = 0, receiverAddress, resource = 'BANDWIDTH', address = this.tronWeb.defaultAddress.hex, lock = false, lockPeriod, options = {}) {
this.validator.notValid([
{
name: 'amount',
type: 'integer',
gt: 0,
value: amount,
},
{
name: 'resource',
type: 'resource',
value: resource,
msg: 'Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"',
},
{
name: 'receiver',
type: 'address',
value: receiverAddress,
},
{
name: 'origin',
type: 'address',
value: address,
},
{
name: 'lock',
type: 'boolean',
value: lock,
},
{
name: 'lock period',
type: 'integer',
gte: 0,
value: lockPeriod,
optional: true,
},
]);
if (toHex(receiverAddress) === toHex(address)) {
throw new Error('Receiver address must not be the same as owner address');
}
const data = {
owner_address: toHex(address),
receiver_address: toHex(receiverAddress),
balance: parseInt(amount),
};
if (resource !== 'BANDWIDTH') {
data.resource = resource;
}
if (lock) {
data.lock = lock;
if (isNotNullOrUndefined(lockPeriod)) {
data.lock_period = lockPeriod;
}
}
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.DelegateResourceContract, data, options?.permissionId, transactionOptions);
}
async undelegateResource(amount = 0, receiverAddress, resource = 'BANDWIDTH', address = this.tronWeb.defaultAddress.hex, options = {}) {
this.validator.notValid([
{
name: 'origin',
type: 'address',
value: address,
},
{
name: 'receiver',
type: 'address',
value: receiverAddress,
},
{
name: 'amount',
type: 'integer',
gt: 0,
value: amount,
},
{
name: 'resource',
type: 'resource',
value: resource,
msg: 'Invalid resource provided: Expected "BANDWIDTH" or "ENERGY"',
},
]);
if (toHex(receiverAddress) === toHex(address)) {
throw new Error('Receiver address must not be the same as owner address');
}
const data = {
owner_address: toHex(address),
receiver_address: toHex(receiverAddress),
balance: parseInt(amount),
};
if (resource !== 'BANDWIDTH') {
data.resource = resource;
}
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.UnDelegateResourceContract, data, options?.permissionId, transactionOptions);
}
async withdrawExpireUnfreeze(address = this.tronWeb.defaultAddress.hex, options = {}) {
this.validator.notValid([
{
name: 'origin',
type: 'address',
value: address,
},
]);
const data = {
owner_address: toHex(address),
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.WithdrawExpireUnfreezeContract, data, options?.permissionId, transactionOptions);
}
async withdrawBlockRewards(address = this.tronWeb.defaultAddress.hex, options = {}) {
this.validator.notValid([
{
name: 'origin',
type: 'address',
value: address,
},
]);
const data = {
owner_address: toHex(address),
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.WithdrawBalanceContract, data, options?.permissionId, transactionOptions);
}
async applyForSR(address = this.tronWeb.defaultAddress.hex, url = '', options = {}) {
this.validator.notValid([
{
name: 'origin',
type: 'address',
value: address,
},
{
name: 'url',
type: 'url',
value: url,
msg: 'Invalid url provided',
},
{
name: 'url',
type: 'string',
value: url,
lte: 256,
msg: 'Invalid url provided',
},
]);
const data = {
owner_address: toHex(address),
url: fromUtf8(url),
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.WitnessCreateContract, data, options?.permissionId, transactionOptions);
}
async vote(votes = {}, voterAddress = this.tronWeb.defaultAddress.hex, options = {}) {
this.validator.notValid([
{
name: 'voter',
type: 'address',
value: voterAddress,
},
{
name: 'votes',
type: 'notEmptyObject',
value: votes,
},
]);
const entries = Object.entries(votes);
for (const [srAddress, voteCount] of entries) {
this.validator.notValid([
{
name: 'SR',
type: 'address',
value: srAddress,
},
{
name: 'vote count',
type: 'integer',
gt: 0,
value: voteCount,
msg: 'Invalid vote count provided for SR: ' + srAddress,
},
]);
}
const voteList = entries.map(([srAddress, voteCount]) => {
return {
vote_address: toHex(srAddress),
vote_count: parseInt(voteCount),
};
});
const data = {
owner_address: toHex(voterAddress),
votes: voteList,
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.VoteWitnessContract, data, options?.permissionId, transactionOptions);
}
async createSmartContract(options = {}, issuerAddress = this.tronWeb.defaultAddress.hex) {
const feeLimit = options.feeLimit || this.tronWeb.feeLimit;
let userFeePercentage = options.userFeePercentage;
if (typeof userFeePercentage !== 'number' && !userFeePercentage) {
userFeePercentage = 100;
}
const originEnergyLimit = options.originEnergyLimit || 10_000_000;
const callValue = options.callValue || 0;
const tokenValue = options.tokenValue;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const tokenId = options.tokenId || options.token_id;
let { abi } = options;
const { parameters = [] } = options;
let parameter = '';
const { bytecode = false, name = '' } = options;
if (abi && isString(abi)) {
try {
abi = JSON.parse(abi);
}
catch {
throw new Error('Invalid options.abi provided');
}
}
const newAbi = abi;
let entries = newAbi;
if (newAbi.entrys) {
entries = newAbi.entrys;
}
if (!isArray(entries))
throw new Error('Invalid options.abi provided');
const payable = entries.some((func) => {
return func.type === 'constructor' && 'payable' === func.stateMutability.toLowerCase();
});
this.validator.notValid([
{
name: 'bytecode',
type: 'hex',
value: bytecode,
},
{
name: 'feeLimit',
type: 'integer',
value: feeLimit,
gt: 0,
},
{
name: 'callValue',
type: 'integer',
value: callValue,
gte: 0,
},
{
name: 'userFeePercentage',
type: 'integer',
value: userFeePercentage,
gte: 0,
lte: 100,
},
{
name: 'originEnergyLimit',
type: 'integer',
value: originEnergyLimit,
gte: 0,
lte: 10_000_000,
},
{
name: 'parameters',
type: 'array',
value: parameters,
},
{
name: 'issuer',
type: 'address',
value: issuerAddress,
},
{
name: 'tokenValue',
type: 'integer',
value: tokenValue,
gte: 0,
optional: true,
},
{
name: 'tokenId',
type: 'integer',
value: tokenId,
gte: 0,
optional: true,
},
]);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (!payable && (callValue > 0 || tokenValue > 0))
throw new Error('When contract is not payable, options.callValue and options.tokenValue must be 0');
const { rawParameter, funcABIV2, parametersV2 } = options;
if (rawParameter && isString(rawParameter)) {
parameter = rawParameter.replace(/^(0x)/, '');
}
else if (funcABIV2) {
parameter = encodeParamsV2ByABI(funcABIV2, parametersV2).replace(/^(0x)/, '');
}
else {
let constructorParams = entries.find((it) => {
return it.type === 'constructor';
});
if (typeof constructorParams !== 'undefined' && constructorParams) {
const abiCoder = new AbiCoder();
const types = [];
const values = [];
constructorParams = constructorParams.inputs;
if (parameters.length != constructorParams.length)
throw new Error(`constructor needs ${constructorParams.length} but ${parameters.length} provided`);
for (let i = 0; i < parameters.length; i++) {
let type = constructorParams[i].type;
let value = parameters[i];
if (!type || !isString(type) || !type.length)
throw new Error('Invalid parameter type provided: ' + type);
const replaceAddressPrefix = (value) => {
if (isArray(value)) {
return value.map((v) => replaceAddressPrefix(v));
}
return toHex(value).replace(ADDRESS_PREFIX_REGEX, '0x');
};
if (type === 'address')
value = replaceAddressPrefix(value);
else if (type.match(/^([^\x5b]*)(\x5b|$)/)?.[0] === 'address[')
value = replaceAddressPrefix(value);
else if (/trcToken/.test(type)) {
type = type.replace(/trcToken/, 'uint256');
}
types.push(type);
values.push(value);
}
try {
parameter = abiCoder.encode(types, values).replace(/^(0x)/, '');
}
catch (ex) {
throw new Error(ex);
}
}
else {
parameter = '';
}
}
const args = {
owner_address: toHex(issuerAddress),
fee_limit: parseInt(feeLimit),
call_value: parseInt(callValue),
consume_user_resource_percent: userFeePercentage,
origin_energy_limit: originEnergyLimit,
abi: JSON.stringify(abi),
bytecode,
parameter,
name,
};
// tokenValue and tokenId can cause errors if provided when the trx10 proposal has not been approved yet. So we set them only if they are passed to the method.
if (isNotNullOrUndefined(tokenValue)) {
args.call_token_value = parseInt(tokenValue);
}
if (isNotNullOrUndefined(tokenId)) {
args.token_id = parseInt(tokenId);
}
const contract = {};
contract.owner_address = args.owner_address;
if (isNotNullOrUndefined(args.call_token_value)) {
contract.call_token_value = args.call_token_value;
}
if (isNotNullOrUndefined(args.token_id)) {
contract.token_id = args.token_id;
}
const new_contract = (contract.new_contract = {});
if (args.abi) {
new_contract.abi = {
entrys: JSON.parse(args.abi),
};
}
else {
new_contract.abi = {};
}
if (args.call_value) {
new_contract.call_value = args.call_value;
}
new_contract.consume_user_resource_percent = args.consume_user_resource_percent;
new_contract.origin_energy_limit = args.origin_energy_limit;
new_contract.origin_address = args.origin_address ?? args.owner_address;
if (args.bytecode + args.parameter) {
new_contract.bytecode = (args.bytecode + args.parameter).replace(/^0x/, '');
}
if (isNotNullOrUndefined(args.name)) {
new_contract.name = args.name;
}
const transactionOptions = getTransactionOptions(options);
const tx = (await createTransaction(this.tronWeb, ContractType.CreateSmartContract, contract, options?.permissionId, {
...transactionOptions,
fee_limit: args.fee_limit,
}));
tx.contract_address = genContractAddress(args.owner_address, tx.txID);
return tx;
}
async triggerSmartContract(contractAddress, functionSelector, options, parameters, issuerAddress) {
const params = [
contractAddress,
functionSelector,
options,
parameters,
issuerAddress,
];
if (typeof params[2] !== 'object') {
params[2] = {
feeLimit: params[2],
callValue: params[3],
};
params.splice(3, 1);
}
if (params[2]?.txLocal) {
return this._triggerSmartContractLocal(...params);
}
return this._triggerSmartContract(...params);
}
async triggerConstantContract(contractAddress, functionSelector, options = {}, parameters = [], issuerAddress = this.tronWeb.defaultAddress.hex) {
options._isConstant = true;
return this._triggerSmartContract(contractAddress, functionSelector, options, parameters, issuerAddress);
}
async triggerConfirmedConstantContract(contractAddress, functionSelector, options = {}, parameters = [], issuerAddress = this.tronWeb.defaultAddress.hex) {
options._isConstant = true;
options.confirmed = true;
return this._triggerSmartContract(contractAddress, functionSelector, options, parameters, issuerAddress);
}
async estimateEnergy(contractAddress, functionSelector, options = {}, parameters = [], issuerAddress = this.tronWeb.defaultAddress.hex) {
options.estimateEnergy = true;
const result = await this._triggerSmartContract(contractAddress, functionSelector, options, parameters, issuerAddress);
return result;
}
async deployConstantContract(options = { input: '', ownerAddress: '' }) {
const { input, ownerAddress, tokenId, tokenValue, callValue = 0 } = options;
this.validator.notValid([
{
name: 'input',
type: 'not-empty-string',
value: input,
},
{
name: 'callValue',
type: 'integer',
value: callValue,
gte: 0,
},
{
name: 'owner',
type: 'address',
value: ownerAddress,
},
{
name: 'tokenValue',
type: 'integer',
value: tokenValue,
gte: 0,
optional: true,
},
{
name: 'tokenId',
type: 'integer',
value: tokenId,
gte: 0,
optional: true,
},
]);
const args = {
data: input,
owner_address: toHex(ownerAddress),
call_value: callValue,
};
if (tokenId) {
args.token_id = tokenId;
}
if (tokenValue) {
args.call_token_value = tokenValue;
}
const pathInfo = `wallet${options.confirmed ? 'solidity' : ''}/estimateenergy`;
const transaction = await this.tronWeb[options.confirmed ? 'solidityNode' : 'fullNode'].request(pathInfo, args, 'post');
if (transaction.Error)
throw new Error(transaction.Error);
if (transaction.result && transaction.result.message) {
throw new Error(this.tronWeb.toUtf8(transaction.result.message));
}
return transaction;
}
_getTriggerSmartContractArgs(contractAddress, functionSelector, options, parameters, issuerAddress, tokenValue, tokenId, callValue, feeLimit) {
const args = {
contract_address: toHex(contractAddress),
owner_address: toHex(issuerAddress),
};
if (functionSelector && isString(functionSelector)) {
functionSelector = functionSelector.replace(/\s*/g, '');
let parameterStr;
if (parameters.length) {
const abiCoder = new AbiCoder();
let types = [];
const values = [];
for (let i = 0; i < parameters.length; i++) {
let { value } = parameters[i];
const { type } = parameters[i];
if (!type || !isString(type) || !type.length)
throw new Error('Invalid parameter type provided: ' + type);
const replaceAddressPrefix = (value) => {
if (isArray(value)) {
return value.map((v) => replaceAddressPrefix(v));
}
return toHex(value).replace(ADDRESS_PREFIX_REGEX, '0x');
};
if (type === 'address')
value = replaceAddressPrefix(value);
else if (type.match(/^([^\x5b]*)(\x5b|$)/)?.[0] === 'address[')
value = replaceAddressPrefix(value);
types.push(type);
values.push(value);
}
try {
// workaround for unsupported trcToken type
types = types.map((type) => {
if (/trcToken/.test(type)) {
type = type.replace(/trcToken/, 'uint256');
}
return type;
});
parameterStr = abiCoder.encode(types, values).replace(/^(0x)/, '');
}
catch (ex) {
throw new Error(ex);
}
}
else
parameterStr = '';
// work for abiv2 if passed the function abi in options
if (options.funcABIV2) {
parameterStr = encodeParamsV2ByABI(options.funcABIV2, options.parametersV2).replace(/^(0x)/, '');
}
if (options.shieldedParameter && isString(options.shieldedParameter)) {
parameterStr = options.shieldedParameter.replace(/^(0x)/, '');
}
if (options.rawParameter && isString(options.rawParameter)) {
parameterStr = options.rawParameter.replace(/^(0x)/, '');
}
args.function_selector = functionSelector;
args.parameter = parameterStr;
}
else if (options.input) {
args.data = options.input;
}
args.call_value = parseInt(callValue);
if (isNotNullOrUndefined(tokenValue))
args.call_token_value = parseInt(tokenValue);
if (isNotNullOrUndefined(tokenId))
args.token_id = parseInt(tokenId);
if (!(options._isConstant || options.estimateEnergy)) {
args.fee_limit = parseInt(feeLimit);
}
if (options.permissionId) {
args.Permission_id = options.permissionId;
}
return args;
}
async _triggerSmartContractLocal(contractAddress, functionSelector, options = {}, parameters = [], issuerAddress = this.tronWeb.defaultAddress.hex) {
const { tokenValue, tokenId, callValue, feeLimit } = Object.assign({
callValue: 0,
feeLimit: this.tronWeb.feeLimit,
}, options);
this.validator.notValid([
{
name: 'feeLimit',
type: 'integer',
value: feeLimit,
gt: 0,
},
{
name: 'callValue',
type: 'integer',
value: callValue,
gte: 0,
},
{
name: 'parameters',
type: 'array',
value: parameters,
},
{
name: 'contract',
type: 'address',
value: contractAddress,
},
{
name: 'issuer',
type: 'address',
value: issuerAddress,
optional: true,
},
{
name: 'tokenValue',
type: 'integer',
value: tokenValue,
gte: 0,
optional: true,
},
{
name: 'tokenId',
type: 'integer',
value: tokenId,
gte: 0,
optional: true,
},
]);
const args = this._getTriggerSmartContractArgs(contractAddress, functionSelector, options, parameters, issuerAddress, tokenValue, tokenId, callValue, feeLimit);
if (args.function_selector) {
args.data = keccak256(Buffer.from(args.function_selector, 'utf-8')).toString().substring(2, 10) + args.parameter;
}
const value = {
data: args.data,
owner_address: args.owner_address,
contract_address: args.contract_address,
};
if (args.call_value) {
value.call_value = args.call_value;
}
if (args.call_token_value) {
value.call_token_value = args.call_token_value;
}
if (args.token_id) {
value.token_id = args.token_id;
}
const transactionOptions = getTransactionOptions(options);
const transaction = await createTransaction(this.tronWeb, ContractType.TriggerSmartContract, value, options.permissionId, {
...transactionOptions,
fee_limit: args.fee_limit,
});
return {
result: {
result: true,
},
transaction,
};
}
async _triggerSmartContract(contractAddress, functionSelector, options = {}, parameters = [], issuerAddress = this.tronWeb.defaultAddress.hex) {
const { tokenValue, tokenId, callValue, feeLimit } = Object.assign({
callValue: 0,
feeLimit: this.tronWeb.feeLimit,
}, options);
this.validator.notValid([
{
name: 'feeLimit',
type: 'integer',
value: feeLimit,
gt: 0,
},
{
name: 'callValue',
type: 'integer',
value: callValue,
gte: 0,
},
{
name: 'parameters',
type: 'array',
value: parameters,
},
{
name: 'contract',
type: 'address',
value: contractAddress,
},
{
name: 'issuer',
type: 'address',
value: issuerAddress,
optional: true,
},
{
name: 'tokenValue',
type: 'integer',
value: tokenValue,
gte: 0,
optional: true,
},
{
name: 'tokenId',
type: 'integer',
value: tokenId,
gte: 0,
optional: true,
},
]);
const args = this._getTriggerSmartContractArgs(contractAddress, functionSelector, options, parameters, issuerAddress, tokenValue, tokenId, callValue, feeLimit);
let pathInfo = 'triggersmartcontract';
if (options._isConstant) {
pathInfo = 'triggerconstantcontract';
}
else if (options.estimateEnergy) {
pathInfo = 'estimateenergy';
}
pathInfo = `wallet${options.confirmed ? 'solidity' : ''}/${pathInfo}`;
const transaction = await this.tronWeb[options.confirmed ? 'solidityNode' : 'fullNode'].request(pathInfo, args, 'post');
return resultManagerTriggerSmartContract(transaction, args, options);
}
async clearABI(contractAddress, ownerAddress = this.tronWeb.defaultAddress.hex, options = {}) {
if (!TronWeb.isAddress(contractAddress))
throw new Error('Invalid contract address provided');
if (!TronWeb.isAddress(ownerAddress))
throw new Error('Invalid owner address provided');
const data = {
contract_address: toHex(contractAddress),
owner_address: toHex(ownerAddress),
};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
if (this.tronWeb.trx.cache.contracts[contractAddress]) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
delete this.tronWeb.trx.cache.contracts[contractAddress];
}
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.ClearABIContract, data, options?.permissionId, transactionOptions);
}
async updateBrokerage(brokerage, ownerAddress = this.tronWeb.defaultAddress.hex, options = {}) {
if (!isNotNullOrUndefined(brokerage))
throw new Error('Invalid brokerage provided');
if (!isInteger(brokerage) || brokerage < 0 || brokerage > 100)
throw new Error('Brokerage must be an integer between 0 and 100');
if (!TronWeb.isAddress(ownerAddress))
throw new Error('Invalid owner address provided');
const data = {
brokerage: parseInt(brokerage),
owner_address: toHex(ownerAddress),
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.UpdateBrokerageContract, data, options?.permissionId, transactionOptions);
}
async createToken(options = {}, issuerAddress = this.tronWeb.defaultAddress.hex) {
const { name = false, abbreviation = false, description = '', url = false, totalSupply = 0, trxRatio = 1, // How much TRX will `tokenRatio` cost
tokenRatio = 1, // How many tokens will `trxRatio` afford
saleStart = Date.now(), saleEnd = false, freeBandwidth = 0, // The creator's "donated" bandwidth for use by token holders
freeBandwidthLimit = 0, // Out of `totalFreeBandwidth`, the amount each token holder get
frozenAmount = 0, frozenDuration = 0,
// for now there is no default for the following values
voteScore, precision, } = options;
this.validator.notValid([
{
name: 'Supply amount',
type: 'positive-integer',
value: totalSupply,
},
{
name: 'TRX ratio',
type: 'positive-integer',
value: trxRatio,
},
{
name: 'Token ratio',
type: 'positive-integer',
value: tokenRatio,
},
{
name: 'token abbreviation',
type: 'string',
value: abbreviation,
lte: 32,
gt: 0,
},
{
name: 'token name',
type: 'not-empty-string',
value: name,
},
{
name: 'token description',
type: 'string',
value: description,
lte: 200,
},
{
name: 'token url',
type: 'url',
value: url,
},
{
name: 'token url',
type: 'string',
value: url,
lte: 256,
},
{
name: 'issuer',
type: 'address',
value: issuerAddress,
},
{
name: 'sale start timestamp',
type: 'integer',
value: saleStart,
gte: Date.now(),
},
{
name: 'sale end timestamp',
type: 'integer',
value: saleEnd,
gt: saleStart,
},
{
name: 'Frozen supply',
type: 'integer',
value: frozenAmount,
gte: 0,
},
{
name: 'Frozen duration',
type: 'integer',
value: frozenDuration,
gte: 0,
},
]);
if (isNotNullOrUndefined(voteScore) && (!isInteger(voteScore) || voteScore <= 0))
throw new Error('voteScore must be a positive integer greater than 0');
if (isNotNullOrUndefined(precision) && (!isInteger(precision) || precision < 0 || precision > 6))
throw new Error('precision must be a positive integer >= 0 and <= 6');
const data = {
owner_address: toHex(issuerAddress),
name: fromUtf8(name),
abbr: fromUtf8(abbreviation),
description: fromUtf8(description),
url: fromUtf8(url),
total_supply: parseInt(totalSupply),
trx_num: parseInt(trxRatio),
num: parseInt(tokenRatio),
start_time: parseInt(saleStart),
end_time: parseInt(saleEnd),
frozen_supply: [
{
frozen_amount: parseInt(frozenAmount),
frozen_days: parseInt(frozenDuration),
},
],
};
['name', 'abbr', 'description', 'url'].forEach((key) => {
if (!data[key]) {
delete data[key];
}
});
if (!(parseInt(frozenAmount) > 0)) {
delete data.frozen_supply;
}
if (freeBandwidth && !isNaN(parseInt(freeBandwidth)) && parseInt(freeBandwidth) >= 0) {
data.free_asset_net_limit = parseInt(freeBandwidth);
}
if (freeBandwidthLimit && !isNaN(parseInt(freeBandwidthLimit)) && parseInt(freeBandwidthLimit) >= 0) {
data.public_free_asset_net_limit = parseInt(freeBandwidthLimit);
}
if (precision && !isNaN(parseInt(precision))) {
data.precision = parseInt(precision);
}
if (voteScore && !isNaN(parseInt(voteScore))) {
data.vote_score = parseInt(voteScore);
}
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.AssetIssueContract, data, options?.permissionId, transactionOptions);
}
async createAccount(accountAddress, address = this.tronWeb.defaultAddress.hex, options = {}) {
this.validator.notValid([
{
name: 'account',
type: 'address',
value: accountAddress,
},
{
name: 'origin',
type: 'address',
value: address,
},
]);
const data = {
owner_address: toHex(address),
account_address: toHex(accountAddress),
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.AccountCreateContract, data, options?.permissionId, transactionOptions);
}
async updateAccount(accountName, address = this.tronWeb.defaultAddress.hex, options = {}) {
this.validator.notValid([
{
name: 'Name',
type: 'string',
lte: 200,
gt: 0,
value: accountName,
msg: 'Invalid accountName',
},
{
name: 'origin',
type: 'address',
value: address,
},
]);
const data = {
account_name: fromUtf8(accountName),
owner_address: toHex(address),
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.AccountUpdateContract, data, options?.permissionId, transactionOptions);
}
async setAccountId(accountId, address = this.tronWeb.defaultAddress.hex, options = {}) {
if (accountId && isString(accountId) && accountId.startsWith('0x')) {
accountId = accountId.slice(2);
}
this.validator.notValid([
{
name: 'accountId',
type: 'hex',
value: accountId,
},
{
name: 'accountId',
type: 'string',
lte: 32,
gte: 8,
value: accountId,
},
{
name: 'origin',
type: 'address',
value: address,
},
]);
const data = {
account_id: accountId,
owner_address: toHex(address),
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.SetAccountIdContract, data, options?.permissionId, transactionOptions);
}
async updateToken(options = {}, issuerAddress = this.tronWeb.defaultAddress.hex) {
const { description = '', url = false, freeBandwidth = 0, // The creator's "donated" bandwidth for use by token holders
freeBandwidthLimit = 0, // Out of `totalFreeBandwidth`, the amount each token holder get
} = options;
this.validator.notValid([
{
name: 'token description',
type: 'string',
value: description,
lte: 200,
},
{
name: 'token url',
type: 'url',
value: url,
},
{
name: 'token url',
type: 'string',
value: url,
lte: 256,
},
{
name: 'issuer',
type: 'address',
value: issuerAddress,
},
]);
const data = {
owner_address: toHex(issuerAddress),
description: fromUtf8(description),
url: fromUtf8(url),
};
if (freeBandwidth && !isNaN(parseInt(freeBandwidth)) && parseInt(freeBandwidth) >= 0) {
data.new_limit = parseInt(freeBandwidth);
}
if (freeBandwidthLimit && !isNaN(parseInt(freeBandwidthLimit)) && parseInt(freeBandwidthLimit) >= 0) {
data.new_public_limit = parseInt(freeBandwidthLimit);
}
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.UpdateAssetContract, data, options?.permissionId, transactionOptions);
}
async sendAsset(to, amount = 0, tokenId, from = this.tronWeb.defaultAddress.hex, options = {}) {
return this.sendToken(to, amount, tokenId, from, options);
}
async purchaseAsset(issuerAddress, tokenId, amount = 0, buyer = this.tronWeb.defaultAddress.hex, options = {}) {
return this.purchaseToken(issuerAddress, tokenId, amount, buyer, options);
}
async createAsset(options, issuerAddress) {
return this.createToken(options, issuerAddress);
}
async updateAsset(options = {}, issuerAddress = this.tronWeb.defaultAddress.hex) {
return this.updateToken(options, issuerAddress);
}
/**
* Creates a proposal to modify the network.
* Can only be created by a current Super Representative.
*/
async createProposal(parameters, issuerAddress = this.tronWeb.defaultAddress.hex, options = {}) {
this.validator.notValid([
{
name: 'issuer',
type: 'address',
value: issuerAddress,
},
]);
const invalid = 'Invalid proposal parameters provided';
if (!parameters)
throw new Error(invalid);
const newParams = isArray(parameters) ? parameters : [parameters];
for (const parameter of newParams) {
if (!isObject(parameter))
throw new Error(invalid);
}
const data = {
owner_address: toHex(issuerAddress),
parameters: newParams,
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(this.tronWeb, ContractType.ProposalCreateContract, data, options?.permissionId, transactionOptions);
}
/**
* Deletes a network modification proposal that the owner issued.
* Only current Super Representative can vote on a proposal.
*/
async deleteProposal(proposalID, issuerAddress = this.tronWeb.defaultAddress.hex, options = {}) {
this.validator.notValid([
{
name: 'issuer',
type: 'address',
value: issuerAddress,
},
{
name: 'proposalID',
type: 'integer',
value: proposalID,
gte: 0,
},
]);
const data = {
owner_address: toHex(issuerAddress),
proposal_id: parseInt(proposalID),
};
const transactionOptions = getTransactionOptions(options);
return createTransaction(