additional tests added and extra mocks added
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
		
							parent
							
								
									1f3dd1be96
								
							
						
					
					
						commit
						c3e0d54be4
					
				
							
								
								
									
										57
									
								
								src/mock/SigUtils.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/mock/SigUtils.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,57 @@ | ||||
| // SPDX-License-Identifier: MIT | ||||
| pragma solidity =0.8.20; | ||||
| 
 | ||||
| contract SigUtils { | ||||
|     bytes32 internal DOMAIN_SEPARATOR; | ||||
| 
 | ||||
|     constructor(bytes32 _DOMAIN_SEPARATOR) { | ||||
|         DOMAIN_SEPARATOR = _DOMAIN_SEPARATOR; | ||||
|     } | ||||
| 
 | ||||
|     // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); | ||||
|     bytes32 public constant PERMIT_TYPEHASH = | ||||
|         0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; | ||||
| 
 | ||||
|     struct Permit { | ||||
|         address owner; | ||||
|         address spender; | ||||
|         uint256 value; | ||||
|         uint256 nonce; | ||||
|         uint256 deadline; | ||||
|     } | ||||
| 
 | ||||
|     // computes the hash of a permit | ||||
|     function getStructHash(Permit memory _permit) | ||||
|         internal | ||||
|         pure | ||||
|         returns (bytes32) | ||||
|     { | ||||
|         return | ||||
|             keccak256( | ||||
|                 abi.encode( | ||||
|                     PERMIT_TYPEHASH, | ||||
|                     _permit.owner, | ||||
|                     _permit.spender, | ||||
|                     _permit.value, | ||||
|                     _permit.nonce, | ||||
|                     _permit.deadline | ||||
|                 ) | ||||
|             ); | ||||
|     } | ||||
| 
 | ||||
|     // computes the hash of the fully encoded EIP-712 message for the domain, which can be used to recover the signer | ||||
|     function getTypedDataHash(Permit memory _permit) | ||||
|         public | ||||
|         view | ||||
|         returns (bytes32) | ||||
|     { | ||||
|         return | ||||
|             keccak256( | ||||
|                 abi.encodePacked( | ||||
|                     "\x19\x01", | ||||
|                     DOMAIN_SEPARATOR, | ||||
|                     getStructHash(_permit) | ||||
|                 ) | ||||
|             ); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										79
									
								
								test/UniswapV2ERC20.t.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								test/UniswapV2ERC20.t.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | ||||
| // SPDX-License-Identifier: MIT | ||||
| pragma solidity =0.8.20; | ||||
| 
 | ||||
| import {Test} from "forge-std/Test.sol"; | ||||
| 
 | ||||
| import "../src/UniswapV2ERC20.sol"; | ||||
| 
 | ||||
| import "./tokens/Permit.t.sol"; | ||||
| import "./tokens/Allowance.t.sol"; | ||||
| import "./tokens/Transfer.t.sol"; | ||||
| 
 | ||||
| contract UniswapV2ERC20Mintable is UniswapV2ERC20 { | ||||
|     function mint(address to, uint256 value) public { | ||||
|         _mint(to, value); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| contract UniswapV2ERC20Test is Test, ERC20PermitTest, ERC20AllowanceTest, ERC20TransferTest { | ||||
|     UniswapV2ERC20Mintable uni; | ||||
| 
 | ||||
|     address constant alice     = 0x0000000000000000000000000000000000000001; | ||||
|     address constant bob       = 0x0000000000000000000000000000000000000002; | ||||
|     uint256 constant amount    = 1e23; | ||||
|     uint256 constant maxAmount = type(uint256).max; | ||||
| 
 | ||||
|     function setUp() public { | ||||
|         uni = new UniswapV2ERC20Mintable(); | ||||
|         initializePermit(address(uni), amount, maxAmount); | ||||
|         initializeAllowance(alice, bob, address(uni), amount, maxAmount, amount); | ||||
|         initializeTransfer(alice, bob, address(uni), amount, 0); | ||||
|     } | ||||
| 
 | ||||
|     function testNameIsCorrect() public view { | ||||
|         assertEq(uni.name(), "Uniswap V2"); | ||||
|     } | ||||
| 
 | ||||
|     function testSymbolIsCorrect() public view { | ||||
|         assertEq(uni.symbol(), "UNI-V2"); | ||||
|     } | ||||
| 
 | ||||
|     function testNumberOfDecimalslIsCorrect() public view { | ||||
|         assertEq(uni.decimals(), 18); | ||||
|     } | ||||
| 
 | ||||
|     function testInitialSupplyIsZero() public view { | ||||
|         assertEq(uni.totalSupply(), 0); | ||||
|     } | ||||
| 
 | ||||
|     function testDomainSeparatorIsCorrect() public view { | ||||
|         assertEq(uni.DOMAIN_SEPARATOR(), keccak256( | ||||
|             abi.encode( | ||||
|                 keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), | ||||
|                 keccak256(bytes(uni.name())), | ||||
|                 keccak256(bytes("1")), | ||||
|                 block.chainid, | ||||
|                 address(uni) | ||||
|             ) | ||||
|         )); | ||||
|     } | ||||
| 
 | ||||
|     function testPermitTypeHashIsCorrect() public view { | ||||
|         assertEq( | ||||
|             uni.PERMIT_TYPEHASH(), | ||||
|             keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)") | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     function _mintTransferTokens(address who, uint256 value) internal override { | ||||
|         uni.mint(who, value); | ||||
|     } | ||||
| 
 | ||||
|     function _mintAllowanceTokens(address who, uint256 value) internal override { | ||||
|         uni.mint(who, value); | ||||
|     } | ||||
| 
 | ||||
|     function _mintPermitTokens(address who, uint256 value) internal override { | ||||
|         uni.mint(who, value); | ||||
|     } | ||||
| } | ||||
| @ -38,6 +38,19 @@ contract UniswapV2FactoryTest is Test { | ||||
|         assertEq(UniswapV2Pair(tokenPair).token1(), _token1); | ||||
|     } | ||||
| 
 | ||||
|     function testCreatePairReverse() public { | ||||
|         address tokenPair = factory.createPair(address(token1), address(token0)); | ||||
|         (address _token0, address _token1) = UniswapV2Library.sortTokens( | ||||
|             address(token0), | ||||
|             address(token1) | ||||
|         ); | ||||
| 
 | ||||
|         assertEq(factory.allPairsLength(), 1); | ||||
|         assertEq(factory.getPair(address(token0), address(token1)), tokenPair); | ||||
|         assertEq(UniswapV2Pair(tokenPair).token0(), _token0); | ||||
|         assertEq(UniswapV2Pair(tokenPair).token1(), _token1); | ||||
|     } | ||||
| 
 | ||||
|     function testCreatePairMultipleTokens() public { | ||||
|         address tokenPair0 = factory.createPair(address(token0), address(token1)); | ||||
|         address tokenPair1 = factory.createPair(address(token2), address(token3)); | ||||
|  | ||||
| @ -45,7 +45,6 @@ contract TestUniswapV2Pair is Test { | ||||
|         factory = new UniswapV2Factory(address(0)); | ||||
|         address tokenAddress = factory.createPair(address(token0), address(token1)); | ||||
|         pair = UniswapV2Pair(tokenAddress); | ||||
|         // pair.initialize(address(token0), address(token1)); | ||||
| 
 | ||||
|         token0.mint(address(this), 10 ether); | ||||
|         token1.mint(address(this), 10 ether); | ||||
|  | ||||
| @ -22,6 +22,9 @@ contract TestUniswapV2Router is Test { | ||||
| 
 | ||||
|     WETH9 public weth; | ||||
| 
 | ||||
|     fallback() external payable {} | ||||
|     receive() external payable {} | ||||
| 
 | ||||
|     function setUp() public { | ||||
|         weth = new WETH9(); | ||||
|         factory = new UniswapV2Factory(address(0)); | ||||
| @ -34,6 +37,34 @@ contract TestUniswapV2Router is Test { | ||||
|         token1.mint(address(this), 10 ether); | ||||
|     } | ||||
| 
 | ||||
|     function testAddLiquidityEthPairFor() public { | ||||
|         token0.approve(address(router), 10 ether); | ||||
| 
 | ||||
|         (address _token0, address _token1) = UniswapV2Library.sortTokens( | ||||
|             address(token0), | ||||
|             address(weth) | ||||
|         ); | ||||
|         address pair = UniswapV2Library.pairFor( | ||||
|             address(factory), | ||||
|             _token0, | ||||
|             _token1 | ||||
|         ); | ||||
| 
 | ||||
|         (, , uint256 liquidity) = router.addLiquidityETH{value: 1 ether}( | ||||
|             address(token0), | ||||
|             1 ether, | ||||
|             1 ether, | ||||
|             1 ether, | ||||
|             address(this), | ||||
|             block.timestamp + 1 | ||||
|         ); | ||||
| 
 | ||||
|         assertEq(liquidity, 1 ether - UniswapV2Pair(pair).MINIMUM_LIQUIDITY()); | ||||
|         assertEq(factory.getPair(address(token0), address(weth)), pair); | ||||
|         assertEq(weth.balanceOf(address(pair)), 1 ether); | ||||
|         assertEq(token0.balanceOf(address(pair)), 1 ether); | ||||
|     } | ||||
| 
 | ||||
|     function testAddLiquidityPairFor() public { | ||||
|         token0.approve(address(router), 10 ether); | ||||
|         token1.approve(address(router), 10 ether); | ||||
| @ -252,6 +283,44 @@ contract TestUniswapV2Router is Test { | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     function testRemoveLiquidityEth() public { | ||||
|         token0.approve(address(router), 10 ether); | ||||
|         weth.approve(address(router), 10 ether); | ||||
| 
 | ||||
|         (uint256 amount0, uint256 amount1, uint256 liquidity) = router.addLiquidityETH{value: 1 ether}( | ||||
|             address(token0), | ||||
|             1 ether, | ||||
|             1 ether, | ||||
|             1 ether, | ||||
|             address(this), | ||||
|             block.timestamp + 1 | ||||
|         ); | ||||
| 
 | ||||
|         uint256 prevAmount0 = token0.balanceOf(address(this)); | ||||
|         uint256 prevAmount1 = address(this).balance; | ||||
| 
 | ||||
|         address pair = factory.getPair(address(token0), address(weth)); | ||||
|         assertEq(IERC20(pair).balanceOf(address(this)), liquidity); | ||||
|         IERC20(pair).approve(address(router), liquidity); | ||||
| 
 | ||||
|         router.removeLiquidityETH( | ||||
|             address(token0), | ||||
|             liquidity, | ||||
|             0, | ||||
|             0, | ||||
|             address(this), | ||||
|             block.timestamp + 1 | ||||
|         ); | ||||
|         UniswapV2Pair(pair).skim(address(this)); | ||||
| 
 | ||||
|         assertEq(IERC20(pair).balanceOf(address(this)), 0); | ||||
|         assertEq(token0.balanceOf(address(this)), prevAmount0 + amount0 - UniswapV2Pair(pair).MINIMUM_LIQUIDITY()); | ||||
|         assertEq(address(this).balance, prevAmount1 + amount1 - UniswapV2Pair(pair).MINIMUM_LIQUIDITY()); | ||||
| 
 | ||||
|         assertEq(token0.balanceOf(pair), UniswapV2Pair(pair).MINIMUM_LIQUIDITY()); | ||||
|         assertEq(weth.balanceOf(address(pair)), UniswapV2Pair(pair).MINIMUM_LIQUIDITY()); | ||||
|     } | ||||
| 
 | ||||
|     function testRemoveLiquidity() public { | ||||
|         token0.approve(address(router), 1 ether); | ||||
|         token1.approve(address(router), 1 ether); | ||||
|  | ||||
							
								
								
									
										80
									
								
								test/tokens/Allowance.t.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								test/tokens/Allowance.t.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,80 @@ | ||||
| // SPDX-License-Identifier: MIT | ||||
| pragma solidity =0.8.20; | ||||
| 
 | ||||
| import {Test} from "forge-std/Test.sol"; | ||||
| import "@openzeppelin-contracts/token/ERC20/ERC20.sol"; | ||||
| 
 | ||||
| abstract contract ERC20AllowanceTest is Test { | ||||
|     ERC20 tokenAllowance; | ||||
|     uint256 amountAllowance; | ||||
|     uint256 maxAmountAllowance; | ||||
|     uint256 maxRealAmountAllowance; | ||||
| 
 | ||||
|     address aliceAllowance; | ||||
|     address bobAllowance; | ||||
| 
 | ||||
|     function initializeAllowance( | ||||
|         address alice, | ||||
|         address bob, | ||||
|         address token, | ||||
|         uint256 amount, | ||||
|         uint256 maxAmount, | ||||
|         uint256 maxRealAmount | ||||
|     ) public { | ||||
|         tokenAllowance = ERC20(token); | ||||
|         amountAllowance = amount; | ||||
|         maxAmountAllowance = maxAmount; | ||||
|         maxRealAmountAllowance = maxRealAmount; | ||||
|         aliceAllowance = alice; | ||||
|         bobAllowance = bob; | ||||
|     } | ||||
| 
 | ||||
|     function test_allowance_couldApproveFunds() public { | ||||
|         _mintAllowanceTokens(aliceAllowance, amountAllowance); | ||||
|         assertEq(tokenAllowance.allowance(aliceAllowance, bobAllowance), 0); | ||||
|         vm.prank(aliceAllowance); | ||||
|         tokenAllowance.approve(bobAllowance, amountAllowance); | ||||
|         assertEq(tokenAllowance.allowance(aliceAllowance, bobAllowance), amountAllowance); | ||||
|     } | ||||
| 
 | ||||
|     function test_allowance_transferFromDecreaseAllowance() public { | ||||
|         _mintAllowanceTokens(aliceAllowance, amountAllowance); | ||||
|         vm.prank(aliceAllowance); | ||||
|         tokenAllowance.approve(bobAllowance, amountAllowance); | ||||
|         assertEq(tokenAllowance.balanceOf(aliceAllowance), amountAllowance); | ||||
|         assertEq(tokenAllowance.balanceOf(bobAllowance), 0); | ||||
|         assertEq(tokenAllowance.allowance(aliceAllowance, bobAllowance), amountAllowance); | ||||
|         vm.prank(bobAllowance); | ||||
|         bool success = tokenAllowance.transferFrom(aliceAllowance, bobAllowance, amountAllowance); | ||||
|         assertEq(success, true); | ||||
|         assertEq(tokenAllowance.balanceOf(aliceAllowance), 0); | ||||
|         assertEq(tokenAllowance.balanceOf(bobAllowance), amountAllowance); | ||||
|         assertEq(tokenAllowance.allowance(aliceAllowance, bobAllowance), 0); | ||||
|     } | ||||
| 
 | ||||
|     function test_allowance_couldNotTransferFromIfNotEnoughFunds() public { | ||||
|         _mintAllowanceTokens(aliceAllowance, amountAllowance); | ||||
|         vm.prank(aliceAllowance); | ||||
|         tokenAllowance.approve(bobAllowance, maxAmountAllowance); | ||||
|         assertEq(tokenAllowance.balanceOf(aliceAllowance), amountAllowance); | ||||
|         assertEq(tokenAllowance.balanceOf(bobAllowance), 0); | ||||
|         assertEq(tokenAllowance.allowance(aliceAllowance, bobAllowance), maxAmountAllowance); | ||||
|         vm.expectRevert(); | ||||
|         vm.prank(bobAllowance); | ||||
|         tokenAllowance.transferFrom(aliceAllowance, bobAllowance, maxAmountAllowance); | ||||
|     } | ||||
| 
 | ||||
|     function test_allowance_couldNotTransferFromIfNotEnoughAllowance() public { | ||||
|         _mintAllowanceTokens(aliceAllowance, maxRealAmountAllowance); | ||||
|         vm.prank(aliceAllowance); | ||||
|         tokenAllowance.approve(bobAllowance, amountAllowance); | ||||
|         assertEq(tokenAllowance.balanceOf(aliceAllowance), maxRealAmountAllowance); | ||||
|         assertEq(tokenAllowance.balanceOf(bobAllowance), 0); | ||||
|         assertEq(tokenAllowance.allowance(aliceAllowance, bobAllowance), amountAllowance); | ||||
|         vm.expectRevert(); | ||||
|         vm.prank(bobAllowance); | ||||
|         tokenAllowance.transferFrom(aliceAllowance, bobAllowance, maxAmountAllowance); | ||||
|     } | ||||
| 
 | ||||
|     function _mintAllowanceTokens(address who, uint256 value) internal virtual; | ||||
| } | ||||
							
								
								
									
										306
									
								
								test/tokens/Permit.t.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										306
									
								
								test/tokens/Permit.t.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,306 @@ | ||||
| // SPDX-License-Identifier: MIT | ||||
| pragma solidity =0.8.20; | ||||
| 
 | ||||
| import {Test} from "forge-std/Test.sol"; | ||||
| import "../../src/mock/SigUtils.sol"; | ||||
| import "@openzeppelin-contracts/token/ERC20/extensions/ERC20Permit.sol"; | ||||
| 
 | ||||
| abstract contract ERC20PermitTest is Test { | ||||
|     SigUtils sigUtils; | ||||
|     ERC20Permit permitToken; | ||||
| 
 | ||||
|     address owner; | ||||
|     address spender; | ||||
|     uint256 permitAmount; | ||||
|     uint256 maxPermitAmount; | ||||
| 
 | ||||
|     uint256 constant ownerPrivateKey   = 0xA11CE; | ||||
|     uint256 constant spenderPrivateKey = 0xB0B; | ||||
| 
 | ||||
|     function initializePermit( | ||||
|         address token, | ||||
|         uint256 amount, | ||||
|         uint256 maxAmount | ||||
|     ) public { | ||||
|         permitAmount = amount; | ||||
|         maxPermitAmount = maxAmount; | ||||
|         permitToken = ERC20Permit(token); | ||||
|         sigUtils = new SigUtils(permitToken.DOMAIN_SEPARATOR()); | ||||
| 
 | ||||
|         owner = vm.addr(ownerPrivateKey); | ||||
|         spender = vm.addr(spenderPrivateKey); | ||||
|     } | ||||
| 
 | ||||
|     function test_permit_initialNonceIsZero() public view { | ||||
|         assertEq(permitToken.nonces(owner), 0); | ||||
|         assertEq(permitToken.nonces(spender), 0); | ||||
|     } | ||||
| 
 | ||||
|     function test_permit_acceptsOwnerSignature() public { | ||||
|         _mintPermitTokens(owner, permitAmount); | ||||
|         SigUtils.Permit memory permit = SigUtils.Permit({ | ||||
|             owner: owner, | ||||
|             spender: spender, | ||||
|             value: permitAmount, | ||||
|             nonce: 0, | ||||
|             deadline: 1 days | ||||
|         }); | ||||
| 
 | ||||
|         bytes32 digest = sigUtils.getTypedDataHash(permit); | ||||
| 
 | ||||
|         (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest); | ||||
| 
 | ||||
|         permitToken.permit( | ||||
|             permit.owner, | ||||
|             permit.spender, | ||||
|             permit.value, | ||||
|             permit.deadline, | ||||
|             v, | ||||
|             r, | ||||
|             s | ||||
|         ); | ||||
| 
 | ||||
|         assertEq(permitToken.allowance(owner, spender), permitAmount); | ||||
|         assertEq(permitToken.nonces(owner), 1); | ||||
|     } | ||||
| 
 | ||||
|     function test_permit_expiredPermit() public { | ||||
|         _mintPermitTokens(owner, permitAmount); | ||||
|         SigUtils.Permit memory permit = SigUtils.Permit({ | ||||
|             owner: owner, | ||||
|             spender: spender, | ||||
|             value: permitAmount, | ||||
|             nonce: permitToken.nonces(owner), | ||||
|             deadline: 1 days | ||||
|         }); | ||||
| 
 | ||||
|         bytes32 digest = sigUtils.getTypedDataHash(permit); | ||||
| 
 | ||||
|         (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest); | ||||
| 
 | ||||
|         vm.warp(1 days + 1 seconds); // fast forward one second past the deadline | ||||
| 
 | ||||
|         vm.expectRevert(); | ||||
|         permitToken.permit( | ||||
|             permit.owner, | ||||
|             permit.spender, | ||||
|             permit.value, | ||||
|             permit.deadline, | ||||
|             v, | ||||
|             r, | ||||
|             s | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     function test_permit_invalidSigner() public { | ||||
|         _mintPermitTokens(owner, permitAmount); | ||||
|         SigUtils.Permit memory permit = SigUtils.Permit({ | ||||
|             owner: owner, | ||||
|             spender: spender, | ||||
|             value: permitAmount, | ||||
|             nonce: permitToken.nonces(owner), | ||||
|             deadline: 1 days | ||||
|         }); | ||||
| 
 | ||||
|         bytes32 digest = sigUtils.getTypedDataHash(permit); | ||||
| 
 | ||||
|         (uint8 v, bytes32 r, bytes32 s) = vm.sign(spenderPrivateKey, digest); // spender signs owner's approval | ||||
| 
 | ||||
|         vm.expectRevert(); | ||||
|         permitToken.permit( | ||||
|             permit.owner, | ||||
|             permit.spender, | ||||
|             permit.value, | ||||
|             permit.deadline, | ||||
|             v, | ||||
|             r, | ||||
|             s | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     function test_permit_invalidNonce() public { | ||||
|         _mintPermitTokens(owner, permitAmount); | ||||
|         SigUtils.Permit memory permit = SigUtils.Permit({ | ||||
|             owner: owner, | ||||
|             spender: spender, | ||||
|             value: permitAmount, | ||||
|             nonce: 1, // owner nonce stored on-chain is 0 | ||||
|             deadline: 1 days | ||||
|         }); | ||||
| 
 | ||||
|         bytes32 digest = sigUtils.getTypedDataHash(permit); | ||||
| 
 | ||||
|         (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest); | ||||
| 
 | ||||
|         vm.expectRevert(); | ||||
|         permitToken.permit( | ||||
|             permit.owner, | ||||
|             permit.spender, | ||||
|             permit.value, | ||||
|             permit.deadline, | ||||
|             v, | ||||
|             r, | ||||
|             s | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     function test_permit_signatureReplay() public { | ||||
|         _mintPermitTokens(owner, permitAmount); | ||||
|         SigUtils.Permit memory permit = SigUtils.Permit({ | ||||
|             owner: owner, | ||||
|             spender: spender, | ||||
|             value: permitAmount, | ||||
|             nonce: 0, | ||||
|             deadline: 1 days | ||||
|         }); | ||||
| 
 | ||||
|         bytes32 digest = sigUtils.getTypedDataHash(permit); | ||||
| 
 | ||||
|         (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest); | ||||
| 
 | ||||
|         permitToken.permit( | ||||
|             permit.owner, | ||||
|             permit.spender, | ||||
|             permit.value, | ||||
|             permit.deadline, | ||||
|             v, | ||||
|             r, | ||||
|             s | ||||
|         ); | ||||
| 
 | ||||
|         vm.expectRevert(); | ||||
|         permitToken.permit( | ||||
|             permit.owner, | ||||
|             permit.spender, | ||||
|             permit.value, | ||||
|             permit.deadline, | ||||
|             v, | ||||
|             r, | ||||
|             s | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     function test_permit_transferFromLimitedPermit() public { | ||||
|         _mintPermitTokens(owner, permitAmount); | ||||
|         SigUtils.Permit memory permit = SigUtils.Permit({ | ||||
|             owner: owner, | ||||
|             spender: spender, | ||||
|             value: permitAmount, | ||||
|             nonce: 0, | ||||
|             deadline: 1 days | ||||
|         }); | ||||
| 
 | ||||
|         bytes32 digest = sigUtils.getTypedDataHash(permit); | ||||
| 
 | ||||
|         (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest); | ||||
| 
 | ||||
|         permitToken.permit( | ||||
|             permit.owner, | ||||
|             permit.spender, | ||||
|             permit.value, | ||||
|             permit.deadline, | ||||
|             v, | ||||
|             r, | ||||
|             s | ||||
|         ); | ||||
| 
 | ||||
|         vm.prank(spender); | ||||
|         permitToken.transferFrom(owner, spender, permitAmount); | ||||
| 
 | ||||
|         assertEq(permitToken.balanceOf(owner), 0); | ||||
|         assertEq(permitToken.balanceOf(spender), permitAmount); | ||||
|         assertEq(permitToken.allowance(owner, spender), 0); | ||||
|     } | ||||
| 
 | ||||
|     function test_permit_transferFromMaxPermit() public { | ||||
|         _mintPermitTokens(owner, permitAmount); | ||||
|         SigUtils.Permit memory permit = SigUtils.Permit({ | ||||
|             owner: owner, | ||||
|             spender: spender, | ||||
|             value: maxPermitAmount, | ||||
|             nonce: 0, | ||||
|             deadline: 1 days | ||||
|         }); | ||||
| 
 | ||||
|         bytes32 digest = sigUtils.getTypedDataHash(permit); | ||||
| 
 | ||||
|         (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest); | ||||
| 
 | ||||
|         permitToken.permit( | ||||
|             permit.owner, | ||||
|             permit.spender, | ||||
|             permit.value, | ||||
|             permit.deadline, | ||||
|             v, | ||||
|             r, | ||||
|             s | ||||
|         ); | ||||
| 
 | ||||
|         vm.prank(spender); | ||||
|         permitToken.transferFrom(owner, spender, permitAmount); | ||||
| 
 | ||||
|         assertEq(permitToken.balanceOf(owner), 0); | ||||
|         assertEq(permitToken.balanceOf(spender), permitAmount); | ||||
|         assertEq(permitToken.allowance(owner, spender), maxPermitAmount); | ||||
|     } | ||||
| 
 | ||||
|     function test_permit_invalidAllowance() public { | ||||
|         _mintPermitTokens(owner, permitAmount); | ||||
|         SigUtils.Permit memory permit = SigUtils.Permit({ | ||||
|            owner: owner, | ||||
|            spender: spender, | ||||
|            value: 1, | ||||
|            nonce: 0, | ||||
|            deadline: 1 days | ||||
|        }); | ||||
| 
 | ||||
|        bytes32 digest = sigUtils.getTypedDataHash(permit); | ||||
| 
 | ||||
|        (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest); | ||||
| 
 | ||||
|        permitToken.permit( | ||||
|            permit.owner, | ||||
|            permit.spender, | ||||
|            permit.value, | ||||
|            permit.deadline, | ||||
|            v, | ||||
|            r, | ||||
|            s | ||||
|        ); | ||||
| 
 | ||||
|        vm.expectRevert(); | ||||
|        vm.prank(spender); | ||||
|        permitToken.transferFrom(owner, spender, permitAmount); | ||||
|     } | ||||
| 
 | ||||
|     function test_permit_ivnalidBalance() public { | ||||
|         _mintPermitTokens(owner, permitAmount); | ||||
|         SigUtils.Permit memory permit = SigUtils.Permit({ | ||||
|             owner: owner, | ||||
|             spender: spender, | ||||
|             value: 2, | ||||
|             nonce: 0, | ||||
|             deadline: 1 days | ||||
|         }); | ||||
| 
 | ||||
|         bytes32 digest = sigUtils.getTypedDataHash(permit); | ||||
| 
 | ||||
|         (uint8 v, bytes32 r, bytes32 s) = vm.sign(ownerPrivateKey, digest); | ||||
| 
 | ||||
|         permitToken.permit( | ||||
|             permit.owner, | ||||
|             permit.spender, | ||||
|             permit.value, | ||||
|             permit.deadline, | ||||
|             v, | ||||
|             r, | ||||
|             s | ||||
|         ); | ||||
| 
 | ||||
|         vm.expectRevert(); | ||||
|         vm.prank(spender); | ||||
|         permitToken.transferFrom(owner, spender, permitAmount); | ||||
|     } | ||||
| 
 | ||||
|     function _mintPermitTokens(address who, uint256 permitAmount) internal virtual; | ||||
| } | ||||
							
								
								
									
										78
									
								
								test/tokens/Transfer.t.sol
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								test/tokens/Transfer.t.sol
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,78 @@ | ||||
| // SPDX-License-Identifier: MIT | ||||
| pragma solidity =0.8.20; | ||||
| 
 | ||||
| import {Test} from "forge-std/Test.sol"; | ||||
| import "@openzeppelin-contracts/token/ERC20/ERC20.sol"; | ||||
| 
 | ||||
| abstract contract ERC20TransferTest is Test { | ||||
|     ERC20 tokenTransfer; | ||||
|     uint256 amountTransfer; | ||||
|     uint256 totalSupplyTransfer; | ||||
| 
 | ||||
|     address aliceTransfer; | ||||
|     address bobTransfer; | ||||
| 
 | ||||
|     function initializeTransfer( | ||||
|         address alice, | ||||
|         address bob, | ||||
|         address token, | ||||
|         uint256 amount, | ||||
|         uint256 totalSupply | ||||
|     ) public { | ||||
|         tokenTransfer = ERC20(token); | ||||
|         amountTransfer = amount; | ||||
|         totalSupplyTransfer = totalSupply; | ||||
|         aliceTransfer = alice; | ||||
|         bobTransfer = bob; | ||||
|     } | ||||
| 
 | ||||
|     function test_transfer_tokenTransfers() public { | ||||
|         _mintTransferTokens(aliceTransfer, amountTransfer); | ||||
|         assertEq(tokenTransfer.balanceOf(aliceTransfer), amountTransfer); | ||||
|         assertEq(tokenTransfer.balanceOf(bobTransfer), 0); | ||||
|         vm.prank(aliceTransfer); | ||||
|         bool success = tokenTransfer.transfer(bobTransfer, amountTransfer); | ||||
|         assertEq(success, true); | ||||
|         assertEq(tokenTransfer.balanceOf(bobTransfer), amountTransfer); | ||||
|         assertEq(tokenTransfer.balanceOf(aliceTransfer), 0); | ||||
|     } | ||||
| 
 | ||||
|     function test_transfer_transferFuzzing(uint64 fuzzingTransferAmount) public { | ||||
|         if (totalSupplyTransfer == 0) vm.assume(fuzzingTransferAmount > 0); | ||||
|         else vm.assume(fuzzingTransferAmount > 0 && fuzzingTransferAmount < totalSupplyTransfer); | ||||
|         _mintTransferTokens(aliceTransfer, fuzzingTransferAmount); | ||||
|         assertEq(tokenTransfer.balanceOf(aliceTransfer), fuzzingTransferAmount); | ||||
|         assertEq(tokenTransfer.balanceOf(bobTransfer), 0); | ||||
|         vm.prank(aliceTransfer); | ||||
|         bool success = tokenTransfer.transfer(bobTransfer, fuzzingTransferAmount); | ||||
|         assertEq(success, true); | ||||
|         assertEq(tokenTransfer.balanceOf(bobTransfer), fuzzingTransferAmount); | ||||
|         assertEq(tokenTransfer.balanceOf(aliceTransfer), 0); | ||||
|     } | ||||
| 
 | ||||
|     function test_transfer_couldNotTransferMoreThanAvailable() public { | ||||
|         _mintTransferTokens(aliceTransfer, amountTransfer); | ||||
|         vm.expectRevert(); | ||||
|         vm.prank(aliceTransfer); | ||||
|         tokenTransfer.transfer(bobTransfer, type(uint256).max); | ||||
|     } | ||||
| 
 | ||||
|     function test_transfer_doesNotChangeTotalSupply() public { | ||||
|         assertEq(tokenTransfer.totalSupply(), totalSupplyTransfer); | ||||
|         _mintTransferTokens(aliceTransfer, amountTransfer); | ||||
|         if (totalSupplyTransfer == 0) { | ||||
|             assertEq(tokenTransfer.totalSupply(), amountTransfer); | ||||
|             vm.prank(aliceTransfer); | ||||
|             tokenTransfer.transfer(bobTransfer, amountTransfer); | ||||
|             assertEq(tokenTransfer.totalSupply(), amountTransfer); | ||||
|         } else { | ||||
|             assertEq(tokenTransfer.totalSupply(), totalSupplyTransfer); | ||||
|             vm.prank(aliceTransfer); | ||||
|             tokenTransfer.transfer(bobTransfer, amountTransfer); | ||||
|             assertEq(tokenTransfer.totalSupply(), totalSupplyTransfer); | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     function _mintTransferTokens(address who, uint256 value) internal virtual; | ||||
| } | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user