Skip to content

Commit e27b7b5

Browse files
chore: update readme and patch wallet error (#4)
This PR: - updates the README to the recommended format for sample apps - fixes a breaking issue when there is more than 1 connected wallet - removes unused dependencies and updates Circle SDKs to latest versions
1 parent 729134f commit e27b7b5

File tree

6 files changed

+88
-49
lines changed

6 files changed

+88
-49
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,8 @@ jobs:
1515
- name: Install Node
1616
uses: actions/setup-node@395ad3262231945c25e8478fd5baf05154b1d79f # v6.1.0
1717

18-
- name: Install pnpm
19-
uses: pnpm/action-setup@v4
20-
2118
- name: Install dependencies
22-
run: pnpm install
19+
run: npm install
2320

2421
scan:
2522
needs: lint-and-test

README.md

Lines changed: 73 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,65 @@
11
# Arc Multichain Wallet
22

3-
This sample app demonstrates how developers can build the best USDC interoperability UX for wallets using Arc and Gateway.
3+
A sample application demonstrating how to build optimal USDC interoperability UX for wallets using Arc and Circle Gateway. This app showcases unified balance management, deposits, and cross-chain transfers across multiple EVM chains using Next.js and Supabase.
44

5-
### Install dependencies
5+
<img width="830" height="658" alt="Interface for depositing to and transfering from a Gateway balance" src="public/screenshot.png" />
66

7-
```bash
8-
# Install dependencies
9-
pnpm install
7+
## Prerequisites
108

11-
# Configure environment variables
12-
cp .env.example .env.local
13-
```
9+
- Node.js 20.x or newer
10+
- npm (automatically installed when Node.js is installed)
11+
- Docker (for running Supabase locally)
12+
- Circle Developer Controlled Wallets [API key](https://console.circle.com/signin) and [Entity Secret](https://developers.circle.com/wallets/dev-controlled/register-entity-secret)
1413

15-
Update `.env.local`:
14+
## Getting Started
1615

17-
```ini
18-
# Supabase
19-
NEXT_PUBLIC_SUPABASE_URL=your-project-url
20-
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=your-publishable-or-anon-key
16+
1. Clone the repository and install dependencies:
2117

22-
# Circle
23-
CIRCLE_API_KEY=your-circle-api-key
24-
CIRCLE_ENTITY_SECRET=your-circle-entity-secret
25-
```
18+
```bash
19+
git clone git@github.com:circlefin/arc-multichain-wallet.git
20+
cd arc-multichain-wallet
21+
npm install
22+
```
2623

27-
### Start Supabase
24+
2. Create a `.env.local` file in the project root:
2825

29-
```bash
30-
pnpx supabase start
31-
```
26+
```bash
27+
cp .env.example .env.local
28+
```
3229

33-
### Run Development Server
30+
Required variables:
3431

35-
```bash
36-
pnpm run dev
37-
```
32+
```bash
33+
# Supabase
34+
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
35+
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=your_publishable_or_anon_key
3836

39-
Visit [http://localhost:3000/wallet](http://localhost:3000/wallet)
37+
# Circle
38+
CIRCLE_API_KEY=your_circle_api_key
39+
CIRCLE_ENTITY_SECRET=your_entity_secret
40+
```
41+
42+
3. Start Supabase locally:
43+
44+
```bash
45+
npx supabase start
46+
```
47+
48+
4. Start the development server:
49+
50+
```bash
51+
npm run dev
52+
```
53+
54+
The app will be available at `http://localhost:3000`.
4055

4156
## How It Works
4257

58+
- Built with [Next.js](https://nextjs.org/) and [Supabase](https://supabase.com/)
59+
- Uses [Circle Gateway](https://developers.circle.com/gateway) for unified USDC balance and cross-chain transfers
60+
- Integrates [Circle Developer Controlled Wallets](https://developers.circle.com/wallets/dev-controlled) for server-side wallet operations
61+
- Demonstrates wallet connectivity with [Wagmi](https://wagmi.sh/) and [Viem](https://viem.sh/)
62+
4363
### Unified Balance
4464

4565
When you deposit USDC to the Gateway Wallet, it becomes part of your unified balance accessible from any supported chain. The Gateway Wallet uses the same address on all chains: `0x0077777d7EBA4688BDeF3E311b846F25870A19B9`
@@ -57,13 +77,36 @@ When you deposit USDC to the Gateway Wallet, it becomes part of your unified bal
5777
3. Call `gatewayMint()` on destination chain
5878
4. USDC minted on destination
5979

60-
## Security Notes
80+
## Environment Variables
81+
82+
| Variable | Scope | Purpose |
83+
| ------------------------------------- | ----------- | ------------------------------------------------------------------------ |
84+
| `NEXT_PUBLIC_SUPABASE_URL` | Public | Supabase project URL |
85+
| `NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY` | Public | Supabase anonymous/public key |
86+
| `CIRCLE_API_KEY` | Server-side | Circle API key for Gateway operations |
87+
| `CIRCLE_ENTITY_SECRET` | Server-side | Circle entity secret for wallet operations |
6188

62-
- This is a **testnet demonstration** only
89+
## Usage Notes
90+
91+
- Designed for testnet only
92+
- Requires valid Circle API credentials and Supabase configuration
6393
- Private keys are processed server-side and never stored
6494
- Never use mainnet private keys with this application
65-
- Always use HTTPS in production
66-
- Consider hardware wallet integration for production use
95+
96+
## Scripts
97+
98+
- `npm run dev`: Start Next.js development server with auto-reload
99+
- `npx supabase start`: Start local Supabase instance
100+
101+
## Security & Usage Model
102+
103+
This sample application:
104+
- Assumes testnet usage only
105+
- Handles secrets via environment variables
106+
- Processes private keys server-side without storage
107+
- Is not intended for production use without modification
108+
109+
See `SECURITY.md` for vulnerability reporting guidelines. Please report issues privately via Circle's bug bounty program.
67110

68111
## Resources
69112

app/api/gateway/deposit/route.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,21 @@ export async function POST(req: NextRequest) {
7474
const amountInAtomicUnits = BigInt(Math.floor(parsedAmount * 1_000_000));
7575

7676
// Custodial flow (Circle Wallet)
77-
const { data: wallet, error: walletError } = await supabase
77+
const { data: wallets, error: walletError } = await supabase
7878
.from("wallets")
7979
.select("circle_wallet_id")
8080
.eq("user_id", user.id)
81-
.single();
81+
.limit(1);
8282

83-
if (walletError || !wallet) {
83+
if (walletError || !wallets || wallets.length === 0) {
8484
return NextResponse.json(
8585
{ error: "No Circle wallet found for this user." },
8686
{ status: 404 }
8787
);
8888
}
8989

90+
const wallet = wallets[0];
91+
9092
const txHash = await initiateDepositFromCustodialWallet(
9193
wallet.circle_wallet_id,
9294
chain as SupportedChain,

app/api/gateway/transfer/route.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,21 @@ export async function POST(req: NextRequest) {
7474
const amountInAtomicUnits = BigInt(Math.floor(parseFloat(amount) * 1_000_000));
7575

7676
// Custodial flow (Circle Wallet)
77-
const { data: wallet, error: walletError } = await supabase
77+
const { data: wallets, error: walletError } = await supabase
7878
.from("wallets")
7979
.select("circle_wallet_id")
8080
.eq("user_id", user.id)
81-
.single();
81+
.limit(1);
8282

83-
if (walletError || !wallet?.circle_wallet_id) {
83+
if (walletError || !wallets || wallets.length === 0) {
8484
return NextResponse.json(
8585
{ error: "No Circle wallet found for this user." },
8686
{ status: 404 }
8787
);
8888
}
8989

90+
const wallet = wallets[0];
91+
9092
const transferResult = await transferUnifiedBalanceCircle(
9193
wallet.circle_wallet_id,
9294
amountInAtomicUnits,

package.json

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
"lint": "eslint ."
88
},
99
"dependencies": {
10-
"@circle-fin/circle-sdk": "^2.9.0",
11-
"@circle-fin/developer-controlled-wallets": "^9.2.1",
12-
"@circle-fin/smart-contract-platform": "^9.2.1",
10+
"@circle-fin/developer-controlled-wallets": "^10.0.1",
1311
"@radix-ui/react-checkbox": "^1.3.3",
1412
"@radix-ui/react-dialog": "^1.1.15",
1513
"@radix-ui/react-dropdown-menu": "^2.1.16",
@@ -26,17 +24,15 @@
2624
"@wagmi/core": "^2.22.1",
2725
"class-variance-authority": "^0.7.1",
2826
"clsx": "^2.1.1",
29-
"dotenv": "^17.2.3",
3027
"lucide-react": "^0.548.0",
3128
"next": "latest",
3229
"next-themes": "^0.4.6",
3330
"react": "^19.2.0",
3431
"react-dom": "^19.2.0",
3532
"sonner": "^2.0.7",
3633
"tailwind-merge": "^3.3.1",
37-
"uuid": "^13.0.0",
38-
"viem": "^2.38.5",
39-
"wagmi": "^2.19.1"
34+
"viem": "^2.44.4",
35+
"wagmi": "^2.19.5"
4036
},
4137
"devDependencies": {
4238
"@eslint/eslintrc": "^3.3.1",
@@ -50,6 +46,5 @@
5046
"tailwindcss": "^4.1.16",
5147
"tailwindcss-animate": "^1.0.7",
5248
"typescript": "^5.9.3"
53-
},
54-
"packageManager": "pnpm@10.24.0+sha512.01ff8ae71b4419903b65c60fb2dc9d34cf8bb6e06d03bde112ef38f7a34d6904c424ba66bea5cdcf12890230bf39f9580473140ed9c946fef328b6e5238a345a"
49+
}
5550
}

public/screenshot.png

24.5 KB
Loading

0 commit comments

Comments
 (0)