@@ -303,6 +303,167 @@ describe('ERC20Token — runtimeAllowance with constraints', () => {
303303// ERC20Token — write
304304// ---------------------------------------------------------------------------
305305
306+ // ---------------------------------------------------------------------------
307+ // ERC20Token — check
308+ // ---------------------------------------------------------------------------
309+
310+ describe ( 'ERC20Token — check' , ( ) => {
311+ const usdc = ERC20Token ( publicClient , USDC_ADDRESS ) ;
312+
313+ it ( 'check(balanceOf) returns a ComposableCall with a functionSig' , ( ) => {
314+ const call = usdc . check ( {
315+ functionName : 'balanceOf' ,
316+ args : [ UNISWAP_V3_ROUTER ] ,
317+ constraints : [ { gte : 0n } ] ,
318+ } ) ;
319+ expect ( typeof call . functionSig ) . toBe ( 'string' ) ;
320+ expect ( call . functionSig . length ) . toBeGreaterThan ( 0 ) ;
321+ } ) ;
322+
323+ it ( 'check(balanceOf) encodes the correct function selector' , ( ) => {
324+ const call = usdc . check ( {
325+ functionName : 'balanceOf' ,
326+ args : [ UNISWAP_V3_ROUTER ] ,
327+ constraints : [ { gte : 0n } ] ,
328+ } ) ;
329+ // keccak256("balanceOf(address)") first 4 bytes = 0x70a08231
330+ expect ( call . functionSig ) . toBe ( '0x70a08231' ) ;
331+ } ) ;
332+
333+ it ( 'check(allowance) encodes the correct function selector' , ( ) => {
334+ const call = usdc . check ( {
335+ functionName : 'allowance' ,
336+ args : [ UNISWAP_V3_ROUTER , WETH_ADDRESS ] ,
337+ constraints : [ { gte : 0n } ] ,
338+ } ) ;
339+ // keccak256("allowance(address,address)") first 4 bytes = 0xdd62ed3e
340+ expect ( call . functionSig ) . toBe ( '0xdd62ed3e' ) ;
341+ } ) ;
342+
343+ it ( 'check(balanceOf) and check(allowance) produce different functionSigs' , ( ) => {
344+ const a = usdc . check ( {
345+ functionName : 'balanceOf' ,
346+ args : [ UNISWAP_V3_ROUTER ] ,
347+ constraints : [ { gte : 0n } ] ,
348+ } ) ;
349+ const b = usdc . check ( {
350+ functionName : 'allowance' ,
351+ args : [ UNISWAP_V3_ROUTER , WETH_ADDRESS ] ,
352+ constraints : [ { gte : 0n } ] ,
353+ } ) ;
354+ expect ( a . functionSig ) . not . toBe ( b . functionSig ) ;
355+ } ) ;
356+
357+ it ( 'check(balanceOf) outputParams is empty' , ( ) => {
358+ const call = usdc . check ( {
359+ functionName : 'balanceOf' ,
360+ args : [ UNISWAP_V3_ROUTER ] ,
361+ constraints : [ { gte : 0n } ] ,
362+ } ) ;
363+ expect ( call . outputParams ) . toHaveLength ( 0 ) ;
364+ } ) ;
365+
366+ it ( 'check(balanceOf) inputParams contains a STATIC_CALL param' , ( ) => {
367+ const call = usdc . check ( {
368+ functionName : 'balanceOf' ,
369+ args : [ UNISWAP_V3_ROUTER ] ,
370+ constraints : [ { gte : 0n } ] ,
371+ } ) ;
372+ const staticCallParam = call . inputParams . find (
373+ ( p ) => p . fetcherType === InputParamFetcherType . STATIC_CALL ,
374+ ) ;
375+ expect ( staticCallParam ) . toBeDefined ( ) ;
376+ } ) ;
377+
378+ it ( 'one constraint is applied to the STATIC_CALL param' , ( ) => {
379+ const call = usdc . check ( {
380+ functionName : 'balanceOf' ,
381+ args : [ UNISWAP_V3_ROUTER ] ,
382+ constraints : [ { gte : 1_000n } ] ,
383+ } ) ;
384+ const staticCallParam = call . inputParams . find (
385+ ( p ) => p . fetcherType === InputParamFetcherType . STATIC_CALL ,
386+ ) ;
387+ expect ( staticCallParam ?. constraints ) . toHaveLength ( 1 ) ;
388+ } ) ;
389+
390+ it ( 'multiple constraints are all applied to the STATIC_CALL param' , ( ) => {
391+ const call = usdc . check ( {
392+ functionName : 'balanceOf' ,
393+ args : [ UNISWAP_V3_ROUTER ] ,
394+ constraints : [ { gte : 1_000n } , { lte : 1_000_000n } ] ,
395+ } ) ;
396+ const staticCallParam = call . inputParams . find (
397+ ( p ) => p . fetcherType === InputParamFetcherType . STATIC_CALL ,
398+ ) ;
399+ expect ( staticCallParam ?. constraints ) . toHaveLength ( 2 ) ;
400+ } ) ;
401+
402+ it ( 'constraints do not affect paramData of the STATIC_CALL param' , ( ) => {
403+ const a = usdc . check ( {
404+ functionName : 'balanceOf' ,
405+ args : [ UNISWAP_V3_ROUTER ] ,
406+ constraints : [ { gte : 999n } ] ,
407+ } ) ;
408+ const b = usdc . check ( {
409+ functionName : 'balanceOf' ,
410+ args : [ UNISWAP_V3_ROUTER ] ,
411+ constraints : [ { lte : 1n } ] ,
412+ } ) ;
413+ const staticA = a . inputParams . find ( ( p ) => p . fetcherType === InputParamFetcherType . STATIC_CALL ) ;
414+ const staticB = b . inputParams . find ( ( p ) => p . fetcherType === InputParamFetcherType . STATIC_CALL ) ;
415+ expect ( staticA ?. paramData ) . toBe ( staticB ?. paramData ) ;
416+ } ) ;
417+
418+ it ( 'check(balanceOf) produces different paramData for different addresses' , ( ) => {
419+ const a = usdc . check ( {
420+ functionName : 'balanceOf' ,
421+ args : [ UNISWAP_V3_ROUTER ] ,
422+ constraints : [ { gte : 0n } ] ,
423+ } ) ;
424+ const b = usdc . check ( {
425+ functionName : 'balanceOf' ,
426+ args : [ WETH_ADDRESS ] ,
427+ constraints : [ { gte : 0n } ] ,
428+ } ) ;
429+ const staticA = a . inputParams . find ( ( p ) => p . fetcherType === InputParamFetcherType . STATIC_CALL ) ;
430+ const staticB = b . inputParams . find ( ( p ) => p . fetcherType === InputParamFetcherType . STATIC_CALL ) ;
431+ expect ( staticA ?. paramData ) . not . toBe ( staticB ?. paramData ) ;
432+ } ) ;
433+
434+ it ( 'check(balanceOf) is deterministic for the same args' , ( ) => {
435+ const a = usdc . check ( {
436+ functionName : 'balanceOf' ,
437+ args : [ UNISWAP_V3_ROUTER ] ,
438+ constraints : [ { gte : 0n } ] ,
439+ } ) ;
440+ const b = usdc . check ( {
441+ functionName : 'balanceOf' ,
442+ args : [ UNISWAP_V3_ROUTER ] ,
443+ constraints : [ { gte : 0n } ] ,
444+ } ) ;
445+ expect ( a . functionSig ) . toBe ( b . functionSig ) ;
446+ expect ( JSON . stringify ( a . inputParams ) ) . toBe ( JSON . stringify ( b . inputParams ) ) ;
447+ } ) ;
448+
449+ it ( 'check on WETH produces different paramData than check on USDC for same owner' , ( ) => {
450+ const weth = ERC20Token ( publicClient , WETH_ADDRESS ) ;
451+ const a = usdc . check ( {
452+ functionName : 'balanceOf' ,
453+ args : [ UNISWAP_V3_ROUTER ] ,
454+ constraints : [ { gte : 0n } ] ,
455+ } ) ;
456+ const b = weth . check ( {
457+ functionName : 'balanceOf' ,
458+ args : [ UNISWAP_V3_ROUTER ] ,
459+ constraints : [ { gte : 0n } ] ,
460+ } ) ;
461+ const staticA = a . inputParams . find ( ( p ) => p . fetcherType === InputParamFetcherType . STATIC_CALL ) ;
462+ const staticB = b . inputParams . find ( ( p ) => p . fetcherType === InputParamFetcherType . STATIC_CALL ) ;
463+ expect ( staticA ?. paramData ) . not . toBe ( staticB ?. paramData ) ;
464+ } ) ;
465+ } ) ;
466+
306467describe ( 'ERC20Token — write' , ( ) => {
307468 const usdc = ERC20Token ( publicClient , USDC_ADDRESS ) ;
308469
0 commit comments