@boostxyz/sdk 0.0.0-alpha.5 → 0.0.0-alpha.7

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.
Files changed (165) hide show
  1. package/dist/Actions/Action.cjs +1 -0
  2. package/dist/Actions/Action.cjs.map +1 -0
  3. package/dist/Actions/Action.js +1 -0
  4. package/dist/Actions/Action.js.map +1 -0
  5. package/dist/Actions/EventAction.cjs +2 -1
  6. package/dist/Actions/EventAction.cjs.map +1 -0
  7. package/dist/Actions/EventAction.d.ts.map +1 -1
  8. package/dist/Actions/EventAction.js +39 -40
  9. package/dist/Actions/EventAction.js.map +1 -0
  10. package/dist/AllowLists/AllowList.cjs +1 -0
  11. package/dist/AllowLists/AllowList.cjs.map +1 -0
  12. package/dist/AllowLists/AllowList.js +1 -0
  13. package/dist/AllowLists/AllowList.js.map +1 -0
  14. package/dist/AllowLists/SimpleAllowList.cjs +1 -0
  15. package/dist/AllowLists/SimpleAllowList.cjs.map +1 -0
  16. package/dist/AllowLists/SimpleAllowList.js +1 -0
  17. package/dist/AllowLists/SimpleAllowList.js.map +1 -0
  18. package/dist/AllowLists/SimpleDenyList.cjs +1 -0
  19. package/dist/AllowLists/SimpleDenyList.cjs.map +1 -0
  20. package/dist/AllowLists/SimpleDenyList.js +1 -0
  21. package/dist/AllowLists/SimpleDenyList.js.map +1 -0
  22. package/dist/Auth/Auth.cjs +1 -0
  23. package/dist/Auth/Auth.cjs.map +1 -0
  24. package/dist/Auth/Auth.js +1 -0
  25. package/dist/Auth/Auth.js.map +1 -0
  26. package/dist/Auth/PassthroughAuth.cjs +1 -0
  27. package/dist/Auth/PassthroughAuth.cjs.map +1 -0
  28. package/dist/Auth/PassthroughAuth.js +1 -0
  29. package/dist/Auth/PassthroughAuth.js.map +1 -0
  30. package/dist/Boost.cjs +1 -0
  31. package/dist/Boost.cjs.map +1 -0
  32. package/dist/Boost.js +1 -0
  33. package/dist/Boost.js.map +1 -0
  34. package/dist/BoostCore.cjs +1 -0
  35. package/dist/BoostCore.cjs.map +1 -0
  36. package/dist/BoostCore.js +1 -0
  37. package/dist/BoostCore.js.map +1 -0
  38. package/dist/BoostRegistry.cjs +1 -0
  39. package/dist/BoostRegistry.cjs.map +1 -0
  40. package/dist/BoostRegistry.js +1 -0
  41. package/dist/BoostRegistry.js.map +1 -0
  42. package/dist/Budgets/Budget.cjs +1 -0
  43. package/dist/Budgets/Budget.cjs.map +1 -0
  44. package/dist/Budgets/Budget.js +1 -0
  45. package/dist/Budgets/Budget.js.map +1 -0
  46. package/dist/Budgets/ManagedBudget.cjs +1 -0
  47. package/dist/Budgets/ManagedBudget.cjs.map +1 -0
  48. package/dist/Budgets/ManagedBudget.js +1 -0
  49. package/dist/Budgets/ManagedBudget.js.map +1 -0
  50. package/dist/Deployable/Contract.cjs +1 -0
  51. package/dist/Deployable/Contract.cjs.map +1 -0
  52. package/dist/Deployable/Contract.js +1 -0
  53. package/dist/Deployable/Contract.js.map +1 -0
  54. package/dist/Deployable/Deployable.cjs +1 -0
  55. package/dist/Deployable/Deployable.cjs.map +1 -0
  56. package/dist/Deployable/Deployable.js +1 -0
  57. package/dist/Deployable/Deployable.js.map +1 -0
  58. package/dist/Deployable/DeployableTarget.cjs +1 -0
  59. package/dist/Deployable/DeployableTarget.cjs.map +1 -0
  60. package/dist/Deployable/DeployableTarget.js +1 -0
  61. package/dist/Deployable/DeployableTarget.js.map +1 -0
  62. package/dist/Incentives/AllowListIncentive.cjs +1 -0
  63. package/dist/Incentives/AllowListIncentive.cjs.map +1 -0
  64. package/dist/Incentives/AllowListIncentive.js +1 -0
  65. package/dist/Incentives/AllowListIncentive.js.map +1 -0
  66. package/dist/Incentives/CGDAIncentive.cjs +1 -0
  67. package/dist/Incentives/CGDAIncentive.cjs.map +1 -0
  68. package/dist/Incentives/CGDAIncentive.js +1 -0
  69. package/dist/Incentives/CGDAIncentive.js.map +1 -0
  70. package/dist/Incentives/ERC20Incentive.cjs +1 -0
  71. package/dist/Incentives/ERC20Incentive.cjs.map +1 -0
  72. package/dist/Incentives/ERC20Incentive.js +1 -0
  73. package/dist/Incentives/ERC20Incentive.js.map +1 -0
  74. package/dist/Incentives/Incentive.cjs +1 -0
  75. package/dist/Incentives/Incentive.cjs.map +1 -0
  76. package/dist/Incentives/Incentive.js +1 -0
  77. package/dist/Incentives/Incentive.js.map +1 -0
  78. package/dist/Incentives/PointsIncentive.cjs +1 -0
  79. package/dist/Incentives/PointsIncentive.cjs.map +1 -0
  80. package/dist/Incentives/PointsIncentive.js +1 -0
  81. package/dist/Incentives/PointsIncentive.js.map +1 -0
  82. package/dist/Validators/SignerValidator.cjs +1 -0
  83. package/dist/Validators/SignerValidator.cjs.map +1 -0
  84. package/dist/Validators/SignerValidator.js +1 -0
  85. package/dist/Validators/SignerValidator.js.map +1 -0
  86. package/dist/Validators/Validator.cjs +1 -0
  87. package/dist/Validators/Validator.cjs.map +1 -0
  88. package/dist/Validators/Validator.js +1 -0
  89. package/dist/Validators/Validator.js.map +1 -0
  90. package/dist/componentInterfaces-CKCBwG16.cjs +1 -0
  91. package/dist/componentInterfaces-CKCBwG16.cjs.map +1 -0
  92. package/dist/componentInterfaces-DYkaxBda.js +1 -0
  93. package/dist/componentInterfaces-DYkaxBda.js.map +1 -0
  94. package/dist/errors.cjs +1 -0
  95. package/dist/errors.cjs.map +1 -0
  96. package/dist/errors.js +1 -0
  97. package/dist/errors.js.map +1 -0
  98. package/dist/generated-Cd-Fe7W7.cjs +1 -0
  99. package/dist/generated-Cd-Fe7W7.cjs.map +1 -0
  100. package/dist/generated-DGpIVcv5.js +1 -0
  101. package/dist/generated-DGpIVcv5.js.map +1 -0
  102. package/dist/index.cjs +1 -0
  103. package/dist/index.cjs.map +1 -0
  104. package/dist/index.js +1 -0
  105. package/dist/index.js.map +1 -0
  106. package/dist/utils.cjs +1 -0
  107. package/dist/utils.cjs.map +1 -0
  108. package/dist/utils.js +1 -0
  109. package/dist/utils.js.map +1 -0
  110. package/package.json +3 -2
  111. package/src/Actions/Action.test.ts +77 -0
  112. package/src/Actions/Action.ts +61 -0
  113. package/src/Actions/ContractAction.test.ts +199 -0
  114. package/src/Actions/ContractAction.ts +238 -0
  115. package/src/Actions/ERC721MintAction.test.ts +112 -0
  116. package/src/Actions/ERC721MintAction.ts +238 -0
  117. package/src/Actions/EventAction.test.ts +182 -0
  118. package/src/Actions/EventAction.ts +380 -0
  119. package/src/AllowLists/AllowList.test.ts +64 -0
  120. package/src/AllowLists/AllowList.ts +60 -0
  121. package/src/AllowLists/SimpleAllowList.test.ts +52 -0
  122. package/src/AllowLists/SimpleAllowList.ts +240 -0
  123. package/src/AllowLists/SimpleDenyList.test.ts +52 -0
  124. package/src/AllowLists/SimpleDenyList.ts +289 -0
  125. package/src/Auth/Auth.ts +11 -0
  126. package/src/Auth/PassthroughAuth.test.ts +12 -0
  127. package/src/Auth/PassthroughAuth.ts +80 -0
  128. package/src/Boost.ts +155 -0
  129. package/src/BoostCore.test.ts +846 -0
  130. package/src/BoostCore.ts +1192 -0
  131. package/src/BoostRegistry.ts +449 -0
  132. package/src/Budgets/Budget.test.ts +27 -0
  133. package/src/Budgets/Budget.ts +61 -0
  134. package/src/Budgets/ManagedBudget.test.ts +154 -0
  135. package/src/Budgets/ManagedBudget.ts +743 -0
  136. package/src/Budgets/SimpleBudget.test.ts +152 -0
  137. package/src/Budgets/SimpleBudget.ts +521 -0
  138. package/src/Budgets/VestingBudget.test.ts +123 -0
  139. package/src/Budgets/VestingBudget.ts +532 -0
  140. package/src/Deployable/Contract.ts +229 -0
  141. package/src/Deployable/Deployable.ts +244 -0
  142. package/src/Deployable/DeployableTarget.ts +210 -0
  143. package/src/Incentives/AllowListIncentive.test.ts +146 -0
  144. package/src/Incentives/AllowListIncentive.ts +290 -0
  145. package/src/Incentives/CGDAIncentive.test.ts +136 -0
  146. package/src/Incentives/CGDAIncentive.ts +364 -0
  147. package/src/Incentives/ERC1155Incentive.test.ts +98 -0
  148. package/src/Incentives/ERC1155Incentive.ts +384 -0
  149. package/src/Incentives/ERC20Incentive.test.ts +141 -0
  150. package/src/Incentives/ERC20Incentive.ts +417 -0
  151. package/src/Incentives/ERC20VariableIncentive.test.ts +156 -0
  152. package/src/Incentives/ERC20VariableIncentive.ts +368 -0
  153. package/src/Incentives/Incentive.test.ts +92 -0
  154. package/src/Incentives/Incentive.ts +85 -0
  155. package/src/Incentives/PointsIncentive.test.ts +142 -0
  156. package/src/Incentives/PointsIncentive.ts +303 -0
  157. package/src/Validators/SignerValidator.test.ts +163 -0
  158. package/src/Validators/SignerValidator.ts +272 -0
  159. package/src/Validators/Validator.test.ts +21 -0
  160. package/src/Validators/Validator.ts +55 -0
  161. package/src/errors.ts +524 -0
  162. package/src/index.test.ts +40 -0
  163. package/src/index.ts +50 -0
  164. package/src/utils.test.ts +44 -0
  165. package/src/utils.ts +2247 -0
@@ -0,0 +1,380 @@
1
+ import {
2
+ eventActionAbi,
3
+ readEventActionGetActionClaimant,
4
+ readEventActionGetActionSteps,
5
+ simulateEventActionExecute,
6
+ writeEventActionExecute,
7
+ } from '@boostxyz/evm';
8
+ import { bytecode } from '@boostxyz/evm/artifacts/contracts/actions/EventAction.sol/EventAction.json';
9
+ import events from '@boostxyz/signatures/events';
10
+ import {
11
+ type Abi,
12
+ type AbiEvent,
13
+ type Address,
14
+ type ContractEventName,
15
+ type Hex,
16
+ type Log,
17
+ isAddressEqual,
18
+ } from 'viem';
19
+ import { getLogs } from 'viem/actions';
20
+ import type {
21
+ DeployableOptions,
22
+ GenericDeployableParams,
23
+ } from '../Deployable/Deployable';
24
+ import { DeployableTarget } from '../Deployable/DeployableTarget';
25
+ import {
26
+ FieldValueNotComparableError,
27
+ FieldValueUndefinedError,
28
+ InvalidNumericalCriteriaError,
29
+ NoEventActionStepsProvidedError,
30
+ TooManyEventActionStepsProvidedError,
31
+ UnrecognizedFilterTypeError,
32
+ } from '../errors';
33
+ import {
34
+ type ActionClaimant,
35
+ type ActionStep,
36
+ type Criteria,
37
+ type EventActionPayload,
38
+ type EventActionPayloadRaw,
39
+ FilterType,
40
+ type GetLogsParams,
41
+ PrimitiveType,
42
+ type ReadParams,
43
+ RegistryType,
44
+ type WriteParams,
45
+ dedupeActionSteps,
46
+ isEventActionPayloadSimple,
47
+ prepareEventActionPayload,
48
+ } from '../utils';
49
+
50
+ export type { EventActionPayload };
51
+
52
+ /**
53
+ * A generic event action
54
+ *
55
+ * @export
56
+ * @class EventAction
57
+ * @typedef {EventAction}
58
+ * @extends {DeployableTarget<EventActionPayload>}
59
+ */
60
+ export class EventAction extends DeployableTarget<
61
+ EventActionPayload,
62
+ typeof eventActionAbi
63
+ > {
64
+ /**
65
+ * @inheritdoc
66
+ *
67
+ * @public
68
+ * @readonly
69
+ * @type {*}
70
+ */
71
+ public override readonly abi = eventActionAbi;
72
+ /**
73
+ * @inheritdoc
74
+ *
75
+ * @public
76
+ * @static
77
+ * @type {Address}
78
+ */
79
+ public static override base: Address = import.meta.env.VITE_EVENT_ACTION_BASE;
80
+ /**
81
+ * @inheritdoc
82
+ *
83
+ * @public
84
+ * @static
85
+ * @type {RegistryType}
86
+ */
87
+ public static override registryType: RegistryType = RegistryType.ACTION;
88
+
89
+ /**
90
+ * Gets a specific action event by index
91
+ *
92
+ * @public
93
+ * @async
94
+ * @param {number} index The index of the action event to retrieve
95
+ * @param {?ReadParams<typeof eventActionAbi, 'getActionStep'>} [params]
96
+ * @returns {Promise<ActionStep>}
97
+ */
98
+ public async getActionStep(
99
+ index: number,
100
+ params?: ReadParams<typeof eventActionAbi, 'getActionStep'>,
101
+ ) {
102
+ const steps = await this.getActionSteps(params);
103
+ return steps.at(index);
104
+ }
105
+
106
+ /**
107
+ * Gets all action events
108
+ *
109
+ * @public
110
+ * @async
111
+ * @param {?ReadParams<typeof eventActionAbi, 'getActionSteps'>} [params]
112
+ * @returns {Promise<ActionStep[]>}
113
+ */
114
+ public async getActionSteps(
115
+ params?: ReadParams<typeof eventActionAbi, 'getActionSteps'>,
116
+ ) {
117
+ const steps = (await readEventActionGetActionSteps(this._config, {
118
+ address: this.assertValidAddress(),
119
+ ...this.optionallyAttachAccount(),
120
+ // biome-ignore lint/suspicious/noExplicitAny: Accept any shape of valid wagmi/viem parameters, wagmi does the same thing internally
121
+ ...(params as any),
122
+ })) as ActionStep[];
123
+ return dedupeActionSteps(steps);
124
+ }
125
+
126
+ /**
127
+ * Gets the count of action events
128
+ *
129
+ * @public
130
+ * @async
131
+ * @param {?ReadParams<typeof eventActionAbi, 'getActionStepsCount'>} [params]
132
+ * @returns {Promise<bigint>}
133
+ */
134
+ public async getActionStepsCount(
135
+ params?: ReadParams<typeof eventActionAbi, 'getActionStepsCount'>,
136
+ ) {
137
+ const steps = await this.getActionSteps(params);
138
+ return steps.length;
139
+ }
140
+
141
+ /**
142
+ * Retrieves the payload describing how claimants can be identified from logs or function calls.
143
+ *
144
+ * @public
145
+ * @async
146
+ * @param {?ReadParams<typeof eventActionAbi, 'getActionClaimant'>} [params]
147
+ * @returns {Promise<ActionClaimant>}
148
+ */
149
+ public async getActionClaimant(
150
+ params?: ReadParams<typeof eventActionAbi, 'getActionClaimant'>,
151
+ ) {
152
+ return readEventActionGetActionClaimant(this._config, {
153
+ address: this.assertValidAddress(),
154
+ ...this.optionallyAttachAccount(),
155
+ // biome-ignore lint/suspicious/noExplicitAny: Accept any shape of valid wagmi/viem parameters, wagmi does the same thing internally
156
+ ...(params as any),
157
+ }) as Promise<ActionClaimant>;
158
+ }
159
+
160
+ /**
161
+ * Executes a prepared event action
162
+ *
163
+ * @public
164
+ * @async
165
+ * @param {Hex} data
166
+ * @param {?WriteParams<typeof eventActionAbi, 'execute'>} [params]
167
+ * @returns {Promise<readonly [boolean, `0x${string}`]>}
168
+ */
169
+ public async execute(
170
+ data: Hex,
171
+ params?: WriteParams<typeof eventActionAbi, 'execute'>,
172
+ ) {
173
+ return this.awaitResult(this.executeRaw(data, params));
174
+ }
175
+
176
+ /**
177
+ * Executes a prepared event action
178
+ *
179
+ * @public
180
+ * @async
181
+ * @param {Hex} data
182
+ * @param {?WriteParams<typeof eventActionAbi, 'execute'>} [params]
183
+ * @returns {unknown}
184
+ */
185
+ public async executeRaw(
186
+ data: Hex,
187
+ params?: WriteParams<typeof eventActionAbi, 'execute'>,
188
+ ) {
189
+ const { request, result } = await simulateEventActionExecute(this._config, {
190
+ address: this.assertValidAddress(),
191
+ ...this.optionallyAttachAccount(),
192
+ // biome-ignore lint/suspicious/noExplicitAny: Accept any shape of valid wagmi/viem parameters, wagmi does the same thing internally
193
+ ...(params as any),
194
+ args: [data],
195
+ });
196
+ const hash = await writeEventActionExecute(this._config, request);
197
+ return { hash, result };
198
+ }
199
+
200
+ /**
201
+ * Retrieves action steps, and uses them to validate against, and optionally fetch logs that match the step's signature.
202
+ * If logs are provided in the optional `params` argument, then those logs will be used instead of fetched with the configured client.
203
+ *
204
+ * @public
205
+ * @async
206
+ * @param {?ReadParams<typeof eventActionAbi, 'getActionSteps'> &
207
+ * GetLogsParams<Abi, ContractEventName<Abi>> & {
208
+ * knownEvents?: Record<Hex, AbiEvent>;
209
+ * logs?: Log[];
210
+ * }} [params]
211
+ * @returns {Promise<boolean>}
212
+ */
213
+ public async validateActionSteps(
214
+ params?: ReadParams<typeof eventActionAbi, 'getActionSteps'> &
215
+ GetLogsParams<Abi, ContractEventName<Abi>> & {
216
+ knownEvents?: Record<Hex, AbiEvent>;
217
+ logs?: Log[];
218
+ },
219
+ ) {
220
+ const actionSteps = await this.getActionSteps(params);
221
+ for (const actionStep of actionSteps) {
222
+ if (!(await this.isActionStepValid(actionStep, params))) {
223
+ return false;
224
+ }
225
+ }
226
+ return true;
227
+ }
228
+
229
+ /**
230
+ * Validates a single action step with a given criteria against logs.
231
+ * If logs are provided in the optional `params` argument, then those logs will be used instead of fetched with the configured client.
232
+ *
233
+ * @public
234
+ * @async
235
+ * @param {ActionStep} actionStep
236
+ * @param {?GetLogsParams<Abi, ContractEventName<Abi>> & {
237
+ * knownEvents?: Record<Hex, AbiEvent>;
238
+ * logs?: Log[];
239
+ * }} [params]
240
+ * @returns {Promise<boolean>}
241
+ */
242
+ public async isActionStepValid(
243
+ actionStep: ActionStep,
244
+ params?: GetLogsParams<Abi, ContractEventName<Abi>> & {
245
+ knownEvents?: Record<Hex, AbiEvent>;
246
+ logs?: Log[];
247
+ },
248
+ ) {
249
+ const criteria = actionStep.actionParameter;
250
+ const signature = actionStep.signature;
251
+ let event: AbiEvent;
252
+ // Lookup ABI based on event signature
253
+ if (params?.knownEvents) {
254
+ event = params.knownEvents[signature] as AbiEvent;
255
+ } else {
256
+ event = (events.abi as Record<Hex, AbiEvent>)[signature] as AbiEvent;
257
+ }
258
+ if (!event) {
259
+ throw new Error(`No known ABI for given event signature: ${signature}`);
260
+ }
261
+ const targetContract = actionStep.targetContract;
262
+ // Get all logs matching the event signature from the target contract
263
+ const logs =
264
+ params?.logs ||
265
+ (await getLogs(this._config.getClient({ chainId: params?.chainId }), {
266
+ // biome-ignore lint/suspicious/noExplicitAny: <explanation>
267
+ ...(params as any),
268
+ address: targetContract,
269
+ event,
270
+ }));
271
+ if (!logs.length) return false;
272
+ for (let log of logs) {
273
+ if (!this.validateLogAgainstCriteria(criteria, log)) {
274
+ return false;
275
+ }
276
+ }
277
+ return true;
278
+ }
279
+
280
+ /**
281
+ * Validates a {@link Log} against a given criteria.
282
+ *
283
+ * @param {Criteria} criteria - The criteria to validate against.
284
+ * @param {Log} log - The Viem event log.
285
+ * @returns {Promise<boolean>} - Returns true if the log passes the criteria, false otherwise.
286
+ */
287
+ public async validateLogAgainstCriteria(criteria: Criteria, log: Log) {
288
+ const fieldValue = log.topics.at(criteria.fieldIndex);
289
+ if (fieldValue === undefined) {
290
+ throw new FieldValueUndefinedError({ log, criteria, fieldValue });
291
+ }
292
+ // Type narrow based on criteria.filterType
293
+ switch (criteria.filterType) {
294
+ case FilterType.EQUAL:
295
+ if (criteria.fieldType === PrimitiveType.ADDRESS) {
296
+ return isAddressEqual(
297
+ criteria.filterData,
298
+ `0x${fieldValue.slice(-40)}`,
299
+ );
300
+ }
301
+ return fieldValue === criteria.filterData;
302
+
303
+ case FilterType.NOT_EQUAL:
304
+ return fieldValue !== criteria.filterData;
305
+
306
+ case FilterType.GREATER_THAN:
307
+ if (criteria.fieldType === PrimitiveType.UINT) {
308
+ return BigInt(fieldValue) > BigInt(criteria.filterData);
309
+ }
310
+ throw new InvalidNumericalCriteriaError({ log, criteria, fieldValue });
311
+
312
+ case FilterType.LESS_THAN:
313
+ if (criteria.fieldType === PrimitiveType.UINT) {
314
+ return BigInt(fieldValue) < BigInt(criteria.filterData);
315
+ }
316
+ throw new InvalidNumericalCriteriaError({ log, criteria, fieldValue });
317
+
318
+ case FilterType.CONTAINS:
319
+ if (
320
+ criteria.fieldType === PrimitiveType.BYTES ||
321
+ criteria.fieldType === PrimitiveType.STRING
322
+ ) {
323
+ return fieldValue.includes(criteria.filterData);
324
+ }
325
+ throw new FieldValueNotComparableError({ log, criteria, fieldValue });
326
+
327
+ default:
328
+ throw new UnrecognizedFilterTypeError({ log, criteria, fieldValue });
329
+ }
330
+ }
331
+
332
+ /**
333
+ * @inheritdoc
334
+ *
335
+ * @public
336
+ * @param {?EventActionPayload} [_payload]
337
+ * @param {?DeployableOptions} [_options]
338
+ * @returns {GenericDeployableParams}
339
+ */
340
+ public override buildParameters(
341
+ _payload?: EventActionPayload,
342
+ _options?: DeployableOptions,
343
+ ): GenericDeployableParams {
344
+ const [payload, options] = this.validateDeploymentConfig(
345
+ _payload,
346
+ _options,
347
+ );
348
+ let rawPayload: EventActionPayloadRaw;
349
+ if (isEventActionPayloadSimple(payload)) {
350
+ // filter out any falsy potential values
351
+ let tmpSteps = payload.actionSteps.filter((step) => !!step);
352
+ if (tmpSteps.length === 0) {
353
+ throw new NoEventActionStepsProvidedError();
354
+ }
355
+ if (tmpSteps.length > 4) {
356
+ throw new TooManyEventActionStepsProvidedError();
357
+ }
358
+ let steps: ActionStep[] = Array.from({ length: 4 }, (_, i) => {
359
+ // use either the provided step at the given index, or reuse the previous step
360
+ // should aways exist
361
+ return tmpSteps.at(i)! || tmpSteps.slice(0, i).at(-1)!;
362
+ });
363
+ rawPayload = {
364
+ actionClaimant: payload.actionClaimant,
365
+ actionStepOne: steps.at(0)!,
366
+ actionStepTwo: steps.at(1)!,
367
+ actionStepThree: steps.at(2)!,
368
+ actionStepFour: steps.at(3)!,
369
+ };
370
+ } else {
371
+ rawPayload = payload;
372
+ }
373
+ return {
374
+ abi: eventActionAbi,
375
+ bytecode: bytecode as Hex,
376
+ args: [prepareEventActionPayload(rawPayload)],
377
+ ...this.optionallyAttachAccount(options.account),
378
+ };
379
+ }
380
+ }
@@ -0,0 +1,64 @@
1
+ import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
2
+ import { beforeAll, describe, expect, test } from 'vitest';
3
+ import {
4
+ type Fixtures,
5
+ defaultOptions,
6
+ deployFixtures,
7
+ } from '../../test/helpers';
8
+ import {
9
+ SimpleAllowList,
10
+ SimpleDenyList,
11
+ allowListFromAddress,
12
+ } from './AllowList';
13
+
14
+ let fixtures: Fixtures;
15
+
16
+ beforeAll(async () => {
17
+ fixtures = await loadFixture(deployFixtures);
18
+ });
19
+
20
+ function freshAllowList(fixtures: Fixtures) {
21
+ return function freshAllowList() {
22
+ return fixtures.registry.clone(
23
+ crypto.randomUUID(),
24
+ new fixtures.bases.SimpleAllowList(defaultOptions, {
25
+ owner: defaultOptions.account.address,
26
+ allowed: [defaultOptions.account.address],
27
+ }),
28
+ );
29
+ };
30
+ }
31
+
32
+ function freshDenyList(fixtures: Fixtures) {
33
+ return function freshDenyList() {
34
+ return fixtures.registry.clone(
35
+ crypto.randomUUID(),
36
+ new fixtures.bases.SimpleDenyList(defaultOptions, {
37
+ owner: defaultOptions.account.address,
38
+ denied: [defaultOptions.account.address],
39
+ }),
40
+ );
41
+ };
42
+ }
43
+
44
+ describe('AllowList', () => {
45
+ test('can automatically instantiate SimpleAllowList given an address', async () => {
46
+ const _allowList = await loadFixture(freshAllowList(fixtures));
47
+ expect(
48
+ await allowListFromAddress(
49
+ defaultOptions,
50
+ _allowList.assertValidAddress(),
51
+ ),
52
+ ).toBeInstanceOf(SimpleAllowList);
53
+ });
54
+
55
+ test('can automatically instantiate SimpleAllowList given an address', async () => {
56
+ const _allowList = await loadFixture(freshDenyList(fixtures));
57
+ expect(
58
+ await allowListFromAddress(
59
+ defaultOptions,
60
+ _allowList.assertValidAddress(),
61
+ ),
62
+ ).toBeInstanceOf(SimpleDenyList);
63
+ });
64
+ });
@@ -0,0 +1,60 @@
1
+ import { aAllowListAbi } from '@boostxyz/evm';
2
+ import {
3
+ ASimpleAllowList,
4
+ ASimpleDenyList,
5
+ } from '@boostxyz/evm/deploys/componentInterfaces.json';
6
+ import { readContract } from '@wagmi/core';
7
+ import type { Address, Hex } from 'viem';
8
+ import type { DeployableOptions } from '../Deployable/Deployable';
9
+ import { InvalidComponentInterfaceError } from '../errors';
10
+ import { SimpleAllowList } from './SimpleAllowList';
11
+ import { SimpleDenyList } from './SimpleDenyList';
12
+
13
+ export { SimpleAllowList, SimpleDenyList };
14
+
15
+ /**
16
+ * A union type representing all valid protocol AllowList implementations
17
+ *
18
+ * @export
19
+ * @typedef {AllowList}
20
+ */
21
+ export type AllowList = SimpleAllowList | SimpleDenyList;
22
+
23
+ /**
24
+ * A map of AllowList component interfaces to their constructors.
25
+ *
26
+ * @type {{ "0x2bc9016b": SimpleAllowList; "0x9d585f63": SimpleDenyList; }}
27
+ */
28
+ export const AllowListByComponentInterface = {
29
+ [ASimpleAllowList as Hex]: SimpleAllowList,
30
+ [ASimpleDenyList as Hex]: SimpleDenyList,
31
+ };
32
+
33
+ /**
34
+ * A function that will read a contract's component interface using `getComponentInterface` and return the correct instantiated instance.
35
+ *
36
+ * @export
37
+ * @async
38
+ * @param {DeployableOptions} options
39
+ * @param {Address} address
40
+ * @returns {Promise<SimpleAllowList | SimpleDenyList>}
41
+ * @throws {@link InvalidComponentInterfaceError}
42
+ */
43
+ export async function allowListFromAddress(
44
+ options: DeployableOptions,
45
+ address: Address,
46
+ ) {
47
+ const interfaceId = (await readContract(options.config, {
48
+ abi: aAllowListAbi,
49
+ functionName: 'getComponentInterface',
50
+ address,
51
+ })) as keyof typeof AllowListByComponentInterface;
52
+ const Ctor = AllowListByComponentInterface[interfaceId];
53
+ if (!Ctor) {
54
+ throw new InvalidComponentInterfaceError(
55
+ Object.keys(AllowListByComponentInterface) as Hex[],
56
+ interfaceId as Hex,
57
+ );
58
+ }
59
+ return new Ctor(options, address);
60
+ }
@@ -0,0 +1,52 @@
1
+ import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
2
+ import { isAddress, zeroAddress } from 'viem';
3
+ import { beforeAll, describe, expect, test } from 'vitest';
4
+ import {
5
+ type Fixtures,
6
+ defaultOptions,
7
+ deployFixtures,
8
+ } from '../../test/helpers';
9
+ import { SimpleAllowList } from './SimpleAllowList';
10
+
11
+ let fixtures: Fixtures;
12
+
13
+ beforeAll(async () => {
14
+ fixtures = await loadFixture(deployFixtures);
15
+ });
16
+
17
+ function freshAllowList(fixtures: Fixtures) {
18
+ return function freshAllowList() {
19
+ return fixtures.registry.clone(
20
+ crypto.randomUUID(),
21
+ new fixtures.bases.SimpleAllowList(defaultOptions, {
22
+ owner: defaultOptions.account.address,
23
+ allowed: [defaultOptions.account.address],
24
+ }),
25
+ );
26
+ };
27
+ }
28
+
29
+ describe('SimpleAllowList', () => {
30
+ test('can successfully be deployed', async () => {
31
+ const allowList = new SimpleAllowList(defaultOptions, {
32
+ owner: defaultOptions.account.address,
33
+ allowed: [],
34
+ });
35
+ await allowList.deploy();
36
+ expect(isAddress(allowList.assertValidAddress())).toBe(true);
37
+ });
38
+
39
+ test('can check is allowed', async () => {
40
+ const allowList = await loadFixture(freshAllowList(fixtures));
41
+ expect(await allowList.isAllowed(defaultOptions.account.address)).toBe(
42
+ true,
43
+ );
44
+ expect(await allowList.isAllowed(zeroAddress)).toBe(false);
45
+ });
46
+
47
+ test('can set allowed', async () => {
48
+ const allowList = await loadFixture(freshAllowList(fixtures));
49
+ await allowList.setAllowed([zeroAddress], [true]);
50
+ expect(await allowList.isAllowed(zeroAddress)).toBe(true);
51
+ });
52
+ });