@@ -6,9 +6,9 @@ import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
66import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol " ;
77import "@openzeppelin/contracts/utils/ReentrancyGuard.sol " ;
88import "@openzeppelin/contracts/utils/Strings.sol " ;
9-
109import "./Errors.sol " ;
1110import "./RateChangeQueue.sol " ;
11+ import "./interfaces/IERC3009.sol " ;
1212
1313interface IValidator {
1414 struct ValidationResult {
@@ -91,9 +91,7 @@ contract Payments is ReentrancyGuard {
9191 event RailTerminated (uint256 indexed railId , address indexed by , uint256 endEpoch );
9292 event RailFinalized (uint256 indexed railId );
9393
94- event DepositRecorded (
95- address indexed token , address indexed from , address indexed to , uint256 amount , bool usedPermit
96- );
94+ event DepositRecorded (address indexed token , address indexed from , address indexed to , uint256 amount );
9795 event WithdrawRecorded (address indexed token , address indexed from , address indexed to , uint256 amount );
9896
9997 struct Account {
@@ -225,8 +223,8 @@ contract Payments is ReentrancyGuard {
225223 _;
226224 }
227225
228- modifier validatePermitRecipient (address to ) {
229- require (to == msg .sender , Errors.PermitRecipientMustBeMsgSender (msg .sender , to));
226+ modifier validateSignerIsRecipient (address to ) {
227+ require (to == msg .sender , Errors.SignerMustBeMsgSender (msg .sender , to));
230228 _;
231229 }
232230
@@ -365,16 +363,16 @@ contract Payments is ReentrancyGuard {
365363 uint256 rateAllowanceIncrease ,
366364 uint256 lockupAllowanceIncrease
367365 ) external nonReentrant validateNonZeroAddress (operator, "operator " ) {
368- _increaseOperatorApproval (token, operator, rateAllowanceIncrease, lockupAllowanceIncrease);
366+ _increaseOperatorApproval (IERC20 ( token) , operator, rateAllowanceIncrease, lockupAllowanceIncrease);
369367 }
370368
371369 function _increaseOperatorApproval (
372- address token ,
370+ IERC20 token ,
373371 address operator ,
374372 uint256 rateAllowanceIncrease ,
375373 uint256 lockupAllowanceIncrease
376374 ) internal {
377- OperatorApproval storage approval = operatorApprovals[token][msg .sender ][operator];
375+ OperatorApproval storage approval = operatorApprovals[address ( token) ][msg .sender ][operator];
378376
379377 // Operator must already be approved
380378 require (approval.isApproved, Errors.OperatorNotApproved (msg .sender , operator));
@@ -384,7 +382,7 @@ contract Payments is ReentrancyGuard {
384382 approval.lockupAllowance += lockupAllowanceIncrease;
385383
386384 emit OperatorApprovalUpdated (
387- token,
385+ address ( token) ,
388386 msg .sender ,
389387 operator,
390388 approval.isApproved,
@@ -475,7 +473,7 @@ contract Payments is ReentrancyGuard {
475473
476474 account.funds += actualAmount;
477475
478- emit DepositRecorded (token, msg .sender , to, actualAmount, false );
476+ emit DepositRecorded (token, msg .sender , to, actualAmount);
479477 }
480478
481479 /**
@@ -524,7 +522,7 @@ contract Payments is ReentrancyGuard {
524522
525523 account.funds += actualAmount;
526524
527- emit DepositRecorded (token, to, to, actualAmount, true );
525+ emit DepositRecorded (token, to, to, actualAmount);
528526 }
529527
530528 /**
@@ -563,7 +561,7 @@ contract Payments is ReentrancyGuard {
563561 nonReentrant
564562 validateNonZeroAddress (operator, "operator " )
565563 validateNonZeroAddress (to, "to " )
566- validatePermitRecipient (to)
564+ validateSignerIsRecipient (to)
567565 settleAccountLockupBeforeAndAfter (token, to, false )
568566 {
569567 _setOperatorApproval (token, operator, true , rateAllowance, lockupAllowance, maxLockupPeriod);
@@ -600,13 +598,161 @@ contract Payments is ReentrancyGuard {
600598 nonReentrant
601599 validateNonZeroAddress (operator, "operator " )
602600 validateNonZeroAddress (to, "to " )
603- validatePermitRecipient (to)
601+ validateSignerIsRecipient (to)
604602 settleAccountLockupBeforeAndAfter (token, to, false )
605603 {
606- _increaseOperatorApproval (token, operator, rateAllowanceIncrease, lockupAllowanceIncrease);
604+ _increaseOperatorApproval (IERC20 ( token) , operator, rateAllowanceIncrease, lockupAllowanceIncrease);
607605 _depositWithPermit (token, to, amount, deadline, v, r, s);
608606 }
609607
608+ /**
609+ * @notice Deposits tokens using an ERC-3009 authorization in a single transaction.
610+ * @param token The ERC-3009-compliant token contract.
611+ * @param to The address whose account within the contract will be credited.
612+ * @param amount The amount of tokens to deposit.
613+ * @param validAfter The timestamp after which the authorization is valid.
614+ * @param validBefore The timestamp before which the authorization is valid.
615+ * @param nonce A unique nonce for the authorization, used to prevent replay attacks.
616+ * @param v,r,s The signature of the authorization.
617+ */
618+ function depositWithAuthorization (
619+ IERC3009 token ,
620+ address to ,
621+ uint256 amount ,
622+ uint256 validAfter ,
623+ uint256 validBefore ,
624+ bytes32 nonce ,
625+ uint8 v ,
626+ bytes32 r ,
627+ bytes32 s
628+ )
629+ external
630+ nonReentrant
631+ validateNonZeroAddress (to, "to " )
632+ settleAccountLockupBeforeAndAfter (address (token), to, false )
633+ {
634+ _depositWithAuthorization (token, to, amount, validAfter, validBefore, nonce, v, r, s);
635+ }
636+
637+ /**
638+ * @notice Deposits tokens using an ERC-3009 authorization in a single transaction.
639+ * while also setting operator approval.
640+ * @param token The ERC-3009-compliant token contract.
641+ * @param to The address whose account within the contract will be credited.
642+ * @param amount The amount of tokens to deposit.
643+ * @param validAfter The timestamp after which the authorization is valid.
644+ * @param validBefore The timestamp before which the authorization is valid.
645+ * @param nonce A unique nonce for the authorization, used to prevent replay attacks.
646+ * @param v,r,s The signature of the authorization.
647+ * @param operator The address of the operator whose approval is being modified.
648+ * @param rateAllowance The maximum payment rate the operator can set across all rails created by the operator
649+ * on behalf of the message sender. If this is less than the current payment rate, the operator will
650+ * only be able to reduce rates until they fall below the target.
651+ * @param lockupAllowance The maximum amount of funds the operator can lock up on behalf of the message sender
652+ * towards future payments. If this exceeds the current total amount of funds locked towards future payments,
653+ * the operator will only be able to reduce future lockup.
654+ * @param maxLockupPeriod The maximum number of epochs (blocks) the operator can lock funds for. If this is less than
655+ * the current lockup period for a rail, the operator will only be able to reduce the lockup period.
656+ */
657+ function depositWithAuthorizationAndApproveOperator (
658+ IERC3009 token ,
659+ address to ,
660+ uint256 amount ,
661+ uint256 validAfter ,
662+ uint256 validBefore ,
663+ bytes32 nonce ,
664+ uint8 v ,
665+ bytes32 r ,
666+ bytes32 s ,
667+ address operator ,
668+ uint256 rateAllowance ,
669+ uint256 lockupAllowance ,
670+ uint256 maxLockupPeriod
671+ )
672+ external
673+ nonReentrant
674+ validateNonZeroAddress (operator, "operator " )
675+ validateNonZeroAddress (to, "to " )
676+ validateSignerIsRecipient (to)
677+ settleAccountLockupBeforeAndAfter (address (token), to, false )
678+ {
679+ _setOperatorApproval (address (token), operator, true , rateAllowance, lockupAllowance, maxLockupPeriod);
680+ _depositWithAuthorization (token, to, amount, validAfter, validBefore, nonce, v, r, s);
681+ }
682+
683+ /**
684+ * @notice Deposits tokens using an ERC-3009 authorization in a single transaction.
685+ * while also setting operator approval.
686+ * @param token The ERC-3009-compliant token contract.
687+ * @param to The address whose account within the contract will be credited.
688+ * @param amount The amount of tokens to deposit.
689+ * @param validAfter The timestamp after which the authorization is valid.
690+ * @param validBefore The timestamp before which the authorization is valid.
691+ * @param nonce A unique nonce for the authorization, used to prevent replay attacks.
692+ * @param v,r,s The signature of the authorization.
693+ * @param operator The address of the operator whose allowances are being increased.
694+ * @param rateAllowanceIncrease The amount to increase the rate allowance by.
695+ * @param lockupAllowanceIncrease The amount to increase the lockup allowance by.
696+ * @custom:constraint Operator must already be approved.
697+ */
698+ function depositWithAuthorizationAndIncreaseOperatorApproval (
699+ IERC3009 token ,
700+ address to ,
701+ uint256 amount ,
702+ uint256 validAfter ,
703+ uint256 validBefore ,
704+ bytes32 nonce ,
705+ uint8 v ,
706+ bytes32 r ,
707+ bytes32 s ,
708+ address operator ,
709+ uint256 rateAllowanceIncrease ,
710+ uint256 lockupAllowanceIncrease
711+ )
712+ external
713+ nonReentrant
714+ validateNonZeroAddress (operator, "operator " )
715+ validateNonZeroAddress (to, "to " )
716+ validateSignerIsRecipient (to)
717+ settleAccountLockupBeforeAndAfter (address (token), to, false )
718+ {
719+ _increaseOperatorApproval (token, operator, rateAllowanceIncrease, lockupAllowanceIncrease);
720+ _depositWithAuthorization (token, to, amount, validAfter, validBefore, nonce, v, r, s);
721+ }
722+
723+ function _depositWithAuthorization (
724+ IERC3009 token ,
725+ address to ,
726+ uint256 amount ,
727+ uint256 validAfter ,
728+ uint256 validBefore ,
729+ bytes32 nonce ,
730+ uint8 v ,
731+ bytes32 r ,
732+ bytes32 s
733+ ) internal {
734+ // Revert if token is address(0) as authorization is not supported for native tokens
735+ require (address (token) != address (0 ), Errors.NativeTokenNotSupported ());
736+
737+ // Use balance-before/balance-after accounting to correctly handle fee-on-transfer tokens
738+ uint256 balanceBefore = token.balanceOf (address (this ));
739+
740+ // Call ERC-3009 receiveWithAuthorization.
741+ // This will transfer 'amount' from 'to' to this contract.
742+ // The token contract itself verifies the signature.
743+ token.receiveWithAuthorization (to, address (this ), amount, validAfter, validBefore, nonce, v, r, s);
744+
745+ uint256 balanceAfter = token.balanceOf (address (this ));
746+ uint256 actualAmount = balanceAfter - balanceBefore;
747+
748+ // Credit the beneficiary's internal account
749+ Account storage account = accounts[address (token)][to];
750+ account.funds += actualAmount;
751+
752+ // Emit an event to record the deposit, marking it as made via an off-chain signature.
753+ emit DepositRecorded (address (token), to, to, actualAmount);
754+ }
755+
610756 /// @notice Withdraws tokens from the caller's account to the caller's account, up to the amount of currently available tokens (the tokens not currently locked in rails).
611757 /// @param token The ERC20 token address to withdraw.
612758 /// @param amount The amount of tokens to withdraw.
0 commit comments