` element, but logging them to the console is enough for the core
integration.
```typescript TypeScript theme={null}
kit.on("*", (payload) => {
console.log("Action:", payload);
});
```
Using other EVM chains? Change the `chain` values in `kit.bridge()` and ensure
the connected wallet holds USDC on the source chain and enough gas to complete
the transfer flow.
You can customize your bridges to
[collect a fee](/app-kit/tutorials/bridge/collect-bridge-fee), use the
[Forwarding Service](/app-kit/tutorials/bridge/use-forwarding-service), or
[estimate gas and provider fees](/app-kit/tutorials/bridge/estimate-costs)
before bridging. Proceed only if the cost works for you.
### 3.3. Verify the transaction
After `kit.bridge()` resolves, inspect the returned `steps` array. Each
transaction step includes an `explorerUrl`. Use those links to confirm the
approve, burn, and mint steps for the amount you bridged.
The following code is an example of how an `approve` event might look in the
browser console after a successful bridge. The values are examples only and are
not a real transaction:
```bash Shell theme={null}
Event received: {
protocol: "cctp",
version: "v2",
traceId: "550afd44ba4c6d1d1bf4880b9ded3840",
values: {
name: "approve",
state: "success",
txHash: "0xdeadbeefcafebabe1234567890abcdef1234567890abcdef1234567890abcd",
data: {
txHash:
"0xdeadbeefcafebabe1234567890abcdef1234567890abcdef1234567890abcd",
status: "success",
cumulativeGasUsed: 17138643n,
gasUsed: 38617n,
blockNumber: 8778959n,
blockHash:
"0xbeadfacefeed1234567890abcdef1234567890abcdef1234567890abcdef12",
transactionIndex: 173,
effectiveGasPrice: 1037232n,
},
explorerUrl:
"https://testnet.arcscan.app/tx/0xdeadbeefcafebabe1234567890abcdef1234567890abcdef1234567890abcd",
},
method: "approve",
}
```
## Extend: Add a Solana source wallet
If you want a Solana browser wallet as the source, keep the EVM destination
adapter from the browser wallet flow above and add a Solana source adapter. The
examples use Solana Devnet and Arc Testnet, but you can use Solana and any
[supported EVM chain](/app-kit/references/supported-blockchains) as the
destination.
In this browser wallet flow, both wallets run in the browser and the user signs
transactions in wallet extensions. Treat wallet connection and bridging as
separate user actions: connect the destination EVM wallet first, connect the
Solana source wallet second, then call `kit.bridge()` after both adapters are
available.
### Additional prerequisites
Before you begin, ensure that you have:
* Worked through the EVM browser wallet flow above first. This Solana path adds
a Solana source wallet to that same browser-wallet pattern.
* Installed a Solana browser wallet that exposes `window.solana`.
* Funded your Solana wallet with testnet USDC from the
[Circle Faucet](https://faucet.circle.com/).
* Funded your Solana wallet with SOL for Solana Devnet transaction fees from the
[Solana Faucet](https://faucet.solana.com/).
### Add the Solana dependencies
Add the Solana adapter dependency to the same project:
```bash Shell theme={null}
npm install @circle-fin/adapter-solana
```
### Connect the Solana source wallet
This step extends the EVM browser wallet flow by adding a Solana source wallet.
You will connect the Solana wallet, create a Solana source adapter, keep the EVM
destination adapter, and pass both adapters into `kit.bridge()`.
The snippets below keep each part of the flow in small helper functions for
readability. The companion browser demo wires this same sequence through
`handleEvmConnect()`, `handleSolanaConnect()`, and `handleBridge()` in a
runnable UI.
#### Connect the Solana wallet and request account access
This pattern assumes a Solana browser wallet that exposes `window.solana`. Keep
wallet connection and App Kit actions as separate user actions so the wallet is
fully connected before you call an App Kit SDK method.
```typescript TypeScript theme={null}
import type { CreateSolanaAdapterFromProviderParams } from "@circle-fin/adapter-solana";
type SolanaWalletProvider = CreateSolanaAdapterFromProviderParams["provider"];
declare global {
interface Window {
solana?: SolanaWalletProvider;
}
}
async function connectSolanaWallet(provider: SolanaWalletProvider) {
const connection = await provider.connect();
return {
connectedAddress:
connection.publicKey?.toString() ??
provider.publicKey?.toString() ??
null,
};
}
```
#### Keep the EVM destination adapter and add a Solana source adapter
This Solana path builds on the EVM browser wallet flow above. Reuse the
connected EVM wallet provider from that flow, then add a Solana provider and
create one adapter for each chain:
```typescript TypeScript theme={null}
import { createViemAdapterFromProvider } from "@circle-fin/adapter-viem-v2";
import { createSolanaAdapterFromProvider } from "@circle-fin/adapter-solana";
import type { EIP1193Provider } from "viem";
async function createBridgeAdapters(
evmProvider: EIP1193Provider,
solanaProvider: SolanaWalletProvider,
) {
const evmAdapter = await createViemAdapterFromProvider({
provider: evmProvider,
});
const solanaAdapter = await createSolanaAdapterFromProvider({
provider: solanaProvider,
});
return {
evmAdapter,
solanaAdapter,
};
}
```
#### Pass the browser wallet adapters into `kit.bridge()`
After you have a connected EVM provider from the earlier browser-wallet flow and
a connected Solana provider from `window.solana`, create both adapters and pass
them into `kit.bridge()`:
```typescript TypeScript theme={null}
import { AppKit } from "@circle-fin/app-kit";
import type { EIP1193Provider } from "viem";
const kit = new AppKit();
async function bridgeUSDCWithSolanaBrowserWallet(
evmProvider: EIP1193Provider,
solanaProvider: SolanaWalletProvider,
) {
const { evmAdapter, solanaAdapter } = await createBridgeAdapters(
evmProvider,
solanaProvider,
);
const result = await kit.bridge({
from: { adapter: solanaAdapter, chain: "Solana_Devnet" },
to: { adapter: evmAdapter, chain: "Arc_Testnet" },
amount: "1.00",
});
console.log(
"Submitted bridge from Solana browser wallet to EVM destination",
{
result,
},
);
return result;
}
```
#### Retry a failed bridge attempt
If the first bridge attempt returns `state: "error"`, retry it with the same
freshly created adapters:
```typescript TypeScript theme={null}
let result = await kit.bridge({
from: { adapter: solanaAdapter, chain: "Solana_Devnet" },
to: { adapter: evmAdapter, chain: "Arc_Testnet" },
amount: "1.00",
});
if (result.state === "error") {
result = await kit.retryBridge(result, {
from: solanaAdapter,
to: evmAdapter,
});
}
```
Download the runnable
[browser demo](https://github.com/circlefin/docs-examples/tree/master/app-kit-bridge-solana)
to see the Solana-to-EVM bridge flow in action.
#### Observe bridge lifecycle events
If you added the `kit.on("*", (payload) => { ... })` listener in the previous
step, it already captures Solana bridge events, and no additional subscription
is needed.
```typescript TypeScript theme={null}
kit.on("*", (payload) => {
console.log("Action:", payload);
});
```
Using a different EVM chain as the destination? Change the `to.chain` value and
ensure the connected Solana wallet holds USDC on the source chain and enough
native gas to complete the transfer flow.
You can customize your bridges to
[collect a fee](/app-kit/tutorials/bridge/collect-bridge-fee), use the
[Forwarding Service](/app-kit/tutorials/bridge/use-forwarding-service), or
[estimate gas and provider fees](/app-kit/tutorials/bridge/estimate-costs)
before bridging. Proceed only if the cost works for you.
#### Verify the transaction
After `kit.bridge()` resolves, inspect the returned `steps` array. Each
transaction step includes an `explorerUrl`. Use those links to confirm the burn,
attestation, and mint steps for the amount you bridged.
The following code is an example of how a `burn` step might look in the browser
console after a successful bridge. The values are examples only and are not a
real transaction:
```bash Shell theme={null}
steps: [
{
name: "burn",
state: "success",
txHash: "5UfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKSNc7WLyFTmgL5RFGujWNqEbUBdNKRkHmx7ZRQR3FVhdEwxKHm",
data: {
txHash:
"5UfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKSNc7WLyFTmgL5RFGujWNqEbUBdNKRkHmx7ZRQR3FVhdEwxKHm",
status: "success",
blockNumber: 312456789n,
blockHash: "HxVTyYH9StYyHKSNc7WLyFTmgL5RFGujWNqEbUBdNK",
transactionIndex: 0,
gasUsed: 25000n,
cumulativeGasUsed: 0n,
effectiveGasPrice: 5000n,
explorerUrl:
"https://solscan.io/tx/5UfgJ5vVZxUxefDGqzqkVLHzHxVTyYH9StYyHKSNc7WLyFTmgL5RFGujWNqEbUBdNKRkHmx7ZRQR3FVhdEwxKHm?cluster=devnet",
},
},
];
```
Everything you need to build onchain finance with stablecoins: start fast, scale reliably.
Move Crosschain
Resources