Skip to content

Commit

Permalink
feat: Parse Logs and LogData (#517)
Browse files Browse the repository at this point in the history
  • Loading branch information
QuinnLee committed Sep 24, 2022
1 parent f202acb commit 6403076
Show file tree
Hide file tree
Showing 10 changed files with 135 additions and 71 deletions.
31 changes: 31 additions & 0 deletions .changeset/tricky-jobs-unite.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
"@fuel-ts/abi-coder": minor
"@fuel-ts/address": minor
"@fuel-ts/constants": minor
"@fuel-ts/contract": minor
"@fuel-ts/example-contract": minor
"fuelchain": minor
"fuels": minor
"@fuel-ts/hasher": minor
"@fuel-ts/hdwallet": minor
"@fuel-ts/interfaces": minor
"@fuel-ts/keystore": minor
"@fuel-ts/math": minor
"@fuel-ts/merkle": minor
"@fuel-ts/merkle-shared": minor
"@fuel-ts/merklesum": minor
"@fuel-ts/mnemonic": minor
"@fuel-ts/predicate": minor
"@fuel-ts/providers": minor
"@fuel-ts/script": minor
"@fuel-ts/signer": minor
"@fuel-ts/sparsemerkle": minor
"@fuel-ts/testcases": minor
"@fuel-ts/transactions": minor
"typechain-target-fuels": minor
"@fuel-ts/wallet": minor
"@fuel-ts/wallet-manager": minor
"@fuel-ts/wordlists": minor
---

Parse Logs and Log Data
26 changes: 24 additions & 2 deletions packages/abi-coder/src/interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,14 @@ import type { InputValue } from './coders/abstract-coder';
import BooleanCoder from './coders/boolean';
import type { Fragment } from './fragments/fragment';
import FunctionFragment from './fragments/function-fragment';
import type { JsonAbi, JsonAbiFragment, JsonFlatAbi } from './json-abi';
import { ABI, isReferenceType } from './json-abi';
import type {
JsonAbiFragment,
JsonFlatAbi,
JsonFlatAbiFragmentType,
JsonAbi,
JsonAbiLogFragment,
} from './json-abi';
import { isFlatJsonAbi, ABI, isReferenceType } from './json-abi';
import { filterEmptyParams } from './utilities';

const logger = new Logger(process.env.BUILD_VERSION || '~');
Expand All @@ -32,9 +38,17 @@ export default class Interface {
readonly fragments: Array<Fragment>;
readonly functions: { [name: string]: FunctionFragment };
readonly abiCoder: AbiCoder;
readonly abi: ABI | null;
readonly types: ReadonlyArray<JsonFlatAbiFragmentType>;
readonly loggedTypes: ReadonlyArray<JsonAbiLogFragment>;

constructor(jsonAbi: JsonAbi | JsonFlatAbi) {
this.abi = isFlatJsonAbi(jsonAbi) ? new ABI(jsonAbi) : null;
this.fragments = coerceFragments(ABI.unflatten(jsonAbi));

this.types = this.abi ? this.abi.types : [];
this.loggedTypes = this.abi ? this.abi.unflattenLoggedTypes() : [];

this.abiCoder = new AbiCoder();
this.functions = {};
this.fragments.forEach((fragment) => {
Expand Down Expand Up @@ -137,6 +151,14 @@ export default class Interface {
return this.abiCoder.decode(fragment.outputs, bytes);
}

decodeLog(data: BytesLike, logId: number): any {
const logType = this.loggedTypes.find((type) => type.logId === logId);
if (!logType?.abiFragmentType) {
throw new Error(`Log ID - ${logId} unknown`);
}
return this.abiCoder.decode(logType.abiFragmentType, data);
}

encodeFunctionResult(
functionFragment: FunctionFragment | string,
values: Array<InputValue>
Expand Down
37 changes: 33 additions & 4 deletions packages/abi-coder/src/json-abi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*/

import { genericRegEx } from './constants';
import type { JsonFragmentType } from './fragments/param-type';
import { ParamType } from './fragments/param-type';

export interface JsonAbiFragmentType {
readonly type: string;
Expand All @@ -25,6 +27,12 @@ export interface JsonAbiFragment {
readonly outputs?: ReadonlyArray<JsonAbiFragmentType>;
}

export interface JsonAbiLogFragment {
readonly logId: number;
readonly loggedType: JsonFlatAbiFragmentArgumentType;
readonly abiFragmentType?: ReadonlyArray<JsonAbiFragmentType>;
}

export interface JsonFlatAbiFragmentType {
readonly typeId: number;
readonly type: string;
Expand All @@ -33,6 +41,11 @@ export interface JsonFlatAbiFragmentType {
readonly typeParameters?: ReadonlyArray<number> | null;
}

export interface JsonFlatAbiFragmentLoggedType {
readonly logId: number;
readonly loggedType: JsonFlatAbiFragmentArgumentType;
}

export interface JsonFlatAbiFragmentArgumentType {
readonly type: number;
readonly name?: string;
Expand All @@ -47,9 +60,11 @@ export interface JsonFlatAbiFragmentFunction {

export interface JsonFlatAbi {
readonly types: ReadonlyArray<JsonFlatAbiFragmentType>;
readonly loggedTypes: ReadonlyArray<JsonFlatAbiFragmentLoggedType>;
readonly functions: ReadonlyArray<JsonFlatAbiFragmentFunction>;
}

export const isFlatJsonAbi = (jsonAbi: JsonAbi): jsonAbi is JsonFlatAbi => !Array.isArray(jsonAbi);
/**
* A JSON ABI object
*/
Expand All @@ -58,10 +73,16 @@ export type JsonAbi = ReadonlyArray<JsonAbiFragment> | JsonFlatAbi;
export class ABI {
readonly types: ReadonlyArray<JsonFlatAbiFragmentType>;
readonly functions: ReadonlyArray<JsonFlatAbiFragmentFunction>;
readonly loggedTypes: ReadonlyArray<JsonFlatAbiFragmentLoggedType>;

constructor(jsonAbi: JsonFlatAbi) {
this.types = jsonAbi.types;
this.functions = jsonAbi.functions;
this.loggedTypes = jsonAbi.loggedTypes;
}

parseLoggedType(loggedType: JsonFlatAbiFragmentLoggedType): JsonAbiFragmentType {
return ParamType.fromObject(this.parseInput(loggedType.loggedType) as JsonFragmentType);
}

parseInput(
Expand Down Expand Up @@ -111,11 +132,19 @@ export class ABI {
}

static unflatten(jsonAbi: JsonAbi) {
if (Array.isArray(jsonAbi)) {
return jsonAbi as ReadonlyArray<JsonAbiFragment>;
if (isFlatJsonAbi(jsonAbi)) {
const abi = new ABI(jsonAbi);
return abi.unflatten();
}
const abi = new ABI(jsonAbi as JsonFlatAbi);
return abi.unflatten();

return jsonAbi;
}

unflattenLoggedTypes(): ReadonlyArray<JsonAbiLogFragment> {
return this.loggedTypes.map((loggedType) => ({
...loggedType,
abiFragmentType: [this.parseLoggedType(loggedType)],
}));
}

unflatten(): ReadonlyArray<JsonAbiFragment> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NativeAssetId } from '@fuel-ts/constants';
import type { BN } from '@fuel-ts/math';
import { bn, toHex } from '@fuel-ts/math';
import { Provider, LogReader } from '@fuel-ts/providers';
import { Provider } from '@fuel-ts/providers';
import { TestUtils } from '@fuel-ts/wallet';
import { readFileSync } from 'fs';
import { join } from 'path';
Expand Down Expand Up @@ -234,20 +234,23 @@ describe('Coverage Contract', () => {
});

it('should test u8 vector input', async () => {
const { value, transactionResult } = await contractInstance.functions
const { value, logs } = await contractInstance.functions
.check_u8_vector([1, 2, 3, 4, 5])
.call();

expect(value).toBeTruthy();
const logReader = new LogReader(transactionResult.receipts);
expect(logReader.toArray()).toStrictEqual([

const formattedLog = logs.map((l) => (typeof l === 'string' ? l : l.toNumber()));

expect(formattedLog).toEqual([
'vector.buf.ptr',
'14464',
14464,
'vector.buf.cap',
'5',
5,
'vector.len',
'5',
5,
'addr_of vector',
'14440',
14440,
]);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,8 @@ export class BaseInvocationScope<TReturn = any> {
return FunctionInvocationResult.build<T>(
this.functionInvocationScopes,
response,
this.isMultiCall
this.isMultiCall,
this.contract
);
}

Expand Down
34 changes: 32 additions & 2 deletions packages/contract/src/contracts/functions/invocation-results.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable max-classes-per-file */
import { U64Coder } from '@fuel-ts/abi-coder';
import type { BN } from '@fuel-ts/math';
import { bn } from '@fuel-ts/math';
import type { CallResult, TransactionResponse, TransactionResult } from '@fuel-ts/providers';
import type {
TransactionResult,
CallResult,
TransactionResponse,
TransactionResultReceipt,
} from '@fuel-ts/providers';
import type { ReceiptScriptResult } from '@fuel-ts/transactions';
import { ReceiptType } from '@fuel-ts/transactions';

import { contractCallScript } from '../../scripts';
import type { InvocationScopeLike } from '../../types';
import type Contract from '../contract';

function getGasUsage(callResult: CallResult) {
const scriptResult = callResult.receipts.find((r) => r.type === ReceiptType.ScriptResult) as
Expand Down Expand Up @@ -47,33 +54,56 @@ export class FunctionInvocationResult<T = any> extends InvocationResult<T> {
readonly transactionId: string;
readonly transactionResponse: TransactionResponse;
readonly transactionResult: TransactionResult<any>;
readonly contract: Contract;
readonly logs!: Array<any>;

constructor(
funcScopes: InvocationScopeLike | Array<InvocationScopeLike>,
transactionResponse: TransactionResponse,
transactionResult: TransactionResult<any>,
contract: Contract,
isMultiCall: boolean
) {
super(funcScopes, transactionResult, isMultiCall);
this.transactionResponse = transactionResponse;
this.transactionResult = transactionResult;
this.transactionId = this.transactionResponse.id;
this.contract = contract;
this.logs = this.getDecodedLogs(transactionResult.receipts);
}

static async build<T>(
funcScope: InvocationScopeLike | Array<InvocationScopeLike>,
transactionResponse: TransactionResponse,
isMultiCall: boolean
isMultiCall: boolean,
contract: Contract
) {
const txResult = await transactionResponse.waitForResult();
const fnResult = new FunctionInvocationResult<T>(
funcScope,
transactionResponse,
txResult,
contract,
isMultiCall
);
return fnResult;
}

protected getDecodedLogs(receipts: Array<TransactionResultReceipt>) {
return receipts.reduce((logs, r) => {
if (r.type === ReceiptType.LogData) {
return logs.concat(...this.contract.interface.decodeLog(r.data, r.val1.toNumber()));
}

if (r.type === ReceiptType.Log) {
return logs.concat(
...this.contract.interface.decodeLog(new U64Coder().encode(r.val0), r.val1.toNumber())
);
}

return logs;
}, []);
}
}

export class InvocationCallResult<T = any> extends InvocationResult<T> {
Expand Down
2 changes: 1 addition & 1 deletion packages/forc-bin/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": true,
"name": "forc-bin",
"version": "0.24.3",
"version": "0.24.4",
"description": "",
"author": "Fuel Labs <contact@fuel.sh> (https://fuel.network/)",
"typedocMain": "src/index.ts",
Expand Down
51 changes: 0 additions & 51 deletions packages/providers/src/LogReader.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/providers/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,3 @@ export { default as Provider } from './provider';
export * from './transaction-request';
export * from './transaction-response';
export * from './util';
export { default as LogReader } from './LogReader';
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type {
AbstractScript,
} from '@fuel-ts/interfaces';
import type { BigNumberish, BN } from '@fuel-ts/math';
import { bn, multiply } from '@fuel-ts/math';
import { bn } from '@fuel-ts/math';
import type { Transaction } from '@fuel-ts/transactions';
import {
TransactionType,
Expand Down

1 comment on commit 6403076

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage report

St.
Category Percentage Covered / Total
🟢 Statements 90.14% 3308/3670
🟡 Branches 70.74% 619/875
🟢 Functions 87.3% 667/764
🟢 Lines 89.97% 3167/3520

Test suite run success

503 tests passing in 44 suites.

Report generated by 🧪jest coverage report action from 6403076

Please sign in to comment.