Skip to content

Commit

Permalink
fix: contract operations in transaction summary (#3704)
Browse files Browse the repository at this point in the history
* fix: contract operations in transaction summary

* chore: minor changeset

* chore: fix doc

Co-authored-by: Peter Smith <peter@blueoceancomputing.co.uk>

* chore: pr is not breaking

* chore: remove unit test

* chore: another test case

* test: add multicall

* chore: operations tests for getTransactionSumarry fn

---------

Co-authored-by: Peter Smith <peter@blueoceancomputing.co.uk>
Co-authored-by: Anderson Arboleya <anderson@arboleya.me>
  • Loading branch information
3 people authored Feb 14, 2025
1 parent 02503a6 commit 194e31f
Show file tree
Hide file tree
Showing 8 changed files with 389 additions and 18 deletions.
6 changes: 6 additions & 0 deletions .changeset/ten-taxis-invent.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@fuel-ts/account": patch
"@fuel-ts/abi-coder": patch
---

fix: contract operations in transaction summary
1 change: 1 addition & 0 deletions apps/docs/src/guide/transactions/getting-the-response.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Once a transaction has been submitted, you may want to extract information regar
- The transaction ID
- The status (submitted, success, squeezed out, or failure)
- Receipts (return data, logs, mints/burns, transfers and panic/reverts)
- Operations (contract calls, transfers, withdrawals)
- Gas fees and usages
- Date and time of the transaction
- The block the transaction was included in
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Provider, Wallet } from 'fuels';

import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env';
import { CounterFactory } from '../../../../typegend';
import { Counter, CounterFactory } from '../../../../typegend';

const provider = new Provider(LOCAL_NETWORK_URL);
const wallet = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider);
Expand All @@ -17,7 +17,10 @@ const call = await contract.functions.increment_count(15).call();
const { transactionResponse } = await call.waitForResult();

// Retrieve the full transaction summary
const transactionSummary = await transactionResponse.getTransactionSummary();
const transactionSummary = await transactionResponse.getTransactionSummary({
// Pass a Contract ID and ABI map to generate the contract operations
[contract.id.toB256()]: Counter.abi,
});
// #endregion transaction-response-1

console.log('transactionSummary', transactionSummary);
1 change: 1 addition & 0 deletions packages/abi-coder/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ export {
ENCODING_V1,
} from './utils/constants';
export type { Bytes, RawSlice, StdString, StrSlice } from './utils/types';
export { decodeScriptData, type DecodedScriptData } from './utils/scriptData';
52 changes: 52 additions & 0 deletions packages/abi-coder/src/utils/scriptData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import type { BN } from '@fuel-ts/math';

import { Interface } from '../Interface';
import { B256Coder } from '../encoding/coders/B256Coder';
import { BigNumberCoder } from '../encoding/coders/BigNumberCoder';
import { StdStringCoder } from '../encoding/coders/StdStringCoder';
import type { JsonAbi } from '../types/JsonAbiNew';

import { WORD_SIZE } from './constants';

export type DecodedScriptData = {
amount: BN;
assetId: string;
contractId: string;
functionSelector: string;
functionArgs: unknown[] | undefined;
};

/**
* Decodes the script data used when calling programs to deduce the amount, asset ID,
* contract ID, function selector and function arguments passed.
*
* @param scriptData - the script data to decode.
* @param abi - the abi used for the call, to deduce arguments (optional).
* @returns the decoded script data.
*/
export const decodeScriptData = (scriptData: Uint8Array, abi?: JsonAbi): DecodedScriptData => {
// Slice all data sections at required offsets
const [amount, amountOffset] = new BigNumberCoder('u64').decode(scriptData, 0);
const [assetId, assetIdOffset] = new B256Coder().decode(scriptData, amountOffset);
const [contractId, contractIdOffset] = new B256Coder().decode(scriptData, assetIdOffset);
const [functionSelector, functionSelectorOffset] = new StdStringCoder().decode(
scriptData,
contractIdOffset + WORD_SIZE + WORD_SIZE
);

// Slice the function arguments after the function selector
const functionArgsBytes = scriptData.slice(functionSelectorOffset);

// Decode the function arguments using the function selector if we have the abi
const functionArgs = abi
? new Interface(abi).getFunction(functionSelector).decodeArguments(functionArgsBytes)
: undefined;

return {
amount,
assetId,
contractId,
functionSelector,
functionArgs,
};
};
42 changes: 29 additions & 13 deletions packages/account/src/providers/transaction-summary/call.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { Interface, type JsonAbi } from '@fuel-ts/abi-coder';
import { type JsonAbi, decodeScriptData } from '@fuel-ts/abi-coder';
import { Interface } from '@fuel-ts/abi-coder';
import { FuelError, ErrorCode } from '@fuel-ts/errors';
import type { BN } from '@fuel-ts/math';
import type { ReceiptCall } from '@fuel-ts/transactions';
import { TransactionCoder } from '@fuel-ts/transactions';
import { arrayify } from '@fuel-ts/utils';

type GetFunctionCallProps = {
abi: JsonAbi;
receipt: ReceiptCall;
rawPayload?: string;
rawPayload: string;
maxInputs: BN;
};

Expand All @@ -17,21 +21,35 @@ export interface FunctionCall {
argumentsProvided: Record<string, unknown> | undefined;
}

export const getFunctionCall = ({ abi, receipt }: GetFunctionCallProps): FunctionCall => {
export const getFunctionCall = ({
abi,
receipt,
rawPayload,
}: GetFunctionCallProps): FunctionCall => {
const [transaction] = new TransactionCoder().decode(arrayify(rawPayload), 0);

if (!transaction.scriptData) {
throw new FuelError(
ErrorCode.NOT_SUPPORTED,
'Cannot get function calls for this transaction type.'
);
}

const { functionArgs, functionSelector } = decodeScriptData(
arrayify(transaction.scriptData),
abi
);

const abiInterface = new Interface(abi);
const callFunctionSelector = receipt.param1.toHex(8);
const functionFragment = abiInterface.getFunction(callFunctionSelector);
const functionFragment = abiInterface.getFunction(functionSelector);
const inputs = functionFragment.jsonFn.inputs;

const encodedArgs = receipt.param2.toHex();
let argumentsProvided;

// use bytes got from rawPayload to decode function params
const data = functionFragment.decodeArguments(encodedArgs);
if (data) {
if (functionArgs) {
// put together decoded data with input names from abi
argumentsProvided = inputs.reduce((prev, input, index) => {
const value = data[index];
const value = functionArgs[index];
const name = input.name;

if (name) {
Expand All @@ -46,12 +64,10 @@ export const getFunctionCall = ({ abi, receipt }: GetFunctionCallProps): Functio
}, {});
}

const call = {
return {
functionSignature: functionFragment.signature,
functionName: functionFragment.name,
argumentsProvided,
...(receipt.amount?.isZero() ? {} : { amount: receipt.amount, assetId: receipt.assetId }),
};

return call;
};
Loading

0 comments on commit 194e31f

Please sign in to comment.