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

# Set up cross-border payments (Solana)

> Process cross-border payments with atomic FX conversion on Solana using Dfns wallets, stablecoins, and policy-controlled signing workflows.

Cross-border payments require burning a source currency, applying an exchange rate, and minting the destination currency to the receiver, coordinating multiple actors (bank, sender, FX provider) across each step. This solution shows how to orchestrate that flow on Solana with Dfns wallets handling key management and transaction signing.

<Card title="Get the code" icon="github" href="https://github.com/dfns/dfns-solutions/tree/m/cross-border-payments-solana">
  dfns/dfns-solutions: cross-border-payments-solana
</Card>

**The system involves three main actors:**

<CardGroup cols={3}>
  <Card title="Bank" icon="building-columns">
    Deploys stablecoins and the payment program, mints tokens, and executes settlements
  </Card>

  <Card title="FX Provider" icon="arrow-right-arrow-left">
    Quotes the exchange rate and records the converted amount on-chain
  </Card>

  <Card title="Receiver" icon="arrow-right-to-bracket">
    Receives funds in the destination currency (e.g., tSGD)
  </Card>
</CardGroup>

**Setup**: The Bank deploys two SPL token stablecoins (tEUR and tSGD) and the cross-border payment Anchor program.

**Payment flow:**

1. The Bank initiates a payment, specifying the receiver and amount in tEUR
2. The FX Provider sets the conversion rate (how much tSGD the receiver gets)
3. The Bank executes the payment. tEUR is burned from the sender and tSGD is minted to the receiver

```mermaid theme={null}
sequenceDiagram
    participant Bank
    participant Dfns
    participant Program as Solana Program
    participant Receiver

    Bank->>Dfns: Initialize payment (500 tEUR)
    Dfns->>Program: Sign & broadcast
    FX Provider->>Dfns: Set FX rate (1 tEUR = 1.6 tSGD)
    Dfns->>Program: Sign & broadcast
    Bank->>Dfns: Execute payment
    Dfns->>Program: Sign & broadcast
    Program-->>Bank: Burn 500 tEUR
    Program-->>Receiver: Mint 800 tSGD
```

The execution is atomic: the burn and mint happen in the same Solana transaction. If either fails, nothing happens.

## Prerequisites

1. Clone the [cross-border-payments-solana solution](https://github.com/dfns/dfns-solutions/tree/m/cross-border-payments-solana)
2. Create a [Dfns organization](https://app.dfns.io) if you don't have one already, and note the organization ID
3. Create a service account for API access (see how to create one [here](/guides/developers/service-account))
4. Create **2 wallets**: Bank and Receiver on **Solana Devnet**
5. Fund the Bank wallet with devnet SOL for transaction fees (`solana airdrop 2 <BANK_WALLET_ADDRESS> --url devnet`)
6. Make sure you have installed Node.js v18+, Rust, and Cargo
7. Install **Solana CLI** and **Anchor** (v0.32+). The included `setup.sh` script can install these

## Project Structure

```text theme={null}
programs/cross-border-payment/
  src/lib.rs            Anchor program, 3 instructions + state

dfns/
  DfnsClient.ts         Dfns SDK setup, env config, exports
  broadcast.ts          Shared helper to serialize + broadcast via Dfns
  deploy-program.ts     Deploy/upgrade the Anchor program binary
  deploy-stablecoin.ts  Deploy SPL token mints with Metaplex metadata
  init-payment.ts       Initialize a payment PDA
  set-fx-rate.ts        Set the FX conversion rate
  execute-payment.ts    Execute the atomic burn/mint swap
  mint-tokens.ts        Mint tokens to any address
  server.ts             Express API server for the web UI
  ui.html               Interactive web UI (single file, no build step)

tests/
  cross-border-payment.ts   Full Anchor test suite (4 tests)

setup.sh                    One-command toolchain installer
```

## Configuration

<Steps>
  <Step title="Clone and install">
    ```bash theme={null}
    git clone https://github.com/dfns/dfns-solutions.git
    cd dfns-solutions/cross-border-payments-solana

    # Install Solana CLI, Anchor, and Node dependencies
    ./setup.sh

    # Or if you already have the toolchain:
    npm install
    ```
  </Step>

  <Step title="Set up environment variables">
    Copy the example environment file and fill in your values:

    ```bash theme={null}
    cd dfns
    cp .env.example .env
    ```

    ```bash dfns/.env theme={null}
    # Dfns API Configuration
    DFNS_API_URL=https://api.dfns.io
    DFNS_ORG_ID=or-...
    DFNS_AUTH_TOKEN=eyJ...
    DFNS_CRED_ID=Y2...
    DFNS_PRIVATE_KEY="-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----"

    # Dfns Wallet ID
    BANK_WALLET_ID=wa-...

    # Set after deploying (steps below)
    SOURCE_MINT=
    TARGET_MINT=
    PROGRAM_ID=CEMoNh21BbxrVdPM6N9xwpqFHD8dxAFkBscZqPEdfrbe
    ```

    | Variable           | Description                                                                         |
    | ------------------ | ----------------------------------------------------------------------------------- |
    | `DFNS_API_URL`     | Dfns API base URL (`https://api.dfns.io`)                                           |
    | `DFNS_ORG_ID`      | Your [organization ID](/guides/find-organization-id)                                |
    | `DFNS_AUTH_TOKEN`  | [Service account](/guides/developers/service-account) auth token                    |
    | `DFNS_CRED_ID`     | Credential ID for the signing key (found in Settings > Service Accounts)            |
    | `DFNS_PRIVATE_KEY` | Private key for [request signing](/guides/developers/signing-requests) (PEM format) |
    | `BANK_WALLET_ID`   | Wallet ID for the bank (`wa-xxxx...`)                                               |
    | `SOURCE_MINT`      | Source stablecoin mint address (set after deploying stablecoins)                    |
    | `TARGET_MINT`      | Target stablecoin mint address (set after deploying stablecoins)                    |
    | `PROGRAM_ID`       | Anchor program ID (set after deploying, or use the default)                         |
  </Step>

  <Step title="Build and test">
    Build the Anchor program and run the test suite against a local validator:

    ```bash theme={null}
    anchor build
    anchor keys sync
    anchor test
    ```

    4 tests cover the full flow: initialization, FX rate setting, atomic execution, and duplicate prevention. No Dfns credentials or devnet access required.
  </Step>
</Steps>

## Deploy

<Steps>
  <Step title="Deploy stablecoins">
    Deploy two SPL token mints that represent the source and target currencies. The bank's Dfns wallet becomes the mint authority.

    ```bash theme={null}
    npm run deploy:stablecoin -- "Test EUR" tEUR
    npm run deploy:stablecoin -- "Test SGD" tSGD
    ```

    Each command prints the mint address. Copy both into your `dfns/.env` as `SOURCE_MINT` and `TARGET_MINT`.
  </Step>

  <Step title="Deploy the program">
    Upload the compiled Anchor program to Solana Devnet. The Dfns wallet is used as the payer and upgrade authority.

    ```bash theme={null}
    anchor build
    npm run deploy:program
    ```

    The upgrade authority stays in the Dfns KMS, meaning future upgrades also go through Dfns.
  </Step>

  <Step title="Mint tokens to the sender">
    ```bash theme={null}
    npm run mint-tokens -- <SOURCE_MINT> <BANK_WALLET_ADDRESS> 1000000000
    ```

    This mints 1,000 tokens (amounts use 6 decimals: `1000000000` = 1,000.000000 tokens).
  </Step>
</Steps>

## End-to-End Payment Flow

```mermaid theme={null}
sequenceDiagram
    participant Bank
    participant Dfns
    participant Program as Solana Program
    participant Receiver

    Bank->>Dfns: init-payment (id=1, 500000 tEUR)
    Dfns->>Program: Sign & broadcast
    Note over Program: PDA created, status: PendingFX
    FX Provider->>Dfns: set-fx-rate (id=1, 800000 tSGD)
    Dfns->>Program: Sign & broadcast
    Note over Program: status: FXRateSet
    Bank->>Dfns: execute-payment (id=1)
    Dfns->>Program: Sign & broadcast
    Program-->>Bank: Burn 500000 tEUR
    Program-->>Receiver: Mint 800000 tSGD
    Note over Program: status: Completed
```

<Steps>
  <Step title="Initialize a payment">
    Create a payment record on-chain. This stores the sender, receiver, and input amount in a PDA.

    ```bash theme={null}
    npm run init-payment -- 1 <RECEIVER_ADDRESS> 500000
    ```

    * `1`: payment ID (unique per sender)
    * `500000`: amount in base units (0.5 tokens)
  </Step>

  <Step title="Set the FX rate">
    The FX provider locks in the conversion rate. In this demo, any signer can call `set_fx_rate`. A production system should restrict this to authorized accounts.

    For example, converting 0.5 tEUR at a rate of 1.6x gives 0.8 tSGD:

    ```bash theme={null}
    npm run set-fx-rate -- 1 <SENDER_ADDRESS> 800000
    ```

    * `800000`: the exact output amount in target token base units
  </Step>

  <Step title="Execute the payment">
    Trigger the atomic swap. The program burns the source tokens from the sender and mints the target tokens to the receiver:

    ```bash theme={null}
    npm run execute-payment -- 1
    ```

    The script automatically creates the receiver's token account if it doesn't exist yet.
  </Step>

  <Step title="Verify balances">
    * **Sender**: should show reduced tEUR balance
    * **Receiver**: should show new tSGD balance
  </Step>
</Steps>

## Interactive UI

The solution includes a web UI for running the full payment flow:

```bash theme={null}
npm run ui
```

Open [http://localhost:3000](http://localhost:3000). The UI provides:

* A **three-step form** to initialize a payment, set the FX rate, and execute the swap
* A **flow visualization** showing the current payment status (PendingFX → FXRateSet → Completed)
* **Live balances** for sender and receiver (source tokens, target tokens, SOL)
* **Solana Explorer links** for every transaction

## CLI Reference

| Script                      | Usage                            | Description                                     |
| --------------------------- | -------------------------------- | ----------------------------------------------- |
| `npm run deploy:stablecoin` | `-- <name> <symbol>`             | Deploy an SPL token mint with Metaplex metadata |
| `npm run deploy:program`    | `[buffer_address]`               | Deploy or upgrade the Anchor program via Dfns   |
| `npm run mint-tokens`       | `-- <mint> <recipient> <amount>` | Mint tokens to an address                       |
| `npm run init-payment`      | `-- <id> <receiver> <amount>`    | Initialize a payment PDA on-chain               |
| `npm run set-fx-rate`       | `-- <id> <sender> <amount_out>`  | Lock in the FX conversion rate                  |
| `npm run execute-payment`   | `-- <id>`                        | Execute the atomic burn/mint swap               |
| `npm run ui`                |                                  | Start the interactive web UI on port 3000       |

All amounts are in **base units** (6 decimals). To send 100 tokens, pass `100000000`.

## Settle to fiat with Payouts

If your cross-border flow ends in fiat bank deposits rather than on-chain tokens, use [Payouts](/features/payouts) to convert stablecoins (USDC, USDT, EURC) to local currency across 94 countries and 63 currencies. Payouts supports Solana. See the [usage guide](/guides/payouts) or [developer guide](/guides/developers/payouts).
