Skip to content

Commit fbf5e61

Browse files
Merge pull request #151 from berachain/feat/honey-x402-demo
feat: 🎸 New x402 Honey Demo
2 parents b9bca0e + b60cf09 commit fbf5e61

51 files changed

Lines changed: 5667 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/honey-x402-demo/.env.example

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Honey x402 Demo - Environment Variables
2+
# Copy this file to .env: cp .env.example .env
3+
4+
# =============================================================================
5+
# FRONTEND: Token Holder private key (signs permits/authorizations, no gas)
6+
# Default: Anvil account #1
7+
PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
8+
9+
# FRONTEND: Gas Subsidizer private key (submits transactions, pays gas)
10+
# Default: Anvil account #2
11+
PRIVATE_KEY_GAS_SUBSIDIZER=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
12+
13+
# =============================================================================
14+
# CONTRACTS: Deployer private key (deploys contracts)
15+
# Default: Anvil account #0
16+
DEPLOYER_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80

apps/honey-x402-demo/.gitignore

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Dependencies
2+
node_modules/
3+
contracts/lib/
4+
frontend/node_modules/
5+
6+
# Build outputs
7+
contracts/out/
8+
contracts/cache/
9+
frontend/dist/
10+
frontend/.vite/
11+
12+
# Deployment files
13+
contracts/deployments.json
14+
frontend/public/deployments.json
15+
16+
# Environment files
17+
.env
18+
.env.local
19+
!.env.example
20+
21+
# IDE
22+
.vscode/
23+
.idea/
24+
*.swp
25+
*.swo
26+
27+
# OS
28+
.DS_Store
29+
Thumbs.db
30+
31+
# Logs
32+
*.log
33+
npm-debug.log*
34+
yarn-debug.log*
35+
yarn-error.log*
36+
37+
# Bun
38+
bun.lockb

apps/honey-x402-demo/.gitmodules

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[submodule "contracts/lib/forge-std"]
2+
path = contracts/lib/forge-std
3+
url = https://github.com/foundry-rs/forge-std
4+
[submodule "contracts/lib/openzeppelin-contracts"]
5+
path = contracts/lib/openzeppelin-contracts
6+
url = https://github.com/OpenZeppelin/openzeppelin-contracts

apps/honey-x402-demo/README.md

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
# Honey Token: EIP-2612, Permit2, and EIP-3009 Demo
2+
3+
This project demonstrates the differences between three gasless transaction standards:
4+
- **EIP-2612 (Permit)**: Gasless token approvals via signed messages
5+
- **Permit2**: Uniswap's universal approval system for any ERC20 token
6+
- **EIP-3009 (Transfer With Authorization)**: Gasless token transfers via authorization signatures
7+
8+
## Project Overview
9+
10+
### Standards Comparison
11+
12+
| Feature | EIP-2612 | Permit2 | EIP-3009 |
13+
|---------|----------|---------|----------|
14+
| **Purpose** | Gasless approvals | Universal approvals/transfers | Gasless transfers |
15+
| **Nonce Type** | Sequential | Non-monotonic | Random (32-byte) |
16+
| **Works With** | EIP-2612 tokens only | Any ERC20 token | EIP-3009 tokens only |
17+
| **Pattern** | Approve + TransferFrom | Direct transfer or allowance | Direct transfer |
18+
| **Parallel Txs** | No (sequential nonces) | Yes | Yes (random nonces) |
19+
20+
### Architecture
21+
22+
The demo uses a two-wallet pattern with private keys loaded from `.env`:
23+
- **Wallet A — Token Holder** (`PRIVATE_KEY`): Has HONEY tokens, signs messages off-chain (no gas)
24+
- **Wallet B — Gas Subsidizer** (`PRIVATE_KEY_GAS_SUBSIDIZER`): Has BERA for gas, executes transactions on-chain (pays gas)
25+
26+
No browser wallet (MetaMask) is needed — signing and execution happen automatically using the private keys.
27+
28+
```mermaid
29+
flowchart TD
30+
TH[Wallet A – Token Holder] -->|Signs off-chain| Sig[EIP-712 Signature]
31+
GS[Wallet B – Gas Subsidizer] -->|Pays Gas| Exec[On-chain Execution]
32+
Sig --> Exec
33+
Exec -->|Calls| Contract[Smart Contract]
34+
Contract -->|Transfers| Tokens[Honey Tokens]
35+
36+
subgraph Methods
37+
EIP2612[EIP-2612: permit + transferFrom]
38+
Permit2[Permit2: signatureTransfer or allowanceTransfer]
39+
EIP3009[EIP-3009: transferWithAuthorization]
40+
end
41+
42+
Exec --> Methods
43+
```
44+
45+
## Prerequisites
46+
47+
### 1. Install Foundry
48+
49+
```bash
50+
curl -L https://foundry.paradigm.xyz | bash
51+
foundryup
52+
```
53+
54+
Verify installation:
55+
```bash
56+
forge --version
57+
```
58+
59+
### 2. Install Bun
60+
61+
```bash
62+
curl -fsSL https://bun.sh/install | bash
63+
```
64+
65+
Verify installation:
66+
```bash
67+
bun --version
68+
```
69+
70+
## Quick Start
71+
72+
### Option A: Automated Setup (Recommended)
73+
74+
```bash
75+
chmod +x setup.sh
76+
./setup.sh
77+
```
78+
79+
This will:
80+
- Check for Foundry and Bun installations
81+
- Install Solidity dependencies (forge-std, OpenZeppelin)
82+
- Build contracts
83+
- Create `.env` from `.env.example`
84+
- Install frontend dependencies
85+
- Copy `deployments.json` to the frontend (if contracts were previously deployed)
86+
87+
After setup completes, follow the **Next steps** printed in the terminal (start Anvil, deploy contracts, start frontend).
88+
89+
### Option B: Manual Setup
90+
91+
#### 1. Setup Environment
92+
93+
```bash
94+
# Copy the example .env (contains Anvil default keys)
95+
cp .env.example .env
96+
```
97+
98+
The `.env` file contains three keys:
99+
100+
```bash
101+
# Token Holder — signs permits/authorizations (no gas cost)
102+
PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
103+
104+
# Gas Subsidizer — submits transactions on-chain (pays gas)
105+
PRIVATE_KEY_GAS_SUBSIDIZER=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d
106+
107+
# Deployer — deploys contracts
108+
DEPLOYER_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
109+
```
110+
111+
> **Note:** These are Anvil default keys (accounts #1, #2, #0 respectively). The frontend has built-in fallbacks to these keys, so `.env` is optional for local development.
112+
113+
#### 2. Setup Contracts
114+
115+
```bash
116+
cd contracts
117+
118+
# Install Solidity dependencies (requires git)
119+
forge install foundry-rs/forge-std OpenZeppelin/openzeppelin-contracts
120+
121+
# Build contracts
122+
forge build
123+
```
124+
125+
#### 3. Start Anvil (Local EVM Network)
126+
127+
In a separate terminal:
128+
129+
```bash
130+
anvil
131+
```
132+
133+
Anvil starts on `http://localhost:8545` with Chain ID `31337` and pre-funded accounts.
134+
135+
#### 4. Deploy Contracts
136+
137+
```bash
138+
cd contracts
139+
forge script script/Deploy.s.sol:DeployScript \
140+
--rpc-url http://localhost:8545 \
141+
--broadcast
142+
```
143+
144+
The deploy script reads `DEPLOYER_PRIVATE_KEY` from the project root `.env`. If not found, it falls back to Anvil's default key.
145+
146+
This will:
147+
- Deploy Permit2
148+
- Deploy the Honey ERC20 token (with EIP-2612 and EIP-3009 support)
149+
- Deploy the Demo contract (wired to Honey and Permit2)
150+
- Transfer 1,000 HONEY tokens to the token holder wallet
151+
- Approve Permit2 to spend the token holder's HONEY (required for Permit2 demos)
152+
- Write `deployments.json` with all contract addresses (Honey, Demo, Permit2)
153+
154+
#### 5. Start Frontend
155+
156+
```bash
157+
cd frontend
158+
159+
# Install dependencies
160+
bun install
161+
162+
# Start development server (auto-copies deployments.json from contracts/)
163+
bun run dev
164+
```
165+
166+
> **Note:** `bun run dev` automatically copies `contracts/deployments.json` to `frontend/public/` before starting the dev server. No manual copy needed.
167+
168+
The frontend will be available at `http://localhost:5173`.
169+
170+
## Project Structure
171+
172+
```
173+
honey-x402-demo/
174+
├── .env.example # Environment variable template
175+
├── contracts/ # Foundry project
176+
│ ├── src/
177+
│ │ ├── Honey.sol # ERC20 with EIP-2612 & EIP-3009
178+
│ │ ├── Demo.sol # Demo contract for all three methods
179+
│ │ └── Permit2/ # Permit2 interfaces
180+
│ ├── script/
181+
│ │ └── Deploy.s.sol # Deployment script (deploys Permit2, Honey, Demo)
182+
│ ├── deployments.json # Generated contract addresses (after deploy)
183+
│ ├── foundry.toml # Foundry configuration
184+
│ └── remappings.txt # Import remappings
185+
├── frontend/ # React + Vite + viem + wagmi
186+
│ ├── src/
187+
│ │ ├── components/
188+
│ │ │ ├── WalletDisplay.tsx # Wallet balance display
189+
│ │ │ ├── EIP2612Demo.tsx # EIP-2612 demo
190+
│ │ │ ├── Permit2Demo.tsx # Permit2 demo
191+
│ │ │ └── EIP3009Demo.tsx # EIP-3009 demo
192+
│ │ ├── config/
193+
│ │ │ ├── wagmi.ts # Wagmi chain config (reads only)
194+
│ │ │ └── clients.ts # viem wallet clients from .env keys
195+
│ │ ├── App.tsx
196+
│ │ └── main.tsx
197+
│ └── package.json
198+
├── setup.sh # Automated setup script
199+
└── README.md
200+
```
201+
202+
## Usage Guide
203+
204+
### How the Demo Works
205+
206+
1. Open `http://localhost:5173` — you'll see two wallet cards:
207+
- **Wallet A — Token Holder**: Shows HONEY and BERA balances (loaded from `PRIVATE_KEY`)
208+
- **Wallet B — Gas Subsidizer**: Shows HONEY and BERA balances (loaded from `PRIVATE_KEY_GAS_SUBSIDIZER`)
209+
210+
2. Each demo section has a **two-step flow** with separate buttons:
211+
- **Step 1 — "Wallet A: Sign … (Off-chain, No Gas)"**: Signs an EIP-712 message using Wallet A's private key. This is free — no gas is spent.
212+
- **Step 2 — "Wallet B: Execute On-chain (Pays Gas)"**: Submits the signed message on-chain using Wallet B's private key. Wallet B pays for gas.
213+
214+
3. Watch the step indicator progress from **"Wallet A Signs"****"Wallet B Executes"**
215+
216+
4. After execution, view the **Transaction Receipt** (gas used, cost) and **Transfer Summary** (who sent, who paid)
217+
218+
### EIP-2612 Demo
219+
220+
- Enter amount of HONEY to transfer
221+
- Click **"1. Wallet A: Sign Permit (Off-chain, No Gas)"** — signs a `permit()` approval
222+
- Click **"2. Wallet B: Execute On-chain (Pays Gas)"** — calls `permit() + transferFrom()` in one transaction
223+
- Uses **sequential nonces** (shown in the UI)
224+
225+
### Permit2 Demo
226+
227+
- Choose method:
228+
- **Signature Transfer** — one-time use, random nonce, exact amount
229+
- **Allowance Transfer** — reusable until cap/expiry, with configurable time-bound limits (allowance expiration and signature deadline)
230+
- Enter amount and click **"1. Wallet A: Sign Permit2 (Off-chain, No Gas)"**
231+
- Click **"2. Wallet B: Execute On-chain (Pays Gas)"**
232+
- Works with any ERC20 token — Permit2 is a universal approval layer
233+
234+
### EIP-3009 Demo
235+
236+
- Enter amount and click **"1. Wallet A: Sign Authorization (Off-chain, No Gas)"**
237+
- Click **"2. Wallet B: Execute On-chain (Pays Gas)"**
238+
- Uses a **random 32-byte nonce** (enables parallel authorizations)
239+
- Does the transfer directly — no separate approval step
240+
241+
## Development Commands
242+
243+
### Contracts
244+
245+
```bash
246+
cd contracts
247+
248+
# Compile
249+
forge build
250+
251+
# Run tests
252+
forge test
253+
254+
# Deploy to Anvil
255+
forge script script/Deploy.s.sol:DeployScript \
256+
--rpc-url http://localhost:8545 \
257+
--broadcast
258+
259+
# Format Solidity code
260+
forge fmt
261+
262+
# Check contract sizes
263+
forge build --sizes
264+
```
265+
266+
### Frontend
267+
268+
```bash
269+
cd frontend
270+
271+
# Install dependencies
272+
bun install
273+
274+
# Start development server
275+
bun run dev
276+
277+
# Build for production
278+
bun run build
279+
280+
# Preview production build
281+
bun run preview
282+
283+
# Run linter
284+
bun run lint
285+
```
286+
287+
## Troubleshooting
288+
289+
### Contracts Won't Compile
290+
291+
- Ensure Solidity dependencies are installed: `forge install foundry-rs/forge-std OpenZeppelin/openzeppelin-contracts`
292+
- Check `remappings.txt` for correct paths
293+
- Verify Solidity version `0.8.24` in `foundry.toml`
294+
295+
### Frontend Can't Connect to Anvil
296+
297+
- Ensure Anvil is running on `http://localhost:8545`
298+
- Check that `vite.config.ts` points `envDir` to the project root (`../`)
299+
- Verify network ID is 31337
300+
301+
### Frontend Shows "Contracts not deployed"
302+
303+
- Deploy contracts first — `deployments.json` is generated by the deploy script
304+
- `bun run dev` auto-copies it, but for a manual copy:
305+
```bash
306+
cp contracts/deployments.json frontend/public/
307+
```
308+
309+
### Signature / Transaction Errors
310+
311+
- Ensure `.env` exists in the project root with correct keys (or rely on the built-in Anvil fallback keys)
312+
- Verify contract addresses in `deployments.json` match deployed contracts
313+
- For EIP-3009, nonces are random — each click generates a new unique nonce
314+
- Check the domain separator (chain ID 31337, correct contract address, name, version)
315+
316+
### Permit2 Errors
317+
318+
- Permit2 is deployed automatically by the deploy script alongside Honey and Demo
319+
- The deploy script also has the token holder approve Permit2 for max HONEY
320+
- If you see allowance errors, redeploy contracts to reset the Permit2 approval
321+
322+
## Additional Resources
323+
324+
- [EIP-2612: Permit Extension for EIP-20](https://eips.ethereum.org/EIPS/eip-2612)
325+
- [EIP-3009: Transfer With Authorization](https://eips.ethereum.org/EIPS/eip-3009)
326+
- [Permit2 Documentation](https://github.com/Uniswap/permit2)
327+
- [viem Documentation](https://viem.sh)
328+
- [Wagmi Documentation](https://wagmi.sh)
329+
330+
## License
331+
332+
MIT

0 commit comments

Comments
 (0)