@boostxyz/sdk 0.0.0-alpha.3 → 0.0.0-alpha.6

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