@boostxyz/sdk 5.2.0 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/Actions/Action.cjs +1 -1
- package/dist/Actions/Action.js +1 -1
- package/dist/Actions/EventAction.cjs +1 -1
- package/dist/Actions/EventAction.cjs.map +1 -1
- package/dist/Actions/EventAction.d.ts +82 -38
- package/dist/Actions/EventAction.d.ts.map +1 -1
- package/dist/Actions/EventAction.js +437 -297
- package/dist/Actions/EventAction.js.map +1 -1
- package/dist/AllowLists/AllowList.cjs +1 -1
- package/dist/AllowLists/AllowList.js +2 -2
- package/dist/AllowLists/SimpleAllowList.cjs +1 -1
- package/dist/AllowLists/SimpleAllowList.js +2 -2
- package/dist/AllowLists/SimpleDenyList.cjs +1 -1
- package/dist/AllowLists/SimpleDenyList.js +3 -3
- package/dist/Auth/PassthroughAuth.cjs +1 -1
- package/dist/Auth/PassthroughAuth.js +1 -1
- package/dist/BoostCore.cjs +2 -2
- package/dist/BoostCore.cjs.map +1 -1
- package/dist/BoostCore.d.ts +42 -1
- package/dist/BoostCore.d.ts.map +1 -1
- package/dist/BoostCore.js +360 -318
- package/dist/BoostCore.js.map +1 -1
- package/dist/BoostRegistry.cjs +1 -1
- package/dist/BoostRegistry.js +2 -2
- package/dist/{Budget-N0YEfSt2.cjs → Budget-AoNx7uFd.cjs} +2 -2
- package/dist/{Budget-N0YEfSt2.cjs.map → Budget-AoNx7uFd.cjs.map} +1 -1
- package/dist/{Budget-C0SMvfEl.js → Budget-DYIV9iNK.js} +3 -3
- package/dist/{Budget-C0SMvfEl.js.map → Budget-DYIV9iNK.js.map} +1 -1
- package/dist/Budgets/Budget.cjs +1 -1
- package/dist/Budgets/Budget.js +2 -2
- package/dist/Budgets/ManagedBudget.cjs +1 -1
- package/dist/Budgets/ManagedBudget.js +2 -2
- package/dist/Deployable/DeployableTarget.cjs +1 -1
- package/dist/Deployable/DeployableTarget.js +1 -1
- package/dist/Deployable/DeployableTargetWithRBAC.cjs +1 -1
- package/dist/Deployable/DeployableTargetWithRBAC.js +2 -2
- package/dist/Incentive-BbkfwGOb.cjs +2 -0
- package/dist/Incentive-BbkfwGOb.cjs.map +1 -0
- package/dist/{Incentive-BwRH0A6j.js → Incentive-qlnv5kQB.js} +203 -124
- package/dist/Incentive-qlnv5kQB.js.map +1 -0
- package/dist/Incentives/AllowListIncentive.cjs +1 -1
- package/dist/Incentives/AllowListIncentive.js +3 -3
- package/dist/Incentives/CGDAIncentive.cjs +1 -1
- package/dist/Incentives/CGDAIncentive.js +2 -2
- package/dist/Incentives/ERC20Incentive.cjs +1 -1
- package/dist/Incentives/ERC20Incentive.js +6 -6
- package/dist/Incentives/ERC20PeggedIncentive.d.ts +10 -1
- package/dist/Incentives/ERC20PeggedIncentive.d.ts.map +1 -1
- package/dist/Incentives/ERC20PeggedVariableCriteriaIncentive.d.ts +16 -4
- package/dist/Incentives/ERC20PeggedVariableCriteriaIncentive.d.ts.map +1 -1
- package/dist/Incentives/ERC20VariableCriteriaIncentive.cjs +1 -1
- package/dist/Incentives/ERC20VariableCriteriaIncentive.js +2 -2
- package/dist/Incentives/ERC20VariableIncentive.cjs +1 -1
- package/dist/Incentives/ERC20VariableIncentive.js +2 -2
- package/dist/Incentives/Incentive.cjs +1 -1
- package/dist/Incentives/Incentive.js +2 -2
- package/dist/Incentives/PointsIncentive.cjs +1 -1
- package/dist/Incentives/PointsIncentive.js +2 -2
- package/dist/{SimpleDenyList-B8QeJthf.js → SimpleDenyList-ByAr4X1r.js} +3 -3
- package/dist/{SimpleDenyList-B8QeJthf.js.map → SimpleDenyList-ByAr4X1r.js.map} +1 -1
- package/dist/{SimpleDenyList-DIb4xX3j.cjs → SimpleDenyList-CsRXJPwm.cjs} +2 -2
- package/dist/{SimpleDenyList-DIb4xX3j.cjs.map → SimpleDenyList-CsRXJPwm.cjs.map} +1 -1
- package/dist/Validators/LimitedSignerValidator.cjs +1 -1
- package/dist/Validators/LimitedSignerValidator.js +2 -2
- package/dist/Validators/SignerValidator.cjs +1 -1
- package/dist/Validators/SignerValidator.js +2 -2
- package/dist/Validators/Validator.cjs +1 -1
- package/dist/Validators/Validator.js +1 -1
- package/dist/{deployments-COxshLqt.js → deployments-D0fs26TV.js} +16 -16
- package/dist/{deployments-COxshLqt.js.map → deployments-D0fs26TV.js.map} +1 -1
- package/dist/{deployments-BGpr4ppG.cjs → deployments-DoIOqxco.cjs} +2 -2
- package/dist/deployments-DoIOqxco.cjs.map +1 -0
- package/dist/deployments.json +3 -3
- package/dist/errors.cjs +1 -1
- package/dist/errors.cjs.map +1 -1
- package/dist/errors.d.ts +40 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +42 -16
- package/dist/errors.js.map +1 -1
- package/dist/{generated-ClbO_ULI.js → generated-Cyvr_Tjx.js} +446 -438
- package/dist/generated-Cyvr_Tjx.js.map +1 -0
- package/dist/{generated-CRD9XfOT.cjs → generated-DtYPHhtX.cjs} +2 -2
- package/dist/generated-DtYPHhtX.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +160 -155
- package/dist/utils.cjs +1 -1
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.js +367 -19
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/Actions/EventAction.test.ts +285 -3
- package/src/Actions/EventAction.ts +402 -124
- package/src/BoostCore.test.ts +51 -3
- package/src/BoostCore.ts +73 -0
- package/src/Incentives/ERC20PeggedIncentive.ts +33 -4
- package/src/Incentives/ERC20PeggedVariableCriteriaIncentive.test.ts +67 -25
- package/src/Incentives/ERC20PeggedVariableCriteriaIncentive.ts +89 -7
- package/src/errors.ts +50 -0
- package/dist/Incentive-BwRH0A6j.js.map +0 -1
- package/dist/Incentive-CZw_hu64.cjs +0 -2
- package/dist/Incentive-CZw_hu64.cjs.map +0 -1
- package/dist/deployments-BGpr4ppG.cjs.map +0 -1
- package/dist/generated-CRD9XfOT.cjs.map +0 -1
- package/dist/generated-ClbO_ULI.js.map +0 -1
|
@@ -7,10 +7,12 @@ import {
|
|
|
7
7
|
} from '@boostxyz/evm';
|
|
8
8
|
import { bytecode } from '@boostxyz/evm/artifacts/contracts/actions/EventAction.sol/EventAction.json';
|
|
9
9
|
import { getTransaction, getTransactionReceipt } from '@wagmi/core';
|
|
10
|
+
import type { AbiEventParameter } from 'abitype';
|
|
10
11
|
import { match } from 'ts-pattern';
|
|
11
12
|
import {
|
|
12
13
|
type AbiEvent,
|
|
13
14
|
type AbiFunction,
|
|
15
|
+
type AbiParameter,
|
|
14
16
|
type Address,
|
|
15
17
|
type GetLogsReturnType,
|
|
16
18
|
type GetTransactionParameters,
|
|
@@ -41,6 +43,8 @@ import {
|
|
|
41
43
|
FieldValueUndefinedError,
|
|
42
44
|
FunctionDataDecodeError,
|
|
43
45
|
InvalidNumericalCriteriaError,
|
|
46
|
+
InvalidTupleDecodingError,
|
|
47
|
+
InvalidTupleEncodingError,
|
|
44
48
|
NoEventActionStepsProvidedError,
|
|
45
49
|
TooManyEventActionStepsProvidedError,
|
|
46
50
|
UnparseableAbiParamError,
|
|
@@ -88,6 +92,8 @@ export enum PrimitiveType {
|
|
|
88
92
|
ADDRESS = 1,
|
|
89
93
|
BYTES = 2,
|
|
90
94
|
STRING = 3,
|
|
95
|
+
// Note: TUPLE remains in the enum but is no longer handled directly by `validateFieldAgainstCriteria`.
|
|
96
|
+
TUPLE = 4,
|
|
91
97
|
}
|
|
92
98
|
|
|
93
99
|
/**
|
|
@@ -113,6 +119,9 @@ export interface Criteria {
|
|
|
113
119
|
/**
|
|
114
120
|
* The index in the logs argument array where the field is located.
|
|
115
121
|
*
|
|
122
|
+
* If `fieldType` is TUPLE, this value is **bitpacked** with up to 5 sub-indexes,
|
|
123
|
+
* with the maximum 6-bit value used as a "terminator" to indicate no further indexes.
|
|
124
|
+
*
|
|
116
125
|
* @type {number}
|
|
117
126
|
*/
|
|
118
127
|
fieldIndex: number;
|
|
@@ -323,6 +332,13 @@ export interface EventActionPayloadRaw {
|
|
|
323
332
|
*/
|
|
324
333
|
export type EventLogs = GetLogsReturnType<AbiEvent, AbiEvent[], true>;
|
|
325
334
|
|
|
335
|
+
/**
|
|
336
|
+
* Single event log
|
|
337
|
+
* @export
|
|
338
|
+
* @typedef {EventLog}
|
|
339
|
+
*/
|
|
340
|
+
export type EventLog = EventLogs[0] & { args: unknown[] };
|
|
341
|
+
|
|
326
342
|
/**
|
|
327
343
|
* A generic event action
|
|
328
344
|
*
|
|
@@ -571,14 +587,7 @@ export class EventAction extends DeployableTarget<
|
|
|
571
587
|
}
|
|
572
588
|
const decodedLogs = receipt.logs
|
|
573
589
|
.filter((log) => log.topics[0] === toEventSelector(event))
|
|
574
|
-
.map((log) =>
|
|
575
|
-
const { eventName, args } = decodeEventLog({
|
|
576
|
-
abi: [event],
|
|
577
|
-
data: log.data,
|
|
578
|
-
topics: log.topics,
|
|
579
|
-
});
|
|
580
|
-
return { ...log, eventName, args };
|
|
581
|
-
});
|
|
590
|
+
.map((log) => decodeAndReorderLogArgs(event, log));
|
|
582
591
|
|
|
583
592
|
for (let log of decodedLogs) {
|
|
584
593
|
if (!isAddressEqual(log.address, claimant.targetContract)) continue;
|
|
@@ -703,7 +712,7 @@ export class EventAction extends DeployableTarget<
|
|
|
703
712
|
|
|
704
713
|
// Use the provided logs, no need to fetch receipt
|
|
705
714
|
if ('logs' in params) {
|
|
706
|
-
return this.isActionEventValid(actionStep, params.logs);
|
|
715
|
+
return this.isActionEventValid(actionStep, params.logs, event);
|
|
707
716
|
}
|
|
708
717
|
|
|
709
718
|
const receipt = await getTransactionReceipt(this._config, {
|
|
@@ -724,17 +733,9 @@ export class EventAction extends DeployableTarget<
|
|
|
724
733
|
|
|
725
734
|
const decodedLogs = receipt.logs
|
|
726
735
|
.filter((log) => log.topics[0] === toEventSelector(event))
|
|
727
|
-
.map((log) =>
|
|
728
|
-
const { eventName, args } = decodeEventLog({
|
|
729
|
-
abi: [event],
|
|
730
|
-
data: log.data,
|
|
731
|
-
topics: log.topics,
|
|
732
|
-
});
|
|
736
|
+
.map((log) => decodeAndReorderLogArgs(event, log));
|
|
733
737
|
|
|
734
|
-
|
|
735
|
-
});
|
|
736
|
-
|
|
737
|
-
return this.isActionEventValid(actionStep, decodedLogs);
|
|
738
|
+
return this.isActionEventValid(actionStep, decodedLogs, event);
|
|
738
739
|
}
|
|
739
740
|
if (actionStep.signatureType === SignatureType.FUNC) {
|
|
740
741
|
if ('hash' in params) {
|
|
@@ -756,20 +757,44 @@ export class EventAction extends DeployableTarget<
|
|
|
756
757
|
|
|
757
758
|
/**
|
|
758
759
|
* Validates a single action event with a given criteria against logs.
|
|
759
|
-
* If logs are provided in the optional `params` argument, then those logs will be used instead of being fetched with the configured client.
|
|
760
760
|
*
|
|
761
761
|
* @public
|
|
762
|
-
* @async
|
|
763
762
|
* @param {ActionStep} actionStep - The action step containing the event to validate.
|
|
764
763
|
* @param {EventLogs} logs - Event logs to validate the given step against
|
|
765
|
-
* @
|
|
764
|
+
* @param {AbiEvent} eventAbi - The ABI definition of the event
|
|
765
|
+
* @returns {boolean} Resolves to true if the action event is valid, throws if input is invalid, otherwise false.
|
|
766
766
|
*/
|
|
767
|
-
public isActionEventValid(
|
|
767
|
+
public isActionEventValid(
|
|
768
|
+
actionStep: ActionStep,
|
|
769
|
+
logs: EventLogs,
|
|
770
|
+
eventAbi: AbiEvent,
|
|
771
|
+
): boolean {
|
|
768
772
|
const criteria = actionStep.actionParameter;
|
|
769
773
|
if (!logs.length) return false;
|
|
774
|
+
|
|
775
|
+
// Check each log
|
|
770
776
|
for (let log of logs) {
|
|
771
|
-
|
|
772
|
-
|
|
777
|
+
// parse out final (scalar) field from the log args
|
|
778
|
+
try {
|
|
779
|
+
if (!Array.isArray(log.args)) {
|
|
780
|
+
throw new DecodedArgsMalformedError({
|
|
781
|
+
log,
|
|
782
|
+
criteria,
|
|
783
|
+
fieldValue: undefined,
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
const { value, type } = this.parseFieldFromAbi(
|
|
787
|
+
log.args,
|
|
788
|
+
criteria.fieldIndex,
|
|
789
|
+
eventAbi.inputs || [],
|
|
790
|
+
criteria.fieldType,
|
|
791
|
+
);
|
|
792
|
+
criteria.fieldType = type;
|
|
793
|
+
if (this.validateFieldAgainstCriteria(criteria, value, { log })) {
|
|
794
|
+
return true;
|
|
795
|
+
}
|
|
796
|
+
} catch {
|
|
797
|
+
// If there's an error on this log, keep trying with the next one
|
|
773
798
|
}
|
|
774
799
|
}
|
|
775
800
|
return false;
|
|
@@ -821,7 +846,15 @@ export class EventAction extends DeployableTarget<
|
|
|
821
846
|
return { ...log, eventName, args };
|
|
822
847
|
});
|
|
823
848
|
|
|
824
|
-
return this.isActionEventValid(actionStep, decodedLogs
|
|
849
|
+
return this.isActionEventValid(actionStep, decodedLogs, {
|
|
850
|
+
name: 'Transfer',
|
|
851
|
+
type: 'event',
|
|
852
|
+
inputs: [
|
|
853
|
+
{ type: 'address', indexed: true },
|
|
854
|
+
{ type: 'address', indexed: true },
|
|
855
|
+
{ type: 'uint256', indexed: true },
|
|
856
|
+
],
|
|
857
|
+
});
|
|
825
858
|
} catch {
|
|
826
859
|
// ERC20
|
|
827
860
|
try {
|
|
@@ -844,13 +877,91 @@ export class EventAction extends DeployableTarget<
|
|
|
844
877
|
return { ...log, eventName, args };
|
|
845
878
|
});
|
|
846
879
|
|
|
847
|
-
return this.isActionEventValid(actionStep, decodedLogs
|
|
880
|
+
return this.isActionEventValid(actionStep, decodedLogs, {
|
|
881
|
+
name: 'Transfer',
|
|
882
|
+
type: 'event',
|
|
883
|
+
inputs: [
|
|
884
|
+
{ type: 'address', indexed: true },
|
|
885
|
+
{ type: 'address', indexed: true },
|
|
886
|
+
{ type: 'uint256' },
|
|
887
|
+
],
|
|
888
|
+
});
|
|
848
889
|
} catch {
|
|
849
890
|
throw new DecodedArgsError('Failed to decode transfer logs');
|
|
850
891
|
}
|
|
851
892
|
}
|
|
852
893
|
}
|
|
853
894
|
|
|
895
|
+
/**
|
|
896
|
+
* Parses the final (scalar) field from a set of decoded arguments, given an ABI definition.
|
|
897
|
+
* If the fieldType is TUPLE, we decode `fieldIndex` as a bitpacked array of indexes to drill down
|
|
898
|
+
* into nested tuples. Otherwise, we parse the single `fieldIndex` as normal.
|
|
899
|
+
*
|
|
900
|
+
* @public
|
|
901
|
+
* @param {readonly unknown[]} allArgs - The decoded arguments array from an event log or function call.
|
|
902
|
+
* @param {number} criteriaIndex - The field index (bitpacked if TUPLE).
|
|
903
|
+
* @param {AbiParameter[]} abiInputs - The ABI inputs describing each decoded argument.
|
|
904
|
+
* @param {PrimitiveType} declaredType - Either TUPLE or a standard scalar type
|
|
905
|
+
* @returns {{ value: string | bigint | Hex; type: Exclude<PrimitiveType, PrimitiveType.TUPLE> }}
|
|
906
|
+
*/
|
|
907
|
+
public parseFieldFromAbi(
|
|
908
|
+
allArgs: readonly unknown[],
|
|
909
|
+
criteriaIndex: number,
|
|
910
|
+
abiInputs: readonly AbiParameter[],
|
|
911
|
+
declaredType: PrimitiveType,
|
|
912
|
+
): {
|
|
913
|
+
value: string | bigint | Hex;
|
|
914
|
+
type: Exclude<PrimitiveType, PrimitiveType.TUPLE>;
|
|
915
|
+
} {
|
|
916
|
+
// If ANY_ACTION_PARAM, return a dummy "any" value so we can do special-case checks
|
|
917
|
+
if (criteriaIndex === CheatCodes.ANY_ACTION_PARAM) {
|
|
918
|
+
return { value: zeroHash, type: PrimitiveType.BYTES };
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// If it's not TUPLE, parse as a single index (existing logic)
|
|
922
|
+
if (declaredType !== PrimitiveType.TUPLE) {
|
|
923
|
+
if (!Array.isArray(allArgs) || criteriaIndex >= allArgs.length) {
|
|
924
|
+
throw new FieldValueUndefinedError({
|
|
925
|
+
fieldValue: allArgs,
|
|
926
|
+
criteria: {
|
|
927
|
+
filterType: FilterType.EQUAL,
|
|
928
|
+
fieldType: declaredType,
|
|
929
|
+
fieldIndex: criteriaIndex,
|
|
930
|
+
filterData: zeroHash,
|
|
931
|
+
},
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
const abiParam = abiInputs[criteriaIndex];
|
|
935
|
+
if (!abiParam || !abiParam.type) {
|
|
936
|
+
throw new UnparseableAbiParamError(criteriaIndex, abiParam as AbiEvent);
|
|
937
|
+
}
|
|
938
|
+
const rawValue = allArgs[criteriaIndex];
|
|
939
|
+
|
|
940
|
+
const finalType = abiTypeToPrimitiveType(abiParam.type);
|
|
941
|
+
|
|
942
|
+
if (
|
|
943
|
+
finalType === PrimitiveType.ADDRESS &&
|
|
944
|
+
(typeof rawValue !== 'string' || !isAddress(rawValue))
|
|
945
|
+
) {
|
|
946
|
+
throw new FieldValueUndefinedError({
|
|
947
|
+
fieldValue: rawValue,
|
|
948
|
+
criteria: {
|
|
949
|
+
fieldIndex: criteriaIndex,
|
|
950
|
+
filterType: FilterType.EQUAL,
|
|
951
|
+
fieldType: finalType,
|
|
952
|
+
filterData: zeroHash,
|
|
953
|
+
},
|
|
954
|
+
});
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
return { value: rawValue as string | bigint | Hex, type: finalType };
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// Otherwise, declaredType === TUPLE => decode bitpacked indexes
|
|
961
|
+
const indexes = unpackFieldIndexes(criteriaIndex);
|
|
962
|
+
return parseNestedTupleValue(allArgs as unknown[], indexes, abiInputs);
|
|
963
|
+
}
|
|
964
|
+
|
|
854
965
|
/**
|
|
855
966
|
* Validates a single action function with a given criteria against the transaction input.
|
|
856
967
|
*
|
|
@@ -870,10 +981,10 @@ export class EventAction extends DeployableTarget<
|
|
|
870
981
|
params: Pick<ValidateActionStepParams, 'abiItem' | 'knownSignatures'>,
|
|
871
982
|
) {
|
|
872
983
|
const criteria = actionStep.actionParameter;
|
|
873
|
-
|
|
984
|
+
const signature = actionStep.signature;
|
|
874
985
|
|
|
875
986
|
let func: AbiFunction;
|
|
876
|
-
if (params.abiItem) func = params
|
|
987
|
+
if (params.abiItem) func = params.abiItem as AbiFunction;
|
|
877
988
|
else {
|
|
878
989
|
const sigPool = params.knownSignatures as Record<Hex, AbiFunction>;
|
|
879
990
|
func = sigPool[signature] as AbiFunction;
|
|
@@ -892,31 +1003,37 @@ export class EventAction extends DeployableTarget<
|
|
|
892
1003
|
throw new FunctionDataDecodeError([func], e as Error);
|
|
893
1004
|
}
|
|
894
1005
|
|
|
895
|
-
|
|
896
|
-
const decodedArgs = decodedData.args;
|
|
897
|
-
|
|
898
|
-
if (!decodedArgs || !decodedData) return false;
|
|
899
|
-
|
|
900
|
-
if (
|
|
901
|
-
!this.validateFunctionAgainstCriteria(
|
|
902
|
-
criteria,
|
|
903
|
-
decodedArgs as (string | bigint)[],
|
|
904
|
-
)
|
|
905
|
-
) {
|
|
1006
|
+
if (!decodedData?.args) {
|
|
906
1007
|
return false;
|
|
907
1008
|
}
|
|
908
1009
|
|
|
909
|
-
|
|
1010
|
+
try {
|
|
1011
|
+
const { value, type } = this.parseFieldFromAbi(
|
|
1012
|
+
decodedData.args as unknown[],
|
|
1013
|
+
criteria.fieldIndex,
|
|
1014
|
+
func.inputs || [],
|
|
1015
|
+
criteria.fieldType,
|
|
1016
|
+
);
|
|
1017
|
+
criteria.fieldType = type;
|
|
1018
|
+
return this.validateFieldAgainstCriteria(criteria, value, {
|
|
1019
|
+
decodedArgs: decodedData.args as readonly (string | bigint)[],
|
|
1020
|
+
});
|
|
1021
|
+
} catch {
|
|
1022
|
+
return false;
|
|
1023
|
+
}
|
|
910
1024
|
}
|
|
1025
|
+
|
|
911
1026
|
/**
|
|
912
|
-
* Validates a field against a given criteria.
|
|
1027
|
+
* Validates a field against a given criteria. The field is assumed to be a non-tuple scalar,
|
|
1028
|
+
* along with its final resolved `PrimitiveType`. (Any TUPLE logic has been extracted elsewhere.)
|
|
913
1029
|
*
|
|
914
1030
|
* @param {Criteria} criteria - The criteria to validate against.
|
|
915
1031
|
* @param {string | bigint | Hex} fieldValue - The field value to validate.
|
|
1032
|
+
* @param {Exclude<PrimitiveType, PrimitiveType.TUPLE>} fieldType - The final resolved primitive type.
|
|
916
1033
|
* @param {Object} input - Additional context for validation.
|
|
917
1034
|
* @param {EventLogs[0]} [input.log] - The event log, if validating an event.
|
|
918
1035
|
* @param {readonly (string | bigint)[]} [input.decodedArgs] - The decoded function arguments, if validating a function call.
|
|
919
|
-
* @returns {
|
|
1036
|
+
* @returns {boolean} - Returns true if the field passes the criteria, false otherwise.
|
|
920
1037
|
*/
|
|
921
1038
|
public validateFieldAgainstCriteria(
|
|
922
1039
|
criteria: Criteria,
|
|
@@ -925,6 +1042,10 @@ export class EventAction extends DeployableTarget<
|
|
|
925
1042
|
| { log: EventLogs[0] }
|
|
926
1043
|
| { decodedArgs: readonly (string | bigint)[] },
|
|
927
1044
|
): boolean {
|
|
1045
|
+
/*
|
|
1046
|
+
* Special-case: ANY_ACTION_PARAM. If we have filterType=EQUAL, fieldType=BYTES, fieldIndex=255,
|
|
1047
|
+
* we consider that a wildcard match. Return true immediately.
|
|
1048
|
+
*/
|
|
928
1049
|
if (
|
|
929
1050
|
criteria.filterType === FilterType.EQUAL &&
|
|
930
1051
|
criteria.fieldType === PrimitiveType.BYTES &&
|
|
@@ -932,11 +1053,17 @@ export class EventAction extends DeployableTarget<
|
|
|
932
1053
|
) {
|
|
933
1054
|
return true;
|
|
934
1055
|
}
|
|
1056
|
+
if (criteria.fieldType === PrimitiveType.TUPLE) {
|
|
1057
|
+
throw new InvalidTupleDecodingError(
|
|
1058
|
+
'Tuples should not be passed into validateFieldAgainstCriteria',
|
|
1059
|
+
);
|
|
1060
|
+
}
|
|
1061
|
+
const fieldType = criteria.fieldType;
|
|
935
1062
|
|
|
936
|
-
//
|
|
1063
|
+
// Evaluate filter based on the final fieldType
|
|
937
1064
|
switch (criteria.filterType) {
|
|
938
1065
|
case FilterType.EQUAL:
|
|
939
|
-
return match(
|
|
1066
|
+
return match(fieldType)
|
|
940
1067
|
.with(PrimitiveType.ADDRESS, () =>
|
|
941
1068
|
isAddressEqual(criteria.filterData, fieldValue as Address),
|
|
942
1069
|
)
|
|
@@ -951,7 +1078,7 @@ export class EventAction extends DeployableTarget<
|
|
|
951
1078
|
.otherwise(() => fieldValue === criteria.filterData);
|
|
952
1079
|
|
|
953
1080
|
case FilterType.NOT_EQUAL:
|
|
954
|
-
return match(
|
|
1081
|
+
return match(fieldType)
|
|
955
1082
|
.with(
|
|
956
1083
|
PrimitiveType.ADDRESS,
|
|
957
1084
|
() => !isAddressEqual(criteria.filterData, fieldValue as Address),
|
|
@@ -967,7 +1094,7 @@ export class EventAction extends DeployableTarget<
|
|
|
967
1094
|
.otherwise(() => fieldValue !== criteria.filterData);
|
|
968
1095
|
|
|
969
1096
|
case FilterType.GREATER_THAN:
|
|
970
|
-
if (
|
|
1097
|
+
if (fieldType === PrimitiveType.UINT) {
|
|
971
1098
|
return BigInt(fieldValue) > BigInt(criteria.filterData);
|
|
972
1099
|
}
|
|
973
1100
|
throw new InvalidNumericalCriteriaError({
|
|
@@ -975,8 +1102,9 @@ export class EventAction extends DeployableTarget<
|
|
|
975
1102
|
criteria,
|
|
976
1103
|
fieldValue,
|
|
977
1104
|
});
|
|
1105
|
+
|
|
978
1106
|
case FilterType.GREATER_THAN_OR_EQUAL:
|
|
979
|
-
if (
|
|
1107
|
+
if (fieldType === PrimitiveType.UINT) {
|
|
980
1108
|
return BigInt(fieldValue) >= BigInt(criteria.filterData);
|
|
981
1109
|
}
|
|
982
1110
|
throw new InvalidNumericalCriteriaError({
|
|
@@ -986,7 +1114,7 @@ export class EventAction extends DeployableTarget<
|
|
|
986
1114
|
});
|
|
987
1115
|
|
|
988
1116
|
case FilterType.LESS_THAN:
|
|
989
|
-
if (
|
|
1117
|
+
if (fieldType === PrimitiveType.UINT) {
|
|
990
1118
|
return BigInt(fieldValue) < BigInt(criteria.filterData);
|
|
991
1119
|
}
|
|
992
1120
|
throw new InvalidNumericalCriteriaError({
|
|
@@ -994,8 +1122,9 @@ export class EventAction extends DeployableTarget<
|
|
|
994
1122
|
criteria,
|
|
995
1123
|
fieldValue,
|
|
996
1124
|
});
|
|
1125
|
+
|
|
997
1126
|
case FilterType.LESS_THAN_OR_EQUAL:
|
|
998
|
-
if (
|
|
1127
|
+
if (fieldType === PrimitiveType.UINT) {
|
|
999
1128
|
return BigInt(fieldValue) <= BigInt(criteria.filterData);
|
|
1000
1129
|
}
|
|
1001
1130
|
throw new InvalidNumericalCriteriaError({
|
|
@@ -1006,11 +1135,11 @@ export class EventAction extends DeployableTarget<
|
|
|
1006
1135
|
|
|
1007
1136
|
case FilterType.CONTAINS:
|
|
1008
1137
|
if (
|
|
1009
|
-
|
|
1010
|
-
|
|
1138
|
+
fieldType === PrimitiveType.BYTES ||
|
|
1139
|
+
fieldType === PrimitiveType.STRING
|
|
1011
1140
|
) {
|
|
1012
1141
|
let substring;
|
|
1013
|
-
if (
|
|
1142
|
+
if (fieldType === PrimitiveType.STRING) {
|
|
1014
1143
|
substring = fromHex(criteria.filterData, 'string');
|
|
1015
1144
|
} else {
|
|
1016
1145
|
// truncate the `0x` prefix
|
|
@@ -1032,12 +1161,11 @@ export class EventAction extends DeployableTarget<
|
|
|
1032
1161
|
fieldValue,
|
|
1033
1162
|
});
|
|
1034
1163
|
}
|
|
1035
|
-
|
|
1036
|
-
if (criteria.fieldType === PrimitiveType.STRING) {
|
|
1037
|
-
// fieldValue is decoded by the ABI
|
|
1164
|
+
if (fieldType === PrimitiveType.STRING) {
|
|
1038
1165
|
const regexString = fromHex(criteria.filterData, 'string');
|
|
1039
1166
|
return new RegExp(regexString).test(fieldValue);
|
|
1040
1167
|
}
|
|
1168
|
+
// Otherwise unrecognized or not applicable
|
|
1041
1169
|
|
|
1042
1170
|
default:
|
|
1043
1171
|
throw new UnrecognizedFilterTypeError({
|
|
@@ -1048,68 +1176,6 @@ export class EventAction extends DeployableTarget<
|
|
|
1048
1176
|
}
|
|
1049
1177
|
}
|
|
1050
1178
|
|
|
1051
|
-
/**
|
|
1052
|
-
* Validates a {@link Log} against a given criteria.
|
|
1053
|
-
* If the criteria's fieldIndex is 255 (using CheatCodes enum), it is reserved for anyValidation
|
|
1054
|
-
*
|
|
1055
|
-
* @param {Criteria} criteria - The criteria to validate against.
|
|
1056
|
-
* @param {Log} log - The Viem event log.
|
|
1057
|
-
* @returns {boolean} - Returns true if the log passes the criteria, false otherwise.
|
|
1058
|
-
*/
|
|
1059
|
-
public validateLogAgainstCriteria(
|
|
1060
|
-
criteria: Criteria,
|
|
1061
|
-
log: EventLogs[0],
|
|
1062
|
-
): boolean {
|
|
1063
|
-
if (
|
|
1064
|
-
!Array.isArray(log.args) ||
|
|
1065
|
-
(log.args.length <= criteria.fieldIndex &&
|
|
1066
|
-
criteria.fieldIndex !== CheatCodes.ANY_ACTION_PARAM)
|
|
1067
|
-
) {
|
|
1068
|
-
throw new DecodedArgsMalformedError({
|
|
1069
|
-
log,
|
|
1070
|
-
criteria,
|
|
1071
|
-
fieldValue: undefined,
|
|
1072
|
-
});
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
const fieldValue =
|
|
1076
|
-
criteria.fieldIndex === CheatCodes.ANY_ACTION_PARAM
|
|
1077
|
-
? zeroHash
|
|
1078
|
-
: log.args.at(criteria.fieldIndex);
|
|
1079
|
-
|
|
1080
|
-
if (fieldValue === undefined) {
|
|
1081
|
-
throw new FieldValueUndefinedError({ log, criteria, fieldValue });
|
|
1082
|
-
}
|
|
1083
|
-
return this.validateFieldAgainstCriteria(criteria, fieldValue, { log });
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
/**
|
|
1087
|
-
* Validates a function's decoded arguments against a given criteria.
|
|
1088
|
-
* If the criteria's fieldIndex is 255 (using CheatCodes enum), it is reserved for anyValidation
|
|
1089
|
-
*
|
|
1090
|
-
* @param {Criteria} criteria - The criteria to validate against.
|
|
1091
|
-
* @param {unknown[]} decodedArgs - The decoded arguments of the function call.
|
|
1092
|
-
* @returns {Promise<boolean>} - Returns true if the decoded argument passes the criteria, false otherwise.
|
|
1093
|
-
*/
|
|
1094
|
-
public validateFunctionAgainstCriteria(
|
|
1095
|
-
criteria: Criteria,
|
|
1096
|
-
decodedArgs: readonly (string | bigint)[],
|
|
1097
|
-
): boolean {
|
|
1098
|
-
const fieldValue =
|
|
1099
|
-
criteria.fieldIndex === CheatCodes.ANY_ACTION_PARAM
|
|
1100
|
-
? zeroHash
|
|
1101
|
-
: decodedArgs[criteria.fieldIndex];
|
|
1102
|
-
if (fieldValue === undefined) {
|
|
1103
|
-
throw new FieldValueUndefinedError({
|
|
1104
|
-
criteria,
|
|
1105
|
-
fieldValue,
|
|
1106
|
-
});
|
|
1107
|
-
}
|
|
1108
|
-
return this.validateFieldAgainstCriteria(criteria, fieldValue, {
|
|
1109
|
-
decodedArgs,
|
|
1110
|
-
});
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
1179
|
/**
|
|
1114
1180
|
* @inheritdoc
|
|
1115
1181
|
*
|
|
@@ -1159,6 +1225,15 @@ export class EventAction extends DeployableTarget<
|
|
|
1159
1225
|
};
|
|
1160
1226
|
}
|
|
1161
1227
|
|
|
1228
|
+
/**
|
|
1229
|
+
* Determines whether a string or bytes field is indexed in the event definition.
|
|
1230
|
+
* If the user tries to filter on an indexed string/bytes, we throw an error.
|
|
1231
|
+
*
|
|
1232
|
+
* @public
|
|
1233
|
+
* @param {ActionStep} step
|
|
1234
|
+
* @param {AbiEvent} event
|
|
1235
|
+
* @returns {boolean}
|
|
1236
|
+
*/
|
|
1162
1237
|
public isArraylikeIndexed(step: ActionStep, event: AbiEvent) {
|
|
1163
1238
|
if (
|
|
1164
1239
|
(step.actionParameter.fieldType === PrimitiveType.STRING ||
|
|
@@ -1171,9 +1246,114 @@ export class EventAction extends DeployableTarget<
|
|
|
1171
1246
|
}
|
|
1172
1247
|
}
|
|
1173
1248
|
|
|
1249
|
+
/**
|
|
1250
|
+
* Checks if a particular ABI parameter is the "tuple" variant that can have `components`.
|
|
1251
|
+
*
|
|
1252
|
+
* @param {AbiParameter} param
|
|
1253
|
+
* @returns {boolean}
|
|
1254
|
+
*/
|
|
1255
|
+
function isTupleAbiParameter(
|
|
1256
|
+
param: AbiParameter,
|
|
1257
|
+
): param is Extract<AbiParameter, { components: readonly AbiParameter[] }> {
|
|
1258
|
+
return param.type === 'tuple' || param.type.startsWith('tuple[');
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
/**
|
|
1262
|
+
* Recursively parses nested tuples by following an array of sub-indexes (unpacked from bitpacked `fieldIndex`).
|
|
1263
|
+
* Each entry in `indexes` is used to pick which sub-component in the current tuple's `components`.
|
|
1264
|
+
* If we encounter the "terminator" or run out of indexes, we stop.
|
|
1265
|
+
*
|
|
1266
|
+
* @param {unknown[]} rawArgs - The top-level arguments array.
|
|
1267
|
+
* @param {number[]} indexes - The array of indexes from `unpackFieldIndexes(...)`.
|
|
1268
|
+
* @param {readonly AbiParameter[]} abiInputs - The top-level ABI inputs for the entire arguments array.
|
|
1269
|
+
* @returns {{ value: string | bigint | Hex, type: Exclude<PrimitiveType, PrimitiveType.TUPLE> }}
|
|
1270
|
+
*/
|
|
1271
|
+
function parseNestedTupleValue(
|
|
1272
|
+
rawArgs: unknown[],
|
|
1273
|
+
indexes: number[],
|
|
1274
|
+
abiInputs: readonly AbiParameter[],
|
|
1275
|
+
): {
|
|
1276
|
+
value: string | bigint | Hex;
|
|
1277
|
+
type: Exclude<PrimitiveType, PrimitiveType.TUPLE>;
|
|
1278
|
+
} {
|
|
1279
|
+
if (!indexes.length) {
|
|
1280
|
+
throw new InvalidTupleDecodingError(
|
|
1281
|
+
`No indexes found; cannot parse TUPLE field`,
|
|
1282
|
+
);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
// The first index picks which top-level ABI param to look at
|
|
1286
|
+
const idx = indexes[0] ?? abiInputs.length + 1;
|
|
1287
|
+
// If idx is out of range or is a "terminator," fail fast
|
|
1288
|
+
if (idx >= abiInputs.length) {
|
|
1289
|
+
throw new InvalidTupleDecodingError(undefined, idx);
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
const param = abiInputs[idx];
|
|
1293
|
+
const rawValue = rawArgs[idx];
|
|
1294
|
+
|
|
1295
|
+
// If param isn't a tuple, we are at a leaf
|
|
1296
|
+
if (!isTupleAbiParameter(param!)) {
|
|
1297
|
+
const finalType = abiTypeToPrimitiveType(param!.type);
|
|
1298
|
+
return { value: rawValue as string | bigint | Hex, type: finalType };
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// Otherwise param is a tuple => rawValue must be an array of subfields
|
|
1302
|
+
if (!Array.isArray(rawValue)) {
|
|
1303
|
+
throw new InvalidTupleDecodingError(
|
|
1304
|
+
`rawValue is not an array, but param.type is tuple`,
|
|
1305
|
+
);
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
// Move to the next sub-index
|
|
1309
|
+
const remaining = indexes.slice(1);
|
|
1310
|
+
if (!remaining.length) {
|
|
1311
|
+
// If there are no more indexes, we can't pick a sub-component
|
|
1312
|
+
// Typically you'd want at least one more index to say "which subfield in the tuple we want"
|
|
1313
|
+
throw new InvalidTupleDecodingError(undefined, -1);
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
// Check the next index for param.components
|
|
1317
|
+
const subIdx = remaining[0] ?? param.components.length + 1;
|
|
1318
|
+
if (subIdx >= param.components.length) {
|
|
1319
|
+
throw new InvalidTupleDecodingError(undefined, subIdx);
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
// Recurse deeper using param.components as the "new top-level" ABI param list
|
|
1323
|
+
return parseNestedTupleValue(rawValue, remaining, param.components);
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
/**
|
|
1327
|
+
* Maps an ABI type string (e.g. 'uint256', 'address', 'bytes', 'string', etc.) to a `PrimitiveType`.
|
|
1328
|
+
*
|
|
1329
|
+
* @param {string} abiType
|
|
1330
|
+
* @returns {Exclude<PrimitiveType, PrimitiveType.TUPLE>}
|
|
1331
|
+
*/
|
|
1332
|
+
function abiTypeToPrimitiveType(
|
|
1333
|
+
abiType: string,
|
|
1334
|
+
): Exclude<PrimitiveType, PrimitiveType.TUPLE> {
|
|
1335
|
+
const lower = abiType.toLowerCase();
|
|
1336
|
+
|
|
1337
|
+
if (lower.startsWith('uint') || lower.startsWith('int')) {
|
|
1338
|
+
return PrimitiveType.UINT;
|
|
1339
|
+
}
|
|
1340
|
+
if (lower === 'address') {
|
|
1341
|
+
return PrimitiveType.ADDRESS;
|
|
1342
|
+
}
|
|
1343
|
+
if (lower === 'bytes' || lower.startsWith('bytes')) {
|
|
1344
|
+
return PrimitiveType.BYTES;
|
|
1345
|
+
}
|
|
1346
|
+
if (lower === 'string') {
|
|
1347
|
+
return PrimitiveType.STRING;
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
// If it doesn't match any known scalar, throw. We expect parseNestedTupleValue() to handle nested tuple logic separately.
|
|
1351
|
+
throw new DecodedArgsError(`Unrecognized ABI type: ${abiType}`);
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1174
1354
|
function _dedupeActionSteps(_steps: ActionStep[]): ActionStep[] {
|
|
1175
|
-
const steps: ActionStep[] = []
|
|
1176
|
-
|
|
1355
|
+
const steps: ActionStep[] = [];
|
|
1356
|
+
const signatures: Record<string, boolean> = {};
|
|
1177
1357
|
for (let step of _steps) {
|
|
1178
1358
|
const signature = JSON.stringify(step);
|
|
1179
1359
|
if (signatures[signature]) continue;
|
|
@@ -1182,6 +1362,7 @@ function _dedupeActionSteps(_steps: ActionStep[]): ActionStep[] {
|
|
|
1182
1362
|
}
|
|
1183
1363
|
return steps;
|
|
1184
1364
|
}
|
|
1365
|
+
|
|
1185
1366
|
type RawActionStep = Overwrite<ActionStep, { chainid: bigint }>;
|
|
1186
1367
|
type RawActionClaimant = Overwrite<ActionClaimant, { chainid: bigint }>;
|
|
1187
1368
|
|
|
@@ -1268,7 +1449,7 @@ export function prepareEventActionPayload({
|
|
|
1268
1449
|
components: [
|
|
1269
1450
|
{ type: 'uint8', name: 'filterType' },
|
|
1270
1451
|
{ type: 'uint8', name: 'fieldType' },
|
|
1271
|
-
{ type: '
|
|
1452
|
+
{ type: 'uint32', name: 'fieldIndex' },
|
|
1272
1453
|
{ type: 'bytes', name: 'filterData' },
|
|
1273
1454
|
],
|
|
1274
1455
|
},
|
|
@@ -1289,7 +1470,7 @@ export function prepareEventActionPayload({
|
|
|
1289
1470
|
components: [
|
|
1290
1471
|
{ type: 'uint8', name: 'filterType' },
|
|
1291
1472
|
{ type: 'uint8', name: 'fieldType' },
|
|
1292
|
-
{ type: '
|
|
1473
|
+
{ type: 'uint32', name: 'fieldIndex' },
|
|
1293
1474
|
{ type: 'bytes', name: 'filterData' },
|
|
1294
1475
|
],
|
|
1295
1476
|
},
|
|
@@ -1310,7 +1491,7 @@ export function prepareEventActionPayload({
|
|
|
1310
1491
|
components: [
|
|
1311
1492
|
{ type: 'uint8', name: 'filterType' },
|
|
1312
1493
|
{ type: 'uint8', name: 'fieldType' },
|
|
1313
|
-
{ type: '
|
|
1494
|
+
{ type: 'uint32', name: 'fieldIndex' },
|
|
1314
1495
|
{ type: 'bytes', name: 'filterData' },
|
|
1315
1496
|
],
|
|
1316
1497
|
},
|
|
@@ -1331,7 +1512,7 @@ export function prepareEventActionPayload({
|
|
|
1331
1512
|
components: [
|
|
1332
1513
|
{ type: 'uint8', name: 'filterType' },
|
|
1333
1514
|
{ type: 'uint8', name: 'fieldType' },
|
|
1334
|
-
{ type: '
|
|
1515
|
+
{ type: 'uint32', name: 'fieldIndex' },
|
|
1335
1516
|
{ type: 'bytes', name: 'filterData' },
|
|
1336
1517
|
],
|
|
1337
1518
|
},
|
|
@@ -1426,3 +1607,100 @@ export function transactionSenderClaimant(chainId: number): ActionClaimant {
|
|
|
1426
1607
|
chainid: chainId,
|
|
1427
1608
|
};
|
|
1428
1609
|
}
|
|
1610
|
+
|
|
1611
|
+
// Helper functions to bit-pack and decode fieldIndex values
|
|
1612
|
+
const MAX_FIELD_INDEX = 0b111111; // Maximum value for 6 bits (63)
|
|
1613
|
+
|
|
1614
|
+
/**
|
|
1615
|
+
* Packs up to five indexes into a single uint32 value.
|
|
1616
|
+
*
|
|
1617
|
+
* @param {number[]} indexes - Array of up to five indexes to pack.
|
|
1618
|
+
* @returns {number} - Packed uint32 value.
|
|
1619
|
+
* @throws {Error} - If more than five indexes are provided or an index exceeds the maximum value.
|
|
1620
|
+
*/
|
|
1621
|
+
export function packFieldIndexes(indexes: number[]): number {
|
|
1622
|
+
if (indexes.length > 5) {
|
|
1623
|
+
throw new InvalidTupleEncodingError('Can only pack up to 5 indexes.');
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
let packed = 0;
|
|
1627
|
+
indexes.forEach((index, i) => {
|
|
1628
|
+
if (index > MAX_FIELD_INDEX) {
|
|
1629
|
+
throw new InvalidTupleEncodingError(
|
|
1630
|
+
`Index ${index} exceeds the maximum allowed value (${MAX_FIELD_INDEX}).`,
|
|
1631
|
+
);
|
|
1632
|
+
}
|
|
1633
|
+
packed |= (index & MAX_FIELD_INDEX) << (i * 6); // Each index occupies 6 bits
|
|
1634
|
+
});
|
|
1635
|
+
|
|
1636
|
+
return packed;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
/**
|
|
1640
|
+
* Unpacks a uint32 fieldIndex value into an array of up to five indexes.
|
|
1641
|
+
*
|
|
1642
|
+
* @param {number} packed - Packed uint32 value.
|
|
1643
|
+
* @returns {number[]} - Array of unpacked indexes.
|
|
1644
|
+
*/
|
|
1645
|
+
export function unpackFieldIndexes(packed: number): number[] {
|
|
1646
|
+
const indexes: number[] = [];
|
|
1647
|
+
for (let i = 0; i < 5; i++) {
|
|
1648
|
+
const index = (packed >> (i * 6)) & MAX_FIELD_INDEX;
|
|
1649
|
+
if (index === MAX_FIELD_INDEX) break; // Terminator value
|
|
1650
|
+
indexes.push(index);
|
|
1651
|
+
}
|
|
1652
|
+
return indexes;
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
/**
|
|
1656
|
+
* Decodes an event log and reorders the arguments to match the original ABI order.
|
|
1657
|
+
* This is necessary because viem's decodeEventLog reorders indexed parameters to the front.
|
|
1658
|
+
*
|
|
1659
|
+
* @param event - The event ABI definition
|
|
1660
|
+
* @param log - The log to decode
|
|
1661
|
+
* @returns {EventLog} The decoded log with arguments in the original ABI order
|
|
1662
|
+
*/
|
|
1663
|
+
export function decodeAndReorderLogArgs(event: AbiEvent, log: Log) {
|
|
1664
|
+
const decodedLog = decodeEventLog({
|
|
1665
|
+
abi: [event],
|
|
1666
|
+
data: log.data,
|
|
1667
|
+
topics: log.topics,
|
|
1668
|
+
});
|
|
1669
|
+
|
|
1670
|
+
const argsArray = Array.isArray(decodedLog.args)
|
|
1671
|
+
? decodedLog.args
|
|
1672
|
+
: Object.values(decodedLog.args);
|
|
1673
|
+
|
|
1674
|
+
if (!event.inputs.some((input) => input.indexed)) {
|
|
1675
|
+
return decodedLog as EventLog;
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
const indexedIndices: number[] = [];
|
|
1679
|
+
const nonIndexedIndices: number[] = [];
|
|
1680
|
+
for (let i = 0; i < event.inputs.length; i++) {
|
|
1681
|
+
if (event.inputs[i]!.indexed) {
|
|
1682
|
+
indexedIndices.push(i);
|
|
1683
|
+
} else {
|
|
1684
|
+
nonIndexedIndices.push(i);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
const reorderedArgs = new Array(event.inputs.length);
|
|
1689
|
+
let currentIndex = 0;
|
|
1690
|
+
|
|
1691
|
+
// Place the indexed arguments in their original positions
|
|
1692
|
+
for (let i = 0; i < indexedIndices.length; i++) {
|
|
1693
|
+
reorderedArgs[indexedIndices[i]!] = argsArray[currentIndex++];
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
// Place the non-indexed arguments in their original positions
|
|
1697
|
+
for (let i = 0; i < nonIndexedIndices.length; i++) {
|
|
1698
|
+
reorderedArgs[nonIndexedIndices[i]!] = argsArray[currentIndex++];
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
return {
|
|
1702
|
+
...log,
|
|
1703
|
+
eventName: decodedLog.eventName,
|
|
1704
|
+
args: reorderedArgs,
|
|
1705
|
+
} as EventLog;
|
|
1706
|
+
}
|