---
# System prepended metadata

title: Nexus API doc

---

# Nexus CA: Creating and Submitting Intents (API)

This guide shows two ways to create and submit Nexus CA intents:
- Option A: Submit to the Statekeeper API
- Option B: Call the Settler contract directly on-chain

Examples are derived from a working integration flow.

## Prerequisites

- A funded (gas) test wallet on the source chain(s)
- RPC URL for each chain
- ERC-20 allowance for Settler to spend the source token
- Statekeeper service (for API path)
  - Base URL: `STATEKEEPER_URL` (default `http://localhost:8080`)
  - Endpoints: `/v1/supported-chains`, `/v1/quote`, `/v1/submit`


## Step 1: Build an Intent

Pick source/destination chains and tokens (with matching `nexus_token_id`). Create:
- A source batch with a Lock
- A destination batch with a Fund
- Inputs/outputs matching your transfer
- A strong random `nonce` (U256)

Rust outline:
```rust
// Pseudocode outline
let source_batch = Actions {
  domain: source_domain.to_string(),
  conditions: vec![],
  funds: vec![],
  settler: FixedBytes::from(source_chain.settler_contract),
  locks: vec![Lock { token: FixedBytes::from(source_token), amount: U256::from(1_000_000) }],
  actions: vec![],
};

let destination_batch = Actions {
  domain: destination_domain.to_string(),
  conditions: vec![],
  funds: vec![Fund {
    token: FixedBytes::from(destination_token),
    amount: U256::from(1_000_000),
    recipient: FixedBytes::from(padded_address_32),
  }],
  settler: FixedBytes::from(destination_chain.settler_contract),
  locks: vec![],
  actions: vec![],
};

let intent = Intent {
  domain: source_domain.to_string(),
  batches: vec![source_batch, destination_batch],
  sender: FixedBytes::from(padded_address_32),
  recipient: FixedBytes::from(padded_address_32),
  nonce, // random U256
  inputs: vec![Resource {
    token: FixedBytes::from(source_token),
    amount: U256::from(1_000_000),
    recipient: FixedBytes::from(source_chain.settler_contract),
    domain: source_domain.to_string(),
  }],
  outputs: vec![Resource {
    token: FixedBytes::from(destination_token),
    amount: U256::from(1_000_000),
    recipient: FixedBytes::from(padded_address_32),
    domain: destination_domain.to_string(),
  }],
};
```

Tip: pad EVM addresses to 32 bytes (20 address bytes + 12 zero bytes).


## Step 2: Quote the Intent (Normalize + Fees)

Send your raw Intent to `/v1/quote`. Use the response Intent for execution.

Curl:
```bash
curl -X POST "$STATEKEEPER_URL/v1/quote" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "...",
    "batches": [ ... ],
    "sender": "...",
    "recipient": "...",
    "nonce": "...",
    "inputs": [ ... ],
    "outputs": [ ... ]
  }'
```

Rust:
```rust
let quote = reqwest::Client::new()
  .post(format!("{}/v1/quote", statekeeper_url))
  .json(&intent)
  .send()
  .await?
  .json::<Intent>()
  .await?;
```

Note: If needed, align `resource.amount` with the selected lock amount (as done in tests).


## Step 3A: Execute via API (/v1/submit)

Sign the quoted Intent hash and POST the `SignedIntent` to `/v1/submit`.

Curl (structure):
```bash
curl -X POST "$STATEKEEPER_URL/v1/submit" \
  -H "Content-Type: application/json" \
  -d '{
    "intent": { ...quotedIntent... },
    "signature": "0x..."  // ECDSA signature bytes
  }'
```

Rust (simplified):
```rust
let signed = SignedIntent {
  intent: quote.clone(),
  signature: Signature::from(real_signature_bytes),
};

let resp = reqwest::Client::new()
  .post(format!("{}/v1/submit", statekeeper_url))
  .json(&signed)
  .send()
  .await?;
```

Pros:
- Easiest path: Statekeeper coordinates and indexes on-chain activity


## Step 3B: Execute Directly On-Chain (Settler.fill)

Call the Settler contract with the ABI-encoded Intent.

1) Ensure allowance:
```rust
let allowance = erc20.allowance(signer.address(), settler_address).call().await?;
if allowance < U256::from(1_200_000) {
  erc20.approve(settler_address, U256::from(10_000_000)).send().await?;
}
```

2) ABI-encode and send:
```rust
let origin_data = Intent::abi_encode(&quoted_intent);
let intent_id = quoted_intent.hash(); // [u8; 32]

let tx = INexusSettler::new(settler_address, provider.clone())
  .fill(
    intent_id.into(),
    Bytes::from(origin_data),
    Bytes::from(""), // signature if your contract requires
  )
  .send()
  .await?;

println!("fill tx hash: {:?}", tx.tx_hash());
```

Pros:
- Full control of on-chain execution flow
- Works without posting to the API (Statekeeper can still index)


## Supported Chains

Discover chain details (settler address, tokens, domains):

Curl:
```bash
curl "$STATEKEEPER_URL/v1/supported-chains"
```

Rust:
```rust
let chains = reqwest::Client::new()
  .get(format!("{}/v1/supported-chains", statekeeper_url))
  .send()
  .await?
  .json::<Vec<ChainDetails>>()
  .await?;
```


## Troubleshooting

- Revert: `ERC20: transfer amount exceeds balance`
  - Ensure your wallet holds enough tokens for the lock amount.
- Allowance too low
  - Call `approve(settler, amount)` and wait for confirmation before calling `fill`.
- Signature rejected on `/v1/submit`
  - Sign the exact Intent hash in the expected format; verify signing scheme and encoding.
- RPC differences
  - It’s fine to use your own RPCs; just ensure they match the chain.


## Minimal End-to-End (Quick Reference)

1) Build Intent (source -> destination)  
2) Quote via `/v1/quote`  
3) Adjust `resource.amount` if needed  
4) Execute:
   - API: `/v1/submit` with `SignedIntent`
   - On-Chain: `Settler.fill(intentId, originData, signature)`
