@@ -5,8 +5,13 @@ import {
55 FACTORY_DEFAULT_FEE_SENTINEL ,
66 buildV2CreateCookieJarArgs ,
77 getFeePercentageOnDeposit ,
8+ getAccessConfigValidationError ,
89} from "@/hooks/jar/createV2CreateArgs" ;
9- import { ETH_ADDRESS , HATS_PROTOCOL_ADDRESS , POAP_TOKEN_ADDRESS } from "@/lib/blockchain/constants" ;
10+ import {
11+ ETH_ADDRESS ,
12+ HATS_PROTOCOL_ADDRESS ,
13+ POAP_TOKEN_ADDRESS ,
14+ } from "@/lib/blockchain/constants" ;
1015
1116vi . mock ( "@/hooks/jar/schemas/jarCreationSchema" , ( ) => ( {
1217 AccessType : {
@@ -169,7 +174,8 @@ describe("buildV2CreateCookieJarArgs", () => {
169174 expect ( poapJar . accessType ) . toBe ( 1 ) ;
170175 expect ( poapAccess . nftRequirement . nftContract ) . toBe ( POAP_TOKEN_ADDRESS ) ;
171176 expect ( poapAccess . nftRequirement . tokenId ) . toBe ( 1234n ) ;
172- expect ( poapAccess . nftRequirement . minBalance ) . toBe ( 1n ) ;
177+ expect ( poapAccess . nftRequirement . minBalance ) . toBe ( 0n ) ;
178+ expect ( poapAccess . nftRequirement . isPoapEventGate ) . toBe ( true ) ;
173179
174180 const [ unlockJar ] = buildV2CreateCookieJarArgs ( {
175181 values : makeValues ( {
@@ -202,12 +208,243 @@ describe("buildV2CreateCookieJarArgs", () => {
202208 const [ hatsJar , hatsAccess ] = buildV2CreateCookieJarArgs ( {
203209 values : makeValues ( {
204210 accessType : AccessType . Hats ,
205- protocolConfig : { accessType : "Hats" , hatsId : 99 } ,
211+ protocolConfig : { accessType : "Hats" , hatsId : "99" } ,
206212 } ) ,
207213 metadata : "metadata" ,
208214 parseAmount : ( ) => 1n ,
209215 } ) ;
210216 expect ( hatsJar . accessType ) . toBe ( 2 ) ;
211217 expect ( hatsAccess . nftRequirement . nftContract ) . toBe ( HATS_PROTOCOL_ADDRESS ) ;
218+ expect ( hatsAccess . nftRequirement . isPoapEventGate ) . toBe ( false ) ;
219+ } ) ;
220+ } ) ;
221+
222+ describe ( "getAccessConfigValidationError" , ( ) => {
223+ it ( "returns an error for NFT-gated access when address list is missing" , ( ) => {
224+ const error = getAccessConfigValidationError (
225+ makeValues ( {
226+ accessType : AccessType . NFTGated ,
227+ nftAddresses : [ ] ,
228+ nftTypes : [ ] ,
229+ } ) ,
230+ ) ;
231+ expect ( error ) . toBe ( "At least one NFT address is required for NFT-gated access" ) ;
232+ } ) ;
233+
234+ it ( "returns an error for NFT-gated access when nftAddresses and nftTypes mismatch" , ( ) => {
235+ const error = getAccessConfigValidationError (
236+ makeValues ( {
237+ accessType : AccessType . NFTGated ,
238+ nftAddresses : [
239+ "0x1111111111111111111111111111111111111111" ,
240+ "0x2222222222222222222222222222222222222222" ,
241+ ] ,
242+ nftTypes : [ NFTType . ERC721 ] ,
243+ } ) ,
244+ ) ;
245+ expect ( error ) . toBe ( "NFT addresses and NFT types must have the same length" ) ;
246+ } ) ;
247+
248+ it ( "returns an error for NFT-gated access when address format is invalid" , ( ) => {
249+ const error = getAccessConfigValidationError (
250+ makeValues ( {
251+ accessType : AccessType . NFTGated ,
252+ nftAddresses : [ "not-an-address" ] ,
253+ nftTypes : [ NFTType . ERC721 ] ,
254+ } ) ,
255+ ) ;
256+ expect ( error ) . toBe ( "NFT address must be a valid Ethereum address" ) ;
257+ } ) ;
258+
259+ it ( "returns undefined for valid NFT-gated access" , ( ) => {
260+ const error = getAccessConfigValidationError (
261+ makeValues ( {
262+ accessType : AccessType . NFTGated ,
263+ nftAddresses : [ "0x1111111111111111111111111111111111111111" ] ,
264+ nftTypes : [ NFTType . ERC1155 ] ,
265+ } ) ,
266+ ) ;
267+ expect ( error ) . toBeUndefined ( ) ;
268+ } ) ;
269+
270+ it ( "returns an error for POAP access when eventId is missing" , ( ) => {
271+ const error = getAccessConfigValidationError (
272+ makeValues ( {
273+ accessType : AccessType . POAP ,
274+ protocolConfig : { accessType : "POAP" } ,
275+ } ) ,
276+ ) ;
277+ expect ( error ) . toBe ( "POAP event is required" ) ;
278+ } ) ;
279+
280+ it ( "returns an error for POAP access when eventId is non-numeric" , ( ) => {
281+ const error = getAccessConfigValidationError (
282+ makeValues ( {
283+ accessType : AccessType . POAP ,
284+ protocolConfig : { accessType : "POAP" , eventId : "abc" } ,
285+ } ) ,
286+ ) ;
287+ expect ( error ) . toBe ( "POAP event must be a valid number" ) ;
288+ } ) ;
289+
290+ it ( "returns an error for POAP access when contract fallback is invalid" , ( ) => {
291+ const error = getAccessConfigValidationError (
292+ makeValues ( {
293+ accessType : AccessType . POAP ,
294+ protocolConfig : {
295+ accessType : "POAP" ,
296+ eventId : "1234" ,
297+ poapContractAddress : "invalid-contract" ,
298+ } ,
299+ } ) ,
300+ ) ;
301+ expect ( error ) . toBe ( "POAP contract address must be a valid Ethereum address" ) ;
302+ } ) ;
303+
304+ it ( "returns undefined for valid POAP access" , ( ) => {
305+ const error = getAccessConfigValidationError (
306+ makeValues ( {
307+ accessType : AccessType . POAP ,
308+ protocolConfig : {
309+ accessType : "POAP" ,
310+ eventId : "1234" ,
311+ poapContractAddress : POAP_TOKEN_ADDRESS ,
312+ } ,
313+ } ) ,
314+ ) ;
315+ expect ( error ) . toBeUndefined ( ) ;
316+ } ) ;
317+
318+ it ( "returns an error for Unlock access when unlockAddress is missing" , ( ) => {
319+ const error = getAccessConfigValidationError (
320+ makeValues ( {
321+ accessType : AccessType . Unlock ,
322+ protocolConfig : { accessType : "Unlock" } ,
323+ } ) ,
324+ ) ;
325+ expect ( error ) . toBe ( "Unlock contract address is required" ) ;
326+ } ) ;
327+
328+ it ( "returns an error for Unlock access when unlockAddress is invalid" , ( ) => {
329+ const error = getAccessConfigValidationError (
330+ makeValues ( {
331+ accessType : AccessType . Unlock ,
332+ protocolConfig : { accessType : "Unlock" , unlockAddress : "invalid" } ,
333+ } ) ,
334+ ) ;
335+ expect ( error ) . toBe ( "Unlock contract address must be a valid Ethereum address" ) ;
336+ } ) ;
337+
338+ it ( "returns undefined for valid Unlock access" , ( ) => {
339+ const error = getAccessConfigValidationError (
340+ makeValues ( {
341+ accessType : AccessType . Unlock ,
342+ protocolConfig : {
343+ accessType : "Unlock" ,
344+ unlockAddress : "0x2222222222222222222222222222222222222222" ,
345+ } ,
346+ } ) ,
347+ ) ;
348+ expect ( error ) . toBeUndefined ( ) ;
349+ } ) ;
350+
351+ it ( "returns an error for Hypercert access when address is missing" , ( ) => {
352+ const error = getAccessConfigValidationError (
353+ makeValues ( {
354+ accessType : AccessType . Hypercert ,
355+ protocolConfig : {
356+ accessType : "Hypercert" ,
357+ hypercertTokenId : "1" ,
358+ } ,
359+ } ) ,
360+ ) ;
361+ expect ( error ) . toBe ( "Hypercert contract address is required" ) ;
362+ } ) ;
363+
364+ it ( "returns an error for Hypercert access when token ID is missing" , ( ) => {
365+ const error = getAccessConfigValidationError (
366+ makeValues ( {
367+ accessType : AccessType . Hypercert ,
368+ protocolConfig : {
369+ accessType : "Hypercert" ,
370+ hypercertAddress : "0x3333333333333333333333333333333333333333" ,
371+ } ,
372+ } ) ,
373+ ) ;
374+ expect ( error ) . toBe ( "Hypercert token ID is required" ) ;
375+ } ) ;
376+
377+ it ( "returns an error for Hypercert access when token ID is non-numeric" , ( ) => {
378+ const error = getAccessConfigValidationError (
379+ makeValues ( {
380+ accessType : AccessType . Hypercert ,
381+ protocolConfig : {
382+ accessType : "Hypercert" ,
383+ hypercertAddress : "0x3333333333333333333333333333333333333333" ,
384+ hypercertTokenId : "abc" ,
385+ } ,
386+ } ) ,
387+ ) ;
388+ expect ( error ) . toBe ( "Hypercert token ID must be a valid number" ) ;
389+ } ) ;
390+
391+ it ( "returns an error for Hypercert access when min balance is invalid" , ( ) => {
392+ const error = getAccessConfigValidationError (
393+ makeValues ( {
394+ accessType : AccessType . Hypercert ,
395+ protocolConfig : {
396+ accessType : "Hypercert" ,
397+ hypercertAddress : "0x3333333333333333333333333333333333333333" ,
398+ hypercertTokenId : "1" ,
399+ hypercertMinBalance : Number . NaN ,
400+ } ,
401+ } ) ,
402+ ) ;
403+ expect ( error ) . toBe ( "Hypercert minimum balance must be a valid number" ) ;
404+ } ) ;
405+
406+ it ( "returns undefined for valid Hypercert access" , ( ) => {
407+ const error = getAccessConfigValidationError (
408+ makeValues ( {
409+ accessType : AccessType . Hypercert ,
410+ protocolConfig : {
411+ accessType : "Hypercert" ,
412+ hypercertAddress : "0x3333333333333333333333333333333333333333" ,
413+ hypercertTokenId : "42" ,
414+ hypercertMinBalance : 1 ,
415+ } ,
416+ } ) ,
417+ ) ;
418+ expect ( error ) . toBeUndefined ( ) ;
419+ } ) ;
420+
421+ it ( "returns an error for Hats access when hatsId is missing" , ( ) => {
422+ const error = getAccessConfigValidationError (
423+ makeValues ( {
424+ accessType : AccessType . Hats ,
425+ protocolConfig : { accessType : "Hats" } ,
426+ } ) ,
427+ ) ;
428+ expect ( error ) . toBe ( "Hat ID is required" ) ;
429+ } ) ;
430+
431+ it ( "returns an error for Hats access when hatsId is non-numeric" , ( ) => {
432+ const error = getAccessConfigValidationError (
433+ makeValues ( {
434+ accessType : AccessType . Hats ,
435+ protocolConfig : { accessType : "Hats" , hatsId : "abc" } ,
436+ } ) ,
437+ ) ;
438+ expect ( error ) . toBe ( "Hat ID must be a valid number" ) ;
439+ } ) ;
440+
441+ it ( "returns undefined for valid Hats access" , ( ) => {
442+ const error = getAccessConfigValidationError (
443+ makeValues ( {
444+ accessType : AccessType . Hats ,
445+ protocolConfig : { accessType : "Hats" , hatsId : "99" } ,
446+ } ) ,
447+ ) ;
448+ expect ( error ) . toBeUndefined ( ) ;
212449 } ) ;
213450} ) ;
0 commit comments