@boostxyz/sdk 0.0.0-alpha.5 → 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 (164) 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 +1 -0
  6. package/dist/Actions/EventAction.cjs.map +1 -0
  7. package/dist/Actions/EventAction.js +1 -0
  8. package/dist/Actions/EventAction.js.map +1 -0
  9. package/dist/AllowLists/AllowList.cjs +1 -0
  10. package/dist/AllowLists/AllowList.cjs.map +1 -0
  11. package/dist/AllowLists/AllowList.js +1 -0
  12. package/dist/AllowLists/AllowList.js.map +1 -0
  13. package/dist/AllowLists/SimpleAllowList.cjs +1 -0
  14. package/dist/AllowLists/SimpleAllowList.cjs.map +1 -0
  15. package/dist/AllowLists/SimpleAllowList.js +1 -0
  16. package/dist/AllowLists/SimpleAllowList.js.map +1 -0
  17. package/dist/AllowLists/SimpleDenyList.cjs +1 -0
  18. package/dist/AllowLists/SimpleDenyList.cjs.map +1 -0
  19. package/dist/AllowLists/SimpleDenyList.js +1 -0
  20. package/dist/AllowLists/SimpleDenyList.js.map +1 -0
  21. package/dist/Auth/Auth.cjs +1 -0
  22. package/dist/Auth/Auth.cjs.map +1 -0
  23. package/dist/Auth/Auth.js +1 -0
  24. package/dist/Auth/Auth.js.map +1 -0
  25. package/dist/Auth/PassthroughAuth.cjs +1 -0
  26. package/dist/Auth/PassthroughAuth.cjs.map +1 -0
  27. package/dist/Auth/PassthroughAuth.js +1 -0
  28. package/dist/Auth/PassthroughAuth.js.map +1 -0
  29. package/dist/Boost.cjs +1 -0
  30. package/dist/Boost.cjs.map +1 -0
  31. package/dist/Boost.js +1 -0
  32. package/dist/Boost.js.map +1 -0
  33. package/dist/BoostCore.cjs +1 -0
  34. package/dist/BoostCore.cjs.map +1 -0
  35. package/dist/BoostCore.js +1 -0
  36. package/dist/BoostCore.js.map +1 -0
  37. package/dist/BoostRegistry.cjs +1 -0
  38. package/dist/BoostRegistry.cjs.map +1 -0
  39. package/dist/BoostRegistry.js +1 -0
  40. package/dist/BoostRegistry.js.map +1 -0
  41. package/dist/Budgets/Budget.cjs +1 -0
  42. package/dist/Budgets/Budget.cjs.map +1 -0
  43. package/dist/Budgets/Budget.js +1 -0
  44. package/dist/Budgets/Budget.js.map +1 -0
  45. package/dist/Budgets/ManagedBudget.cjs +1 -0
  46. package/dist/Budgets/ManagedBudget.cjs.map +1 -0
  47. package/dist/Budgets/ManagedBudget.js +1 -0
  48. package/dist/Budgets/ManagedBudget.js.map +1 -0
  49. package/dist/Deployable/Contract.cjs +1 -0
  50. package/dist/Deployable/Contract.cjs.map +1 -0
  51. package/dist/Deployable/Contract.js +1 -0
  52. package/dist/Deployable/Contract.js.map +1 -0
  53. package/dist/Deployable/Deployable.cjs +1 -0
  54. package/dist/Deployable/Deployable.cjs.map +1 -0
  55. package/dist/Deployable/Deployable.js +1 -0
  56. package/dist/Deployable/Deployable.js.map +1 -0
  57. package/dist/Deployable/DeployableTarget.cjs +1 -0
  58. package/dist/Deployable/DeployableTarget.cjs.map +1 -0
  59. package/dist/Deployable/DeployableTarget.js +1 -0
  60. package/dist/Deployable/DeployableTarget.js.map +1 -0
  61. package/dist/Incentives/AllowListIncentive.cjs +1 -0
  62. package/dist/Incentives/AllowListIncentive.cjs.map +1 -0
  63. package/dist/Incentives/AllowListIncentive.js +1 -0
  64. package/dist/Incentives/AllowListIncentive.js.map +1 -0
  65. package/dist/Incentives/CGDAIncentive.cjs +1 -0
  66. package/dist/Incentives/CGDAIncentive.cjs.map +1 -0
  67. package/dist/Incentives/CGDAIncentive.js +1 -0
  68. package/dist/Incentives/CGDAIncentive.js.map +1 -0
  69. package/dist/Incentives/ERC20Incentive.cjs +1 -0
  70. package/dist/Incentives/ERC20Incentive.cjs.map +1 -0
  71. package/dist/Incentives/ERC20Incentive.js +1 -0
  72. package/dist/Incentives/ERC20Incentive.js.map +1 -0
  73. package/dist/Incentives/Incentive.cjs +1 -0
  74. package/dist/Incentives/Incentive.cjs.map +1 -0
  75. package/dist/Incentives/Incentive.js +1 -0
  76. package/dist/Incentives/Incentive.js.map +1 -0
  77. package/dist/Incentives/PointsIncentive.cjs +1 -0
  78. package/dist/Incentives/PointsIncentive.cjs.map +1 -0
  79. package/dist/Incentives/PointsIncentive.js +1 -0
  80. package/dist/Incentives/PointsIncentive.js.map +1 -0
  81. package/dist/Validators/SignerValidator.cjs +1 -0
  82. package/dist/Validators/SignerValidator.cjs.map +1 -0
  83. package/dist/Validators/SignerValidator.js +1 -0
  84. package/dist/Validators/SignerValidator.js.map +1 -0
  85. package/dist/Validators/Validator.cjs +1 -0
  86. package/dist/Validators/Validator.cjs.map +1 -0
  87. package/dist/Validators/Validator.js +1 -0
  88. package/dist/Validators/Validator.js.map +1 -0
  89. package/dist/componentInterfaces-CKCBwG16.cjs +1 -0
  90. package/dist/componentInterfaces-CKCBwG16.cjs.map +1 -0
  91. package/dist/componentInterfaces-DYkaxBda.js +1 -0
  92. package/dist/componentInterfaces-DYkaxBda.js.map +1 -0
  93. package/dist/errors.cjs +1 -0
  94. package/dist/errors.cjs.map +1 -0
  95. package/dist/errors.js +1 -0
  96. package/dist/errors.js.map +1 -0
  97. package/dist/generated-Cd-Fe7W7.cjs +1 -0
  98. package/dist/generated-Cd-Fe7W7.cjs.map +1 -0
  99. package/dist/generated-DGpIVcv5.js +1 -0
  100. package/dist/generated-DGpIVcv5.js.map +1 -0
  101. package/dist/index.cjs +1 -0
  102. package/dist/index.cjs.map +1 -0
  103. package/dist/index.js +1 -0
  104. package/dist/index.js.map +1 -0
  105. package/dist/utils.cjs +1 -0
  106. package/dist/utils.cjs.map +1 -0
  107. package/dist/utils.js +1 -0
  108. package/dist/utils.js.map +1 -0
  109. package/package.json +3 -2
  110. package/src/Actions/Action.test.ts +77 -0
  111. package/src/Actions/Action.ts +61 -0
  112. package/src/Actions/ContractAction.test.ts +199 -0
  113. package/src/Actions/ContractAction.ts +238 -0
  114. package/src/Actions/ERC721MintAction.test.ts +112 -0
  115. package/src/Actions/ERC721MintAction.ts +238 -0
  116. package/src/Actions/EventAction.test.ts +182 -0
  117. package/src/Actions/EventAction.ts +382 -0
  118. package/src/AllowLists/AllowList.test.ts +64 -0
  119. package/src/AllowLists/AllowList.ts +60 -0
  120. package/src/AllowLists/SimpleAllowList.test.ts +52 -0
  121. package/src/AllowLists/SimpleAllowList.ts +240 -0
  122. package/src/AllowLists/SimpleDenyList.test.ts +52 -0
  123. package/src/AllowLists/SimpleDenyList.ts +289 -0
  124. package/src/Auth/Auth.ts +11 -0
  125. package/src/Auth/PassthroughAuth.test.ts +12 -0
  126. package/src/Auth/PassthroughAuth.ts +80 -0
  127. package/src/Boost.ts +155 -0
  128. package/src/BoostCore.test.ts +846 -0
  129. package/src/BoostCore.ts +1192 -0
  130. package/src/BoostRegistry.ts +449 -0
  131. package/src/Budgets/Budget.test.ts +27 -0
  132. package/src/Budgets/Budget.ts +61 -0
  133. package/src/Budgets/ManagedBudget.test.ts +154 -0
  134. package/src/Budgets/ManagedBudget.ts +743 -0
  135. package/src/Budgets/SimpleBudget.test.ts +152 -0
  136. package/src/Budgets/SimpleBudget.ts +521 -0
  137. package/src/Budgets/VestingBudget.test.ts +123 -0
  138. package/src/Budgets/VestingBudget.ts +532 -0
  139. package/src/Deployable/Contract.ts +229 -0
  140. package/src/Deployable/Deployable.ts +244 -0
  141. package/src/Deployable/DeployableTarget.ts +210 -0
  142. package/src/Incentives/AllowListIncentive.test.ts +146 -0
  143. package/src/Incentives/AllowListIncentive.ts +290 -0
  144. package/src/Incentives/CGDAIncentive.test.ts +136 -0
  145. package/src/Incentives/CGDAIncentive.ts +364 -0
  146. package/src/Incentives/ERC1155Incentive.test.ts +98 -0
  147. package/src/Incentives/ERC1155Incentive.ts +384 -0
  148. package/src/Incentives/ERC20Incentive.test.ts +141 -0
  149. package/src/Incentives/ERC20Incentive.ts +417 -0
  150. package/src/Incentives/ERC20VariableIncentive.test.ts +156 -0
  151. package/src/Incentives/ERC20VariableIncentive.ts +368 -0
  152. package/src/Incentives/Incentive.test.ts +92 -0
  153. package/src/Incentives/Incentive.ts +85 -0
  154. package/src/Incentives/PointsIncentive.test.ts +142 -0
  155. package/src/Incentives/PointsIncentive.ts +303 -0
  156. package/src/Validators/SignerValidator.test.ts +163 -0
  157. package/src/Validators/SignerValidator.ts +272 -0
  158. package/src/Validators/Validator.test.ts +21 -0
  159. package/src/Validators/Validator.ts +55 -0
  160. package/src/errors.ts +524 -0
  161. package/src/index.test.ts +40 -0
  162. package/src/index.ts +50 -0
  163. package/src/utils.test.ts +44 -0
  164. package/src/utils.ts +2247 -0
@@ -0,0 +1,846 @@
1
+ import { loadFixture } from '@nomicfoundation/hardhat-toolbox-viem/network-helpers';
2
+ import { parseEther, zeroAddress } from 'viem';
3
+ import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest';
4
+ import {
5
+ type BudgetFixtures,
6
+ type Fixtures,
7
+ defaultOptions,
8
+ deployFixtures,
9
+ fundBudget,
10
+ makeMockEventActionPayload,
11
+ } from '../test/helpers';
12
+ import { ContractAction } from './Actions/ContractAction';
13
+ import { PassthroughAuth } from './Auth/PassthroughAuth';
14
+ import { BoostCore } from './BoostCore';
15
+ import type { ERC20Incentive } from './Incentives/ERC20Incentive';
16
+ import { IncentiveNotCloneableError } from './errors';
17
+ import {
18
+ ActionStep,
19
+ ERC1155StrategyType,
20
+ FilterType,
21
+ PrimitiveType,
22
+ SignatureType,
23
+ StrategyType,
24
+ bytes4,
25
+ } from './utils';
26
+
27
+ let fixtures: Fixtures, budgets: BudgetFixtures;
28
+
29
+ describe('BoostCore', () => {
30
+ beforeAll(async () => {
31
+ fixtures = await loadFixture(deployFixtures);
32
+ });
33
+ beforeEach(async () => {
34
+ budgets = await loadFixture(fundBudget(defaultOptions, fixtures));
35
+ });
36
+
37
+ test('can get the total number of boosts', async () => {
38
+ const { core, bases } = fixtures;
39
+ const client = new BoostCore({
40
+ ...defaultOptions,
41
+ address: core.assertValidAddress(),
42
+ });
43
+
44
+ // to whom it may concern, this syntax is only used because we need to use test classes
45
+ // that are preconfigured with the dynamic base addresses generated at test time.
46
+ // normally you would use the follow api for brevity
47
+ // budget: client.SimpleBudget({} | '0xaddress')
48
+ const { budget, erc20 } = budgets;
49
+ await client.createBoost({
50
+ protocolFee: 1n,
51
+ referralFee: 2n,
52
+ maxParticipants: 100n,
53
+ budget: budget,
54
+ action: new bases.EventAction(
55
+ defaultOptions,
56
+ makeMockEventActionPayload(
57
+ core.assertValidAddress(),
58
+ erc20.assertValidAddress(),
59
+ ),
60
+ ),
61
+ validator: new bases.SignerValidator(defaultOptions, {
62
+ signers: [defaultOptions.account.address],
63
+ validatorCaller: defaultOptions.account.address,
64
+ }),
65
+ allowList: new bases.SimpleAllowList(defaultOptions, {
66
+ owner: defaultOptions.account.address,
67
+ allowed: [defaultOptions.account.address],
68
+ }),
69
+ incentives: [
70
+ new bases.ERC20Incentive(defaultOptions, {
71
+ asset: erc20.assertValidAddress(),
72
+ reward: parseEther('1'),
73
+ limit: 100n,
74
+ strategy: StrategyType.POOL,
75
+ }),
76
+ ],
77
+ });
78
+ expect(await client.getBoostCount()).toBe(1n);
79
+ });
80
+
81
+ test('can successfully create a boost using all base contract implementations', async () => {
82
+ const { core, bases } = fixtures;
83
+ const client = new BoostCore({
84
+ ...defaultOptions,
85
+ address: core.assertValidAddress(),
86
+ });
87
+
88
+ // to whom it may concern, this syntax is only used because we need to use test classes
89
+ // that are preconfigured with the dynamic base addresses generated at test time.
90
+ // normally you would use the follow api for brevity
91
+ // budget: client.SimpleBudget({} | '0xaddress')
92
+ const { budget, erc20 } = budgets;
93
+ const boost = await client.createBoost({
94
+ protocolFee: 1n,
95
+ referralFee: 2n,
96
+ maxParticipants: 100n,
97
+ budget: budget,
98
+ action: new bases.EventAction(
99
+ defaultOptions,
100
+ makeMockEventActionPayload(
101
+ core.assertValidAddress(),
102
+ erc20.assertValidAddress(),
103
+ ),
104
+ ),
105
+ validator: new bases.SignerValidator(defaultOptions, {
106
+ signers: [defaultOptions.account.address],
107
+ validatorCaller: defaultOptions.account.address,
108
+ }),
109
+ allowList: new bases.SimpleAllowList(defaultOptions, {
110
+ owner: defaultOptions.account.address,
111
+ allowed: [defaultOptions.account.address],
112
+ }),
113
+ incentives: [
114
+ new bases.ERC20Incentive(defaultOptions, {
115
+ asset: erc20.assertValidAddress(),
116
+ reward: parseEther('1'),
117
+ limit: 100n,
118
+ strategy: StrategyType.POOL,
119
+ }),
120
+ ],
121
+ });
122
+ const onChainBoost = await client.readBoost(boost.id);
123
+
124
+ expect(boost.owner).toBe(onChainBoost.owner);
125
+ expect(boost.protocolFee).toBe(onChainBoost.protocolFee);
126
+ expect(boost.referralFee).toBe(onChainBoost.referralFee);
127
+ expect(boost.maxParticipants).toBe(onChainBoost.maxParticipants);
128
+
129
+ expect(boost.action.address).toBe(onChainBoost.action);
130
+ // just get some type safety here
131
+ if (boost.action instanceof ContractAction === false) return;
132
+ expect(await boost.action.chainId()).toBe(BigInt(31_337));
133
+ expect((await boost.action.target()).toLowerCase()).toBe(
134
+ core.assertValidAddress().toLowerCase(),
135
+ );
136
+ expect(await boost.action.selector()).toBe('0xdeadbeef');
137
+ expect(await boost.action.value()).toBe(0n);
138
+
139
+ expect(boost.validator.address?.toLowerCase()).toBe(
140
+ onChainBoost.validator.toLowerCase(),
141
+ );
142
+ expect(await boost.validator.signers(defaultOptions.account.address)).toBe(
143
+ true,
144
+ );
145
+
146
+ expect(boost.allowList.address?.toLowerCase()).toBe(
147
+ onChainBoost.allowList.toLowerCase(),
148
+ );
149
+ expect(
150
+ await boost.allowList.isAllowed(defaultOptions.account.address),
151
+ ).toBe(true);
152
+ expect(await boost.allowList.isAllowed(zeroAddress)).toBe(false);
153
+
154
+ expect(boost.budget.address?.toLowerCase()).toBe(
155
+ onChainBoost.budget.toLowerCase(),
156
+ );
157
+ expect(
158
+ await boost.budget.isAuthorized(defaultOptions.account.address),
159
+ ).toBe(true);
160
+
161
+ expect(boost.incentives.length).toBe(onChainBoost.incentives.length);
162
+ const incentive = boost.incentives.at(0) as ERC20Incentive;
163
+ expect(incentive.address?.toLowerCase()).toBe(
164
+ onChainBoost.incentives.at(0)?.toLowerCase(),
165
+ );
166
+ expect((await incentive.asset()).toLowerCase()).toBe(
167
+ erc20.address?.toLowerCase(),
168
+ );
169
+ expect(await incentive.currentReward()).toBe(parseEther('1'));
170
+ expect(await incentive.limit()).toBe(100n);
171
+ expect(await incentive.claims()).toBe(0n);
172
+ });
173
+
174
+ test('can read the raw on chain representation of a boost', async () => {
175
+ const { core, bases } = fixtures;
176
+ const client = new BoostCore({
177
+ ...defaultOptions,
178
+ address: core.assertValidAddress(),
179
+ });
180
+
181
+ // to whom it may concern, this syntax is only used because we need to use test classes
182
+ // that are preconfigured with the dynamic base addresses generated at test time.
183
+ // normally you would use the follow api for brevity
184
+ // budget: client.SimpleBudget({} | '0xaddress')
185
+ const { budget, erc20 } = budgets;
186
+ const _boost = await client.createBoost({
187
+ protocolFee: 1n,
188
+ referralFee: 2n,
189
+ maxParticipants: 100n,
190
+ budget: budget,
191
+ action: new bases.EventAction(
192
+ defaultOptions,
193
+ makeMockEventActionPayload(
194
+ core.assertValidAddress(),
195
+ erc20.assertValidAddress(),
196
+ ),
197
+ ),
198
+ validator: new bases.SignerValidator(defaultOptions, {
199
+ signers: [defaultOptions.account.address],
200
+ validatorCaller: defaultOptions.account.address,
201
+ }),
202
+ allowList: new bases.SimpleAllowList(defaultOptions, {
203
+ owner: defaultOptions.account.address,
204
+ allowed: [defaultOptions.account.address],
205
+ }),
206
+ incentives: [
207
+ new bases.ERC20Incentive(defaultOptions, {
208
+ asset: erc20.assertValidAddress(),
209
+ reward: parseEther('1'),
210
+ limit: 100n,
211
+ strategy: StrategyType.POOL,
212
+ }),
213
+ ],
214
+ });
215
+ const boost = await client.readBoost(_boost.id);
216
+ expect(boost.protocolFee).toBe(1001n);
217
+ expect(boost.referralFee).toBe(1002n);
218
+ expect(boost.maxParticipants).toBe(100n);
219
+ expect(boost.budget).toBe(_boost.budget.assertValidAddress());
220
+ expect(boost.action).toBe(_boost.action.assertValidAddress());
221
+ expect(boost.validator).toBe(_boost.validator.assertValidAddress());
222
+ expect(boost.allowList).toBe(_boost.allowList.assertValidAddress());
223
+ expect(boost.incentives.at(0)).toBe(
224
+ _boost.incentives.at(0)?.assertValidAddress(),
225
+ );
226
+ });
227
+
228
+ test('can reuse an existing action', async () => {
229
+ const { core, bases } = fixtures;
230
+ const client = new BoostCore({
231
+ ...defaultOptions,
232
+ address: core.assertValidAddress(),
233
+ });
234
+
235
+ // to whom it may concern, this syntax is only used because we need to use test classes
236
+ // that are preconfigured with the dynamic base addresses generated at test time.
237
+ // normally you would use the follow api for brevity
238
+ // budget: client.SimpleBudget({} | '0xaddress')
239
+ const { budget, erc20 } = budgets;
240
+
241
+ // allocate more funds to the budget
242
+ await erc20.mint(defaultOptions.account.address, parseEther('100'));
243
+ await erc20.approve(budget.assertValidAddress(), parseEther('100'));
244
+ await budget.allocate({
245
+ amount: parseEther('100'),
246
+ asset: erc20.assertValidAddress(),
247
+ target: defaultOptions.account.address,
248
+ });
249
+
250
+ const _boost = await client.createBoost({
251
+ budget: budget,
252
+ action: new bases.EventAction(
253
+ defaultOptions,
254
+ makeMockEventActionPayload(
255
+ core.assertValidAddress(),
256
+ erc20.assertValidAddress(),
257
+ ),
258
+ ),
259
+ validator: new bases.SignerValidator(defaultOptions, {
260
+ signers: [defaultOptions.account.address],
261
+ validatorCaller: defaultOptions.account.address,
262
+ }),
263
+ allowList: new bases.SimpleAllowList(defaultOptions, {
264
+ owner: defaultOptions.account.address,
265
+ allowed: [defaultOptions.account.address],
266
+ }),
267
+ incentives: [
268
+ new bases.ERC20Incentive(defaultOptions, {
269
+ asset: erc20.assertValidAddress(),
270
+ reward: parseEther('1'),
271
+ limit: 100n,
272
+ strategy: StrategyType.POOL,
273
+ }),
274
+ ],
275
+ });
276
+ const boost = await client.createBoost({
277
+ budget: budget,
278
+ action: new bases.EventAction(
279
+ defaultOptions,
280
+ _boost.action.assertValidAddress(),
281
+ false,
282
+ ),
283
+ validator: new bases.SignerValidator(defaultOptions, {
284
+ signers: [defaultOptions.account.address],
285
+ validatorCaller: defaultOptions.account.address,
286
+ }),
287
+ allowList: new bases.SimpleAllowList(defaultOptions, {
288
+ owner: defaultOptions.account.address,
289
+ allowed: [defaultOptions.account.address],
290
+ }),
291
+ incentives: [
292
+ new bases.ERC20Incentive(defaultOptions, {
293
+ asset: erc20.assertValidAddress(),
294
+ reward: parseEther('1'),
295
+ limit: 100n,
296
+ strategy: StrategyType.POOL,
297
+ }),
298
+ ],
299
+ });
300
+ const onChainBoost = await client.readBoost(boost.id);
301
+ expect(onChainBoost.action).toBe(_boost.action.assertValidAddress());
302
+ });
303
+
304
+ test('can reuse an existing validator', async () => {
305
+ const { core, bases } = fixtures;
306
+ const client = new BoostCore({
307
+ ...defaultOptions,
308
+ address: core.assertValidAddress(),
309
+ });
310
+
311
+ // to whom it may concern, this syntax is only used because we need to use test classes
312
+ // that are preconfigured with the dynamic base addresses generated at test time.
313
+ // normally you would use the follow api for brevity
314
+ // budget: client.SimpleBudget({} | '0xaddress')
315
+ const { budget, erc20 } = budgets;
316
+
317
+ // allocate more erc20 funds to the budget from the owning accound
318
+ await erc20.mint(defaultOptions.account.address, parseEther('100'));
319
+ await erc20.approve(budget.assertValidAddress(), parseEther('100'));
320
+ await budget.allocate({
321
+ amount: parseEther('100'),
322
+ asset: erc20.assertValidAddress(),
323
+ target: defaultOptions.account.address,
324
+ });
325
+
326
+ const _boost = await client.createBoost({
327
+ budget: budget,
328
+ action: new bases.EventAction(
329
+ defaultOptions,
330
+ makeMockEventActionPayload(
331
+ core.assertValidAddress(),
332
+ erc20.assertValidAddress(),
333
+ ),
334
+ ),
335
+ validator: new bases.SignerValidator(defaultOptions, {
336
+ signers: [defaultOptions.account.address],
337
+ validatorCaller: defaultOptions.account.address,
338
+ }),
339
+ allowList: new bases.SimpleAllowList(defaultOptions, {
340
+ owner: defaultOptions.account.address,
341
+ allowed: [defaultOptions.account.address],
342
+ }),
343
+ incentives: [
344
+ new bases.ERC20Incentive(defaultOptions, {
345
+ asset: erc20.assertValidAddress(),
346
+ reward: parseEther('1'),
347
+ limit: 100n,
348
+ strategy: StrategyType.POOL,
349
+ }),
350
+ ],
351
+ });
352
+ const boost = await client.createBoost({
353
+ budget: budget,
354
+ action: new bases.EventAction(
355
+ defaultOptions,
356
+ makeMockEventActionPayload(
357
+ core.assertValidAddress(),
358
+ erc20.assertValidAddress(),
359
+ ),
360
+ ),
361
+ validator: new bases.SignerValidator(
362
+ defaultOptions,
363
+ _boost.validator.assertValidAddress(),
364
+ false,
365
+ ),
366
+ allowList: new bases.SimpleAllowList(defaultOptions, {
367
+ owner: defaultOptions.account.address,
368
+ allowed: [defaultOptions.account.address],
369
+ }),
370
+ incentives: [
371
+ new bases.ERC20Incentive(defaultOptions, {
372
+ asset: erc20.assertValidAddress(),
373
+ reward: parseEther('1'),
374
+ limit: 100n,
375
+ strategy: StrategyType.POOL,
376
+ }),
377
+ ],
378
+ });
379
+ const onChainBoost = await client.readBoost(boost.id);
380
+ expect(onChainBoost.validator).toBe(_boost.validator.assertValidAddress());
381
+ });
382
+
383
+ test('can reuse an existing allowlist', async () => {
384
+ const { core, bases } = fixtures;
385
+ const client = new BoostCore({
386
+ ...defaultOptions,
387
+ address: core.assertValidAddress(),
388
+ });
389
+
390
+ // to whom it may concern, this syntax is only used because we need to use test classes
391
+ // that are preconfigured with the dynamic base addresses generated at test time.
392
+ // normally you would use the follow api for brevity
393
+ // budget: client.SimpleBudget({} | '0xaddress')
394
+ const { budget, erc20 } = budgets;
395
+
396
+ // allocate more erc20 funds to the budget from the owning accound
397
+ await erc20.mint(defaultOptions.account.address, parseEther('100'));
398
+ await erc20.approve(budget.assertValidAddress(), parseEther('100'));
399
+ await budget.allocate({
400
+ amount: parseEther('100'),
401
+ asset: erc20.assertValidAddress(),
402
+ target: defaultOptions.account.address,
403
+ });
404
+
405
+ const _boost = await client.createBoost({
406
+ budget: budget,
407
+ action: new bases.EventAction(
408
+ defaultOptions,
409
+ makeMockEventActionPayload(
410
+ core.assertValidAddress(),
411
+ erc20.assertValidAddress(),
412
+ ),
413
+ ),
414
+ validator: new bases.SignerValidator(defaultOptions, {
415
+ signers: [defaultOptions.account.address],
416
+ validatorCaller: defaultOptions.account.address,
417
+ }),
418
+ allowList: new bases.SimpleAllowList(defaultOptions, {
419
+ owner: defaultOptions.account.address,
420
+ allowed: [defaultOptions.account.address],
421
+ }),
422
+ incentives: [
423
+ new bases.ERC20Incentive(defaultOptions, {
424
+ asset: erc20.assertValidAddress(),
425
+ reward: parseEther('1'),
426
+ limit: 100n,
427
+ strategy: StrategyType.POOL,
428
+ }),
429
+ ],
430
+ });
431
+ const boost = await client.createBoost({
432
+ budget: budget,
433
+ action: new bases.EventAction(
434
+ defaultOptions,
435
+ makeMockEventActionPayload(
436
+ core.assertValidAddress(),
437
+ erc20.assertValidAddress(),
438
+ ),
439
+ ),
440
+ validator: new bases.SignerValidator(defaultOptions, {
441
+ signers: [defaultOptions.account.address],
442
+ validatorCaller: defaultOptions.account.address,
443
+ }),
444
+ allowList: new bases.SimpleAllowList(
445
+ defaultOptions,
446
+ _boost.allowList.assertValidAddress(),
447
+ false,
448
+ ),
449
+ incentives: [
450
+ new bases.ERC20Incentive(defaultOptions, {
451
+ asset: erc20.assertValidAddress(),
452
+ reward: parseEther('1'),
453
+ limit: 100n,
454
+ strategy: StrategyType.POOL,
455
+ }),
456
+ ],
457
+ });
458
+ const onChainBoost = await client.readBoost(boost.id);
459
+ expect(onChainBoost.allowList).toBe(_boost.allowList.assertValidAddress());
460
+ });
461
+
462
+ test('cannot reuse an existing incentive', async () => {
463
+ const { core, bases } = fixtures;
464
+ const client = new BoostCore({
465
+ ...defaultOptions,
466
+ address: core.assertValidAddress(),
467
+ });
468
+
469
+ // to whom it may concern, this syntax is only used because we need to use test classes
470
+ // that are preconfigured with the dynamic base addresses generated at test time.
471
+ // normally you would use the follow api for brevity
472
+ // budget: client.SimpleBudget({} | '0xaddress')
473
+ const { budget, erc20 } = budgets;
474
+
475
+ // allocate more erc20 funds to the budget from the owning accound
476
+ await erc20.mint(defaultOptions.account.address, parseEther('100'));
477
+ await erc20.approve(budget.assertValidAddress(), parseEther('100'));
478
+ await budget.allocate({
479
+ amount: parseEther('100'),
480
+ asset: erc20.assertValidAddress(),
481
+ target: defaultOptions.account.address,
482
+ });
483
+
484
+ const incentive = new bases.ERC20Incentive(defaultOptions, {
485
+ asset: erc20.assertValidAddress(),
486
+ reward: parseEther('1'),
487
+ limit: 100n,
488
+ strategy: StrategyType.POOL,
489
+ });
490
+ const _boost = await client.createBoost({
491
+ budget: budget,
492
+ action: new bases.EventAction(
493
+ defaultOptions,
494
+ makeMockEventActionPayload(
495
+ core.assertValidAddress(),
496
+ erc20.assertValidAddress(),
497
+ ),
498
+ ),
499
+ validator: new bases.SignerValidator(defaultOptions, {
500
+ signers: [defaultOptions.account.address],
501
+ validatorCaller: defaultOptions.account.address,
502
+ }),
503
+ allowList: new bases.SimpleAllowList(defaultOptions, {
504
+ owner: defaultOptions.account.address,
505
+ allowed: [defaultOptions.account.address],
506
+ }),
507
+ incentives: [incentive],
508
+ });
509
+ try {
510
+ await client.createBoost({
511
+ budget: budget,
512
+ action: new bases.EventAction(
513
+ defaultOptions,
514
+ makeMockEventActionPayload(
515
+ core.assertValidAddress(),
516
+ erc20.assertValidAddress(),
517
+ ),
518
+ ),
519
+ validator: new bases.SignerValidator(defaultOptions, {
520
+ signers: [defaultOptions.account.address],
521
+ validatorCaller: defaultOptions.account.address,
522
+ }),
523
+ allowList: new bases.SimpleAllowList(defaultOptions, {
524
+ owner: defaultOptions.account.address,
525
+ allowed: [defaultOptions.account.address],
526
+ }),
527
+ incentives: [incentive],
528
+ });
529
+ } catch (e) {
530
+ expect(e).toBeInstanceOf(IncentiveNotCloneableError);
531
+ }
532
+ });
533
+
534
+ test('can offer multiple incentives', async () => {
535
+ const { registry, core, bases } = fixtures;
536
+ const client = new BoostCore({
537
+ ...defaultOptions,
538
+ address: core.assertValidAddress(),
539
+ });
540
+
541
+ // to whom it may concern, this syntax is only used because we need to use test classes
542
+ // that are preconfigured with the dynamic base addresses generated at test time.
543
+ // normally you would use the follow api for brevity
544
+ // budget: client.SimpleBudget({} | '0xaddress')
545
+ const { budget, erc20, points, erc1155 } = budgets;
546
+ const allowList = await registry.clone(
547
+ 'SharedAllowList',
548
+ new bases.SimpleAllowList(defaultOptions, {
549
+ owner: defaultOptions.account.address,
550
+ allowed: [defaultOptions.account.address],
551
+ }),
552
+ );
553
+
554
+ const erc20Incentive = new bases.ERC20Incentive(defaultOptions, {
555
+ asset: erc20.assertValidAddress(),
556
+ reward: 1n,
557
+ limit: 10n,
558
+ strategy: StrategyType.POOL,
559
+ });
560
+ const erc1155Incentive = new bases.ERC1155Incentive(defaultOptions, {
561
+ asset: erc1155.assertValidAddress(),
562
+ strategy: ERC1155StrategyType.POOL,
563
+ limit: 1n,
564
+ tokenId: 1n,
565
+ extraData: '0x',
566
+ });
567
+ const cgdaIncentive = new bases.CGDAIncentive(defaultOptions, {
568
+ asset: erc20.assertValidAddress(),
569
+ initialReward: 1n,
570
+ totalBudget: 10n,
571
+ rewardBoost: 1n,
572
+ rewardDecay: 1n,
573
+ });
574
+ const allowListIncentive = new bases.AllowListIncentive(defaultOptions, {
575
+ allowList: allowList.assertValidAddress(),
576
+ limit: 5n,
577
+ });
578
+ const pointsIncentive = new bases.PointsIncentive(defaultOptions, {
579
+ venue: points.assertValidAddress(),
580
+ selector: bytes4('issue(address,uint256)'),
581
+ reward: 1n,
582
+ limit: 10n,
583
+ });
584
+
585
+ await client.createBoost({
586
+ protocolFee: 1n,
587
+ referralFee: 2n,
588
+ maxParticipants: 100n,
589
+ budget: budget,
590
+ action: new bases.EventAction(
591
+ defaultOptions,
592
+ makeMockEventActionPayload(
593
+ core.assertValidAddress(),
594
+ erc20.assertValidAddress(),
595
+ ),
596
+ ),
597
+ validator: new bases.SignerValidator(defaultOptions, {
598
+ signers: [defaultOptions.account.address],
599
+ validatorCaller: defaultOptions.account.address,
600
+ }),
601
+ allowList: new bases.SimpleAllowList(
602
+ defaultOptions,
603
+ allowList.assertValidAddress(),
604
+ false,
605
+ ),
606
+ incentives: [
607
+ erc1155Incentive,
608
+ erc20Incentive,
609
+ cgdaIncentive,
610
+ allowListIncentive,
611
+ pointsIncentive,
612
+ ],
613
+ });
614
+ expect(await erc20Incentive.reward()).toEqual(1n);
615
+ expect(await erc20Incentive.limit()).toEqual(10n);
616
+ expect((await cgdaIncentive.asset()).toLowerCase()).toEqual(
617
+ erc20.address?.toLowerCase(),
618
+ );
619
+ expect(await cgdaIncentive.currentReward()).toEqual(1n);
620
+ expect(
621
+ await (await allowListIncentive.allowList()).isAllowed(
622
+ defaultOptions.account.address,
623
+ ),
624
+ ).toEqual(true);
625
+ expect(await pointsIncentive.reward()).toEqual(1n);
626
+ expect(await pointsIncentive.currentReward()).toEqual(1n);
627
+ expect(await pointsIncentive.limit()).toEqual(10n);
628
+ });
629
+
630
+ test('can get the protocol fee', async () => {
631
+ const { core } = fixtures;
632
+ const client = new BoostCore({
633
+ ...defaultOptions,
634
+ address: core.assertValidAddress(),
635
+ });
636
+
637
+ expect(await client.protocolFee()).toBe(1000n);
638
+ });
639
+
640
+ test('can get the protocol fee receiver', async () => {
641
+ const { core } = fixtures;
642
+ const client = new BoostCore({
643
+ ...defaultOptions,
644
+ address: core.assertValidAddress(),
645
+ });
646
+
647
+ expect(await client.protocolFeeReceiver()).toBe(
648
+ defaultOptions.account.address,
649
+ );
650
+ });
651
+
652
+ test('can set the protocol fee receiver', async () => {
653
+ const { core } = fixtures;
654
+ const client = new BoostCore({
655
+ ...defaultOptions,
656
+ address: core.assertValidAddress(),
657
+ });
658
+
659
+ await client.setProcolFeeReceiver(zeroAddress);
660
+
661
+ expect(await client.protocolFeeReceiver()).toBe(zeroAddress);
662
+ });
663
+
664
+ test('can get the claim fee', async () => {
665
+ const { core } = fixtures;
666
+ const client = new BoostCore({
667
+ ...defaultOptions,
668
+ address: core.assertValidAddress(),
669
+ });
670
+
671
+ expect(await client.claimFee()).toBe(75000000000000n);
672
+ });
673
+
674
+ test('can set the claim fee', async () => {
675
+ const { core } = fixtures;
676
+ const client = new BoostCore({
677
+ ...defaultOptions,
678
+ address: core.assertValidAddress(),
679
+ });
680
+
681
+ await client.setClaimFee(100n);
682
+
683
+ expect(await client.claimFee()).toBe(100n);
684
+ });
685
+
686
+ test('binds all actions, budgets, allowlists, incentives, and validators to reuse core options and account', async () => {
687
+ const { core } = fixtures;
688
+
689
+ // const contractAction = core.ContractAction(zeroAddress);
690
+ // expect(contractAction._config).toEqual(defaultOptions.config);
691
+ // expect(contractAction._account).toEqual(defaultOptions.account);
692
+
693
+ // const erc721MintAction = core.ERC721MintAction(zeroAddress);
694
+ // expect(erc721MintAction._config).toEqual(defaultOptions.config);
695
+ // expect(erc721MintAction._account).toEqual(defaultOptions.account);
696
+
697
+ const eventAction = core.EventAction(zeroAddress);
698
+ expect(eventAction._config).toEqual(defaultOptions.config);
699
+ expect(eventAction._account).toEqual(defaultOptions.account);
700
+
701
+ const allowList = core.SimpleAllowList(zeroAddress);
702
+ expect(allowList._config).toEqual(defaultOptions.config);
703
+ expect(allowList._account).toEqual(defaultOptions.account);
704
+
705
+ const denyList = core.SimpleDenyList(zeroAddress);
706
+ expect(denyList._config).toEqual(defaultOptions.config);
707
+ expect(denyList._account).toEqual(defaultOptions.account);
708
+
709
+ const managedBudget = core.ManagedBudget(zeroAddress);
710
+ expect(managedBudget._config).toEqual(defaultOptions.config);
711
+ expect(managedBudget._account).toEqual(defaultOptions.account);
712
+
713
+ // const simpleBudget = core.SimpleBudget(zeroAddress);
714
+ // expect(simpleBudget._config).toEqual(defaultOptions.config);
715
+ // expect(simpleBudget._account).toEqual(defaultOptions.account);
716
+
717
+ // const vestingBudget = core.VestingBudget(zeroAddress);
718
+ // expect(vestingBudget._config).toEqual(defaultOptions.config);
719
+ // expect(vestingBudget._account).toEqual(defaultOptions.account);
720
+
721
+ const allowListIncentive = core.AllowListIncentive({
722
+ allowList: zeroAddress,
723
+ limit: 0n,
724
+ });
725
+ expect(allowListIncentive._config).toEqual(defaultOptions.config);
726
+ expect(allowListIncentive._account).toEqual(defaultOptions.account);
727
+
728
+ const cgdaIncentive = core.CGDAIncentive({
729
+ asset: zeroAddress,
730
+ initialReward: 0n,
731
+ rewardDecay: 0n,
732
+ rewardBoost: 0n,
733
+ totalBudget: 0n,
734
+ });
735
+ expect(cgdaIncentive._config).toEqual(defaultOptions.config);
736
+ expect(cgdaIncentive._account).toEqual(defaultOptions.account);
737
+
738
+ const erc20Incentive = core.ERC20Incentive({
739
+ asset: zeroAddress,
740
+ strategy: 0,
741
+ reward: 0n,
742
+ limit: 0n,
743
+ });
744
+ expect(erc20Incentive._config).toEqual(defaultOptions.config);
745
+ expect(erc20Incentive._account).toEqual(defaultOptions.account);
746
+
747
+ const erc20VariableIncentive = core.ERC20VariableIncentive({
748
+ asset: zeroAddress,
749
+ reward: 0n,
750
+ limit: 0n,
751
+ });
752
+ expect(erc20VariableIncentive._config).toEqual(defaultOptions.config);
753
+ expect(erc20VariableIncentive._account).toEqual(defaultOptions.account);
754
+
755
+ // const erc1155Incentive = core.ERC1155Incentive({
756
+ // asset: zeroAddress,
757
+ // strategy: 0,
758
+ // tokenId: 1n,
759
+ // limit: 0n,
760
+ // extraData: '0x',
761
+ // });
762
+ // expect(erc1155Incentive._config).toEqual(defaultOptions.config);
763
+ // expect(erc1155Incentive._account).toEqual(defaultOptions.account);
764
+
765
+ const pointsIncentive = core.PointsIncentive({
766
+ venue: zeroAddress,
767
+ selector: '0x',
768
+ reward: 0n,
769
+ limit: 0n,
770
+ });
771
+ expect(pointsIncentive._config).toEqual(defaultOptions.config);
772
+ expect(pointsIncentive._account).toEqual(defaultOptions.account);
773
+
774
+ const signerValidator = core.SignerValidator(zeroAddress);
775
+ expect(signerValidator._config).toEqual(defaultOptions.config);
776
+ expect(signerValidator._account).toEqual(defaultOptions.account);
777
+ });
778
+
779
+ test('can subscribe to contract events', async () => {
780
+ const subscription = vi.fn();
781
+
782
+ const { core, bases } = fixtures;
783
+ const client = new BoostCore({
784
+ ...defaultOptions,
785
+ address: core.assertValidAddress(),
786
+ });
787
+ client.subscribe(subscription, { pollingInterval: 100 });
788
+
789
+ // to whom it may concern, this syntax is only used because we need to use test classes
790
+ // that are preconfigured with the dynamic base addresses generated at test time.
791
+ // normally you would use the follow api for brevity
792
+ // budget: client.SimpleBudget({} | '0xaddress')
793
+ const { budget, erc20 } = budgets;
794
+ await client.createBoost({
795
+ protocolFee: 1n,
796
+ referralFee: 2n,
797
+ maxParticipants: 100n,
798
+ budget: budget,
799
+ action: new bases.EventAction(
800
+ defaultOptions,
801
+ makeMockEventActionPayload(
802
+ core.assertValidAddress(),
803
+ erc20.assertValidAddress(),
804
+ ),
805
+ ),
806
+ validator: new bases.SignerValidator(defaultOptions, {
807
+ signers: [defaultOptions.account.address],
808
+ validatorCaller: defaultOptions.account.address,
809
+ }),
810
+ allowList: new bases.SimpleAllowList(defaultOptions, {
811
+ owner: defaultOptions.account.address,
812
+ allowed: [defaultOptions.account.address],
813
+ }),
814
+ incentives: [
815
+ new bases.ERC20Incentive(defaultOptions, {
816
+ asset: erc20.assertValidAddress(),
817
+ reward: parseEther('1'),
818
+ limit: 100n,
819
+ strategy: StrategyType.POOL,
820
+ }),
821
+ ],
822
+ });
823
+
824
+ await new Promise((resolve) => {
825
+ setTimeout(resolve, 500);
826
+ });
827
+
828
+ expect(subscription).toHaveBeenCalledTimes(1);
829
+ });
830
+
831
+ test('can set a passthrough auth scheme', async () => {
832
+ const { core } = fixtures;
833
+ const client = new BoostCore({
834
+ ...defaultOptions,
835
+ address: core.assertValidAddress(),
836
+ });
837
+
838
+ const auth = client.PassthroughAuth();
839
+ await auth.deploy();
840
+ await client.setCreateBoostAuth(auth);
841
+ expect((await client.createBoostAuth()).toLowerCase()).toBe(
842
+ auth.assertValidAddress(),
843
+ );
844
+ expect(await client.isAuthorized(zeroAddress)).toBe(true);
845
+ });
846
+ });