@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,449 @@
1
+ import {
2
+ boostRegistryAbi,
3
+ readBoostRegistryGetBaseImplementation,
4
+ readBoostRegistryGetCloneIdentifier,
5
+ readBoostRegistryGetClones,
6
+ simulateBoostRegistryDeployClone,
7
+ simulateBoostRegistryRegister,
8
+ writeBoostRegistryDeployClone,
9
+ writeBoostRegistryRegister,
10
+ } from '@boostxyz/evm';
11
+ import { bytecode } from '@boostxyz/evm/artifacts/contracts/BoostRegistry.sol/BoostRegistry.json';
12
+ import {
13
+ type Address,
14
+ type ContractEventName,
15
+ type Hex,
16
+ isAddress,
17
+ } from 'viem';
18
+ import {
19
+ Deployable,
20
+ type DeployableOptions,
21
+ type GenericDeployableParams,
22
+ } from './Deployable/Deployable';
23
+ import type { DeployableTarget } from './Deployable/DeployableTarget';
24
+ import {
25
+ type GenericLog,
26
+ type HashAndSimulatedResult,
27
+ type ReadParams,
28
+ RegistryType,
29
+ type WriteParams,
30
+ } from './utils';
31
+
32
+ export { RegistryType, boostRegistryAbi };
33
+
34
+ /**
35
+ * The fixed address for the Boost Registry.
36
+ * By default, `new BoostRegistry` will use this address if not otherwise provided.
37
+ *
38
+ * @type {Address}
39
+ */
40
+ export const BOOST_REGISTRY_ADDRESS: Address = import.meta.env
41
+ .VITE_BOOST_REGISTRY_ADDRESS;
42
+
43
+ /**
44
+ * A record of `BoostRegistry` event names to `AbiEvent` objects for use with `getLogs`
45
+ *
46
+ * @export
47
+ * @typedef {BoostRegistryLog}
48
+ * @template {ContractEventName<typeof boostRegistryAbi>} [event=ContractEventName<
49
+ * typeof boostRegistryAbi
50
+ * >]
51
+ */
52
+ export type BoostRegistryLog<
53
+ event extends ContractEventName<typeof boostRegistryAbi> = ContractEventName<
54
+ typeof boostRegistryAbi
55
+ >,
56
+ > = GenericLog<typeof boostRegistryAbi, event>;
57
+
58
+ /**
59
+ * Instantiation options for a previously deployed Boost Registry
60
+ *
61
+ * @export
62
+ * @interface BoostRegistryDeployedOptions
63
+ * @typedef {BoostRegistryDeployedOptions}
64
+ * @extends {DeployableOptions}
65
+ */
66
+ export interface BoostRegistryDeployedOptions extends DeployableOptions {
67
+ /**
68
+ * The address for a Boost Registry, if different than `BOOST_REGISTRY_ADDRESS`
69
+ *
70
+ * @type {?Address}
71
+ */
72
+ address?: Address;
73
+ }
74
+
75
+ /**
76
+ * A typeguard to determine if instantiation is using a custom address.
77
+ *
78
+ * @param {*} opts
79
+ * @returns {opts is BoostRegistryDeployedOptions}
80
+ */
81
+ function isBoostRegistryDeployed(
82
+ // biome-ignore lint/suspicious/noExplicitAny: type guard
83
+ opts: any,
84
+ ): opts is BoostRegistryDeployedOptions {
85
+ return opts.address && isAddress(opts.address);
86
+ }
87
+
88
+ /**
89
+ * The Boost Registry does not take any construction arguments, so if you'd like to deploy a new Boost Registry, pass an explicit null to the `address` field.
90
+ *
91
+ * @export
92
+ * @interface BoostRegistryOptionsWithPayload
93
+ * @typedef {BoostRegistryOptionsWithPayload}
94
+ * @extends {DeployableOptions}
95
+ */
96
+ export interface BoostRegistryOptionsWithPayload extends DeployableOptions {
97
+ /**
98
+ *
99
+ * @type {null}
100
+ */
101
+ address: null;
102
+ }
103
+
104
+ /**
105
+ * A typeguard to determine if the user is intending to deploy a new Boost Registry before usage
106
+ *
107
+ * @param {*} opts
108
+ * @returns {opts is BoostRegistryOptionsWithPayload}
109
+ */
110
+ function isBoostRegistryDeployable(
111
+ // biome-ignore lint/suspicious/noExplicitAny: type guard
112
+ opts: any,
113
+ ): opts is BoostRegistryOptionsWithPayload {
114
+ return opts.address === null;
115
+ }
116
+
117
+ /**
118
+ * Instantiation options for a Boost Registry.
119
+ *
120
+ * @example
121
+ * To target Boost's Registry, omit the address field.
122
+ * Otherwise, supply a custom address to a previously deployed custom Boost Registry.
123
+ * You can also pass `{ address: null }` if you are intending to deploy a new Boost Registry.
124
+ * ```ts
125
+ * let registry = new BoostRegistry({ config, account })
126
+ * // or
127
+ * registry = new BoostRegistry({ config, account, address: CUSTOM_ADDRESS })
128
+ * // or
129
+ * registry = new BoostRegistry({ config, account, address: null })
130
+ * await registry.deploy()
131
+ * ```
132
+ *
133
+ * @export
134
+ * @typedef {BoostRegistryConfig}
135
+ */
136
+ export type BoostRegistryConfig =
137
+ | BoostRegistryDeployedOptions
138
+ | BoostRegistryOptionsWithPayload;
139
+
140
+ /**
141
+ * Constructs a new Boost Registry. A registry for base implementations and cloned instances.
142
+ * This contract is used to register base implementations and deploy new instances of those implementations for use within the Boost protocol.
143
+ *
144
+ * @see {@link BoostRegistryConfig}
145
+ * @export
146
+ * @class BoostRegistry
147
+ * @typedef {BoostRegistry}
148
+ * @extends {Deployable<never[]>}
149
+ */
150
+ export class BoostRegistry extends Deployable<
151
+ never[],
152
+ typeof boostRegistryAbi
153
+ > {
154
+ /**
155
+ * Creates an instance of BoostRegistry.
156
+ *
157
+ * @see {@link BoostRegistryConfig}
158
+ * @constructor
159
+ * @param {BoostRegistryConfig} param0
160
+ * @param {Config} param0.config - [Wagmi Configuration](https://wagmi.sh/core/api/createConfig)
161
+ * @param {?Account} [param0.account] - [Viem Local Account](https://viem.sh/docs/accounts/local)
162
+ * @param {({ address?: Address; } | {})} param0....options
163
+ */
164
+ constructor({ config, account, ...options }: BoostRegistryConfig) {
165
+ if (isBoostRegistryDeployed(options) && options.address) {
166
+ super({ account, config }, options.address);
167
+ } else if (isBoostRegistryDeployable(options)) {
168
+ super({ account, config }, []);
169
+ } else {
170
+ super({ account, config }, BOOST_REGISTRY_ADDRESS);
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Register a new base implementation of a given type
176
+ *
177
+ * @public
178
+ * @async
179
+ * @param {RegistryType} registryType - The base type for the implementation
180
+ * @param {string} name - A name for the implementation (must be unique within the given type)
181
+ * @param {Address} implementation - The address of the implementation contract
182
+ * @param {?WriteParams<typeof boostRegistryAbi, 'register'>} [params] - Optional params to provide the underlying Viem contract call
183
+ * @returns {unknown}
184
+ * @example
185
+ * ```ts
186
+ * await registry.register(ContractAction.registryType, 'ContractAction', ContractAction.base)
187
+ * ```
188
+ */
189
+ public async register(
190
+ registryType: RegistryType,
191
+ name: string,
192
+ implementation: Address,
193
+ params?: WriteParams<typeof boostRegistryAbi, 'register'>,
194
+ ) {
195
+ return this.awaitResult(
196
+ this.registerRaw(registryType, name, implementation, params),
197
+ );
198
+ }
199
+
200
+ /**
201
+ * @see {@link register}
202
+ * @public
203
+ * @async
204
+ * @param {RegistryType} registryType
205
+ * @param {string} name
206
+ * @param {Address} implementation
207
+ * @param {?WriteParams<typeof boostRegistryAbi, 'register'>} [params]
208
+ * @returns {unknown}
209
+ */
210
+ public async registerRaw(
211
+ registryType: RegistryType,
212
+ name: string,
213
+ implementation: Address,
214
+ params?: WriteParams<typeof boostRegistryAbi, 'register'>,
215
+ ) {
216
+ const { request, result } = await simulateBoostRegistryRegister(
217
+ this._config,
218
+ {
219
+ address: this.assertValidAddress(),
220
+ args: [registryType, name, implementation],
221
+ ...this.optionallyAttachAccount(),
222
+ // biome-ignore lint/suspicious/noExplicitAny: Accept any shape of valid wagmi/viem parameters, wagmi does the same thing internally
223
+ ...(params as any),
224
+ },
225
+ );
226
+ const hash = await writeBoostRegistryRegister(this._config, request);
227
+ return { hash, result };
228
+ }
229
+
230
+ /**
231
+ * Deploy a new instance of a registered base implementation, returning the provided target with a new address set on it.
232
+ *
233
+ * @public
234
+ * @async
235
+ * @template {DeployableTarget} Target
236
+ * @param {string} displayName - The display name for the clone
237
+ * @param {Target} target - An instance of a target contract to clone and initialize
238
+ * @param {?WriteParams<typeof boostRegistryAbi, 'deployClone'>} [params]
239
+ * @returns {Target} - The provided instance, but with a new address attached.
240
+ * biome-ignore lint/suspicious/noExplicitAny: any deployable target will suffice
241
+ */
242
+ public async clone<Target extends DeployableTarget<any, any>>(
243
+ displayName: string,
244
+ target: Target,
245
+ params?: WriteParams<typeof boostRegistryAbi, 'deployClone'>,
246
+ ): Promise<Target> {
247
+ const instance = await this.deployClone(displayName, target, params);
248
+ return target.at(instance);
249
+ }
250
+
251
+ /**
252
+ *
253
+ * @see {@link clone}
254
+ * @public
255
+ * @async
256
+ * @template {DeployableTarget} Target
257
+ * @param {string} displayName
258
+ * @param {Target} target
259
+ * @param {?WriteParams<typeof boostRegistryAbi, 'deployClone'>} [params]
260
+ * @returns {Target}
261
+ * biome-ignore lint/suspicious/noExplicitAny: any deployable target will suffice
262
+ */
263
+ public async deployClone<Target extends DeployableTarget<any, any>>(
264
+ displayName: string,
265
+ target: Target,
266
+ params?: WriteParams<typeof boostRegistryAbi, 'deployClone'>,
267
+ ): Promise<Address> {
268
+ return this.awaitResult(this.deployCloneRaw(displayName, target, params));
269
+ }
270
+
271
+ /**
272
+ * @see {@link clone}
273
+ * @public
274
+ * @async
275
+ * @param {string} displayName
276
+ * @param {DeployableTarget} target
277
+ * @param {?WriteParams<typeof boostRegistryAbi, 'deployClone'>} [params]
278
+ * @returns {unknown} - The transaction hash
279
+ * biome-ignore lint/suspicious/noExplicitAny: any deployable target will suffice
280
+ */
281
+ public async deployCloneRaw<Target extends DeployableTarget<any, any>>(
282
+ displayName: string,
283
+ target: Target,
284
+ params?: WriteParams<typeof boostRegistryAbi, 'deployClone'>,
285
+ ): Promise<HashAndSimulatedResult<Address>> {
286
+ const payload = target.buildParameters(undefined, {
287
+ config: this._config,
288
+ account: this._account,
289
+ });
290
+ const { request, result } = await simulateBoostRegistryDeployClone(
291
+ this._config,
292
+ {
293
+ address: this.assertValidAddress(),
294
+ args: [
295
+ target.registryType,
296
+ target.base,
297
+ displayName,
298
+ payload.args.at(0)!,
299
+ ],
300
+ ...this.optionallyAttachAccount(),
301
+ // biome-ignore lint/suspicious/noExplicitAny: Accept any shape of valid wagmi/viem parameters, wagmi does the same thing internally
302
+ ...(params as any),
303
+ },
304
+ );
305
+ const hash = await writeBoostRegistryDeployClone(this._config, request);
306
+ return { hash, result };
307
+ }
308
+
309
+ /**
310
+ * Get the address of a registered base implementation.
311
+ * This function will revert if the implementation is not registered
312
+ *
313
+ * @public
314
+ * @async
315
+ * @param {Hex} identifier - The unique identifier for the implementation (see {getIdentifier})
316
+ * @param {?ReadParams<typeof boostRegistryAbi, 'getBaseImplementation'>} [params]
317
+ * @returns {unknown} - The address of the implementation
318
+ */
319
+ public async getBaseImplementation(
320
+ identifier: Hex,
321
+ params?: ReadParams<typeof boostRegistryAbi, 'getBaseImplementation'>,
322
+ ) {
323
+ return readBoostRegistryGetBaseImplementation(this._config, {
324
+ address: this.assertValidAddress(),
325
+ args: [identifier],
326
+ ...this.optionallyAttachAccount(),
327
+ // biome-ignore lint/suspicious/noExplicitAny: Accept any shape of valid wagmi/viem parameters, wagmi does the same thing internally
328
+ ...(params as any),
329
+ });
330
+ }
331
+
332
+ /**
333
+ * Get the address of a deployed clone by its identifier (index in incentives array)
334
+ *
335
+ * @public
336
+ * @async
337
+ * @param {Hex} identifier - The unique identifier for the deployed clone (see {getCloneIdentifier})
338
+ * @param {?ReadParams<typeof boostRegistryAbi, 'getClone'>} [params]
339
+ * @returns {Promise<Address>} - The address of the deployed clone
340
+ */
341
+ public async getClone(
342
+ identifier: Hex,
343
+ params?: ReadParams<typeof boostRegistryAbi, 'getClone'>,
344
+ ) {
345
+ return readBoostRegistryGetBaseImplementation(this._config, {
346
+ address: this.assertValidAddress(),
347
+ args: [identifier],
348
+ ...this.optionallyAttachAccount(),
349
+ // biome-ignore lint/suspicious/noExplicitAny: Accept any shape of valid wagmi/viem parameters, wagmi does the same thing internally
350
+ ...(params as any),
351
+ });
352
+ }
353
+
354
+ /**
355
+ * Get the list of identifiers of deployed clones for a given deployer
356
+ *
357
+ * @public
358
+ * @async
359
+ * @param {Address} deployer - The address of the deployer
360
+ * @param {?ReadParams<typeof boostRegistryAbi, 'getClones'>} [params]
361
+ * @returns {Promise<Address[]>} - The list of deployed clones for the given deployer
362
+ */
363
+ public async getClones(
364
+ deployer: Address,
365
+ params?: ReadParams<typeof boostRegistryAbi, 'getClones'>,
366
+ ) {
367
+ return readBoostRegistryGetClones(this._config, {
368
+ address: this.assertValidAddress(),
369
+ args: [deployer],
370
+ ...this.optionallyAttachAccount(),
371
+ // biome-ignore lint/suspicious/noExplicitAny: Accept any shape of valid wagmi/viem parameters, wagmi does the same thing internally
372
+ ...(params as any),
373
+ });
374
+ }
375
+
376
+ /**
377
+ * Build the identifier for a clone of a base implementation
378
+ *
379
+ * @public
380
+ * @async
381
+ * @param {RegistryType} registryType - The base type for the implementation
382
+ * @param {Address} base - The address of the base implementation
383
+ * @param {Address} deployer - The address of the deployer
384
+ * @param {string} displayName - The display name of the clone
385
+ * @param {?ReadParams<typeof boostRegistryAbi, 'getCloneIdentifier'>} [params]
386
+ * @returns {Promise<Hex>} - The unique identifier for the clone
387
+ */
388
+ public async getCloneIdentifier(
389
+ registryType: RegistryType,
390
+ base: Address,
391
+ deployer: Address,
392
+ displayName: string,
393
+ params?: ReadParams<typeof boostRegistryAbi, 'getCloneIdentifier'>,
394
+ ) {
395
+ return readBoostRegistryGetCloneIdentifier(this._config, {
396
+ address: this.assertValidAddress(),
397
+ args: [registryType, base, deployer, displayName],
398
+ ...this.optionallyAttachAccount(),
399
+ // biome-ignore lint/suspicious/noExplicitAny: Accept any shape of valid wagmi/viem parameters, wagmi does the same thing internally
400
+ ...(params as any),
401
+ });
402
+ }
403
+
404
+ /**
405
+ * Build the identifier for a base implementation
406
+ *
407
+ * @public
408
+ * @async
409
+ * @param {RegistryType} registryType - The base type for the implementation
410
+ * @param {string} displayName - The name of the implementation
411
+ * @param {?ReadParams<typeof boostRegistryAbi, 'getIdentifier'>} [params]
412
+ * @returns {Promise<Hex>} - The unique identifier for the implementation
413
+ */
414
+ public async getIdentifier(
415
+ registryType: RegistryType,
416
+ displayName: string,
417
+ params?: ReadParams<typeof boostRegistryAbi, 'getIdentifier'>,
418
+ ) {
419
+ return readBoostRegistryGetCloneIdentifier(this._config, {
420
+ address: this.assertValidAddress(),
421
+ args: [registryType, displayName],
422
+ ...this.optionallyAttachAccount(),
423
+ // biome-ignore lint/suspicious/noExplicitAny: Accept any shape of valid wagmi/viem parameters, wagmi does the same thing internally
424
+ ...(params as any),
425
+ });
426
+ }
427
+
428
+ /**
429
+ * @inheritdoc
430
+ *
431
+ * @public
432
+ * @param {?never[]} [_payload]
433
+ * @param {?DeployableOptions} [_options]
434
+ * @returns {GenericDeployableParams}
435
+ */
436
+ public override buildParameters(
437
+ _payload?: never[],
438
+ _options?: DeployableOptions,
439
+ ): GenericDeployableParams {
440
+ const [, options] = this.validateDeploymentConfig([], _options);
441
+ return {
442
+ abi: boostRegistryAbi,
443
+ bytecode: bytecode as Hex,
444
+ // biome-ignore lint/suspicious/noExplicitAny: Registry doesn't construct or initialize
445
+ args: [] as any,
446
+ ...this.optionallyAttachAccount(options.account),
447
+ };
448
+ }
449
+ }
@@ -0,0 +1,27 @@
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
+ freshManagedBudget,
8
+ } from '../../test/helpers';
9
+ import { budgetFromAddress } from './Budget';
10
+ import { ManagedBudget } from './ManagedBudget';
11
+
12
+ let fixtures: Fixtures;
13
+
14
+ beforeAll(async () => {
15
+ fixtures = await loadFixture(deployFixtures);
16
+ });
17
+
18
+ describe('Budget', () => {
19
+ test('can automatically instantiate ManagedBudget given an address', async () => {
20
+ const budget = await loadFixture(
21
+ freshManagedBudget(defaultOptions, fixtures),
22
+ );
23
+ expect(
24
+ await budgetFromAddress(defaultOptions, budget.assertValidAddress()),
25
+ ).toBeInstanceOf(ManagedBudget);
26
+ });
27
+ });
@@ -0,0 +1,61 @@
1
+ import { aBudgetAbi } from '@boostxyz/evm';
2
+ import { AManagedBudget } from '@boostxyz/evm/deploys/componentInterfaces.json';
3
+ import { readContract } from '@wagmi/core';
4
+ import type { Address, Hex } from 'viem';
5
+ import type { DeployableOptions } from '../Deployable/Deployable';
6
+ import { InvalidComponentInterfaceError } from '../errors';
7
+ import { ManagedBudget } from './ManagedBudget';
8
+
9
+ export {
10
+ // SimpleBudget,
11
+ // VestingBudget,
12
+ ManagedBudget,
13
+ };
14
+
15
+ /**
16
+ * A union type representing all valid protocol Budget implementations
17
+ *
18
+ * @export
19
+ * @typedef {Budget}
20
+ */
21
+ export type Budget = ManagedBudget; // | SimpleBudget | VestingBudget
22
+
23
+ /**
24
+ * A map of Budget component interfaces to their constructors.
25
+ *
26
+ * @type {{ "0xa0109882": typeof ManagedBudget; }}
27
+ */
28
+ export const BudgetByComponentInterface = {
29
+ // ['0x64683da1']: VestingBudget,
30
+ // ['0x2929d19c']: SimpleBudget,
31
+ [AManagedBudget as Hex]: ManagedBudget,
32
+ };
33
+
34
+ /**
35
+ * A function that will read a contract's component interface using `getComponentInterface` and return the correct instantiated instance.
36
+ *
37
+ * @export
38
+ * @async
39
+ * @param {DeployableOptions} options
40
+ * @param {Address} address
41
+ * @returns {Promise<ManagedBudget>}
42
+ * @throws {@link InvalidComponentInterfaceError}
43
+ */
44
+ export async function budgetFromAddress(
45
+ options: DeployableOptions,
46
+ address: Address,
47
+ ) {
48
+ const interfaceId = (await readContract(options.config, {
49
+ abi: aBudgetAbi,
50
+ functionName: 'getComponentInterface',
51
+ address,
52
+ })) as keyof typeof BudgetByComponentInterface;
53
+ const Ctor = BudgetByComponentInterface[interfaceId];
54
+ if (!Ctor) {
55
+ throw new InvalidComponentInterfaceError(
56
+ Object.keys(BudgetByComponentInterface) as Hex[],
57
+ interfaceId as Hex,
58
+ );
59
+ }
60
+ return new Ctor(options, address);
61
+ }
@@ -0,0 +1,154 @@
1
+ import { writeMockErc1155SetApprovalForAll } from '@boostxyz/evm';
2
+ import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
3
+ import { isAddress, parseEther, zeroAddress } from 'viem';
4
+ import { beforeAll, beforeEach, describe, expect, test } from 'vitest';
5
+ import type { MockERC20 } from '../../test/MockERC20';
6
+ import type { MockERC1155 } from '../../test/MockERC1155';
7
+ import {
8
+ type Fixtures,
9
+ defaultOptions,
10
+ deployFixtures,
11
+ freshBudget,
12
+ freshManagedBudget,
13
+ fundErc20,
14
+ fundErc1155,
15
+ fundManagedBudget,
16
+ } from '../../test/helpers';
17
+ import { testAccount } from '../../test/viem';
18
+ import { ManagedBudget } from './ManagedBudget';
19
+
20
+ let fixtures: Fixtures,
21
+ budget: ManagedBudget,
22
+ erc20: MockERC20,
23
+ erc1155: MockERC1155;
24
+
25
+ beforeAll(async () => {
26
+ fixtures = await loadFixture(deployFixtures);
27
+ });
28
+
29
+ describe('ManagedBudget', () => {
30
+ test('can successfully be deployed', async () => {
31
+ const action = new ManagedBudget(defaultOptions, {
32
+ owner: testAccount.address,
33
+ authorized: [],
34
+ roles: [],
35
+ });
36
+ await action.deploy();
37
+ expect(isAddress(action.assertValidAddress())).toBe(true);
38
+ });
39
+
40
+ test('can be owned', async () => {
41
+ const budget = await loadFixture(freshBudget(defaultOptions, fixtures));
42
+ expect(await budget.owner()).toBe(defaultOptions.account.address);
43
+ });
44
+
45
+ test('can have authorized users', async () => {
46
+ const budget = await loadFixture(freshBudget(defaultOptions, fixtures));
47
+ expect(await budget.isAuthorized(defaultOptions.account.address)).toBe(
48
+ true,
49
+ );
50
+ expect(await budget.isAuthorized(zeroAddress)).toBe(false);
51
+ });
52
+
53
+ test('can have no initial balance', async () => {
54
+ const budget = await loadFixture(freshBudget(defaultOptions, fixtures));
55
+ expect(await budget.available(zeroAddress)).toBe(0n);
56
+ });
57
+
58
+ describe('can allocate', () => {
59
+ beforeEach(async () => {
60
+ budget = await loadFixture(freshManagedBudget(defaultOptions, fixtures));
61
+ erc20 = await loadFixture(fundErc20(defaultOptions));
62
+ erc1155 = await loadFixture(fundErc1155(defaultOptions));
63
+ });
64
+
65
+ test('native assets', async () => {
66
+ await budget.allocate(
67
+ {
68
+ amount: parseEther('1.0'),
69
+ asset: zeroAddress,
70
+ target: defaultOptions.account.address,
71
+ },
72
+ {
73
+ value: parseEther('1.0'),
74
+ },
75
+ );
76
+ expect(await budget.available(zeroAddress)).toBe(parseEther('1.0'));
77
+ });
78
+
79
+ test('erc20', async () => {
80
+ await erc20.approve(budget.assertValidAddress(), parseEther('100'));
81
+ await budget.allocate({
82
+ amount: parseEther('100'),
83
+ asset: erc20.assertValidAddress(),
84
+ target: defaultOptions.account.address,
85
+ });
86
+ expect(await budget.available(erc20.assertValidAddress())).toBe(
87
+ parseEther('100'),
88
+ );
89
+ });
90
+
91
+ test('erc1155', async () => {
92
+ await writeMockErc1155SetApprovalForAll(defaultOptions.config, {
93
+ args: [budget.assertValidAddress(), true],
94
+ address: erc1155.assertValidAddress(),
95
+ account: defaultOptions.account,
96
+ });
97
+ await budget.allocate({
98
+ tokenId: 1n,
99
+ amount: 100n,
100
+ asset: erc1155.assertValidAddress(),
101
+ target: defaultOptions.account.address,
102
+ });
103
+ expect(await budget.available(erc1155.assertValidAddress(), 1n)).toBe(
104
+ 100n,
105
+ );
106
+ });
107
+ });
108
+
109
+ describe('can disburse', () => {
110
+ beforeEach(async () => {
111
+ const budgetFixtures = await loadFixture(
112
+ fundManagedBudget(defaultOptions, fixtures),
113
+ );
114
+ budget = budgetFixtures.budget as ManagedBudget;
115
+ erc20 = budgetFixtures.erc20;
116
+ erc1155 = budgetFixtures.erc1155;
117
+ });
118
+
119
+ test('native assets', async () => {
120
+ await budget.disburse({
121
+ amount: parseEther('1.0'),
122
+ asset: zeroAddress,
123
+ target: defaultOptions.account.address,
124
+ });
125
+
126
+ expect(await budget.available(zeroAddress)).toBe(0n);
127
+ });
128
+
129
+ test('erc20 assets', async () => {
130
+ await budget.disburse({
131
+ amount: parseEther('10'),
132
+ asset: erc20.assertValidAddress(),
133
+ target: defaultOptions.account.address,
134
+ });
135
+
136
+ expect(await budget.available(erc20.assertValidAddress())).toBe(
137
+ parseEther('90'),
138
+ );
139
+ });
140
+
141
+ test('erc1155 assets', async () => {
142
+ await budget.disburse({
143
+ tokenId: 1n,
144
+ amount: 5n,
145
+ asset: erc1155.assertValidAddress(),
146
+ target: defaultOptions.account.address,
147
+ });
148
+
149
+ expect(await budget.available(erc1155.assertValidAddress(), 1n)).to.equal(
150
+ 95n,
151
+ );
152
+ });
153
+ });
154
+ });