Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.25;

interface ITemporaryApproval {
interface IERC7674 {
/**
* @dev Sets a `value` amount of tokens as the temporary allowance of `spender` over the
* caller's tokens within a single transaction.
Expand Down
10 changes: 5 additions & 5 deletions contracts/TransientToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ pragma solidity ^0.8.25;
import { ERC20 } from "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import { Math } from "openzeppelin-contracts/contracts/utils/math/Math.sol";

import { ITemporaryApproval } from "contracts/ITemporaryApproval.sol";
import { IERC7674 } from "contracts/IERC7674.sol";

/**
* @title Token with temporary approvals
* @notice Contract that implements temporary approvals for ERC20 tokens.
*/
contract TransientToken is ERC20, ITemporaryApproval {
contract TransientToken is ERC20, IERC7674 {
bytes32 private constant _TEMPORARY_ALLOWANCES_SLOT = keccak256("_temporaryAllowances");

constructor(string memory name_, string memory symbol_) ERC20(name_, symbol_) {}
Expand Down Expand Up @@ -39,15 +39,15 @@ contract TransientToken is ERC20, ITemporaryApproval {
/**
* @dev See {IERC20-allowance}.
* Temporary allowances are added to the permanent allowances.
* This value changes when {approve}, {temporaryApprove} or {transferFrom} are called.
* This value changes when {IERC20-approve}, {IERC7674-temporaryApprove} or {IERC20-transferFrom} are called.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
uint256 temporaryAllowance = _getTemporaryAllowance(owner, spender);
if (temporaryAllowance == type(uint256).max) {
return temporaryAllowance;
}
(bool success, uint256 allowed) = Math.tryAdd(temporaryAllowance, super.allowance(owner, spender));
return success ? allowed : type(uint256).max;
(bool success, uint256 amount) = Math.tryAdd(temporaryAllowance, super.allowance(owner, spender));
return success ? amount : type(uint256).max;
}

/// @dev The permanent allowance can only be spent after the temporary allowance has been exhausted.
Expand Down
12 changes: 6 additions & 6 deletions test/TransientToken.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { IERC20Errors } from "openzeppelin-contracts/contracts/interfaces/draft-

import { Test } from "forge-std/Test.sol";

import { ITemporaryApproval } from "contracts/ITemporaryApproval.sol";
import { IERC7674 } from "contracts/IERC7674.sol";

import { TransientTokenMock } from "test/mocks/TransientTokenMock.sol";

Expand Down Expand Up @@ -35,7 +35,7 @@ contract TransientTokenTest is Test {
uint256 amountToSpend = 5 ether;

vm.prank(alice);
ITemporaryApproval(transientToken).temporaryApprove(address(this), amountToApprove);
IERC7674(transientToken).temporaryApprove(address(this), amountToApprove);
IERC20(transientToken).transferFrom(alice, bob, amountToSpend);

assertEq(IERC20(transientToken).allowance(alice, address(this)), amountToApprove - amountToSpend);
Expand Down Expand Up @@ -75,7 +75,7 @@ contract TransientTokenTest is Test {

vm.startPrank(alice);
IERC20(transientToken).approve(address(this), amountToApprove);
ITemporaryApproval(transientToken).temporaryApprove(address(this), amountToApproveTemporarily);
IERC7674(transientToken).temporaryApprove(address(this), amountToApproveTemporarily);
vm.stopPrank();

assertEq(IERC20(transientToken).allowance(alice, address(this)), amountToApprove + amountToApproveTemporarily);
Expand All @@ -98,7 +98,7 @@ contract TransientTokenTest is Test {

vm.startPrank(alice);
IERC20(transientToken).approve(address(this), amountToApprove);
ITemporaryApproval(transientToken).temporaryApprove(address(this), amountToApproveTemporarily);
IERC7674(transientToken).temporaryApprove(address(this), amountToApproveTemporarily);
vm.stopPrank();

assertEq(IERC20(transientToken).allowance(alice, address(this)), amountToApprove + amountToApproveTemporarily);
Expand All @@ -118,11 +118,11 @@ contract TransientTokenTest is Test {
function testDoenNotApproveFromZero() public {
vm.expectRevert(abi.encodePacked(IERC20Errors.ERC20InvalidApprover.selector, abi.encode(address(0))));
vm.prank(address(0));
ITemporaryApproval(transientToken).temporaryApprove(address(0), 10 ether);
IERC7674(transientToken).temporaryApprove(address(0), 10 ether);
}

function testDoenNotApproveToZero() public {
vm.expectRevert(abi.encodePacked(IERC20Errors.ERC20InvalidSpender.selector, abi.encode(address(0))));
ITemporaryApproval(transientToken).temporaryApprove(address(0), 10 ether);
IERC7674(transientToken).temporaryApprove(address(0), 10 ether);
}
}