> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pesapay.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Developer quickstart

> Move USDC from an EVM chain into a Stellar wallet in about 30 lines.

This quickstart runs a CCTP deposit on testnet: USDC on Avalanche Fuji arrives as native USDC in a Stellar testnet account. Your user's own EVM wallet signs and pays gas (direct funding).

<Note>
  The Pesa SDK is in preview. Access is by request until the package is published on npm. Write to the team through [pesapay.xyz](https://pesapay.xyz).
</Note>

## Before you start

* A Stellar testnet account with a USDC trustline to Circle's testnet issuer.
* An EVM wallet on Avalanche Fuji with AVAX for gas and testnet USDC from [Circle's faucet](https://faucet.circle.com).
* A Pesa API token for the Stellar account. See [Authentication](/developers/authentication).

## Install

```bash theme={null}
npm install @pesa/sdk viem @stellar/stellar-sdk
```

## Run a deposit

```ts theme={null}
import { createWalletClient, createPublicClient, custom, http } from "viem";
import { avalancheFuji } from "viem/chains";
import { createTransfersClient, findEvmChain, STELLAR_CCTP, verifyCctpCalls } from "@pesa/sdk";

const pesa = createTransfersClient({
  baseUrl: "https://api-testnet.pesapay.xyz",
  getToken: () => sessionStorage.getItem("pesa-token"),
});

const wallet = createWalletClient({ chain: avalancheFuji, transport: custom(window.ethereum) });
const reader = createPublicClient({ chain: avalancheFuji, transport: http() });
const [evmAddress] = await wallet.requestAddresses();
const stellarAddress = "G..."; // the signed-in Stellar account

const quote = await pesa.quote({
  route: "cctp-deposit",
  source_chain: "eip155:43113",
  source_address: evmAddress,
  dest_address: stellarAddress,
  amount_in: "5000000", // 5 USDC, 6 decimals
  funding_mode: "direct",
});

const transfer = await pesa.createTransfer(quote.id, crypto.randomUUID());
const action = await pesa.getActions(transfer.id);
if (action?.kind !== "evm_calls") throw new Error("unexpected action");

const calls = action.calls.map((c) => ({ ...c, value: BigInt(c.value) }));
verifyCctpCalls(calls, {
  chain: findEvmChain("testnet", "eip155:43113"),
  stellar: STELLAR_CCTP.testnet,
  recipient: stellarAddress,
  amount6: 5_000_000n,
});

let burnHash;
for (const call of calls) {
  burnHash = await wallet.sendTransaction({ account: evmAddress, ...call });
  await reader.waitForTransactionReceipt({ hash: burnHash });
}
await pesa.submit(transfer.id, { kind: "evm_tx", hash: burnHash });
```

`verifyCctpCalls` throws if the server's calls would send the USDC anywhere but the user's own Stellar account. Always call it before the wallet prompt. A wrong field in a burn loses the funds for good.

## Watch it arrive

```ts theme={null}
let t = await pesa.getTransfer(transfer.id);
while (t.status !== "completed") {
  await new Promise((r) => setTimeout(r, 5000));
  t = await pesa.getTransfer(transfer.id);
}
console.log("credited", t.amount_out, t.explorer.destination);
```

Avalanche reaches finality in seconds, so the USDC usually lands within a minute. Base, Arbitrum and Ethereum take 15 to 19 minutes under CCTP Standard Transfer.

## Next

<CardGroup cols={2}>
  <Card title="Deposit guide" icon="arrow-down" href="/guides/deposit">
    Deposit addresses, sponsored gas and USDT0.
  </Card>

  <Card title="Withdraw guide" icon="arrow-up" href="/guides/withdraw">
    Send USDC from Stellar back to an EVM chain.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.