> For the complete documentation index, see [llms.txt](https://docs.zebec.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.zebec.io/developer-docs/partner-api/topup-flow/evm.md).

# EVM example

Execute a Carbon card top-up from an EVM wallet through the Zebec Partner API.

Use this flow for quotes whose `payment.chain` is `ETHEREUM`, `BASE`, `BINANCE`, or `POLYGON`. It applies to the EVM network returned by the quote, not to a network selected from a wallet default.

Start with the shared [top-up flow](/developer-docs/partner-api/topup-flow.md) through preflight. Use the same EVM account for preflight, execution, and the final signature.

## Request and inspect the quote

```http
GET /partner/v1/topup/quote?amount=100&currencyCode=USD&sourceChain=BASE&sourceTokenMint=<token-contract>
Authorization: Bearer <accessToken>
```

For an EVM quote, inspect `payment.flow`:

* `BUY_CARD_DIRECT` — pay the configured Zebec Card contract using the direct USDC purchase route.
* `SWAP_AND_BUY` — execute the quote-provided route and card-contract call; the swap output is routed to the card contract.

Use the quoted `cardContractAddress`, token amounts, output amounts, and, for a swap, the complete `swapQuote`. A plain ERC-20 transfer is not a valid EVM top-up receipt.

## Execute with the Partners Card SDK

The preferred wallet integration is the Partners Card SDK's EVM `purchaseCard()` method. Pass the Partner API quote through the integration adapter together with the user's EVM signer. The adapter must preserve the quote's contract-bound data; it must not rebuild a route or change an amount.

```ts
const { receipt } = await partnerCardService.purchaseCard({
  amount: quote.sourceAmount,
  quote: partnerQuoteAdapter.toSdkQuote(quote),
  token: partnerQuoteAdapter.toSdkToken(quote),
  cardProgramId: program.programId,
  recipient,
});

const txHash = receipt.hash;
```

`purchaseCard()` selects the direct `buyCardDirect` or quote-provided `swapAndBuy` path. If your integration calls the contract directly, reproduce the exact same quote-authorized path and do not replace it with a transfer.

## Sign and submit

Sign the exact `quote.signingMessage` with EIP-191 `personal_sign` semantics, then submit the confirmed transaction hash:

```ts
const buyerAddress = await signer.getAddress();
const signature = await signer.signMessage(quote.signingMessage);

await partnerApi.submitTopup({
  quoteId: quote.id,
  receipt: { deposit: { txHash, buyerAddress } },
  userSignature: {
    signature,
    publicKey: buyerAddress,
    scheme: "evm",
  },
});
```

`buyerAddress` must be the address used for preflight and for the contract transaction. Wait for the transaction receipt before submission.
