-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ae61a8d
commit 1614cdd
Showing
11 changed files
with
296 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
[submodule "lib/forge-std"] | ||
path = lib/forge-std | ||
url = https://github.com/foundry-rs/forge-std | ||
[submodule "lib/openzeppelin-contracts"] | ||
path = lib/openzeppelin-contracts | ||
url = https://github.com/openzeppelin/openzeppelin-contracts |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
-include .env | ||
|
||
.PHONY: all test test-zk clean deploy fund help install snapshot format anvil install deploy deploy-zk deploy-zk-sepolia deploy-sepolia verify | ||
|
||
DEFAULT_ANVIL_KEY := 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 | ||
DEFAULT_ZKSYNC_LOCAL_KEY := 0x7726827caac94a7f9e1b160f7ea819f172f7b6f9d2a97f992c38edeab82d4110 | ||
|
||
all: clean remove install update build | ||
|
||
# Clean the repo | ||
clean :; forge clean | ||
|
||
# Remove modules | ||
remove :; rm -rf .gitmodules && rm -rf .git/modules/* && rm -rf lib && touch .gitmodules && git add . && git commit -m "modules" | ||
|
||
install :; forge install cyfrin/[email protected] --no-commit && forge install foundry-rs/[email protected] --no-commit && forge install openzeppelin/[email protected] --no-commit | ||
|
||
# Update Dependencies | ||
update:; forge update | ||
|
||
build:; forge build | ||
|
||
test :; forge test | ||
|
||
test-zk :; foundryup-zksync && forge test --zksync && foundryup | ||
|
||
snapshot :; forge snapshot | ||
|
||
format :; forge fmt | ||
|
||
anvil :; anvil -m 'test test test test test test test test test test test junk' --steps-tracing --block-time 1 | ||
|
||
deploy: | ||
@forge script script/DeployOurToken.s.sol:DeployOurToken --rpc-url http://localhost:8545 --private-key $(DEFAULT_ANVIL_KEY) --broadcast | ||
|
||
|
||
deploy-sepolia: | ||
@forge script script/DeployOurToken.s.sol:DeployOurToken --rpc-url $(SEPOLIA_RPC_URL) --account $(ACCOUNT) --sender $(SENDER) --etherscan-api-key $(ETHERSCAN_API_KEY) --broadcast --verify | ||
|
||
deploy-zk: | ||
@forge script script/DeployOurToken.s.sol --rpc-url http://127.0.0.1:8011 --private-key $(DEFAULT_ZKSYNC_LOCAL_KEY) --legacy --zksync | ||
|
||
deploy-zk-sepolia: | ||
@forge script script/DeployOurToken.s.sol --rpc-url $(ZKSYNC_SEPOLIA_RPC_URL) --account $(ACCOUNT) --legacy --zksync | ||
|
||
deploy-zk-bad: | ||
@forge script script/DeployOurToken.s.sol --rpc-url https://sepolia.era.zksync.dev --private-key $(PRIVATE_KEY) --legacy --zksync | ||
|
||
verify: | ||
@forge verify-contract --chain-id 11155111 --num-of-optimizations 200 --watch --constructor-args 0x00000000000000000000000000000000000000000000d3c21bcecceda1000000 --etherscan-api-key $(ETHERSCAN_API_KEY) --compiler-version v0.8.19+commit.7dd6d404 0x089dc24123e0a27d44282a1ccc2fd815989e3300 src/OurToken.sol:OurToken |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule openzeppelin-contracts
added at
dbb610
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.18; | ||
|
||
import {Script} from "forge-std/Script.sol"; | ||
import {OurToken} from "src/OurToken.sol"; | ||
|
||
contract DeployOurToken is Script { | ||
uint256 public constant INITIAL_SUPPLY = 1000 ether; | ||
|
||
function run() external returns (OurToken) { | ||
vm.startBroadcast(); | ||
OurToken ot = new OurToken(INITIAL_SUPPLY); | ||
vm.stopBroadcast(); | ||
return ot; | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.18; | ||
|
||
contract ManualToken { | ||
mapping(address => uint256) private s_balances; | ||
|
||
function name() public pure returns (string memory) { | ||
return "ManualToken"; | ||
} | ||
|
||
// string public name = "ManualToken"; //Another way of doing the same thing | ||
function totalSupply() public pure returns (uint256) { | ||
return 100 ether; | ||
} | ||
|
||
function decimals() public pure returns (uint8) { | ||
return 18; | ||
} | ||
|
||
function balanceOf(address _owner) public view returns (uint256) { | ||
return s_balances[_owner]; | ||
} | ||
|
||
function transfer(address _to, uint256 _amount) public { | ||
uint256 previousBalances = balanceOf(msg.sender) + balanceOf(_to); | ||
s_balances[msg.sender] -= _amount; | ||
s_balances[_to] += _amount; | ||
require(balanceOf(msg.sender) + balanceOf(_to) == previousBalances); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
//SPDX-License-Identifier:MIT | ||
|
||
pragma solidity ^0.8.18; | ||
|
||
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ||
|
||
contract OurToken is ERC20 { | ||
constructor(uint256 initialSupply) ERC20("OurToken", "OT") { | ||
_mint(msg.sender, initialSupply); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.18; | ||
|
||
import {DeployOurToken} from "../script/DeployOurToken.s.sol"; | ||
import {OurToken} from "../src/OurToken.sol"; | ||
import {Test, console} from "forge-std/Test.sol"; | ||
import {StdCheats} from "forge-std/StdCheats.sol"; | ||
|
||
contract OurTokenTest is StdCheats, Test { | ||
OurToken public ourToken; | ||
DeployOurToken public deployer; | ||
|
||
address public user1 = address(0x1); | ||
address public user2 = address(0x2); | ||
|
||
function setUp() public { | ||
deployer = new DeployOurToken(); | ||
ourToken = deployer.run(); | ||
vm.deal(user1, 1 ether); // Fund user1 with ETH for gas | ||
vm.deal(user2, 1 ether); // Fund user2 with ETH for gas | ||
} | ||
|
||
// Test the initial supply is set correctly | ||
function testInitialSupply() public view { | ||
assertEq(ourToken.totalSupply(), deployer.INITIAL_SUPPLY()); | ||
} | ||
|
||
// Test that users cannot mint new tokens | ||
function testUsersCantMint() public { | ||
vm.expectRevert("function selector was not recognized"); | ||
// Attempt to call a non-existent mint function on the contract | ||
(bool success, ) = address(ourToken).call( | ||
abi.encodeWithSignature("mint(address,uint256)", address(this), 1) | ||
); | ||
assertFalse(success); | ||
} | ||
|
||
// Test transferring tokens between accounts | ||
function testTransferTokens() public { | ||
uint256 transferAmount = 1000 ether; | ||
|
||
// Mint some tokens to user1 for testing | ||
deal(address(ourToken), user1, transferAmount); | ||
|
||
// Ensure user1 balance is updated | ||
assertEq(ourToken.balanceOf(user1), transferAmount); | ||
|
||
// Transfer tokens from user1 to user2 | ||
vm.prank(user1); | ||
ourToken.transfer(user2, transferAmount); | ||
|
||
// Check final balances | ||
assertEq(ourToken.balanceOf(user1), 0); | ||
assertEq(ourToken.balanceOf(user2), transferAmount); | ||
} | ||
|
||
// Test transfers revert when the sender has insufficient balance | ||
function testTransferRevertsOnInsufficientBalance() public { | ||
vm.prank(user1); | ||
vm.expectRevert(); | ||
ourToken.transfer(user2, 1 ether); | ||
} | ||
|
||
// Test allowances: approve and transferFrom | ||
function testAllowanceWorks() public { | ||
uint256 approveAmount = 500 ether; | ||
|
||
// Approve user2 to spend tokens on behalf of user1 | ||
vm.prank(user1); | ||
ourToken.approve(user2, approveAmount); | ||
|
||
// Check allowance is set | ||
assertEq(ourToken.allowance(user1, user2), approveAmount); | ||
|
||
// Mint tokens to user1 for testing | ||
deal(address(ourToken), user1, approveAmount); | ||
|
||
// Perform transferFrom using allowance | ||
vm.prank(user2); | ||
ourToken.transferFrom(user1, user2, approveAmount); | ||
|
||
// Check balances and allowance after transfer | ||
assertEq(ourToken.balanceOf(user1), 0); | ||
assertEq(ourToken.balanceOf(user2), approveAmount); | ||
assertEq(ourToken.allowance(user1, user2), 0); | ||
} | ||
|
||
// Test that transferFrom reverts when the spender tries to exceed allowance | ||
function testTransferFromRevertsOnExceedingAllowance() public { | ||
uint256 approveAmount = 500 ether; | ||
|
||
// Approve user2 to spend tokens on behalf of user1 | ||
vm.prank(user1); | ||
ourToken.approve(user2, approveAmount); | ||
|
||
// Attempt transfer exceeding allowance | ||
vm.prank(user2); | ||
vm.expectRevert(); | ||
ourToken.transferFrom(user1, user2, approveAmount + 1 ether); | ||
} | ||
|
||
// Test that approve overwrites previous allowance | ||
function testApproveOverwritesPreviousAllowance() public { | ||
uint256 firstApproveAmount = 500 ether; | ||
uint256 secondApproveAmount = 300 ether; | ||
|
||
// Approve user2 with the first amount | ||
vm.prank(user1); | ||
ourToken.approve(user2, firstApproveAmount); | ||
assertEq(ourToken.allowance(user1, user2), firstApproveAmount); | ||
|
||
// Overwrite allowance with a second approval | ||
vm.prank(user1); | ||
ourToken.approve(user2, secondApproveAmount); | ||
assertEq(ourToken.allowance(user1, user2), secondApproveAmount); | ||
} | ||
|
||
function testBurnTokens() public { | ||
uint256 initialBalance = 1000 ether; | ||
uint256 burnAmount = 500 ether; | ||
|
||
// Mint tokens to user1 for testing | ||
deal(address(ourToken), user1, initialBalance); | ||
|
||
// Attempt to burn tokens by transferring to the zero address | ||
vm.prank(user1); | ||
vm.expectRevert( | ||
abi.encodeWithSignature("ERC20InvalidReceiver(address)", address(0)) | ||
); | ||
ourToken.transfer(address(0), burnAmount); | ||
|
||
// Verify that the balance and total supply remain unchanged | ||
assertEq(ourToken.balanceOf(user1), initialBalance); | ||
assertEq(ourToken.totalSupply(), deployer.INITIAL_SUPPLY()); | ||
} | ||
|
||
// Test approve and transferFrom when allowance is partially used | ||
function testPartialAllowanceUsage() public { | ||
uint256 approveAmount = 1000 ether; | ||
uint256 transferAmount = 500 ether; | ||
|
||
// Approve user2 to spend tokens on behalf of user1 | ||
vm.prank(user1); | ||
ourToken.approve(user2, approveAmount); | ||
|
||
// Mint tokens to user1 | ||
deal(address(ourToken), user1, approveAmount); | ||
|
||
// Use part of the allowance | ||
vm.prank(user2); | ||
ourToken.transferFrom(user1, user2, transferAmount); | ||
|
||
// Check remaining allowance | ||
assertEq( | ||
ourToken.allowance(user1, user2), | ||
approveAmount - transferAmount | ||
); | ||
assertEq(ourToken.balanceOf(user1), approveAmount - transferAmount); | ||
assertEq(ourToken.balanceOf(user2), transferAmount); | ||
} | ||
|
||
function testTransferToZeroAddressReverts() public { | ||
uint256 transferAmount = 100 ether; | ||
|
||
// Mint tokens to user1 for testing | ||
deal(address(ourToken), user1, transferAmount); | ||
|
||
// Expect the transfer to zero address to revert with the custom error | ||
vm.prank(user1); | ||
vm.expectRevert( | ||
abi.encodeWithSignature("ERC20InvalidReceiver(address)", address(0)) | ||
); | ||
ourToken.transfer(address(0), transferAmount); | ||
|
||
// Verify that the balance of user1 remains unchanged | ||
assertEq(ourToken.balanceOf(user1), transferAmount); | ||
} | ||
|
||
function testConstructorInitializesCorrectly() public view { | ||
uint256 expectedSupply = 1e21; // Expected initial supply | ||
assertEq(ourToken.totalSupply(), expectedSupply); // Check total supply | ||
assertEq(ourToken.balanceOf(address(msg.sender)), expectedSupply); // Check deployer’s balance | ||
} | ||
} |