@@ -4,6 +4,8 @@ pragma solidity 0.8.28;
44
55import {SignatureChecker} from 'src/dependencies/openzeppelin/SignatureChecker.sol ' ;
66import {SafeERC20, IERC20 } from 'src/dependencies/openzeppelin/SafeERC20.sol ' ;
7+ import {SlotDerivation} from 'src/dependencies/openzeppelin/SlotDerivation.sol ' ;
8+ import {TransientSlot} from 'src/dependencies/openzeppelin/TransientSlot.sol ' ;
79import {EIP712} from 'src/dependencies/solady/EIP712.sol ' ;
810import {MathUtils} from 'src/libraries/math/MathUtils.sol ' ;
911import {NoncesKeyed} from 'src/utils/NoncesKeyed.sol ' ;
@@ -24,15 +26,27 @@ contract AllowancePositionManager is
2426 using SafeERC20 for IERC20 ;
2527 using EIP712Hash for * ;
2628 using MathUtils for uint256 ;
29+ using SlotDerivation for bytes32 ;
30+ using TransientSlot for * ;
2731
2832 /// @notice Mapping of withdraw allowances.
2933 mapping (address owner = > mapping (address spender = > mapping (uint256 reserveId = > uint256 amount )))
3034 private _withdrawAllowances;
3135
36+ /// @notice Slot for the temporary withdraw allowances.
37+ /// @dev keccak256('temporary.withdrawAllowances')
38+ bytes32 private constant _TEMPORARY_WITHDRAW_ALLOWANCES_SLOT =
39+ 0x1c6a61279a13a86a789311ddf30aee38e2f4a9f6c4aad1ff4a2e75a4018e68c3 ;
40+
3241 /// @notice Mapping of credit delegations.
3342 mapping (address owner = > mapping (address spender = > mapping (uint256 reserveId = > uint256 amount )))
3443 private _creditDelegations;
3544
45+ /// @notice Slot for the temporary credit delegations.
46+ /// @dev keccak256('temporary.creditDelegations')
47+ bytes32 private constant _TEMPORARY_CREDIT_DELEGATIONS_SLOT =
48+ 0xcd470af8670f5baa744a0341af8a2e3f5d7ca086178908432a5cfaf39cb9299d ;
49+
3650 /// @dev Constructor.
3751 /// @param spoke_ The address of the spoke contract.
3852 constructor (address spoke_ ) PositionManagerBase (spoke_) {}
@@ -58,6 +72,11 @@ contract AllowancePositionManager is
5872 emit WithdrawApproval (user, params.spender, params.reserveId, params.amount);
5973 }
6074
75+ /// @inheritdoc IAllowancePositionManager
76+ function temporaryApproveWithdraw (address spender , uint256 reserveId , uint256 amount ) external {
77+ _temporaryWithdrawAllowancesSlot (msg .sender , spender, reserveId).tstore (amount);
78+ }
79+
6180 /// @inheritdoc IAllowancePositionManager
6281 function approveCreditDelegation (address spender , uint256 reserveId , uint256 amount ) external {
6382 _creditDelegations[msg .sender ][spender][reserveId] = amount;
@@ -79,6 +98,15 @@ contract AllowancePositionManager is
7998 emit CreditDelegation (user, params.spender, params.reserveId, params.amount);
8099 }
81100
101+ /// @inheritdoc IAllowancePositionManager
102+ function temporaryApproveCreditDelegation (
103+ address spender ,
104+ uint256 reserveId ,
105+ uint256 amount
106+ ) external {
107+ _temporaryCreditDelegationsSlot (msg .sender , spender, reserveId).tstore (amount);
108+ }
109+
82110 /// @inheritdoc IAllowancePositionManager
83111 function renounceWithdrawAllowance (address owner , uint256 reserveId ) external {
84112 _withdrawAllowances[owner][msg .sender ][reserveId] = 0 ;
@@ -98,9 +126,7 @@ contract AllowancePositionManager is
98126 address onBehalfOf
99127 ) external returns (uint256 , uint256 ) {
100128 require (amount > 0 , InvalidAmount ());
101- uint256 currentAllowance = _withdrawAllowances[onBehalfOf][msg .sender ][reserveId];
102- require (currentAllowance >= amount, InsufficientWithdrawAllowance (currentAllowance, amount));
103- _withdrawAllowances[onBehalfOf][msg .sender ][reserveId] = currentAllowance.uncheckedSub (amount);
129+ _spendWithdrawAllowance (onBehalfOf, msg .sender , reserveId, amount);
104130
105131 IERC20 asset = _getReserveUnderlying (reserveId);
106132 (uint256 withdrawnShares , uint256 withdrawnAmount ) = ISpokeBase (SPOKE).withdraw (
@@ -120,9 +146,7 @@ contract AllowancePositionManager is
120146 address onBehalfOf
121147 ) external returns (uint256 , uint256 ) {
122148 require (amount > 0 , InvalidAmount ());
123- uint256 currentAllowance = _creditDelegations[onBehalfOf][msg .sender ][reserveId];
124- require (currentAllowance >= amount, InsufficientCreditDelegation (currentAllowance, amount));
125- _creditDelegations[onBehalfOf][msg .sender ][reserveId] = currentAllowance.uncheckedSub (amount);
149+ _spendCreditDelegation (onBehalfOf, msg .sender , reserveId, amount);
126150
127151 IERC20 asset = _getReserveUnderlying (reserveId);
128152 (uint256 borrowedShares , uint256 borrowedAmount ) = ISpokeBase (SPOKE).borrow (
@@ -171,4 +195,78 @@ contract AllowancePositionManager is
171195 function _domainNameAndVersion () internal pure override returns (string memory , string memory ) {
172196 return ('AllowancePositionManager ' , '1 ' );
173197 }
198+
199+ /// @dev Temporary allowance takes precedence over stored allowance, and does not cumulate.
200+ function _spendWithdrawAllowance (
201+ address onBehalfOf ,
202+ address spender ,
203+ uint256 reserveId ,
204+ uint256 amount
205+ ) internal {
206+ uint256 temporaryAllowance = _temporaryWithdrawAllowancesSlot (onBehalfOf, spender, reserveId)
207+ .tload ();
208+ if (temporaryAllowance > 0 ) {
209+ require (
210+ temporaryAllowance >= amount,
211+ InsufficientWithdrawAllowance (temporaryAllowance, amount)
212+ );
213+ _temporaryWithdrawAllowancesSlot (onBehalfOf, spender, reserveId).tstore (
214+ temporaryAllowance.uncheckedSub (amount)
215+ );
216+ } else {
217+ uint256 allowance = _withdrawAllowances[onBehalfOf][spender][reserveId];
218+ require (allowance >= amount, InsufficientWithdrawAllowance (allowance, amount));
219+ _withdrawAllowances[onBehalfOf][spender][reserveId] = allowance.uncheckedSub (amount);
220+ }
221+ }
222+
223+ /// @dev Temporary allowance takes precedence over stored allowance, and does not cumulate.
224+ function _spendCreditDelegation (
225+ address onBehalfOf ,
226+ address spender ,
227+ uint256 reserveId ,
228+ uint256 amount
229+ ) internal {
230+ uint256 temporaryAllowance = _temporaryCreditDelegationsSlot (onBehalfOf, spender, reserveId)
231+ .tload ();
232+ if (temporaryAllowance > 0 ) {
233+ require (
234+ temporaryAllowance >= amount,
235+ InsufficientCreditDelegation (temporaryAllowance, amount)
236+ );
237+ _temporaryCreditDelegationsSlot (onBehalfOf, spender, reserveId).tstore (
238+ temporaryAllowance.uncheckedSub (amount)
239+ );
240+ } else {
241+ uint256 allowance = _creditDelegations[onBehalfOf][spender][reserveId];
242+ require (allowance >= amount, InsufficientCreditDelegation (allowance, amount));
243+ _creditDelegations[onBehalfOf][spender][reserveId] = allowance.uncheckedSub (amount);
244+ }
245+ }
246+
247+ function _temporaryWithdrawAllowancesSlot (
248+ address owner ,
249+ address spender ,
250+ uint256 reserveId
251+ ) internal pure returns (TransientSlot.Uint256Slot) {
252+ return
253+ _TEMPORARY_WITHDRAW_ALLOWANCES_SLOT
254+ .deriveMapping (owner)
255+ .deriveMapping (spender)
256+ .deriveMapping (reserveId)
257+ .asUint256 ();
258+ }
259+
260+ function _temporaryCreditDelegationsSlot (
261+ address owner ,
262+ address spender ,
263+ uint256 reserveId
264+ ) internal pure returns (TransientSlot.Uint256Slot) {
265+ return
266+ _TEMPORARY_CREDIT_DELEGATIONS_SLOT
267+ .deriveMapping (owner)
268+ .deriveMapping (spender)
269+ .deriveMapping (reserveId)
270+ .asUint256 ();
271+ }
174272}
0 commit comments