@boostxyz/sdk 5.2.1 → 5.3.1
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 +84 -41
- package/dist/Actions/EventAction.d.ts.map +1 -1
- package/dist/Actions/EventAction.js +1866 -378
- 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-DBZHQ9Np.js → Incentive-qlnv5kQB.js} +77 -50
- 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/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/package.json +1 -1
- package/src/Actions/EventAction.test.ts +294 -3
- package/src/Actions/EventAction.ts +417 -154
- package/src/BoostCore.test.ts +51 -3
- package/src/BoostCore.ts +73 -0
- package/src/Incentives/ERC20PeggedIncentive.ts +33 -4
- package/src/errors.ts +50 -0
- package/dist/Incentive-BpZePiOD.cjs +0 -2
- package/dist/Incentive-BpZePiOD.cjs.map +0 -1
- package/dist/Incentive-DBZHQ9Np.js.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
|
@@ -6,11 +6,13 @@ import {
|
|
|
6
6
|
writeEventActionExecute,
|
|
7
7
|
} from '@boostxyz/evm';
|
|
8
8
|
import { bytecode } from '@boostxyz/evm/artifacts/contracts/actions/EventAction.sol/EventAction.json';
|
|
9
|
+
import { abi } from '@boostxyz/signatures/events';
|
|
9
10
|
import { getTransaction, getTransactionReceipt } from '@wagmi/core';
|
|
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
|
*
|
|
@@ -569,16 +585,15 @@ export class EventAction extends DeployableTarget<
|
|
|
569
585
|
) {
|
|
570
586
|
return undefined;
|
|
571
587
|
}
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
});
|
|
588
|
+
|
|
589
|
+
let decodedLogs: EventLogs;
|
|
590
|
+
if (signature === TRANSFER_SIGNATURE) {
|
|
591
|
+
({ decodedLogs } = await this.decodeTransferLogs(receipt));
|
|
592
|
+
} else {
|
|
593
|
+
decodedLogs = receipt.logs
|
|
594
|
+
.filter((log) => log.topics[0] === toEventSelector(event))
|
|
595
|
+
.map((log) => decodeAndReorderLogArgs(event, log));
|
|
596
|
+
}
|
|
582
597
|
|
|
583
598
|
for (let log of decodedLogs) {
|
|
584
599
|
if (!isAddressEqual(log.address, claimant.targetContract)) continue;
|
|
@@ -703,7 +718,7 @@ export class EventAction extends DeployableTarget<
|
|
|
703
718
|
|
|
704
719
|
// Use the provided logs, no need to fetch receipt
|
|
705
720
|
if ('logs' in params) {
|
|
706
|
-
return this.isActionEventValid(actionStep, params.logs);
|
|
721
|
+
return this.isActionEventValid(actionStep, params.logs, event);
|
|
707
722
|
}
|
|
708
723
|
|
|
709
724
|
const receipt = await getTransactionReceipt(this._config, {
|
|
@@ -719,22 +734,15 @@ export class EventAction extends DeployableTarget<
|
|
|
719
734
|
|
|
720
735
|
// Special handling for Transfer events
|
|
721
736
|
if (actionStep.signature === TRANSFER_SIGNATURE) {
|
|
722
|
-
|
|
737
|
+
const { decodedLogs, event } = await this.decodeTransferLogs(receipt);
|
|
738
|
+
return this.isActionEventValid(actionStep, decodedLogs, event);
|
|
723
739
|
}
|
|
724
740
|
|
|
725
741
|
const decodedLogs = receipt.logs
|
|
726
742
|
.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
|
-
});
|
|
733
|
-
|
|
734
|
-
return { ...log, eventName, args };
|
|
735
|
-
});
|
|
743
|
+
.map((log) => decodeAndReorderLogArgs(event, log));
|
|
736
744
|
|
|
737
|
-
return this.isActionEventValid(actionStep, decodedLogs);
|
|
745
|
+
return this.isActionEventValid(actionStep, decodedLogs, event);
|
|
738
746
|
}
|
|
739
747
|
if (actionStep.signatureType === SignatureType.FUNC) {
|
|
740
748
|
if ('hash' in params) {
|
|
@@ -756,27 +764,51 @@ export class EventAction extends DeployableTarget<
|
|
|
756
764
|
|
|
757
765
|
/**
|
|
758
766
|
* 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
767
|
*
|
|
761
768
|
* @public
|
|
762
|
-
* @async
|
|
763
769
|
* @param {ActionStep} actionStep - The action step containing the event to validate.
|
|
764
770
|
* @param {EventLogs} logs - Event logs to validate the given step against
|
|
765
|
-
* @
|
|
771
|
+
* @param {AbiEvent} eventAbi - The ABI definition of the event
|
|
772
|
+
* @returns {boolean} Resolves to true if the action event is valid, throws if input is invalid, otherwise false.
|
|
766
773
|
*/
|
|
767
|
-
public isActionEventValid(
|
|
774
|
+
public isActionEventValid(
|
|
775
|
+
actionStep: ActionStep,
|
|
776
|
+
logs: EventLogs,
|
|
777
|
+
eventAbi: AbiEvent,
|
|
778
|
+
): boolean {
|
|
768
779
|
const criteria = actionStep.actionParameter;
|
|
769
780
|
if (!logs.length) return false;
|
|
781
|
+
|
|
782
|
+
// Check each log
|
|
770
783
|
for (let log of logs) {
|
|
771
|
-
|
|
772
|
-
|
|
784
|
+
// parse out final (scalar) field from the log args
|
|
785
|
+
try {
|
|
786
|
+
if (!Array.isArray(log.args)) {
|
|
787
|
+
throw new DecodedArgsMalformedError({
|
|
788
|
+
log,
|
|
789
|
+
criteria,
|
|
790
|
+
fieldValue: undefined,
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
const { value, type } = this.parseFieldFromAbi(
|
|
794
|
+
log.args,
|
|
795
|
+
criteria.fieldIndex,
|
|
796
|
+
eventAbi.inputs || [],
|
|
797
|
+
criteria.fieldType,
|
|
798
|
+
);
|
|
799
|
+
criteria.fieldType = type;
|
|
800
|
+
if (this.validateFieldAgainstCriteria(criteria, value, { log })) {
|
|
801
|
+
return true;
|
|
802
|
+
}
|
|
803
|
+
} catch {
|
|
804
|
+
// If there's an error on this log, keep trying with the next one
|
|
773
805
|
}
|
|
774
806
|
}
|
|
775
807
|
return false;
|
|
776
808
|
}
|
|
777
809
|
|
|
778
810
|
/**
|
|
779
|
-
* Decodes
|
|
811
|
+
* Decodes logs specifically for ERC721 and ERC20 Transfer events.
|
|
780
812
|
*
|
|
781
813
|
* This special handling is required because both ERC20 and ERC721 Transfer events:
|
|
782
814
|
* 1. Share the same event signature (0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef)
|
|
@@ -788,69 +820,127 @@ export class EventAction extends DeployableTarget<
|
|
|
788
820
|
* try decoding both ways to determine which type of Transfer event we're dealing with.
|
|
789
821
|
*
|
|
790
822
|
* @param {GetTransactionReceiptReturnType} receipt - The transaction receipt containing the logs
|
|
791
|
-
* @
|
|
792
|
-
* @returns {Promise<boolean>} - Returns true if the transfer logs are valid for either ERC20 or ERC721
|
|
823
|
+
* @returns {Promise<{ decodedLogs: EventLogs; event: AbiEvent }>} - Returns the decoded logs and the transfer event ABI used for decoding
|
|
793
824
|
* @throws {DecodedArgsError} - Throws if neither ERC20 nor ERC721 decoding succeeds
|
|
794
825
|
*/
|
|
795
826
|
private async decodeTransferLogs(
|
|
796
827
|
receipt: GetTransactionReceiptReturnType,
|
|
797
|
-
|
|
798
|
-
) {
|
|
828
|
+
): Promise<{ decodedLogs: EventLogs; event: AbiEvent }> {
|
|
799
829
|
const filteredLogs = receipt.logs.filter(
|
|
800
830
|
(log) => log.topics[0] === TRANSFER_SIGNATURE,
|
|
801
831
|
);
|
|
832
|
+
const event = abi[
|
|
833
|
+
'Transfer(address indexed,address indexed,uint256 indexed)'
|
|
834
|
+
] as AbiEvent;
|
|
802
835
|
|
|
803
836
|
// ERC721
|
|
804
837
|
try {
|
|
805
838
|
const decodedLogs = filteredLogs.map((log) => {
|
|
806
839
|
const { eventName, args } = decodeEventLog({
|
|
807
|
-
abi: [
|
|
808
|
-
{
|
|
809
|
-
name: 'Transfer',
|
|
810
|
-
type: 'event',
|
|
811
|
-
inputs: [
|
|
812
|
-
{ type: 'address', indexed: true },
|
|
813
|
-
{ type: 'address', indexed: true },
|
|
814
|
-
{ type: 'uint256', indexed: true },
|
|
815
|
-
],
|
|
816
|
-
},
|
|
817
|
-
],
|
|
840
|
+
abi: [event],
|
|
818
841
|
data: log.data,
|
|
819
842
|
topics: log.topics,
|
|
820
843
|
});
|
|
821
844
|
return { ...log, eventName, args };
|
|
822
845
|
});
|
|
823
846
|
|
|
824
|
-
return
|
|
847
|
+
return {
|
|
848
|
+
decodedLogs,
|
|
849
|
+
event,
|
|
850
|
+
};
|
|
825
851
|
} catch {
|
|
826
852
|
// ERC20
|
|
827
853
|
try {
|
|
854
|
+
event.inputs[2]!.indexed = false;
|
|
828
855
|
const decodedLogs = filteredLogs.map((log) => {
|
|
829
856
|
const { eventName, args } = decodeEventLog({
|
|
830
|
-
abi: [
|
|
831
|
-
{
|
|
832
|
-
name: 'Transfer',
|
|
833
|
-
type: 'event',
|
|
834
|
-
inputs: [
|
|
835
|
-
{ type: 'address', indexed: true },
|
|
836
|
-
{ type: 'address', indexed: true },
|
|
837
|
-
{ type: 'uint256' },
|
|
838
|
-
],
|
|
839
|
-
},
|
|
840
|
-
],
|
|
857
|
+
abi: [event],
|
|
841
858
|
data: log.data,
|
|
842
859
|
topics: log.topics,
|
|
843
860
|
});
|
|
844
861
|
return { ...log, eventName, args };
|
|
845
862
|
});
|
|
846
863
|
|
|
847
|
-
return
|
|
864
|
+
return {
|
|
865
|
+
decodedLogs,
|
|
866
|
+
event,
|
|
867
|
+
};
|
|
848
868
|
} catch {
|
|
849
869
|
throw new DecodedArgsError('Failed to decode transfer logs');
|
|
850
870
|
}
|
|
851
871
|
}
|
|
852
872
|
}
|
|
853
873
|
|
|
874
|
+
/**
|
|
875
|
+
* Parses the final (scalar) field from a set of decoded arguments, given an ABI definition.
|
|
876
|
+
* If the fieldType is TUPLE, we decode `fieldIndex` as a bitpacked array of indexes to drill down
|
|
877
|
+
* into nested tuples. Otherwise, we parse the single `fieldIndex` as normal.
|
|
878
|
+
*
|
|
879
|
+
* @public
|
|
880
|
+
* @param {readonly unknown[]} allArgs - The decoded arguments array from an event log or function call.
|
|
881
|
+
* @param {number} criteriaIndex - The field index (bitpacked if TUPLE).
|
|
882
|
+
* @param {AbiParameter[]} abiInputs - The ABI inputs describing each decoded argument.
|
|
883
|
+
* @param {PrimitiveType} declaredType - Either TUPLE or a standard scalar type
|
|
884
|
+
* @returns {{ value: string | bigint | Hex; type: Exclude<PrimitiveType, PrimitiveType.TUPLE> }}
|
|
885
|
+
*/
|
|
886
|
+
public parseFieldFromAbi(
|
|
887
|
+
allArgs: readonly unknown[],
|
|
888
|
+
criteriaIndex: number,
|
|
889
|
+
abiInputs: readonly AbiParameter[],
|
|
890
|
+
declaredType: PrimitiveType,
|
|
891
|
+
): {
|
|
892
|
+
value: string | bigint | Hex;
|
|
893
|
+
type: Exclude<PrimitiveType, PrimitiveType.TUPLE>;
|
|
894
|
+
} {
|
|
895
|
+
// If ANY_ACTION_PARAM, return a dummy "any" value so we can do special-case checks
|
|
896
|
+
if (criteriaIndex === CheatCodes.ANY_ACTION_PARAM) {
|
|
897
|
+
return { value: zeroHash, type: PrimitiveType.BYTES };
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
// If it's not TUPLE, parse as a single index (existing logic)
|
|
901
|
+
if (declaredType !== PrimitiveType.TUPLE) {
|
|
902
|
+
if (!Array.isArray(allArgs) || criteriaIndex >= allArgs.length) {
|
|
903
|
+
throw new FieldValueUndefinedError({
|
|
904
|
+
fieldValue: allArgs,
|
|
905
|
+
criteria: {
|
|
906
|
+
filterType: FilterType.EQUAL,
|
|
907
|
+
fieldType: declaredType,
|
|
908
|
+
fieldIndex: criteriaIndex,
|
|
909
|
+
filterData: zeroHash,
|
|
910
|
+
},
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
const abiParam = abiInputs[criteriaIndex];
|
|
914
|
+
if (!abiParam || !abiParam.type) {
|
|
915
|
+
throw new UnparseableAbiParamError(criteriaIndex, abiParam as AbiEvent);
|
|
916
|
+
}
|
|
917
|
+
const rawValue = allArgs[criteriaIndex];
|
|
918
|
+
|
|
919
|
+
const finalType = abiTypeToPrimitiveType(abiParam.type);
|
|
920
|
+
|
|
921
|
+
if (
|
|
922
|
+
finalType === PrimitiveType.ADDRESS &&
|
|
923
|
+
(typeof rawValue !== 'string' || !isAddress(rawValue))
|
|
924
|
+
) {
|
|
925
|
+
throw new FieldValueUndefinedError({
|
|
926
|
+
fieldValue: rawValue,
|
|
927
|
+
criteria: {
|
|
928
|
+
fieldIndex: criteriaIndex,
|
|
929
|
+
filterType: FilterType.EQUAL,
|
|
930
|
+
fieldType: finalType,
|
|
931
|
+
filterData: zeroHash,
|
|
932
|
+
},
|
|
933
|
+
});
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return { value: rawValue as string | bigint | Hex, type: finalType };
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// Otherwise, declaredType === TUPLE => decode bitpacked indexes
|
|
940
|
+
const indexes = unpackFieldIndexes(criteriaIndex);
|
|
941
|
+
return parseNestedTupleValue(allArgs as unknown[], indexes, abiInputs);
|
|
942
|
+
}
|
|
943
|
+
|
|
854
944
|
/**
|
|
855
945
|
* Validates a single action function with a given criteria against the transaction input.
|
|
856
946
|
*
|
|
@@ -870,10 +960,10 @@ export class EventAction extends DeployableTarget<
|
|
|
870
960
|
params: Pick<ValidateActionStepParams, 'abiItem' | 'knownSignatures'>,
|
|
871
961
|
) {
|
|
872
962
|
const criteria = actionStep.actionParameter;
|
|
873
|
-
|
|
963
|
+
const signature = actionStep.signature;
|
|
874
964
|
|
|
875
965
|
let func: AbiFunction;
|
|
876
|
-
if (params.abiItem) func = params
|
|
966
|
+
if (params.abiItem) func = params.abiItem as AbiFunction;
|
|
877
967
|
else {
|
|
878
968
|
const sigPool = params.knownSignatures as Record<Hex, AbiFunction>;
|
|
879
969
|
func = sigPool[signature] as AbiFunction;
|
|
@@ -892,31 +982,37 @@ export class EventAction extends DeployableTarget<
|
|
|
892
982
|
throw new FunctionDataDecodeError([func], e as Error);
|
|
893
983
|
}
|
|
894
984
|
|
|
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
|
-
) {
|
|
985
|
+
if (!decodedData?.args) {
|
|
906
986
|
return false;
|
|
907
987
|
}
|
|
908
988
|
|
|
909
|
-
|
|
989
|
+
try {
|
|
990
|
+
const { value, type } = this.parseFieldFromAbi(
|
|
991
|
+
decodedData.args as unknown[],
|
|
992
|
+
criteria.fieldIndex,
|
|
993
|
+
func.inputs || [],
|
|
994
|
+
criteria.fieldType,
|
|
995
|
+
);
|
|
996
|
+
criteria.fieldType = type;
|
|
997
|
+
return this.validateFieldAgainstCriteria(criteria, value, {
|
|
998
|
+
decodedArgs: decodedData.args as readonly (string | bigint)[],
|
|
999
|
+
});
|
|
1000
|
+
} catch {
|
|
1001
|
+
return false;
|
|
1002
|
+
}
|
|
910
1003
|
}
|
|
1004
|
+
|
|
911
1005
|
/**
|
|
912
|
-
* Validates a field against a given criteria.
|
|
1006
|
+
* Validates a field against a given criteria. The field is assumed to be a non-tuple scalar,
|
|
1007
|
+
* along with its final resolved `PrimitiveType`. (Any TUPLE logic has been extracted elsewhere.)
|
|
913
1008
|
*
|
|
914
1009
|
* @param {Criteria} criteria - The criteria to validate against.
|
|
915
1010
|
* @param {string | bigint | Hex} fieldValue - The field value to validate.
|
|
1011
|
+
* @param {Exclude<PrimitiveType, PrimitiveType.TUPLE>} fieldType - The final resolved primitive type.
|
|
916
1012
|
* @param {Object} input - Additional context for validation.
|
|
917
1013
|
* @param {EventLogs[0]} [input.log] - The event log, if validating an event.
|
|
918
1014
|
* @param {readonly (string | bigint)[]} [input.decodedArgs] - The decoded function arguments, if validating a function call.
|
|
919
|
-
* @returns {
|
|
1015
|
+
* @returns {boolean} - Returns true if the field passes the criteria, false otherwise.
|
|
920
1016
|
*/
|
|
921
1017
|
public validateFieldAgainstCriteria(
|
|
922
1018
|
criteria: Criteria,
|
|
@@ -925,6 +1021,10 @@ export class EventAction extends DeployableTarget<
|
|
|
925
1021
|
| { log: EventLogs[0] }
|
|
926
1022
|
| { decodedArgs: readonly (string | bigint)[] },
|
|
927
1023
|
): boolean {
|
|
1024
|
+
/*
|
|
1025
|
+
* Special-case: ANY_ACTION_PARAM. If we have filterType=EQUAL, fieldType=BYTES, fieldIndex=255,
|
|
1026
|
+
* we consider that a wildcard match. Return true immediately.
|
|
1027
|
+
*/
|
|
928
1028
|
if (
|
|
929
1029
|
criteria.filterType === FilterType.EQUAL &&
|
|
930
1030
|
criteria.fieldType === PrimitiveType.BYTES &&
|
|
@@ -932,11 +1032,17 @@ export class EventAction extends DeployableTarget<
|
|
|
932
1032
|
) {
|
|
933
1033
|
return true;
|
|
934
1034
|
}
|
|
1035
|
+
if (criteria.fieldType === PrimitiveType.TUPLE) {
|
|
1036
|
+
throw new InvalidTupleDecodingError(
|
|
1037
|
+
'Tuples should not be passed into validateFieldAgainstCriteria',
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
const fieldType = criteria.fieldType;
|
|
935
1041
|
|
|
936
|
-
//
|
|
1042
|
+
// Evaluate filter based on the final fieldType
|
|
937
1043
|
switch (criteria.filterType) {
|
|
938
1044
|
case FilterType.EQUAL:
|
|
939
|
-
return match(
|
|
1045
|
+
return match(fieldType)
|
|
940
1046
|
.with(PrimitiveType.ADDRESS, () =>
|
|
941
1047
|
isAddressEqual(criteria.filterData, fieldValue as Address),
|
|
942
1048
|
)
|
|
@@ -951,7 +1057,7 @@ export class EventAction extends DeployableTarget<
|
|
|
951
1057
|
.otherwise(() => fieldValue === criteria.filterData);
|
|
952
1058
|
|
|
953
1059
|
case FilterType.NOT_EQUAL:
|
|
954
|
-
return match(
|
|
1060
|
+
return match(fieldType)
|
|
955
1061
|
.with(
|
|
956
1062
|
PrimitiveType.ADDRESS,
|
|
957
1063
|
() => !isAddressEqual(criteria.filterData, fieldValue as Address),
|
|
@@ -967,7 +1073,7 @@ export class EventAction extends DeployableTarget<
|
|
|
967
1073
|
.otherwise(() => fieldValue !== criteria.filterData);
|
|
968
1074
|
|
|
969
1075
|
case FilterType.GREATER_THAN:
|
|
970
|
-
if (
|
|
1076
|
+
if (fieldType === PrimitiveType.UINT) {
|
|
971
1077
|
return BigInt(fieldValue) > BigInt(criteria.filterData);
|
|
972
1078
|
}
|
|
973
1079
|
throw new InvalidNumericalCriteriaError({
|
|
@@ -975,8 +1081,9 @@ export class EventAction extends DeployableTarget<
|
|
|
975
1081
|
criteria,
|
|
976
1082
|
fieldValue,
|
|
977
1083
|
});
|
|
1084
|
+
|
|
978
1085
|
case FilterType.GREATER_THAN_OR_EQUAL:
|
|
979
|
-
if (
|
|
1086
|
+
if (fieldType === PrimitiveType.UINT) {
|
|
980
1087
|
return BigInt(fieldValue) >= BigInt(criteria.filterData);
|
|
981
1088
|
}
|
|
982
1089
|
throw new InvalidNumericalCriteriaError({
|
|
@@ -986,7 +1093,7 @@ export class EventAction extends DeployableTarget<
|
|
|
986
1093
|
});
|
|
987
1094
|
|
|
988
1095
|
case FilterType.LESS_THAN:
|
|
989
|
-
if (
|
|
1096
|
+
if (fieldType === PrimitiveType.UINT) {
|
|
990
1097
|
return BigInt(fieldValue) < BigInt(criteria.filterData);
|
|
991
1098
|
}
|
|
992
1099
|
throw new InvalidNumericalCriteriaError({
|
|
@@ -994,8 +1101,9 @@ export class EventAction extends DeployableTarget<
|
|
|
994
1101
|
criteria,
|
|
995
1102
|
fieldValue,
|
|
996
1103
|
});
|
|
1104
|
+
|
|
997
1105
|
case FilterType.LESS_THAN_OR_EQUAL:
|
|
998
|
-
if (
|
|
1106
|
+
if (fieldType === PrimitiveType.UINT) {
|
|
999
1107
|
return BigInt(fieldValue) <= BigInt(criteria.filterData);
|
|
1000
1108
|
}
|
|
1001
1109
|
throw new InvalidNumericalCriteriaError({
|
|
@@ -1006,11 +1114,11 @@ export class EventAction extends DeployableTarget<
|
|
|
1006
1114
|
|
|
1007
1115
|
case FilterType.CONTAINS:
|
|
1008
1116
|
if (
|
|
1009
|
-
|
|
1010
|
-
|
|
1117
|
+
fieldType === PrimitiveType.BYTES ||
|
|
1118
|
+
fieldType === PrimitiveType.STRING
|
|
1011
1119
|
) {
|
|
1012
1120
|
let substring;
|
|
1013
|
-
if (
|
|
1121
|
+
if (fieldType === PrimitiveType.STRING) {
|
|
1014
1122
|
substring = fromHex(criteria.filterData, 'string');
|
|
1015
1123
|
} else {
|
|
1016
1124
|
// truncate the `0x` prefix
|
|
@@ -1032,12 +1140,11 @@ export class EventAction extends DeployableTarget<
|
|
|
1032
1140
|
fieldValue,
|
|
1033
1141
|
});
|
|
1034
1142
|
}
|
|
1035
|
-
|
|
1036
|
-
if (criteria.fieldType === PrimitiveType.STRING) {
|
|
1037
|
-
// fieldValue is decoded by the ABI
|
|
1143
|
+
if (fieldType === PrimitiveType.STRING) {
|
|
1038
1144
|
const regexString = fromHex(criteria.filterData, 'string');
|
|
1039
1145
|
return new RegExp(regexString).test(fieldValue);
|
|
1040
1146
|
}
|
|
1147
|
+
// Otherwise unrecognized or not applicable
|
|
1041
1148
|
|
|
1042
1149
|
default:
|
|
1043
1150
|
throw new UnrecognizedFilterTypeError({
|
|
@@ -1048,68 +1155,6 @@ export class EventAction extends DeployableTarget<
|
|
|
1048
1155
|
}
|
|
1049
1156
|
}
|
|
1050
1157
|
|
|
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
1158
|
/**
|
|
1114
1159
|
* @inheritdoc
|
|
1115
1160
|
*
|
|
@@ -1159,6 +1204,15 @@ export class EventAction extends DeployableTarget<
|
|
|
1159
1204
|
};
|
|
1160
1205
|
}
|
|
1161
1206
|
|
|
1207
|
+
/**
|
|
1208
|
+
* Determines whether a string or bytes field is indexed in the event definition.
|
|
1209
|
+
* If the user tries to filter on an indexed string/bytes, we throw an error.
|
|
1210
|
+
*
|
|
1211
|
+
* @public
|
|
1212
|
+
* @param {ActionStep} step
|
|
1213
|
+
* @param {AbiEvent} event
|
|
1214
|
+
* @returns {boolean}
|
|
1215
|
+
*/
|
|
1162
1216
|
public isArraylikeIndexed(step: ActionStep, event: AbiEvent) {
|
|
1163
1217
|
if (
|
|
1164
1218
|
(step.actionParameter.fieldType === PrimitiveType.STRING ||
|
|
@@ -1171,9 +1225,114 @@ export class EventAction extends DeployableTarget<
|
|
|
1171
1225
|
}
|
|
1172
1226
|
}
|
|
1173
1227
|
|
|
1228
|
+
/**
|
|
1229
|
+
* Checks if a particular ABI parameter is the "tuple" variant that can have `components`.
|
|
1230
|
+
*
|
|
1231
|
+
* @param {AbiParameter} param
|
|
1232
|
+
* @returns {boolean}
|
|
1233
|
+
*/
|
|
1234
|
+
function isTupleAbiParameter(
|
|
1235
|
+
param: AbiParameter,
|
|
1236
|
+
): param is Extract<AbiParameter, { components: readonly AbiParameter[] }> {
|
|
1237
|
+
return param.type === 'tuple' || param.type.startsWith('tuple[');
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
/**
|
|
1241
|
+
* Recursively parses nested tuples by following an array of sub-indexes (unpacked from bitpacked `fieldIndex`).
|
|
1242
|
+
* Each entry in `indexes` is used to pick which sub-component in the current tuple's `components`.
|
|
1243
|
+
* If we encounter the "terminator" or run out of indexes, we stop.
|
|
1244
|
+
*
|
|
1245
|
+
* @param {unknown[]} rawArgs - The top-level arguments array.
|
|
1246
|
+
* @param {number[]} indexes - The array of indexes from `unpackFieldIndexes(...)`.
|
|
1247
|
+
* @param {readonly AbiParameter[]} abiInputs - The top-level ABI inputs for the entire arguments array.
|
|
1248
|
+
* @returns {{ value: string | bigint | Hex, type: Exclude<PrimitiveType, PrimitiveType.TUPLE> }}
|
|
1249
|
+
*/
|
|
1250
|
+
function parseNestedTupleValue(
|
|
1251
|
+
rawArgs: unknown[],
|
|
1252
|
+
indexes: number[],
|
|
1253
|
+
abiInputs: readonly AbiParameter[],
|
|
1254
|
+
): {
|
|
1255
|
+
value: string | bigint | Hex;
|
|
1256
|
+
type: Exclude<PrimitiveType, PrimitiveType.TUPLE>;
|
|
1257
|
+
} {
|
|
1258
|
+
if (!indexes.length) {
|
|
1259
|
+
throw new InvalidTupleDecodingError(
|
|
1260
|
+
`No indexes found; cannot parse TUPLE field`,
|
|
1261
|
+
);
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
// The first index picks which top-level ABI param to look at
|
|
1265
|
+
const idx = indexes[0] ?? abiInputs.length + 1;
|
|
1266
|
+
// If idx is out of range or is a "terminator," fail fast
|
|
1267
|
+
if (idx >= abiInputs.length) {
|
|
1268
|
+
throw new InvalidTupleDecodingError(undefined, idx);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
const param = abiInputs[idx];
|
|
1272
|
+
const rawValue = rawArgs[idx];
|
|
1273
|
+
|
|
1274
|
+
// If param isn't a tuple, we are at a leaf
|
|
1275
|
+
if (!isTupleAbiParameter(param!)) {
|
|
1276
|
+
const finalType = abiTypeToPrimitiveType(param!.type);
|
|
1277
|
+
return { value: rawValue as string | bigint | Hex, type: finalType };
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
// Otherwise param is a tuple => rawValue must be an array of subfields
|
|
1281
|
+
if (!Array.isArray(rawValue)) {
|
|
1282
|
+
throw new InvalidTupleDecodingError(
|
|
1283
|
+
`rawValue is not an array, but param.type is tuple`,
|
|
1284
|
+
);
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
// Move to the next sub-index
|
|
1288
|
+
const remaining = indexes.slice(1);
|
|
1289
|
+
if (!remaining.length) {
|
|
1290
|
+
// If there are no more indexes, we can't pick a sub-component
|
|
1291
|
+
// Typically you'd want at least one more index to say "which subfield in the tuple we want"
|
|
1292
|
+
throw new InvalidTupleDecodingError(undefined, -1);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
// Check the next index for param.components
|
|
1296
|
+
const subIdx = remaining[0] ?? param.components.length + 1;
|
|
1297
|
+
if (subIdx >= param.components.length) {
|
|
1298
|
+
throw new InvalidTupleDecodingError(undefined, subIdx);
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// Recurse deeper using param.components as the "new top-level" ABI param list
|
|
1302
|
+
return parseNestedTupleValue(rawValue, remaining, param.components);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
/**
|
|
1306
|
+
* Maps an ABI type string (e.g. 'uint256', 'address', 'bytes', 'string', etc.) to a `PrimitiveType`.
|
|
1307
|
+
*
|
|
1308
|
+
* @param {string} abiType
|
|
1309
|
+
* @returns {Exclude<PrimitiveType, PrimitiveType.TUPLE>}
|
|
1310
|
+
*/
|
|
1311
|
+
function abiTypeToPrimitiveType(
|
|
1312
|
+
abiType: string,
|
|
1313
|
+
): Exclude<PrimitiveType, PrimitiveType.TUPLE> {
|
|
1314
|
+
const lower = abiType.toLowerCase();
|
|
1315
|
+
|
|
1316
|
+
if (lower.startsWith('uint') || lower.startsWith('int')) {
|
|
1317
|
+
return PrimitiveType.UINT;
|
|
1318
|
+
}
|
|
1319
|
+
if (lower === 'address') {
|
|
1320
|
+
return PrimitiveType.ADDRESS;
|
|
1321
|
+
}
|
|
1322
|
+
if (lower === 'bytes' || lower.startsWith('bytes')) {
|
|
1323
|
+
return PrimitiveType.BYTES;
|
|
1324
|
+
}
|
|
1325
|
+
if (lower === 'string') {
|
|
1326
|
+
return PrimitiveType.STRING;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// If it doesn't match any known scalar, throw. We expect parseNestedTupleValue() to handle nested tuple logic separately.
|
|
1330
|
+
throw new DecodedArgsError(`Unrecognized ABI type: ${abiType}`);
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1174
1333
|
function _dedupeActionSteps(_steps: ActionStep[]): ActionStep[] {
|
|
1175
|
-
const steps: ActionStep[] = []
|
|
1176
|
-
|
|
1334
|
+
const steps: ActionStep[] = [];
|
|
1335
|
+
const signatures: Record<string, boolean> = {};
|
|
1177
1336
|
for (let step of _steps) {
|
|
1178
1337
|
const signature = JSON.stringify(step);
|
|
1179
1338
|
if (signatures[signature]) continue;
|
|
@@ -1182,6 +1341,7 @@ function _dedupeActionSteps(_steps: ActionStep[]): ActionStep[] {
|
|
|
1182
1341
|
}
|
|
1183
1342
|
return steps;
|
|
1184
1343
|
}
|
|
1344
|
+
|
|
1185
1345
|
type RawActionStep = Overwrite<ActionStep, { chainid: bigint }>;
|
|
1186
1346
|
type RawActionClaimant = Overwrite<ActionClaimant, { chainid: bigint }>;
|
|
1187
1347
|
|
|
@@ -1268,7 +1428,7 @@ export function prepareEventActionPayload({
|
|
|
1268
1428
|
components: [
|
|
1269
1429
|
{ type: 'uint8', name: 'filterType' },
|
|
1270
1430
|
{ type: 'uint8', name: 'fieldType' },
|
|
1271
|
-
{ type: '
|
|
1431
|
+
{ type: 'uint32', name: 'fieldIndex' },
|
|
1272
1432
|
{ type: 'bytes', name: 'filterData' },
|
|
1273
1433
|
],
|
|
1274
1434
|
},
|
|
@@ -1289,7 +1449,7 @@ export function prepareEventActionPayload({
|
|
|
1289
1449
|
components: [
|
|
1290
1450
|
{ type: 'uint8', name: 'filterType' },
|
|
1291
1451
|
{ type: 'uint8', name: 'fieldType' },
|
|
1292
|
-
{ type: '
|
|
1452
|
+
{ type: 'uint32', name: 'fieldIndex' },
|
|
1293
1453
|
{ type: 'bytes', name: 'filterData' },
|
|
1294
1454
|
],
|
|
1295
1455
|
},
|
|
@@ -1310,7 +1470,7 @@ export function prepareEventActionPayload({
|
|
|
1310
1470
|
components: [
|
|
1311
1471
|
{ type: 'uint8', name: 'filterType' },
|
|
1312
1472
|
{ type: 'uint8', name: 'fieldType' },
|
|
1313
|
-
{ type: '
|
|
1473
|
+
{ type: 'uint32', name: 'fieldIndex' },
|
|
1314
1474
|
{ type: 'bytes', name: 'filterData' },
|
|
1315
1475
|
],
|
|
1316
1476
|
},
|
|
@@ -1331,7 +1491,7 @@ export function prepareEventActionPayload({
|
|
|
1331
1491
|
components: [
|
|
1332
1492
|
{ type: 'uint8', name: 'filterType' },
|
|
1333
1493
|
{ type: 'uint8', name: 'fieldType' },
|
|
1334
|
-
{ type: '
|
|
1494
|
+
{ type: 'uint32', name: 'fieldIndex' },
|
|
1335
1495
|
{ type: 'bytes', name: 'filterData' },
|
|
1336
1496
|
],
|
|
1337
1497
|
},
|
|
@@ -1426,3 +1586,106 @@ export function transactionSenderClaimant(chainId: number): ActionClaimant {
|
|
|
1426
1586
|
chainid: chainId,
|
|
1427
1587
|
};
|
|
1428
1588
|
}
|
|
1589
|
+
|
|
1590
|
+
// Helper functions to bit-pack and decode fieldIndex values
|
|
1591
|
+
const MAX_FIELD_INDEX = 0b111111; // Maximum value for 6 bits (63)
|
|
1592
|
+
|
|
1593
|
+
/**
|
|
1594
|
+
* Packs up to five indexes into a single uint32 value.
|
|
1595
|
+
*
|
|
1596
|
+
* @param {number[]} indexes - Array of up to five indexes to pack.
|
|
1597
|
+
* @returns {number} - Packed uint32 value.
|
|
1598
|
+
* @throws {Error} - If more than five indexes are provided or an index exceeds the maximum value.
|
|
1599
|
+
*/
|
|
1600
|
+
export function packFieldIndexes(indexes: number[]): number {
|
|
1601
|
+
if (indexes.length > 5) {
|
|
1602
|
+
throw new InvalidTupleEncodingError('Can only pack up to 5 indexes.');
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
let packed = 0;
|
|
1606
|
+
indexes.forEach((index, i) => {
|
|
1607
|
+
if (index > MAX_FIELD_INDEX) {
|
|
1608
|
+
throw new InvalidTupleEncodingError(
|
|
1609
|
+
`Index ${index} exceeds the maximum allowed value (${MAX_FIELD_INDEX}).`,
|
|
1610
|
+
);
|
|
1611
|
+
}
|
|
1612
|
+
packed |= (index & MAX_FIELD_INDEX) << (i * 6); // Each index occupies 6 bits
|
|
1613
|
+
});
|
|
1614
|
+
if (indexes.length < 5) {
|
|
1615
|
+
packed |= MAX_FIELD_INDEX << (indexes.length * 6); // Terminator
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
return packed;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
/**
|
|
1622
|
+
* Unpacks a uint32 fieldIndex value into an array of up to five indexes.
|
|
1623
|
+
*
|
|
1624
|
+
* @param {number} packed - Packed uint32 value.
|
|
1625
|
+
* @returns {number[]} - Array of unpacked indexes.
|
|
1626
|
+
*/
|
|
1627
|
+
export function unpackFieldIndexes(packed: number): number[] {
|
|
1628
|
+
const indexes: number[] = [];
|
|
1629
|
+
for (let i = 0; i < 5; i++) {
|
|
1630
|
+
const index = (packed >> (i * 6)) & MAX_FIELD_INDEX;
|
|
1631
|
+
if (index === MAX_FIELD_INDEX) break; // Terminator value
|
|
1632
|
+
indexes.push(index);
|
|
1633
|
+
}
|
|
1634
|
+
return indexes;
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
/**
|
|
1638
|
+
* Decodes an event log and reorders the arguments to match the original ABI order.
|
|
1639
|
+
* This is necessary because viem's decodeEventLog reorders indexed parameters to the front.
|
|
1640
|
+
*
|
|
1641
|
+
* @param event - The event ABI definition
|
|
1642
|
+
* @param log - The log to decode
|
|
1643
|
+
* @returns {EventLog} The decoded log with arguments in the original ABI order
|
|
1644
|
+
*/
|
|
1645
|
+
export function decodeAndReorderLogArgs(event: AbiEvent, log: Log) {
|
|
1646
|
+
const decodedLog = decodeEventLog({
|
|
1647
|
+
abi: [event],
|
|
1648
|
+
data: log.data,
|
|
1649
|
+
topics: log.topics,
|
|
1650
|
+
});
|
|
1651
|
+
|
|
1652
|
+
const argsArray = Array.isArray(decodedLog.args)
|
|
1653
|
+
? decodedLog.args
|
|
1654
|
+
: Object.values(decodedLog.args);
|
|
1655
|
+
|
|
1656
|
+
if (!event.inputs.some((input) => input.indexed)) {
|
|
1657
|
+
return {
|
|
1658
|
+
...log,
|
|
1659
|
+
...decodedLog,
|
|
1660
|
+
} as EventLog;
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
const indexedIndices: number[] = [];
|
|
1664
|
+
const nonIndexedIndices: number[] = [];
|
|
1665
|
+
for (let i = 0; i < event.inputs.length; i++) {
|
|
1666
|
+
if (event.inputs[i]!.indexed) {
|
|
1667
|
+
indexedIndices.push(i);
|
|
1668
|
+
} else {
|
|
1669
|
+
nonIndexedIndices.push(i);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
const reorderedArgs = Array.from({ length: event.inputs.length });
|
|
1674
|
+
let currentIndex = 0;
|
|
1675
|
+
|
|
1676
|
+
// Place the indexed arguments in their original positions
|
|
1677
|
+
for (let i = 0; i < indexedIndices.length; i++) {
|
|
1678
|
+
reorderedArgs[indexedIndices[i]!] = argsArray[currentIndex++];
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
// Place the non-indexed arguments in their original positions
|
|
1682
|
+
for (let i = 0; i < nonIndexedIndices.length; i++) {
|
|
1683
|
+
reorderedArgs[nonIndexedIndices[i]!] = argsArray[currentIndex++];
|
|
1684
|
+
}
|
|
1685
|
+
|
|
1686
|
+
return {
|
|
1687
|
+
...log,
|
|
1688
|
+
eventName: decodedLog.eventName,
|
|
1689
|
+
args: reorderedArgs,
|
|
1690
|
+
} as EventLog;
|
|
1691
|
+
}
|