Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Run with `npm install && npm run dev`.

### [`app-kit-unified-balance`](./app-kit-unified-balance)

Browser app that deposits USDC into a unified balance from Base Sepolia and
Browser app that deposits USDC into a unified balance from Avalanche Fuji and
Solana Devnet, reads the balance, and spends it on Arc Testnet using
[App Kit](https://www.npmjs.com/package/@circle-fin/app-kit) with the viem and
Solana adapters. Connects an EVM wallet and a Solana wallet.
Expand Down
8 changes: 4 additions & 4 deletions app-kit-bridge-evm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
},
"devDependencies": {
"typescript": "~7.0.2",
"vite": "^8.2.1"
"vite": "^8.2.2"
},
"dependencies": {
"@circle-fin/app-kit": "^1.12.0",
"@circle-fin/adapter-viem-v2": "^1.15.1",
"viem": "^2.55.17"
"@circle-fin/app-kit": "^1.14.0",
"@circle-fin/adapter-viem-v2": "^1.17.1",
"viem": "^2.56.3"
}
}
10 changes: 8 additions & 2 deletions app-kit-bridge-evm/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,13 @@ async function handleEvmConnect() {
})) as string[];

walletInfo.textContent = accounts[0] ?? "Connected";
output.textContent = "";
bridgeButton.disabled = !evmProvider;
} catch (error) {
render({ error: error instanceof Error ? error.message : "Unknown error" });
evmProvider = null;
render({
error: (error as { message?: string })?.message ?? String(error),
});
} finally {
connectEvmButton.disabled = Boolean(evmProvider);
}
Expand Down Expand Up @@ -118,7 +122,9 @@ async function handleBridge() {

render(result);
} catch (error) {
render({ error: error instanceof Error ? error.message : "Unknown error" });
render({
error: (error as { message?: string })?.message ?? String(error),
});
} finally {
bridgeButton.disabled = false;
}
Expand Down
10 changes: 5 additions & 5 deletions app-kit-bridge-solana/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
},
"devDependencies": {
"typescript": "~7.0.2",
"vite": "^8.2.1"
"vite": "^8.2.2"
},
"dependencies": {
"@circle-fin/adapter-solana": "^1.6.5",
"@circle-fin/adapter-viem-v2": "^1.15.1",
"@circle-fin/app-kit": "^1.12.0",
"viem": "^2.55.17"
"@circle-fin/adapter-solana": "^1.7.2",
"@circle-fin/adapter-viem-v2": "^1.17.1",
"@circle-fin/app-kit": "^1.14.0",
"viem": "^2.56.3"
}
}
16 changes: 13 additions & 3 deletions app-kit-bridge-solana/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,13 @@ async function handleEvmConnect() {
})) as string[];

walletInfo.textContent = accounts[0] ?? "Connected";
output.textContent = "";
bridgeButton.disabled = !evmProvider || !solanaProvider;
} catch (error) {
render({ error: error instanceof Error ? error.message : "Unknown error" });
evmProvider = null;
render({
error: (error as { message?: string })?.message ?? String(error),
});
} finally {
connectEvmButton.disabled = Boolean(evmProvider);
}
Expand All @@ -113,9 +117,13 @@ async function handleSolanaConnect() {
connection.publicKey?.toString() ??
solanaProvider.publicKey?.toString() ??
"Connected";
output.textContent = "";
bridgeButton.disabled = !evmProvider || !solanaProvider;
} catch (error) {
render({ error: error instanceof Error ? error.message : "Unknown error" });
solanaProvider = null;
render({
error: (error as { message?: string })?.message ?? String(error),
});
} finally {
connectSolButton.disabled = Boolean(solanaProvider);
}
Expand Down Expand Up @@ -152,7 +160,9 @@ async function handleBridge() {

render(result);
} catch (error) {
render({ error: error instanceof Error ? error.message : "Unknown error" });
render({
error: (error as { message?: string })?.message ?? String(error),
});
} finally {
bridgeButton.disabled = false;
}
Expand Down
11 changes: 4 additions & 7 deletions app-kit-send/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
# Send USDC on an EVM chain

Use [`@circle-fin/app-kit`](https://www.npmjs.com/package/@circle-fin/app-kit)
to estimate and send USDC on Arc Testnet with a browser wallet and the viem
adapter.
to send USDC on Arc Testnet with a browser wallet and the viem adapter.

This Vite + TypeScript page connects an EIP-6963 wallet (for example MetaMask),
creates a Circle adapter from the provider, then calls `estimateSend()` and
`send()`.
creates a Circle adapter from the provider, then calls `send()`.

## Prerequisites

Expand Down Expand Up @@ -34,11 +32,10 @@ npm run build

1. Discovers an EIP-6963 browser wallet and requests account access.
2. Creates a viem adapter with `createViemAdapterFromProvider()`.
3. Estimates the transfer with `kit.estimateSend()`.
4. Sends 1 USDC on `Arc_Testnet` to the entered recipient with `kit.send()`.
3. Sends 1 USDC on `Arc_Testnet` to the entered recipient with `kit.send()`.

## Key file

- `src/main.ts` — wallet connect, adapter creation, estimate, and send.
- `src/main.ts` — wallet connect, adapter creation, and send.
Change `chain`, `amount`, and `token` in the send params for a different
chain, amount, or token.
8 changes: 4 additions & 4 deletions app-kit-send/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
},
"devDependencies": {
"typescript": "~7.0.2",
"vite": "^8.2.1"
"vite": "^8.2.2"
},
"dependencies": {
"@circle-fin/adapter-viem-v2": "^1.15.1",
"@circle-fin/app-kit": "^1.12.0",
"viem": "^2.55.17"
"@circle-fin/adapter-viem-v2": "^1.17.1",
"@circle-fin/app-kit": "^1.14.0",
"viem": "^2.56.3"
}
}
22 changes: 12 additions & 10 deletions app-kit-send/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
*/

import { AppKit } from "@circle-fin/app-kit";
import type { SendParams } from "@circle-fin/app-kit";
import { createViemAdapterFromProvider } from "@circle-fin/adapter-viem-v2";
import type { CreateViemAdapterFromProviderParams } from "@circle-fin/adapter-viem-v2";

Expand Down Expand Up @@ -83,15 +82,19 @@ async function handleWalletConnect() {
})) as string[];

walletInfo.textContent = accounts[0] ?? "Connected";
output.textContent = "";
sendButton.disabled = !walletProvider;
} catch (error) {
render({ error: error instanceof Error ? error.message : "Unknown error" });
walletProvider = null;
render({
error: (error as { message?: string })?.message ?? String(error),
});
} finally {
connectWalletButton.disabled = Boolean(walletProvider);
}
}

/** Estimate and send 1 USDC on Arc Testnet to the recipient. */
/** Send 1 USDC on Arc Testnet to the recipient. */
async function handleSend(event: Event) {
event.preventDefault();

Expand All @@ -111,19 +114,18 @@ async function handleSend(event: Event) {
provider: walletProvider,
});

const sendParams: SendParams = {
const result = await kit.send({
from: { adapter, chain: "Arc_Testnet" },
to: recipientAddress,
amount: "1.00",
token: "USDC",
};

const estimate = await kit.estimateSend(sendParams);
const result = await kit.send(sendParams);
});

render({ estimate, result });
render(result);
} catch (error) {
render({ error: error instanceof Error ? error.message : "Unknown error" });
render({
error: (error as { message?: string })?.message ?? String(error),
});
} finally {
sendButton.disabled = false;
}
Expand Down
54 changes: 30 additions & 24 deletions app-kit-swap/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
# Swap USDC on Arc Testnet

Use [`@circle-fin/app-kit`](https://www.npmjs.com/package/@circle-fin/app-kit)
to estimate and swap USDC for EURC on Arc Testnet with a browser wallet and the
viem adapter.
to swap USDC for EURC on Arc Testnet with a browser wallet and the viem
adapter.

This Vite + TypeScript page connects an EIP-6963 wallet (for example MetaMask),
creates a Circle adapter from the provider, then calls `estimateSwap()` and
`swap()`.
creates a Circle adapter from the provider, then calls `swap()`.

## Prerequisites

Expand Down Expand Up @@ -34,35 +33,42 @@ npm run build

1. Discovers an EIP-6963 browser wallet and requests account access.
2. Creates a viem adapter with `createViemAdapterFromProvider()`.
3. Estimates the swap with `kit.estimateSwap()`.
4. Swaps 1 USDC for EURC on `Arc_Testnet` with `kit.swap()`.
3. Swaps 1 USDC for EURC on `Arc_Testnet` with `kit.swap()`.

## Thin liquidity on Arc Testnet
## Troubleshooting

Arc Testnet swap pools are often thin or imbalanced. Quotes and swaps can fail
even when your code and wallet setup are correct. This is an environment
limitation on testnet, not a bug in your integration.
### Thin liquidity on Arc Testnet

Common errors:
Arc Testnet pools are often thin or imbalanced. Failures here are usually the
environment, not your integration.

- **No route available** — the swap router could not find a quote for that pair
or amount (liquidity path missing or temporarily unavailable).
- **On-chain simulation failed** — a quote existed, but the pool could not meet
the minimum output under the configured slippage.
- **No route available** — no quote for that pair/amount; try a smaller
`amountIn`, flip the direction (`EURC` → `USDC` often has more depth), or
retry later.
- **On-chain simulation failed** — quote existed but slippage/minimum output
could not be met; lower `amountIn` or raise `config.slippageBps` above the
default `300` (3%).

If you hit either error:
### Wallet stuck on “Pending” / approve never confirms

1. Retry with a smaller `amountIn` (for example `0.1` or `1` USDC).
2. Prefer `EURC → USDC` when probing for available depth — Arc Testnet pools
are often USDC-heavy, so that direction usually has less price impact.
3. Optionally raise `config.slippageBps` above the default `300` (3%) for
testnet experiments.
4. Retry later — testnet liquidity and routing availability change over time.
Browser swaps need the wallet to broadcast approve (if needed) then the swap
tx. If MetaMask (or another wallet) shows **Pending** forever and the tx hash
is **not** on [Arcscan](https://testnet.arcscan.app):

Use mainnet liquidity expectations only after you move off Arc Testnet.
1. The wallet’s Arc Testnet RPC likely failed to broadcast — fix or replace
the network RPC in the wallet settings.
2. Clear stuck local activity (MetaMask: Settings → Advanced → Clear activity
tab data / Reset account) so a phantom pending does not block the next
prompt.
3. Confirm you are on **Arc Testnet** with USDC/EURC and native gas, then
retry. Another EIP-6963 wallet can work if one wallet’s RPC is bad; this
example prefers MetaMask when both are installed.

A custodial / Circle Wallets script can succeed while a browser wallet stalls
for the same route — that points at wallet RPC/queue, not App Kit.

## Key file

- `src/main.ts` — wallet connect, adapter creation, estimate, and swap.
- `src/main.ts` — wallet connect, adapter creation, and swap.
Change `chain`, `tokenIn` / `tokenOut`, and `amountIn` in the swap params
for a different chain, pair, or amount.
8 changes: 4 additions & 4 deletions app-kit-swap/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
},
"devDependencies": {
"typescript": "~7.0.2",
"vite": "^8.2.1"
"vite": "^8.2.2"
},
"dependencies": {
"@circle-fin/adapter-viem-v2": "^1.15.1",
"@circle-fin/app-kit": "^1.12.0",
"viem": "^2.55.17"
"@circle-fin/adapter-viem-v2": "^1.17.1",
"@circle-fin/app-kit": "^1.14.0",
"viem": "^2.56.3"
}
}
22 changes: 12 additions & 10 deletions app-kit-swap/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
*/

import { AppKit } from "@circle-fin/app-kit";
import type { SwapParams } from "@circle-fin/app-kit";
import { createViemAdapterFromProvider } from "@circle-fin/adapter-viem-v2";
import type { CreateViemAdapterFromProviderParams } from "@circle-fin/adapter-viem-v2";

Expand Down Expand Up @@ -83,15 +82,19 @@ async function handleWalletConnect() {
})) as string[];

walletInfo.textContent = accounts[0] ?? "Connected";
output.textContent = "";
swapButton.disabled = !walletProvider;
} catch (error) {
render({ error: error instanceof Error ? error.message : "Unknown error" });
walletProvider = null;
render({
error: (error as { message?: string })?.message ?? String(error),
});
} finally {
connectWalletButton.disabled = Boolean(walletProvider);
}
}

/** Estimate and swap 1 USDC for EURC on Arc Testnet. */
/** Swap 1 USDC for EURC on Arc Testnet. */
async function handleSwap() {
try {
if (!walletProvider) {
Expand All @@ -104,19 +107,18 @@ async function handleSwap() {
provider: walletProvider,
});

const swapParams: SwapParams = {
const result = await kit.swap({
from: { adapter, chain: "Arc_Testnet" },
tokenIn: "USDC",
tokenOut: "EURC",
amountIn: "1.00",
};

const estimate = await kit.estimateSwap(swapParams);
const result = await kit.swap(swapParams);
});

render({ estimate, result });
render(result);
} catch (error) {
render({ error: error instanceof Error ? error.message : "Unknown error" });
render({
error: (error as { message?: string })?.message ?? String(error),
});
} finally {
swapButton.disabled = false;
}
Expand Down
16 changes: 13 additions & 3 deletions app-kit-unified-balance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Circle adapters with the chains this demo uses, then calls
- [Node.js 22 or later](https://nodejs.org/)
- An EIP-6963 EVM browser wallet such as MetaMask
- A Solana browser wallet on `window.solana` such as Phantom
- Testnet USDC on Base Sepolia and Solana Devnet
- Testnet USDC on Avalanche Fuji and Solana Devnet
- Native gas tokens for both wallets

This project does not require environment variables or API keys.
Expand All @@ -34,15 +34,25 @@ npm run build

## What this example does

1. Connects an EVM wallet and builds a viem adapter for Base Sepolia and Arc
1. Connects an EVM wallet and builds a viem adapter for Avalanche Fuji and Arc
Testnet.
2. Connects a Solana wallet and builds a Solana adapter for Solana Devnet.
3. Deposits 2 USDC from Base Sepolia with `kit.unifiedBalance.deposit()`.
3. Deposits 2 USDC from Avalanche Fuji with `kit.unifiedBalance.deposit()`.
4. Deposits 1 USDC from Solana Devnet with `kit.unifiedBalance.deposit()`.
5. Reads the unified balance with `kit.unifiedBalance.getBalances()`.
6. Spends 2.50 USDC to a recipient on Arc Testnet with
`kit.unifiedBalance.spend()`.

## Spend signing (EVM + Solana)

This demo deposits on Avalanche Fuji (2 USDC) and Solana Devnet (1 USDC), then
spends 2.50 USDC. Auto-allocation therefore draws from **both** wallets.

EVM burn intents can share one EIP-712 `BurnIntentSet` signature. Solana uses a
separate Ed25519 signature and cannot join that set. Expect **two wallet
prompts** on Spend: one in your EVM wallet, then one in your Solana wallet.
Both signed intents still go in a single Gateway transfer.

## Key file

- `src/main.ts` — wallet connect, adapters, deposit, balance, and spend.
Expand Down
Loading
Loading