Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions snapshots/AllowancePositionManager.Operations.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
{
"approveWithdraw": "46795",
"approveWithdrawWithSig": "63006",
"borrowOnBehalfOf": "307050",
"borrowOnBehalfOf (with temporary allowance)": "247328",
"delegateCredit": "46784",
"delegateCreditWithSig": "63081",
"renounceCreditDelegation": "24937",
"renounceWithdrawAllowance": "24837",
"temporaryApproveWithdraw": "22680",
"temporaryDelegateCredit": "22768",
"withdrawOnBehalfOf: full": "122006",
"withdrawOnBehalfOf: full (with temporary allowance)": "56184",
"withdrawOnBehalfOf: partial": "132307",
"withdrawOnBehalfOf: partial (with temporary allowance)": "66984"
"approveWithdraw": "49895",
"approveWithdrawWithSig": "66560",
"borrowOnBehalfOf": "310092",
"borrowOnBehalfOf (with temporary allowance)": "247979",
"delegateCredit": "49864",
"delegateCreditWithSig": "66505",
"renounceCreditDelegation": "28020",
"renounceWithdrawAllowance": "28007",
"temporaryApproveWithdraw": "25552",
"temporaryDelegateCredit": "25528",
"withdrawOnBehalfOf: full": "121837",
"withdrawOnBehalfOf: full (with temporary allowance)": "56154",
"withdrawOnBehalfOf: partial": "132096",
"withdrawOnBehalfOf: partial (with temporary allowance)": "66954"
}
175 changes: 99 additions & 76 deletions src/position-manager/AllowancePositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ contract AllowancePositionManager is
using MathUtils for uint256;
using SlotDerivation for bytes32;
using TransientSlot for *;
using EIP712Hash for *;

/// @notice Slot for the temporary withdraw allowances.
/// @dev keccak256(abi.encode(uint256(keccak256("aave.transient.WITHDRAW_ALLOWANCES")) - 1)) & ~bytes32(uint256(0xff))
Expand Down Expand Up @@ -87,13 +88,27 @@ contract AllowancePositionManager is
}

/// @inheritdoc IAllowancePositionManager
function temporaryApproveWithdraw(address spender, uint256 reserveId, uint256 amount) external {
_temporaryWithdrawAllowancesSlot({owner: msg.sender, spender: spender, reserveId: reserveId})
.tstore(amount);
function temporaryApproveWithdraw(
address spoke,
uint256 reserveId,
address spender,
uint256 amount
) external onlyRegisteredSpoke(spoke) {
_temporaryWithdrawAllowancesSlot({
spoke: spoke,
reserveId: reserveId,
owner: msg.sender,
spender: spender
}).tstore(amount);
}

/// @inheritdoc IAllowancePositionManager
function delegateCredit(address spender, uint256 reserveId, uint256 amount) external {
function delegateCredit(
address spoke,
uint256 reserveId,
address spender,
uint256 amount
) external onlyRegisteredSpoke(spoke) {
_updateCreditDelegation({
spoke: spoke,
reserveId: reserveId,
Expand Down Expand Up @@ -126,14 +141,27 @@ contract AllowancePositionManager is
}

/// @inheritdoc IAllowancePositionManager
function temporaryDelegateCredit(address spender, uint256 reserveId, uint256 amount) external {
_temporaryDelegateCreditsSlot({owner: msg.sender, spender: spender, reserveId: reserveId})
.tstore(amount);
function temporaryDelegateCredit(
address spoke,
uint256 reserveId,
address spender,
uint256 amount
) external onlyRegisteredSpoke(spoke) {
_temporaryDelegateCreditsSlot({
spoke: spoke,
reserveId: reserveId,
owner: msg.sender,
spender: spender
}).tstore(amount);
}

/// @inheritdoc IAllowancePositionManager
function renounceWithdrawAllowance(address owner, uint256 reserveId) external {
if (_withdrawAllowances[owner][msg.sender][reserveId] == 0) {
function renounceWithdrawAllowance(
address spoke,
uint256 reserveId,
address owner
) external onlyRegisteredSpoke(spoke) {
if (_withdrawAllowances[spoke][reserveId][owner][msg.sender] == 0) {
return;
}
_updateWithdrawAllowance({
Expand Down Expand Up @@ -250,59 +278,6 @@ contract AllowancePositionManager is
return EIP712Hash.CREDIT_DELEGATION_TYPEHASH;
}

/// @dev Temporary allowance takes precedence over stored allowance, and does not cumulate.
function _spendWithdrawAllowance(
address owner,
address spender,
uint256 reserveId,
uint256 amount
) internal {
uint256 temporaryAllowance = _temporaryWithdrawAllowancesSlot({
owner: owner,
spender: spender,
reserveId: reserveId
}).tload();
if (temporaryAllowance > 0) {
require(
temporaryAllowance >= amount,
InsufficientTemporaryWithdrawAllowance(temporaryAllowance, amount)
);
_temporaryWithdrawAllowancesSlot({owner: owner, spender: spender, reserveId: reserveId})
.tstore(temporaryAllowance.uncheckedSub(amount));
} else {
uint256 allowance = _withdrawAllowances[owner][spender][reserveId];
require(allowance >= amount, InsufficientWithdrawAllowance(allowance, amount));
_withdrawAllowances[owner][spender][reserveId] = allowance.uncheckedSub(amount);
}
}

/// @dev Temporary allowance takes precedence over stored allowance, and does not cumulate.
function _spendCreditDelegation(
address owner,
address spender,
uint256 reserveId,
uint256 amount
) internal {
uint256 temporaryAllowance = _temporaryDelegateCreditsSlot({
owner: owner,
spender: spender,
reserveId: reserveId
}).tload();
if (temporaryAllowance > 0) {
require(
temporaryAllowance >= amount,
InsufficientTemporaryCreditDelegation(temporaryAllowance, amount)
);
_temporaryDelegateCreditsSlot({owner: owner, spender: spender, reserveId: reserveId}).tstore(
temporaryAllowance.uncheckedSub(amount)
);
} else {
uint256 allowance = _creditDelegations[owner][spender][reserveId];
require(allowance >= amount, InsufficientCreditDelegation(allowance, amount));
_creditDelegations[owner][spender][reserveId] = allowance.uncheckedSub(amount);
}
}

function _updateWithdrawAllowance(
address spoke,
uint256 reserveId,
Expand All @@ -325,57 +300,105 @@ contract AllowancePositionManager is
emit CreditDelegation(spoke, reserveId, owner, spender, newCreditDelegation);
}

/// @dev Temporary allowance takes precedence over stored allowance, and does not cumulate.
function _spendWithdrawAllowance(
address spoke,
uint256 reserveId,
address owner,
address spender,
uint256 amount
) internal {
uint256 currentAllowance = _withdrawAllowances[spoke][reserveId][owner][spender];
require(currentAllowance >= amount, InsufficientWithdrawAllowance(currentAllowance, amount));
if (currentAllowance != type(uint256).max) {
_withdrawAllowances[spoke][reserveId][owner][spender] = currentAllowance.uncheckedSub(amount);
uint256 temporaryAllowance = _temporaryWithdrawAllowancesSlot({
spoke: spoke,
reserveId: reserveId,
owner: owner,
spender: spender
}).tload();
if (temporaryAllowance > 0) {
require(
temporaryAllowance >= amount,
InsufficientTemporaryWithdrawAllowance(temporaryAllowance, amount)
);
if (temporaryAllowance != type(uint256).max) {
_temporaryWithdrawAllowancesSlot({
spoke: spoke,
reserveId: reserveId,
owner: owner,
spender: spender
}).tstore(temporaryAllowance.uncheckedSub(amount));
}
} else {
uint256 allowance = _withdrawAllowances[spoke][reserveId][owner][spender];
require(allowance >= amount, InsufficientWithdrawAllowance(allowance, amount));
if (allowance != type(uint256).max) {
_withdrawAllowances[spoke][reserveId][owner][spender] = allowance.uncheckedSub(amount);
}
}
}

/// @dev Temporary allowance takes precedence over stored allowance, and does not cumulate.
function _spendCreditDelegation(
address spoke,
uint256 reserveId,
address owner,
address spender,
uint256 amount
) internal {
uint256 currentAllowance = _creditDelegations[spoke][reserveId][owner][spender];
require(currentAllowance >= amount, InsufficientCreditDelegation(currentAllowance, amount));
if (currentAllowance != type(uint256).max) {
_creditDelegations[spoke][reserveId][owner][spender] = currentAllowance.uncheckedSub(amount);
uint256 temporaryAllowance = _temporaryDelegateCreditsSlot({
spoke: spoke,
reserveId: reserveId,
owner: owner,
spender: spender
}).tload();
if (temporaryAllowance > 0) {
require(
temporaryAllowance >= amount,
InsufficientTemporaryCreditDelegation(temporaryAllowance, amount)
);
if (temporaryAllowance != type(uint256).max) {
_temporaryDelegateCreditsSlot({
spoke: spoke,
reserveId: reserveId,
owner: owner,
spender: spender
}).tstore(temporaryAllowance.uncheckedSub(amount));
}
} else {
uint256 allowance = _creditDelegations[spoke][reserveId][owner][spender];
require(allowance >= amount, InsufficientCreditDelegation(allowance, amount));
if (allowance != type(uint256).max) {
_creditDelegations[spoke][reserveId][owner][spender] = allowance.uncheckedSub(amount);
}
}
}

function _temporaryWithdrawAllowancesSlot(
address spoke,
uint256 reserveId,
address owner,
address spender,
uint256 reserveId
address spender
) internal pure returns (TransientSlot.Uint256Slot) {
return
_TEMPORARY_WITHDRAW_ALLOWANCES_SLOT
.deriveMapping(spoke)
.deriveMapping(reserveId)
.deriveMapping(owner)
.deriveMapping(spender)
.deriveMapping(reserveId)
.asUint256();
}

function _temporaryDelegateCreditsSlot(
address spoke,
uint256 reserveId,
address owner,
address spender,
uint256 reserveId
address spender
) internal pure returns (TransientSlot.Uint256Slot) {
return
_TEMPORARY_CREDIT_DELEGATIONS_SLOT
.deriveMapping(spoke)
.deriveMapping(reserveId)
.deriveMapping(owner)
.deriveMapping(spender)
.deriveMapping(reserveId)
.asUint256();
}

Expand Down
20 changes: 16 additions & 4 deletions src/position-manager/interfaces/IAllowancePositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,16 @@ interface IAllowancePositionManager is IPositionManagerBase {
/// @notice Temporarily approves a spender to withdraw assets from the specified reserve on the spoke.
/// @dev Temporary allowance takes precedence over stored allowance, and does not cumulate.
/// @dev The allowance is discarded after the transaction.
/// @param spender The address of the spender to receive the allowance.
/// @param spoke The address of the spoke.
/// @param reserveId The identifier of the reserve.
/// @param spender The address of the spender to receive the allowance.
/// @param amount The amount of allowance.
function temporaryApproveWithdraw(address spender, uint256 reserveId, uint256 amount) external;
function temporaryApproveWithdraw(
address spoke,
uint256 reserveId,
address spender,
uint256 amount
) external;

/// @notice Approves a credit delegation allowance for a spender.
/// @param spoke The address of the spoke.
Expand All @@ -97,10 +103,16 @@ interface IAllowancePositionManager is IPositionManagerBase {
/// @notice Temporarily approves a credit delegation allowance for a spender.
/// @dev Temporary allowance takes precedence over stored allowance, and does not cumulate.
/// @dev The allowance is discarded after the transaction.
/// @param spender The address of the spender to receive the allowance.
/// @param spoke The address of the spoke.
/// @param reserveId The identifier of the reserve.
/// @param spender The address of the spender to receive the allowance.
/// @param amount The amount of allowance.
function temporaryDelegateCredit(address spender, uint256 reserveId, uint256 amount) external;
function temporaryDelegateCredit(
address spoke,
uint256 reserveId,
address spender,
uint256 amount
) external;

/// @notice Renounces the withdraw allowance given by the owner.
/// @param spoke The address of the spoke.
Expand Down
31 changes: 23 additions & 8 deletions tests/gas/PositionManagers.Operations.gas.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -147,20 +147,30 @@ contract AllowancePositionManager_Gas_Tests is SpokeBase {
uint256 amount = 100e18;

vm.prank(alice);
positionManager.temporaryApproveWithdraw(bob, _daiReserveId(spoke1), UINT256_MAX);
positionManager.temporaryApproveWithdraw(
address(spoke1),
_daiReserveId(spoke1),
bob,
UINT256_MAX
);

Utils.supply(spoke1, _daiReserveId(spoke1), alice, mintAmount_DAI, alice);
Utils.withdraw(spoke1, _daiReserveId(spoke1), alice, amount, alice);

vm.prank(bob);
positionManager.withdrawOnBehalfOf(_daiReserveId(spoke1), amount, alice);
positionManager.withdrawOnBehalfOf(address(spoke1), _daiReserveId(spoke1), amount, alice);
vm.snapshotGasLastCall(NAMESPACE, 'withdrawOnBehalfOf: partial (with temporary allowance)');

vm.prank(alice);
positionManager.temporaryApproveWithdraw(bob, _daiReserveId(spoke1), UINT256_MAX);
positionManager.temporaryApproveWithdraw(
address(spoke1),
_daiReserveId(spoke1),
bob,
UINT256_MAX
);

vm.prank(bob);
positionManager.withdrawOnBehalfOf(_daiReserveId(spoke1), UINT256_MAX, alice);
positionManager.withdrawOnBehalfOf(address(spoke1), _daiReserveId(spoke1), UINT256_MAX, alice);
vm.snapshotGasLastCall(NAMESPACE, 'withdrawOnBehalfOf: full (with temporary allowance)');
}

Expand All @@ -187,13 +197,18 @@ contract AllowancePositionManager_Gas_Tests is SpokeBase {
uint256 borrowAmount = 750e18;

vm.prank(alice);
positionManager.temporaryDelegateCredit(bob, _daiReserveId(spoke1), borrowAmount);
positionManager.temporaryDelegateCredit(
address(spoke1),
_daiReserveId(spoke1),
bob,
borrowAmount
);

Utils.supplyCollateral(spoke1, _daiReserveId(spoke1), alice, aliceSupplyAmount, alice);
Utils.supplyCollateral(spoke1, _daiReserveId(spoke1), bob, bobSupplyAmount, bob);

vm.prank(bob);
positionManager.borrowOnBehalfOf(_daiReserveId(spoke1), borrowAmount, alice);
positionManager.borrowOnBehalfOf(address(spoke1), _daiReserveId(spoke1), borrowAmount, alice);
vm.snapshotGasLastCall(NAMESPACE, 'borrowOnBehalfOf (with temporary allowance)');
}

Expand Down Expand Up @@ -233,7 +248,7 @@ contract AllowancePositionManager_Gas_Tests is SpokeBase {
uint256 amount = 100e18;

vm.prank(alice);
positionManager.temporaryApproveWithdraw(bob, _daiReserveId(spoke1), amount);
positionManager.temporaryApproveWithdraw(address(spoke1), _daiReserveId(spoke1), bob, amount);
vm.snapshotGasLastCall(NAMESPACE, 'temporaryApproveWithdraw');
}

Expand Down Expand Up @@ -284,7 +299,7 @@ contract AllowancePositionManager_Gas_Tests is SpokeBase {
uint256 amount = 100e18;

vm.prank(alice);
positionManager.temporaryDelegateCredit(bob, _daiReserveId(spoke1), amount);
positionManager.temporaryDelegateCredit(address(spoke1), _daiReserveId(spoke1), bob, amount);
vm.snapshotGasLastCall(NAMESPACE, 'temporaryDelegateCredit');
}

Expand Down
Loading
Loading