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

# Cross-Chain Transfers

> Move PAXGy between Ethereum and X Layer, or mint on Ethereum straight onto X Layer

PAXGy moves between Ethereum mainnet and X Layer over [Chainlink CCIP](https://docs.chain.link/ccip). The tokens are burned on the source chain and issued on the destination. There is no wrapped representation and no liquidity pool.

Two flows:

* **Transfer.** Move PAXGy you already hold, in either direction. `GET /v2/amplify/bridge`
* **Mint and transfer.** Deposit PAXG on Ethereum and receive PAXGy on X Layer, in one transaction. `GET /v2/amplify/deposit` with cross-chain parameters

<Info>
  PAXG exists only on Ethereum, so minting and redemption are Ethereum-only.
  Transfers move PAXGy in both directions, but to redeem, the PAXGy has to come
  back to Ethereum first.
</Info>

## Transferring held PAXGy

```bash theme={null}
curl -G "https://api.paxoslabs.com/v2/amplify/bridge" \
  -H "x-api-key: pxl_your_key" \
  --data-urlencode "vaultAddress=0x6c6494Fd9962eB98B94ffA48F6679058F820700e" \
  --data-urlencode "shareAmount=1000000000000000000" \
  --data-urlencode "userAddress=0xYourWallet" \
  --data-urlencode "chainId=1" \
  --data-urlencode "destinationChainId=196" \
  --data-urlencode "messageGas=100000"
```

### Parameters

| Parameter                  | Type     | Required | Description                                                                |
| -------------------------- | -------- | -------- | -------------------------------------------------------------------------- |
| `vaultAddress`             | `string` | Yes      | The PAXGy token address                                                    |
| `shareAmount`              | `string` | Yes      | PAXGy to move, in base units                                               |
| `userAddress`              | `string` | Yes      | Wallet sending the transaction                                             |
| `chainId`                  | `number` | Yes      | Source chain                                                               |
| `destinationChainId`       | `number` | Yes      | Destination chain: `1` for Ethereum, `196` for X Layer                     |
| `destinationChainReceiver` | `string` | No       | Who receives the PAXGy on the destination chain. Defaults to `userAddress` |
| `messageGas`               | `string` | Yes      | Gas to buy for execution on the destination chain                          |

### Response

```json theme={null}
{
  "transaction": {
    "to": "0x41AB5d8387e60E30AACCF11357C306f6d875A6f7",
    "data": "0x...",
    "value": "431000000000000"
  },
  "fee": {
    "raw": "431000000000000",
    "formatted": "0.000431",
    "decimals": 18
  },
  "lane": {
    "destinationChainId": 196,
    "targetTeller": "0x41AB5d8387e60E30AACCF11357C306f6d875A6f7",
    "minimumMessageGas": "0",
    "messageGasLimit": "100000"
  }
}
```

## The delivery fee must be exact

Cross-chain delivery costs a fee in the **source chain's native token**, on top of gas. It is already set as `transaction.value`, and `fee` reports the same number for display.

<Warning>
  Send `transaction.value` exactly as returned. The transaction is rejected
  on-chain if the value differs **at all**, including if it is too high, so
  padding it "to be safe" will fail. The quote is priced at the current block
  and moves with gas conditions: request it again if the user does not confirm
  promptly.
</Warning>

Show `fee.formatted` in your confirmation UI. A user approving a transfer sees a native-token charge they did not ask for otherwise.

```typescript theme={null}
const hash = await walletClient.sendTransaction({
  to: prepared.transaction.to,
  data: prepared.transaction.data,
  value: BigInt(prepared.transaction.value), // exact, never rounded or padded
  chainId: 1,
})
```

## Choosing `messageGas`

`messageGas` buys execution on the destination chain. Each route accepts a range, returned on every response:

```typescript theme={null}
prepared.lane.minimumMessageGas // "0"
prepared.lane.messageGasLimit   // "100000"
```

A value outside that range returns a `400` naming both bounds, so you can correct it without guessing. If you have no reason to pick otherwise, request `messageGasLimit`.

## Minting onto X Layer

Because PAXG only exists on Ethereum, the mint leg always starts there, so `chainId` is `1`. Pass the cross-chain fields to have the PAXGy issued on X Layer instead of Ethereum, in the same transaction:

```bash theme={null}
curl -G "https://api.paxoslabs.com/v2/amplify/deposit" \
  -H "x-api-key: pxl_your_key" \
  --data-urlencode "vaultAddress=0x6c6494Fd9962eB98B94ffA48F6679058F820700e" \
  --data-urlencode "depositAsset=0x45804880De22913dAFE09f4980848ECE6EcbAf78" \
  --data-urlencode "depositAmount=1000000000000000000" \
  --data-urlencode "userAddress=0xYourWallet" \
  --data-urlencode "chainId=1" \
  --data-urlencode "destinationChainId=196" \
  --data-urlencode "messageGas=100000"
```

Three differences from a plain mint:

* `transaction.value` carries the delivery fee, with the same exactness rule.
* **No mint fee applies** on this route.
* `to` and `permitSignature` are rejected with a `400`. The PAXGy is issued on the destination chain, so a source-chain `to` cannot be honoured. Use `destinationChainReceiver` instead. And a single transaction cannot both consume a permit and transfer across chains, so approve PAXG first.

## Errors

| Status | Cause                                                                                                                                          |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | No route open to `destinationChainId`, `messageGas` outside the accepted range, or `to` / `permitSignature` combined with `destinationChainId` |
| `403`  | This wallet is not permitted to move PAXGy across chains                                                                                       |
| `503`  | The chain is unreachable and the fee could not be priced                                                                                       |

<Info>
  A `403` is a property of the wallet, not a transient failure, so retrying will
  not help. Cross-chain transfers are enabled per address as part of
  [access approval](/v1.0.0/intro/products/paxgy/overview#access).
</Info>

## Settlement time

The source transaction confirming does not mean the PAXGy has arrived. CCIP finalizes on its own schedule, and the destination balance appears after that. Model the gap explicitly: show the transfer as in-flight and poll the destination balance rather than assuming completion on the source receipt.

## Next

<CardGroup cols={2}>
  <Card title="Minting" icon="arrow-down" href="/v1.0.0/intro/products/paxgy/api-calldata/minting">
    Deposit PAXG and receive PAXGy.
  </Card>

  <Card title="Direct Contract" icon="file-code" href="/v1.0.0/intro/products/paxgy/developers">
    The same transfer without the API.
  </Card>
</CardGroup>
