Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pages/fraxnet/addresses/fraxnet-custodians.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Frax tokens
title: FraxNet Custodians
lang: en-US
---

Expand Down
5 changes: 1 addition & 4 deletions pages/fraxnet/addresses/fraxnet-deposit.mdx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
---
title: Fraxtal contracts
title: FraxNet X-Chain Minting/Redeeming
lang: en-US
---

import * as mainnet from '~/misc/constants/mainnet';
import * as testnet from '~/misc/constants/testnet';

# FraxNet Cross Chain Minting and Redeeming Contracts

### Ethereum Mainnet
Expand Down
1 change: 1 addition & 0 deletions pages/fraxnet/api/_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"index": "Overview",
"frxusd-endpoints": "frxUSD Endpoints",
"branded-endpoints": "SaaS Stablecoin Endpoints",
"status": "Tracking & Status",
"cctp-relay": "CCTP Relay",
"reference": "Reference"
}
28 changes: 19 additions & 9 deletions pages/fraxnet/api/branded-endpoints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ These endpoints handle Stablecoins as a Service (SaaS) operations: minting SaaS

> Branded stablecoin endpoints (`/branded/*`) only support EVM chains. Solana EIDs are not accepted.

The `process*` endpoints behave the same way as their [standard frxUSD counterparts](/fraxnet/api/frxusd-endpoints): execution is handed to the orchestrator, responses include an `executionId`, calls are idempotent, and the [`pending`](/fraxnet/api/frxusd-endpoints#pending-response) and [`alreadyProcessed`](/fraxnet/api/frxusd-endpoints#already-processed-response) response shapes apply. Set your HTTP client timeout to at least 90 seconds.

---

## `GET /branded/fetchAddress`
Expand Down Expand Up @@ -62,10 +64,14 @@ Process a USDC deposit for a branded token. Mints frxUSD, mints the branded toke
"processDepositTx": "0x...",
"depositAddress": "0x...",
"asset": "0x...",
"usdcAmount": "1000000"
"usdcAmount": "1000000",
"executionId": "badfe4dc-1eb0-49fb-9f44-1672260a76bf",
"orchestrator": "fn-orc"
}
```

The cross-chain delivery leg continues after the response returns — track it with [`GET /status?id=<executionId>`](/fraxnet/api/status).

---

## `GET /branded/processRedemption`
Expand All @@ -86,34 +92,38 @@ Redeem a branded stablecoin back to frxUSD, then to USDC, and relay the USDC to
```json
{
"ok": true,
"createFraxNetDepositTx": "0x...",
"contractAlreadyDeployed": false,
"createFraxNetDepositTx": "skipped - contract already deployed",
"contractAlreadyDeployed": true,
"processRedemptionTx": "0x...",
"depositAddress": "0x...",
"asset": "0x...",
"brandedTokenAmount": "1000000",
"cctpRelay": {
"status": "pending_attestation",
"jobId": "0x...",
"destDomain": 13
}
"status": "delegated_to_orchestrator",
"executionId": "badfe4dc-1eb0-49fb-9f44-1672260a76bf"
},
"executionId": "badfe4dc-1eb0-49fb-9f44-1672260a76bf",
"orchestrator": "fn-orc"
}
```

The `cctpRelay` object follows the same semantics as [`/processRedemption`](/fraxnet/api/frxusd-endpoints#the-cctprelay-object): `delegated_to_orchestrator` means the USDC delivery is tracked through the `executionId`; `not_applicable` means the payout destination is Ethereum and no relay is needed. **The `processRedemptionTx` hash is no longer usable as a relay `jobId`** — use the `executionId`.

---

## Branded Deposit Flow (USDC → Branded Token)

1. Call `/branded/fetchAddress` to get the deposit address
2. Send USDC to the deposit address on Ethereum via CCTP `depositForBurn` from the source chain
3. Call `/scheduleCCTPRelay` to relay the CCTP message to Ethereum
4. Poll `/jobStatus` until `complete`
4. Poll `GET /status?id=<jobId>` until the relay job is `complete`
5. Call `/branded/processDeposit` to mint frxUSD, mint the branded token, and send it cross-chain
6. Poll `GET /status?id=<executionId>` until `execution.status` is `completed`

## Branded Redemption Flow (Branded Token → USDC)

1. Call `/branded/fetchAddress` to get the deposit address
2. Approve and bridge branded tokens from the source chain to the deposit address on Ethereum via the `CrossChainRouter`
3. Wait for LayerZero settlement (~1-5 minutes)
4. Call `/branded/processRedemption` to redeem the branded token to frxUSD to USDC, and relay USDC via CCTP
5. Poll `/jobStatus` until USDC arrives (~20+ minutes)
5. Poll `GET /status?id=<executionId>` until `execution.status` is `completed` (~20+ minutes for USDC to arrive)
45 changes: 39 additions & 6 deletions pages/fraxnet/api/cctp-relay.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ lang: en-US

# CCTP Relay

The Orchestration API includes a built-in CCTP relay service that handles Circle attestation polling and message relay on destination chains. Relay jobs are automatically created by `/processRedemption` and `/branded/processRedemption`, or can be manually scheduled.
The Orchestration API includes a built-in CCTP relay service that handles Circle attestation polling and message relay on destination chains. Use it to relay a `depositForBurn` transaction you submitted yourself — for example, moving USDC to Ethereum before calling a `process*` endpoint.

> Cross-chain redemptions processed through the orchestrator do **not** create relay jobs — their USDC delivery is tracked through the `executionId` returned by `/processRedemption` and `/branded/processRedemption`. The endpoints on this page apply to manually scheduled relays.

Relay `jobId`s are held in memory and do not survive a service restart. For anything with an `executionId`, prefer [`GET /status`](/fraxnet/api/status).

---

Expand All @@ -28,21 +32,35 @@ Manually schedule a CCTP relay for an existing `depositForBurn` transaction.
"jobId": "0x...",
"sourceDomain": 13,
"destDomain": 0,
"status": "pending_attestation"
"status": "waiting_for_finality"
}
```

The `jobId` equals the `txHash`. If a job already exists for that transaction, the response instead includes `"alreadyScheduled": true` with the existing job's status:

```json
{
"ok": true,
"alreadyScheduled": true,
"jobId": "0x...",
"destDomain": 0,
"status": "PendingAttestation"
}
```

(Note the `status` casing differs in the already-scheduled variant.)

---

## `GET /jobStatus`

Check the status of a CCTP relay job.
Check the status of a CCTP relay job. `GET /status?id=<jobId>` returns the same `job` object.

### Parameters

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `jobId` | `string` | Yes | Job ID returned by other endpoints |
| `jobId` | `string` | Yes | Job ID returned by `/scheduleCCTPRelay` |

### Response

Expand All @@ -51,6 +69,7 @@ Check the status of a CCTP relay job.
"ok": true,
"job": {
"job_id": "0x...",
"message_hex": "000000010000000d...",
"dest_domain": 13,
"status": "complete",
"relay_tx": "0x...",
Expand All @@ -60,6 +79,8 @@ Check the status of a CCTP relay job.
}
```

`message_hex` is the raw CCTP message parsed from the burn transaction's `MessageSent` event (informational, no `0x` prefix).

---

## `GET /activeJobs`
Expand All @@ -74,7 +95,8 @@ Summary of all CCTP relay jobs and their statuses. No parameters required.
"total_jobs": 42,
"total_active": 5,
"counts": {
"pending_attestation": 3,
"waiting_for_finality": 1,
"pending_attestation": 2,
"relaying": 2,
"complete": 30,
"already_relayed": 5,
Expand All @@ -87,20 +109,31 @@ Summary of all CCTP relay jobs and their statuses. No parameters required.
"dest_domain": 0,
"created_at": 1709729600
}
],
"failed_jobs": [
{
"job_id": "0x...",
"error": "receiveMessage submission failed: ...",
"dest_domain": 13,
"created_at": 1709729000
}
]
}
```

`total_active` counts jobs in `waiting_for_finality`, `pending_attestation`, and `relaying`.

---

## Job Status Lifecycle

| Status | Description |
| --- | --- |
| `waiting_for_finality` | Transient initial state at job creation |
| `pending_attestation` | Polling Circle's attestation API for signature |
| `relaying` | Submitting `receiveMessage()` on the destination chain |
| `complete` | USDC delivered on destination |
| `already_relayed` | Message was relayed by another party |
| `failed` | Unrecoverable error |

The relay worker polls Circle's attestation API every 15 seconds for up to ~25 minutes. Circle's attestation API handles finality internally, returning `pending_confirmations` until sufficient block finality is reached, then `complete` with the attestation signature.
The relay worker polls Circle's attestation API every 15 seconds for up to ~35 minutes. Circle's attestation API handles finality internally, returning `pending_confirmations` until sufficient block finality is reached, then `complete` with the attestation signature. If attestation times out but the message was already relayed by another party, the job resolves to `already_relayed` rather than `failed`.
87 changes: 80 additions & 7 deletions pages/fraxnet/api/frxusd-endpoints.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ lang: en-US

These endpoints handle standard frxUSD cross-chain operations: minting frxUSD from USDC deposits, and redeeming frxUSD back to USDC with cross-chain relay via CCTP.

The `process*` endpoints hand execution to the [orchestrator](/fraxnet/api) and wait synchronously for up to ~75 seconds. Set your HTTP client timeout to at least 90 seconds, and handle the [`pending`](#pending-response) and [`alreadyProcessed`](#already-processed-response) response shapes described below. All `process*` calls are idempotent — retrying converges on the same execution.

## `GET /health`

Health check returning plain text `ok`.
Expand Down Expand Up @@ -62,10 +64,16 @@ Process a USDC deposit at the deposit address on Ethereum. Mints frxUSD and send
"contractAlreadyDeployed": false,
"processDepositTx": "0x...",
"depositAddress": "0x...",
"usdcAmount": "1000000"
"usdcAmount": "1000000",
"executionId": "badfe4dc-1eb0-49fb-9f44-1672260a76bf",
"orchestrator": "fn-orc"
}
```

When the deposit contract is already deployed, `createFraxNetDepositTx` is the literal string `"skipped - contract already deployed"` and `contractAlreadyDeployed` is `true`.

`processDepositTx` is the confirmed mint transaction on Ethereum. The cross-chain LayerZero delivery leg continues after the response returns — track it to completion with [`GET /status?id=<executionId>`](/fraxnet/api/status).

---

## `GET /processRedemption`
Expand All @@ -85,19 +93,82 @@ Redeem frxUSD at the deposit address on Ethereum to USDC, then relay the USDC to
```json
{
"ok": true,
"createFraxNetDepositTx": "0x...",
"contractAlreadyDeployed": false,
"createFraxNetDepositTx": "skipped - contract already deployed",
"contractAlreadyDeployed": true,
"processRedemptionTx": "0x...",
"depositAddress": "0x...",
"frxUSDAmount": "1000000000000000000",
"cctpRelay": {
"status": "pending_attestation",
"jobId": "0x...",
"destDomain": 13
}
"status": "delegated_to_orchestrator",
"executionId": "badfe4dc-1eb0-49fb-9f44-1672260a76bf"
},
"executionId": "badfe4dc-1eb0-49fb-9f44-1672260a76bf",
"orchestrator": "fn-orc"
}
```

### The `cctpRelay` object

| `cctpRelay.status` | Meaning |
| --- | --- |
| `delegated_to_orchestrator` | The USDC delivery to the destination chain is handled by the orchestrator as part of the same execution. Poll `GET /status?id=<executionId>` until `execution.status` is `completed`. |
| `not_applicable` | The payout destination is Ethereum itself — no CCTP relay is needed. |
| `waiting_for_finality` | Legacy (pre-orchestrator) shape: a local relay job was created and `cctpRelay.jobId` / `cctpRelay.destDomain` are present. Poll `GET /status?id=<jobId>`. |

> **Breaking change for existing integrators:** cross-chain redemptions no longer create a local CCTP relay job, so the `processRedemptionTx` hash can no longer be used as a `jobId` — polling `/jobStatus` with it returns `"No job found"` even though delivery is proceeding normally. Track delivery with the `executionId` instead.

---

## Pending Response

If processing takes longer than the synchronous wait window (~75 seconds) — most commonly a first-time deposit that must also deploy the deposit contract — the API returns:

```json
{
"ok": false,
"pending": true,
"executionId": "badfe4dc-1eb0-49fb-9f44-1672260a76bf",
"orchestrator": "fn-orc",
"error": "Processing accepted and in progress; re-request to continue"
}
```

This is **not a failure**: processing continues in the background. Either re-issue the identical call (it converges on the same execution and returns the final result once ready) or poll `GET /status?id=<executionId>`.

## Already Processed Response

The orchestrator also detects and processes funded deposit addresses autonomously. If the deposit you ask about was already processed, the API answers with the transaction that actually moved the funds:

```json
{
"ok": true,
"alreadyProcessed": true,
"processDepositTx": "0x...",
"depositAddress": "0x...",
"usdcAmount": "3000000",
"executionId": "badfe4dc-1eb0-49fb-9f44-1672260a76bf"
}
```

Treat this as success and do not retry.

---

## Deposit Flow (USDC → frxUSD)

1. Call `/fetchAddress` to get the deposit address for your beneficiary and target chain
2. Send USDC to the deposit address on Ethereum (directly, or from another chain via CCTP `depositForBurn` + `/scheduleCCTPRelay`)
3. Call `/processDeposit` to mint frxUSD and send it to the target chain via LayerZero
4. Poll `GET /status?id=<executionId>` until `execution.status` is `completed`

## Redemption Flow (frxUSD → USDC)

1. Call `/fetchAddress` to get the deposit address
2. Bridge frxUSD from the source chain to the deposit address on Ethereum (LayerZero OFT send)
3. Wait for LayerZero settlement (~1-5 minutes)
4. Call `/processRedemption` to redeem frxUSD to USDC and relay it to the target chain via CCTP
5. Poll `GET /status?id=<executionId>` until `execution.status` is `completed` (~20+ minutes for cross-chain delivery)

---

## Deposit Address Verification
Expand All @@ -117,3 +188,5 @@ const address = await factory.getDeploymentAddress(
ethers.constants.HashZero // ATA (zero for EVM)
);
```

Solana targets (EID 30168) are not supported by the API — their address derivation requires the target's USDC associated token account (ATA) as the third parameter.
Loading