UNPKG

@hashgraph/sdk

Version:
1,061 lines (965 loc) 29 kB
// SPDX-License-Identifier: Apache-2.0 import ContractLogInfo from "./ContractLogInfo.js"; import ContractId from "./ContractId.js"; import AccountId from "../account/AccountId.js"; import BigNumber from "bignumber.js"; import * as hex from "../encoding/hex.js"; import * as utf8 from "../encoding/utf8.js"; import * as util from "../util.js"; // eslint-disable-next-line @typescript-eslint/no-unused-vars import { ParamType, defaultAbiCoder } from "@ethersproject/abi"; import Long from "long"; import ContractNonceInfo from "./ContractNonceInfo.js"; /** * @typedef {import("./ContractStateChange.js").default} ContractStateChange */ /** * @namespace proto * @typedef {import("@hashgraph/proto").proto.IContractFunctionResult} HieroProto.proto.IContractFunctionResult * @typedef {import("@hashgraph/proto").proto.IContractID} HieroProto.proto.IContractID */ /** * The result returned by a call to a smart contract function. This is part of the response to * a ContractCallLocal query, and is in the record for a ContractCall or ContractCreateInstance * transaction. The ContractCreateInstance transaction record has the results of the call to * the constructor. */ export default class ContractFunctionResult { /** * Constructor isn't part of the stable API * * @param {object} result * @param {boolean} result._createResult * @param {?ContractId} result.contractId * @param {?string} result.errorMessage * @param {Uint8Array} result.bloom * @param {Long} result.gasUsed * @param {ContractLogInfo[]} result.logs * @param {ContractId[]} result.createdContractIds * @param {Uint8Array | null} result.evmAddress * @param {Uint8Array} result.bytes * @param {Long} result.gas * @param {Long} result.amount * @param {Uint8Array} result.functionParameters * @param {?AccountId} result.senderAccountId * @param {ContractStateChange[]} result.stateChanges * @param {ContractNonceInfo[]} result.contractNonces * @param {Long | null} result.signerNonce */ constructor(result) { /** * Determines if this result came from `record.contractCreateResult` if true * or `record.contractCallResult` if false */ this._createResult = result._createResult; /** * The smart contract instance whose function was called. */ this.contractId = result.contractId; this.bytes = result.bytes; /** * Message In case there was an error during smart contract execution. */ this.errorMessage = result.errorMessage; /** * Bloom filter for record */ this.bloom = result.bloom; /** * Units of gas used to execute contract. */ this.gasUsed = result.gasUsed; /** * The log info for events returned by the function. */ this.logs = result.logs; /** * @deprecated the list of smart contracts that were created by the function call. * * The created ids will now _also_ be externalized through internal transaction * records, where each record has its alias field populated with the new contract's * EVM address. (This is needed for contracts created with CREATE2, since * there is no longer a simple relationship between the new contract's 0.0.X id * and its Solidity address.) */ // eslint-disable-next-line deprecation/deprecation this.createdContractIds = result.createdContractIds; this.evmAddress = result.evmAddress; /** * @deprecated - Use mirror node for contract traceability instead */ // eslint-disable-next-line deprecation/deprecation this.stateChanges = result.stateChanges; /** * The amount of gas available for the call, aka the gasLimit. */ this.gas = result.gas; /** * Number of tinybars sent (the function must be payable if this is nonzero). */ this.amount = result.amount; /** * The parameters passed into the contract call. */ this.functionParameters = result.functionParameters; /** * The account that is the "sender." If not present it is the accountId from the transactionId. * * This field should only be populated when the paired TransactionBody in the record stream is not a * ContractCreateTransactionBody or a ContractCallTransactionBody. */ this.senderAccountId = result.senderAccountId; /** * A list of updated contract account nonces containing the new nonce value for each contract account. * This is always empty in a ContractCallLocalResponse#ContractFunctionResult message, since no internal creations can happen in a static EVM call. */ this.contractNonces = result.contractNonces; /** * If not null this field specifies what the value of the signer account nonce is post transaction execution. * For transactions that don't update the signer nonce (like HAPI ContractCall and ContractCreate transactions) this field should be null. */ this.signerNonce = result.signerNonce; } /** * @param {HieroProto.proto.IContractFunctionResult} result * @param {boolean} _createResult * @returns {ContractFunctionResult} */ static _fromProtobuf(result, _createResult) { const contractId = /** @type {HieroProto.proto.IContractID | null} */ ( result.contractID ); const gasUsed = /** @type {Long} */ (result.gasUsed); const gas = /** @type {Long} */ (result.gas ? result.gas : -1); const amount = /** @type {Long} */ (result.amount ? result.amount : -1); return new ContractFunctionResult({ _createResult, bytes: /** @type {Uint8Array} */ (result.contractCallResult), contractId: contractId != null ? ContractId._fromProtobuf(contractId) : null, errorMessage: result.errorMessage != null ? result.errorMessage : null, bloom: /** @type {Uint8Array} */ (result.bloom), gasUsed: gasUsed instanceof Long ? gasUsed : Long.fromValue(gasUsed), logs: (result.logInfo != null ? result.logInfo : []).map((info) => ContractLogInfo._fromProtobuf(info), ), createdContractIds: (result.createdContractIDs != null ? result.createdContractIDs : [] ).map((contractId) => ContractId._fromProtobuf(contractId)), evmAddress: result.evmAddress != null && Object.hasOwn(result.evmAddress, "value") && result.evmAddress.value != null ? result.evmAddress.value : null, stateChanges: [], gas: gas instanceof Long ? gas : Long.fromValue(gas), amount: amount instanceof Long ? amount : Long.fromValue(amount), functionParameters: /** @type {Uint8Array} */ ( result.functionParameters ), senderAccountId: result.senderId != null ? AccountId._fromProtobuf(result.senderId) : null, contractNonces: (result.contractNonces != null ? result.contractNonces : [] ).map((contractNonce) => ContractNonceInfo._fromProtobuf(contractNonce), ), signerNonce: result.signerNonce != null ? Object.hasOwn(result.signerNonce, "value") ? result.signerNonce.value || null : null : null, }); } /** * @returns {Uint8Array} */ asBytes() { return this.bytes; } /** * @param {number} [index] * @returns {string} */ getString(index) { return utf8.decode(this.getBytes(index)); } /** * @private * @param {number} [index] * @returns {Uint8Array} */ getBytes(index) { // Len should never be larger than Number.MAX // index * 32 is the position of the lenth // (index + 1) * 32 onward to (index + 1) * 32 + len will be the elements of the array // Arrays in solidity cannot be longer than 1024: // https://solidity.readthedocs.io/en/v0.4.21/introduction-to-smart-contracts.html const offset = this.getInt32(index); const len = util.safeView(this.bytes).getInt32(offset + 28); return this.bytes.subarray(offset + 32, offset + 32 + len); } /** * @param {number} [index] * @returns {Uint8Array} */ getBytes32(index) { return this.bytes.subarray( (index != null ? index : 0) * 32, (index != null ? index : 0) * 32 + 32, ); } /** * @param {number} [index] * @returns {boolean} */ getBool(index) { return this.bytes[(index != null ? index : 0) * 32 + 31] !== 0; } /** * @param {number} [index] * @returns {number} */ getInt8(index) { const position = (index != null ? index : 0) * 32 + 31; return util.safeView(this.bytes).getInt8(position); } /** * @param {number} [index] * @returns {number} */ getUint8(index) { return this.bytes[(index != null ? index : 0) * 32 + 31]; } /** * @param {number} [index] * @returns {number} */ getInt16(index) { // .getInt32() interprets as big-endian // Using DataView instead of Uint32Array because the latter interprets // using platform endianness which is little-endian on x86 const position = (index != null ? index : 0) * 32 + 28; return util.safeView(this.bytes).getInt32(position); } /** * @param {number} [index] * @returns {number} */ getUint16(index) { // .getUint32() interprets as big-endian // Using DataView instead of Uint32Array because the latter interprets // using platform endianness which is little-endian on x86 const position = (index != null ? index : 0) * 32 + 28; return util.safeView(this.bytes).getUint32(position); } /** * @param {number} [index] * @returns {number} */ getInt24(index) { // .getInt32() interprets as big-endian // Using DataView instead of Uint32Array because the latter interprets // using platform endianness which is little-endian on x86 const position = (index != null ? index : 0) * 32 + 28; return util.safeView(this.bytes).getInt32(position); } /** * @param {number} [index] * @returns {number} */ getUint24(index) { // .getUint32() interprets as big-endian // Using DataView instead of Uint32Array because the latter interprets // using platform endianness which is little-endian on x86 const position = (index != null ? index : 0) * 32 + 28; return util.safeView(this.bytes).getUint32(position); } /** * @param {number} [index] * @returns {number} */ getInt32(index) { // .getInt32() interprets as big-endian // Using DataView instead of Uint32Array because the latter interprets // using platform endianness which is little-endian on x86 const position = (index != null ? index : 0) * 32 + 28; return util.safeView(this.bytes).getInt32(position); } /** * @param {number} [index] * @returns {number} */ getUint32(index) { // .getUint32() interprets as big-endian // Using DataView instead of Uint32Array because the latter interprets // using platform endianness which is little-endian on x86 const position = (index != null ? index : 0) * 32 + 28; return util.safeView(this.bytes).getUint32(position); } /** * @param {number} [index] * @returns {BigNumber} */ getInt40(index) { const result = defaultAbiCoder.decode( ["int40"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint40(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(27, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt48(index) { const result = defaultAbiCoder.decode( ["int48"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint48(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(26, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt56(index) { const result = defaultAbiCoder.decode( ["int56"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint56(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(25, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt64(index) { const result = defaultAbiCoder.decode( ["int64"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint64(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(24, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt72(index) { const result = defaultAbiCoder.decode( ["int72"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint72(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(23, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt80(index) { const result = defaultAbiCoder.decode( ["int80"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint80(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(22, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt88(index) { const result = defaultAbiCoder.decode( ["int88"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint88(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(21, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt96(index) { const result = defaultAbiCoder.decode( ["int96"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint96(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(20, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt104(index) { const result = defaultAbiCoder.decode( ["int104"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint104(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(19, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt112(index) { const result = defaultAbiCoder.decode( ["int112"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint112(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(18, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt120(index) { const result = defaultAbiCoder.decode( ["int120"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint120(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(17, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt128(index) { const result = defaultAbiCoder.decode( ["int128"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint128(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(16, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt136(index) { const result = defaultAbiCoder.decode( ["int136"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint136(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(15, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt144(index) { const result = defaultAbiCoder.decode( ["int144"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint144(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(14, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt152(index) { const result = defaultAbiCoder.decode( ["int152"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint152(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(13, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt160(index) { const result = defaultAbiCoder.decode( ["int160"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint160(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(12, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt168(index) { const result = defaultAbiCoder.decode( ["int168"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint168(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(11, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt176(index) { const result = defaultAbiCoder.decode( ["int176"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint176(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(10, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt184(index) { const result = defaultAbiCoder.decode( ["int184"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint184(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(9, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt192(index) { const result = defaultAbiCoder.decode( ["int192"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint192(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(8, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt200(index) { const result = defaultAbiCoder.decode( ["int200"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint200(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(7, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt208(index) { const result = defaultAbiCoder.decode( ["int208"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint208(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(6, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt216(index) { const result = defaultAbiCoder.decode( ["int216"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint216(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(5, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt224(index) { const result = defaultAbiCoder.decode( ["int224"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint224(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(4, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt232(index) { const result = defaultAbiCoder.decode( ["int232"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint232(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(3, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt240(index) { const result = defaultAbiCoder.decode( ["int240"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint240(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(2, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt248(index) { const result = defaultAbiCoder.decode( ["int248"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint248(index) { return new BigNumber( hex.encode(this._getBytes32(index).subarray(1, 32)), 16, ); } /** * @param {number} [index] * @returns {BigNumber} */ getInt256(index) { const result = defaultAbiCoder.decode( ["int256"], this._getBytes32(index != null ? index : 0), ); return new BigNumber(result.toString()); } /** * @param {number} [index] * @returns {BigNumber} */ getUint256(index) { return new BigNumber(hex.encode(this._getBytes32(index)), 16); } /** * @param {number} [index] * @returns {string} */ getAddress(index) { return hex.encode( this.bytes.subarray( (index != null ? index : 0) * 32 + 12, (index != null ? index : 0) * 32 + 32, ), ); } /** * @description Decode the data according to the array of types, each of which may be a string or ParamType. * @param {Array<string | ParamType>} types * @returns {string | any} */ getResult(types) { return defaultAbiCoder.decode(types, this.bytes); } /** * @param {number} [index] * @returns {Uint8Array} */ _getBytes32(index) { return this.bytes.subarray( (index != null ? index : 0) * 32, (index != null ? index : 0) * 32 + 32, ); } /** * @returns {HieroProto.proto.IContractFunctionResult} */ _toProtobuf() { return { contractID: this.contractId != null ? this.contractId._toProtobuf() : null, contractCallResult: this.bytes, errorMessage: this.errorMessage, bloom: this.bloom, gasUsed: this.gasUsed, logInfo: this.logs.map((log) => log._toProtobuf()), // eslint-disable-next-line deprecation/deprecation createdContractIDs: this.createdContractIds.map((id) => id._toProtobuf(), ), evmAddress: this.evmAddress != null ? { value: this.evmAddress, } : null, gas: this.gas, amount: this.amount, functionParameters: this.functionParameters, senderId: this.senderAccountId != null ? this.senderAccountId._toProtobuf() : null, contractNonces: this.contractNonces.map((contractNonce) => contractNonce._toProtobuf(), ), signerNonce: this.signerNonce != null ? { value: this.signerNonce, } : null, }; } }