Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Hamadiftikharr committed Nov 28, 2024
1 parent ae61a8d commit 1614cdd
Show file tree
Hide file tree
Showing 11 changed files with 296 additions and 57 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
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
50 changes: 50 additions & 0 deletions Makefile
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
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
src = "src"
out = "out"
libs = ["lib"]
remappings = ["@openzeppelin=lib/openzeppelin-contracts"]

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at dbb610
19 changes: 0 additions & 19 deletions script/Counter.s.sol

This file was deleted.

16 changes: 16 additions & 0 deletions script/DeployOurToken.s.sol
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;
}
}
14 changes: 0 additions & 14 deletions src/Counter.sol

This file was deleted.

30 changes: 30 additions & 0 deletions src/ManualToken.sol
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);
}
}
11 changes: 11 additions & 0 deletions src/OurToken.sol
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);
}
}
24 changes: 0 additions & 24 deletions test/Counter.t.sol

This file was deleted.

184 changes: 184 additions & 0 deletions test/OurTokenTest.t.sol
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
}
}

0 comments on commit 1614cdd

Please sign in to comment.