@@ -15,10 +15,46 @@ const fastForward = async (seconds) => {
1515 await ethers . provider . send ( "evm_increaseTime" , [ seconds ] ) ;
1616 await ethers . provider . send ( "evm_mine" ) ;
1717}
18+
19+ // Helper function to sign ERC20Permit data
20+ async function signPermit ( signer , token , spender , amount , deadline , nonce ) {
21+ const name = await token . name ( ) ;
22+ const chainId = ( await ethers . provider . getNetwork ( ) ) . chainId ;
23+
24+ const domain = {
25+ name : name ,
26+ version : "1" ,
27+ chainId : chainId ,
28+ verifyingContract : token . address ,
29+ } ;
30+
31+ const types = {
32+ Permit : [
33+ { name : "owner" , type : "address" } ,
34+ { name : "spender" , type : "address" } ,
35+ { name : "value" , type : "uint256" } ,
36+ { name : "nonce" , type : "uint256" } ,
37+ { name : "deadline" , type : "uint256" } ,
38+ ] ,
39+ } ;
40+
41+ const value = {
42+ owner : signer . address ,
43+ spender : spender ,
44+ value : amount ,
45+ nonce : nonce ,
46+ deadline : deadline ,
47+ } ;
48+
49+ const signature = await signer . _signTypedData ( domain , types , value ) ;
50+ return ethers . utils . splitSignature ( signature ) ;
51+ }
52+
1853// Start test block
1954describe ( 'Escrow tests' , function ( ) {
2055 let Mock20Contract ;
2156 let Mock20DecimalsContract ;
57+ let Mock20PermitContract ;
2258 let EscrowContract ;
2359 let FactoryRouter
2460 let signers ;
@@ -37,11 +73,14 @@ describe('Escrow tests', function () {
3773 const EnterpriseFeeCollector = await ethers . getContractFactory ( "EnterpriseFeeCollector" ) ;
3874 const MockErc20 = await ethers . getContractFactory ( 'MockERC20' ) ;
3975 const MockErc20Decimals = await ethers . getContractFactory ( 'MockERC20Decimals' ) ;
76+ const MockErc20Permit = await ethers . getContractFactory ( 'MockERC20Permit' ) ;
4077 const Escrow = await ethers . getContractFactory ( 'EnterpriseEscrow' ) ;
4178 Mock20Contract = await MockErc20 . deploy ( signers [ 0 ] . address , "MockERC20" , 'MockERC20' ) ;
4279 Mock20DecimalsContract = await MockErc20Decimals . deploy ( "Mock6Digits" , 'Mock6Digits' , 6 ) ;
80+ Mock20PermitContract = await MockErc20Permit . deploy ( "MockPermit" , 'MPERMIT' , 18 ) ;
4381 await Mock20Contract . deployed ( ) ;
4482 await Mock20DecimalsContract . deployed ( ) ;
83+ await Mock20PermitContract . deployed ( ) ;
4584 // DEPLOY ROUTER, SETTING OWNER
4685 FactoryRouter = await Router . deploy (
4786 signers [ 0 ] . address ,
@@ -62,8 +101,13 @@ describe('Escrow tests', function () {
62101 await Mock20DecimalsContract . transfer ( payer1 . address , ethers . utils . parseUnits ( "10000" , 6 ) )
63102 await Mock20DecimalsContract . transfer ( payer2 . address , ethers . utils . parseUnits ( "10000" , 6 ) )
64103 await Mock20DecimalsContract . transfer ( payer3 . address , ethers . utils . parseUnits ( "10000" , 6 ) )
104+ // Transfer permit tokens to payers
105+ await Mock20PermitContract . transfer ( payer1 . address , web3 . utils . toWei ( "10000" ) )
106+ await Mock20PermitContract . transfer ( payer2 . address , web3 . utils . toWei ( "10000" ) )
107+ await Mock20PermitContract . transfer ( payer3 . address , web3 . utils . toWei ( "10000" ) )
65108 await EnterpriseFeeCollectorContract . connect ( payer1 ) . updateToken ( Mock20Contract . address , 1 , 10 , ethers . utils . parseEther ( '0.01' ) , true ) ;
66109 await EnterpriseFeeCollectorContract . connect ( payer1 ) . updateToken ( Mock20DecimalsContract . address , 5 , 50 , ethers . utils . parseEther ( '0.1' ) , true ) ;
110+ await EnterpriseFeeCollectorContract . connect ( payer1 ) . updateToken ( Mock20PermitContract . address , 1 , 10 , ethers . utils . parseEther ( '0.01' ) , true ) ;
67111 } ) ;
68112
69113
@@ -337,4 +381,152 @@ it('Escrow - lock', async function () {
337381 await EscrowContract . connect ( payer1 ) . withdraw ( [ Mock20Contract . address ] , [ payer1Funds . available ] ) ;
338382 expect ( await EscrowContract . connect ( payer1 ) . getUserTokens ( payer1 . address ) ) . does . not . include ( Mock20Contract . address ) ;
339383 } ) ;
384+
385+ it ( 'Escrow - depositWithPermit' , async function ( ) {
386+ const depositAmount = web3 . utils . toWei ( "100" ) ;
387+ const block = await ethers . provider . getBlock ( "latest" ) ;
388+ const deadline = block . timestamp + 3600 ; // 1 hour from now
389+ const nonce = await Mock20PermitContract . nonces ( payer1 . address ) ;
390+
391+ // Get initial balances
392+ const contractBalanceBefore = await Mock20PermitContract . balanceOf ( EscrowContract . address ) ;
393+ const payerBalanceBefore = await Mock20PermitContract . balanceOf ( payer1 . address ) ;
394+ const fundTokensBefore = await EscrowContract . connect ( payer1 ) . getUserTokens ( payer1 . address ) ;
395+
396+ // Sign permit
397+ const { v, r, s } = await signPermit (
398+ payer1 ,
399+ Mock20PermitContract ,
400+ EscrowContract . address ,
401+ depositAmount ,
402+ deadline ,
403+ nonce
404+ ) ;
405+
406+ // Deposit with permit (no prior approval needed)
407+ const tx = await EscrowContract . connect ( payer1 ) . depositWithPermit (
408+ Mock20PermitContract . address ,
409+ depositAmount ,
410+ deadline ,
411+ v ,
412+ r ,
413+ s
414+ ) ;
415+ const txReceipt = await tx . wait ( ) ;
416+
417+ // Check balances after deposit
418+ const contractBalanceAfter = await Mock20PermitContract . balanceOf ( EscrowContract . address ) ;
419+ const payerBalanceAfter = await Mock20PermitContract . balanceOf ( payer1 . address ) ;
420+ const fundTokensAfter = await EscrowContract . connect ( payer1 ) . getUserTokens ( payer1 . address ) ;
421+
422+ // Verify balances
423+ expect ( contractBalanceAfter ) . to . equal ( contractBalanceBefore . add ( depositAmount ) ) ;
424+ expect ( payerBalanceAfter ) . to . equal ( payerBalanceBefore . sub ( depositAmount ) ) ;
425+ expect ( fundTokensAfter ) . to . include ( Mock20PermitContract . address ) ;
426+
427+ // Verify funds
428+ const funds = await EscrowContract . connect ( payer1 ) . getFunds ( Mock20PermitContract . address ) ;
429+ expect ( funds . available ) . to . equal ( depositAmount ) ;
430+ expect ( funds . locked ) . to . equal ( 0 ) ;
431+
432+ // Check event
433+ const event = getEventFromTx ( txReceipt , "Deposit" ) ;
434+ expect ( event ) . to . exist ;
435+ expect ( event . args . payer ) . to . equal ( payer1 . address ) ;
436+ expect ( event . args . token ) . to . equal ( Mock20PermitContract . address ) ;
437+ expect ( event . args . amount ) . to . equal ( depositAmount ) ;
438+
439+ // Verify allowance was consumed
440+ const allowance = await Mock20PermitContract . allowance ( payer1 . address , EscrowContract . address ) ;
441+ expect ( allowance ) . to . equal ( 0 ) ;
442+ } ) ;
443+
444+ it ( 'Escrow - depositWithPermit should revert with expired deadline' , async function ( ) {
445+ const depositAmount = web3 . utils . toWei ( "100" ) ;
446+ const block = await ethers . provider . getBlock ( "latest" ) ;
447+ const expiredDeadline = block . timestamp - 3600 ; // 1 hour ago
448+ const nonce = await Mock20PermitContract . nonces ( payer2 . address ) ;
449+
450+ const { v, r, s } = await signPermit (
451+ payer2 ,
452+ Mock20PermitContract ,
453+ EscrowContract . address ,
454+ depositAmount ,
455+ expiredDeadline ,
456+ nonce
457+ ) ;
458+
459+ await expect (
460+ EscrowContract . connect ( payer2 ) . depositWithPermit (
461+ Mock20PermitContract . address ,
462+ depositAmount ,
463+ expiredDeadline ,
464+ v ,
465+ r ,
466+ s
467+ )
468+ ) . to . be . revertedWith ( "ERC20Permit: expired deadline" ) ;
469+ } ) ;
470+
471+ it ( 'Escrow - depositWithPermit should revert with invalid signature' , async function ( ) {
472+ const depositAmount = web3 . utils . toWei ( "100" ) ;
473+ const block = await ethers . provider . getBlock ( "latest" ) ;
474+ const deadline = block . timestamp + 3600 ;
475+ const nonce = await Mock20PermitContract . nonces ( payer2 . address ) ;
476+
477+ // Sign with wrong signer (payer3 instead of payer2)
478+ const { v, r, s } = await signPermit (
479+ payer3 ,
480+ Mock20PermitContract ,
481+ EscrowContract . address ,
482+ depositAmount ,
483+ deadline ,
484+ nonce
485+ ) ;
486+
487+ await expect (
488+ EscrowContract . connect ( payer2 ) . depositWithPermit (
489+ Mock20PermitContract . address ,
490+ depositAmount ,
491+ deadline ,
492+ v ,
493+ r ,
494+ s
495+ )
496+ ) . to . be . revertedWith ( "ERC20Permit: invalid signature" ) ;
497+ } ) ;
498+
499+ it ( 'Escrow - depositWithPermit works without prior approval' , async function ( ) {
500+ const depositAmount = web3 . utils . toWei ( "50" ) ;
501+ const block = await ethers . provider . getBlock ( "latest" ) ;
502+ const deadline = block . timestamp + 3600 ;
503+ const nonce = await Mock20PermitContract . nonces ( payer3 . address ) ;
504+
505+ // Verify no allowance before
506+ const allowanceBefore = await Mock20PermitContract . allowance ( payer3 . address , EscrowContract . address ) ;
507+ expect ( allowanceBefore ) . to . equal ( 0 ) ;
508+
509+ const { v, r, s } = await signPermit (
510+ payer3 ,
511+ Mock20PermitContract ,
512+ EscrowContract . address ,
513+ depositAmount ,
514+ deadline ,
515+ nonce
516+ ) ;
517+
518+ // Deposit with permit (no prior approval needed)
519+ await EscrowContract . connect ( payer3 ) . depositWithPermit (
520+ Mock20PermitContract . address ,
521+ depositAmount ,
522+ deadline ,
523+ v ,
524+ r ,
525+ s
526+ ) ;
527+
528+ // Verify deposit succeeded
529+ const funds = await EscrowContract . connect ( payer3 ) . getFunds ( Mock20PermitContract . address ) ;
530+ expect ( funds . available ) . to . equal ( depositAmount ) ;
531+ } ) ;
340532} ) ;
0 commit comments