Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update code in /sample-dapps/evm-token-factory To Support Foundry #67

Merged
merged 6 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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: 15 additions & 13 deletions sample-dapps/evm-token-factory/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

# QuickNode EVM Token Factory Demo

## Overview
Expand All @@ -12,10 +13,22 @@ The demo uses [Next.js 14](https://nextjs.org/) project bootstrapped with [`crea

## Getting Started

Open the project directory:

```bash
cd sample-dapps/evm-token-factory
```

### Set Environment Variables

1. Rename `.env.example` to `.env.local `and update it with RPC URLs for each blockchain. Also, include your [WalletConnect](https://cloud.walletconnect.com/). project ID (optionally, you can leave this blank but some features will not be supported). To create RPC URLs for each chain, you can run your own node locally or use a service like [QuickNode](https://quicknode.com) to quickly spin up a node endpoint.

### Configure Smart Contract Addresses

2. Update the `factoryAddress` value in `evm-token-factory/app/utils/ethereum.ts` with your deployed factory contract address. This is the address you received in the output during the [Deployment](https://quicknode.com/guides/ethereum-development/dapps/how-to-create-an-evm-token-factory-dapp#deployment) section.

3. Remove any unused chains (e.g., mainnet) from the `src/context/web3modal.tsx` file.

### RPC Configuration

This app requires a valid RPC URL for each blockchain you want to support. Here are more details on how RPC is configured throughout the app.
Expand All @@ -27,12 +40,6 @@ If you do not want to support a blockchain(s), you can remove references of the

### Install Dependencies

Open the project directory:

```bash
cd sample-dapps/evm-token-factory
```

Then, install the dependencies:

```bash
Expand All @@ -45,7 +52,7 @@ pnpm install
bun install
```

First, run the development server:
After, start the development server:

```bash
npm run dev
ferhatqn marked this conversation as resolved.
Show resolved Hide resolved
Expand Down Expand Up @@ -109,12 +116,7 @@ The ERC-20 Token Factory is built with two smart contracts:

To deploy the Factory contract on a new chain using Foundry, follow these steps:

1. Ensure [Foundry](https://book.getfoundry.sh/) is installed and navigate inside the `smart-contracts` directory. Install the required dependencies with the following commands:

```sh
forge install OpenZeppelin/openzeppelin-contracts --no-commit
forge install foundry-rs/forge-std --no-commit
```
1. Ensure [Foundry](https://book.getfoundry.sh/) is installed and navigate inside the `contracts` directory. Install the required dependencies with the following commands:

2. Build (compile) the smart contracts using the `forge build` command.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ethers } from 'ethers';
import { RequestBody, CHAINS } from '@/app/utils/ethereum';
import abi from '@/smart-contracts/abi/factory.json'
import abi from '@/app/utils/abi.json';

export async function POST(request: Request) {
try {
Expand Down
1 change: 1 addition & 0 deletions sample-dapps/evm-token-factory/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export default function Home() {
const chainConfig = CHAINS[chainId ?? 1];
const populateTxn = await signer.populateTransaction(data.apiResponse)
const send = await signer.sendTransaction(populateTxn)
await send.wait()
const receipt = await ethersProvider.getTransactionReceipt(send.hash)
const tokenAddressLogs = receipt?.logs[3].topics[1] as string;
const tokenAddress = decoder.decode(['address'], tokenAddressLogs)[0]
Expand Down
2 changes: 1 addition & 1 deletion sample-dapps/evm-token-factory/app/utils/abi.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"inputs":[{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"uint256","name":"initialSupply","type":"uint256"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tokenAddress","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"initialSupply","type":"uint256"}],"name":"TokenCreated","type":"event"},{"inputs":[{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"uint256","name":"initialSupply","type":"uint256"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"}],"name":"createToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"}]
14 changes: 14 additions & 0 deletions sample-dapps/evm-token-factory/contracts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Compiler files
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/

# Docs
docs/

# Dotenv file
.env
ferhatqn marked this conversation as resolved.
Show resolved Hide resolved
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@
src = "src"
out = "out"
libs = ["lib"]
remappings = ["@openzeppelin/=node_modules/@openzeppelin/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 sample-dapps/evm-token-factory/contracts/remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/
12 changes: 12 additions & 0 deletions sample-dapps/evm-token-factory/contracts/script/Counter.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Script, console} from "forge-std/Script.sol";

contract CounterScript is Script {
function setUp() public {}

function run() public {
vm.broadcast();
}
}
23 changes: 23 additions & 0 deletions sample-dapps/evm-token-factory/contracts/script/CreateToken.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Script.sol";
import "../src/Factory.sol";

contract CreateToken is Script {
function run() public {
vm.startBroadcast();

address initialOwner = msg.sender;
uint256 initialSupply = 1000;
string memory name = "MyToken";
string memory symbol = "MTK";

TokenFactory factory = new TokenFactory();

address tokenAddress = factory.createToken(initialOwner, initialSupply, name, symbol);
console.log("Token created at address:", tokenAddress);

vm.stopBroadcast();
}
}
14 changes: 14 additions & 0 deletions sample-dapps/evm-token-factory/contracts/src/Counter.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

contract Counter {
uint256 public number;

function setNumber(uint256 newNumber) public {
number = newNumber;
}

function increment() public {
number++;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ contract TokenFactory {
return address(newToken);

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import "openzeppelin-contracts/contracts/access/Ownable.sol";

contract Token is ERC20, Ownable {
constructor(address initialOwner, uint256 initialSupply, string memory name, string memory symbol)
constructor(address initialOwner, uint256 initialSupply, string memory
name, string memory symbol)
ERC20(name, symbol)
Ownable(initialOwner)
{
_transferOwnership(initialOwner);
_mint(initialOwner, initialSupply * 10 ** 18);

}
}
}
24 changes: 24 additions & 0 deletions sample-dapps/evm-token-factory/contracts/test/Counter.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Test, console} from "forge-std/Test.sol";
import {Counter} from "../src/Counter.sol";

contract CounterTest is Test {
Counter public counter;

function setUp() public {
counter = new Counter();
counter.setNumber(0);
}

function test_Increment() public {
counter.increment();
assertEq(counter.number(), 1);
}

function testFuzz_SetNumber(uint256 x) public {
counter.setNumber(x);
assertEq(counter.number(), x);
}
}
35 changes: 35 additions & 0 deletions sample-dapps/evm-token-factory/contracts/test/Factory.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Test.sol";
import "../src/Factory.sol";
import "../src/Token.sol";

contract TokenFactoryTest is Test {
TokenFactory factory;
address initialOwner;
uint256 initialSupply = 1000;
string name = "MyToken";
string symbol = "MTK";

event TokenCreated(address indexed tokenAddress, address indexed owner, uint256 initialSupply);

function setUp() public {
factory = new TokenFactory();
initialOwner = address(this);
}

function testCreateToken() public {
vm.expectEmit(false, false, false, false);
emit TokenCreated(address(0), initialOwner, initialSupply);

address tokenAddr = factory.createToken(initialOwner, initialSupply, name, symbol);
assertTrue(tokenAddr != address(0), "Token creation failed");

Token token = Token(tokenAddr);
assertEq(token.owner(), initialOwner, "Owner is not set correctly");
assertEq(token.totalSupply(), initialSupply * 10 ** 18, "Initial supply is incorrect");
assertEq(token.name(), name, "Token name is incorrect");
assertEq(token.symbol(), symbol, "Token symbol is incorrect");
}
}
33 changes: 33 additions & 0 deletions sample-dapps/evm-token-factory/contracts/test/Token.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "forge-std/Test.sol";
import "../src/Token.sol";

contract TokenTest is Test {
Token token;
address initialOwner;
uint initialSupply = 1000;
string name = "TestToken";
string symbol = "TT";

function setUp() public {
initialOwner = address(this);
token = new Token(initialOwner, initialSupply, name, symbol);
}

function testInitialOwner() public view {
assertEq(token.owner(), initialOwner);
}

function testInitialSupply() public view {
uint expectedSupply = initialSupply * 10 ** token.decimals();
assertEq(token.totalSupply(), expectedSupply);
assertEq(token.balanceOf(initialOwner), expectedSupply);
}

function testNameAndSymbol() public view {
assertEq(token.name(), name);
assertEq(token.symbol(), symbol);
}
}
Loading
Loading