Skip to content
Merged
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
31 changes: 26 additions & 5 deletions docs/MAINNET_DEPLOYMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,38 @@ Set these via environment variables or in a `.env.mainnet` file. Scripts will fa
4. Verify WASM hashes match the deployment issue.
5. Submit deployment transactions through the approved signer.
6. Record transaction hashes and deployed contract IDs.
7. Update backend production secrets with:
- `INVOICE_CONTRACT_ID`
- `TREASURY_CONTRACT_ID`
- `COMPLIANCE_CONTRACT_ID`
8. Run backend `GET /health/rpc` and a low-value end-to-end invoice payment smoke test.
7. Deploy and initialize the compliance contract:
- Deploy the compliance WASM to Soroban mainnet.
- Call `initialize` with the protocol admin address.
- Populate the initial allowlist with the admin, treasury signers, and any pre-approved merchants by calling `allow_address` for each.
- Record the `COMPLIANCE_CONTRACT_ID` in the ceremony log.
8. Deploy and initialize the invoice contract:
- Deploy the invoice WASM to Soroban mainnet.
- Call `initialize` with the protocol admin address and the deployed compliance contract address.
- Configure the grace window via `set_grace_window` if the default is not appropriate.
- Record the `INVOICE_CONTRACT_ID` in the ceremony log.
9. Deploy and initialize the treasury contract:
- Deploy the treasury WASM to Soroban mainnet.
- Call `initialize` with the protocol admin address, the list of initial signers and their weights, and the required approval threshold.
- Record the `TREASURY_CONTRACT_ID` in the ceremony log.
10. Update backend production secrets with:
- `INVOICE_CONTRACT_ID`
- `TREASURY_CONTRACT_ID`
- `COMPLIANCE_CONTRACT_ID`
11. Run backend `GET /health/rpc` and a low-value end-to-end invoice payment smoke test.

## Compliance-Specific Admin Key Handling

- The compliance contract's admin keypair **must** be distinct from the invoice and treasury admin keypairs where possible to limit blast radius in the event of key compromise.
- The compliance admin key must be stored in a separate KMS key or hardware wallet from other contract admin keys.
- During signing ceremony, the compliance `initialize` and `allow_address` transactions should be signed and submitted **before** the invoice contract is initialized, because the invoice contract references the compliance contract at initialization time.

## Abort Conditions

- Any signer mismatch
- Any WASM hash mismatch
- Soroban RPC health degraded across all configured endpoints
- Compliance contract initialization fails or `is_allowed` returns unexpected results for the initial allowlist
- Any failed low-value payment smoke test

---
Expand Down
99 changes: 59 additions & 40 deletions docs/glossary.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,33 +30,36 @@ An optional SHA-256 (or equivalent) hash of off-chain invoice metadata (e.g. lin
**payment_link_hash**
An optional hash of a payment-link URI, enabling deterministic linking between on-chain state and an off-chain checkout page.

**Grace Window**
The duration (in seconds) after an invoice's `expires_at` during which payment is still accepted. Set by the contract admin via `set_grace_window`. A grace window allows for ledger drift and network congestion without forcing merchants to reissue invoices. Configurable per deployment; default matches the invoice contract's `DEFAULT_GRACE_WINDOW` constant.

### InvoiceStatus

| Status | Meaning |
|-------------------|-------------------------------------------------------------------------|
| `Pending` | Created and awaiting payment. Can be paid, cancelled, or expired. |
| `Paid` | Marked paid by the admin. Payer and `paid_at` timestamp are recorded. |
| `Expired` | The ledger passed `expires_at` before payment. Set by `batch_expire`. |
| `Cancelled` | Cancelled by the merchant or admin before payment. |
| `RefundRequested` | The payer requested a refund on a paid invoice (initiates escrow dispute). |
| Status | Meaning |
|-------------------|-----------------------------------------------------------------------------|
| `Pending` | Created and awaiting payment. Can be paid, cancelled, or expired. |
| `Paid` | Marked paid by the admin. Payer and `paid_at` timestamp are recorded. |
| `Expired` | The ledger passed `expires_at` before payment. Set by `batch_expire`. |
| `Cancelled` | Cancelled by the merchant or admin before payment. |
| `RefundRequested` | The payer requested a refund on a paid invoice (initiates escrow dispute). |
| `Released` | Escrow funds have been released to the merchant after payment confirmation. |

### InvoiceError

| Code | Name | Trigger |
|------|-----------------------|------------------------------------------------------------------|
| 1 | `Unauthorized` | Caller is not the merchant, admin, or payer. |
| 2 | `ContractPaused` | A state-changing call was made while the contract is paused. |
| 3 | `InvalidAmount` | `amount_usdc` ≤ 0 or `gross_usdc` < `amount_usdc`. |
| 4 | `NotPending` | Operation requires `Pending` status but invoice is in another state. |
| 5 | `Expired` | Payment attempted after `expires_at`. |
| 6 | `NotFound` | No invoice exists for the given ID. |
| 7 | `AlreadyInitialized` | `initialize` called when the contract is already set up. |
| 8 | `ZeroDuration` | `expires_in_seconds` was 0 on invoice creation. |
| 9 | `ExpiryOverflow` | `ledger_timestamp + expires_in_seconds` overflows `u64`. |
| 10 | `NotPaid` | `request_refund` or `release_escrow` called on a non-`Paid` invoice. |
| 12 | `AmountPrecision` | Amount is below 1 USDC (10,000,000 stroops). |
| 13 | `DuplicateNonce` | Merchant nonce has already been used for a previous invoice. |
| Code | Name | Trigger |
|------|----------------------|----------------------------------------------------------------------|
| 1 | `Unauthorized` | Caller is not the merchant, admin, or payer. |
| 2 | `ContractPaused` | A state-changing call was made while the contract is paused. |
| 3 | `InvalidAmount` | `amount_usdc` ≤ 0 or `gross_usdc` < `amount_usdc`. |
| 4 | `NotPending` | Operation requires `Pending` status but invoice is in another state. |
| 5 | `Expired` | Payment attempted after `expires_at`. |
| 6 | `NotFound` | No invoice exists for the given ID. |
| 7 | `AlreadyInitialized` | `initialize` called when the contract is already set up. |
| 8 | `ZeroDuration` | `expires_in_seconds` was 0 on invoice creation. |
| 9 | `ExpiryOverflow` | `ledger_timestamp + expires_in_seconds` overflows `u64`. |
| 10 | `NotPaid` | `request_refund` or `release_escrow` called on a non-`Paid` invoice. |
| 12 | `AmountPrecision` | Amount is below 1 USDC (10,000,000 stroops). |
| 13 | `DuplicateNonce` | Merchant nonce has already been used for a previous invoice. |

---

Expand All @@ -77,25 +80,28 @@ The minimum cumulative signer weight required to execute a settlement. Set at in
**Token Allowlist**
An optional list of token contract addresses accepted for settlement. If non-empty, any unlisted token is rejected with `TokenNotAllowed`.

**On-Hold Settlement**
A settlement whose status is `OnHold`, meaning execution is blocked pending review. A settlement enters the on-hold state automatically when a dispute is raised against it. It can also be placed on hold by an admin for compliance, fraud, or KYC reasons (see `SettlementHoldReason`). An on-hold settlement cannot be executed until the hold is lifted.

### SettlementStatus

| Status | Meaning |
|----------------------|-------------------------------------------------------------------------|
| `Pending` | Proposed and awaiting sufficient approvals. |
| `Executed` | Full amount transferred to the merchant. |
| `PartiallyExecuted` | A partial amount was transferred. |
| `OnHold` | Blocked from execution (compliance review or open dispute). |
| `Cancelled` | Cancelled by an authorised signer before execution. |
| Status | Meaning |
|---------------------|-------------------------------------------------------------|
| `Pending` | Proposed and awaiting sufficient approvals. |
| `Executed` | Full amount transferred to the merchant. |
| `PartiallyExecuted` | A partial amount was transferred. |
| `OnHold` | Blocked from execution (compliance review or open dispute). |
| `Cancelled` | Cancelled by an authorised signer before execution. |

### SettlementHoldReason

| Variant | Meaning |
|---------------------|-------------------------------------------------|
| `None` | Not on hold (default state). |
| `ComplianceReview` | Held pending a compliance review. |
| `FraudCheck` | Held for fraud investigation. |
| `KycPending` | Held until KYC verification is complete. |
| `AdminHold` | Held by an admin for an unspecified reason. |
| Variant | Meaning |
|--------------------|---------------------------------------------|
| `None` | Not on hold (default state). |
| `ComplianceReview` | Held pending a compliance review. |
| `FraudCheck` | Held for fraud investigation. |
| `KycPending` | Held until KYC verification is complete. |
| `AdminHold` | Held by an admin for an unspecified reason. |

---

Expand All @@ -107,13 +113,16 @@ An on-chain record raised by a claimant against a counterparty over a specific s
**resolution_weight**
Cumulative weight of signers who have voted on the dispute resolution. When it reaches the treasury threshold the dispute transitions to `ResolvedClaimant` or `ResolvedCounterparty`.

**Dispute Quorum**
The minimum cumulative signer weight required to finalise a dispute resolution. This is the same value as the treasury's `threshold` — when the `resolution_weight` of signers who have voted reaches this threshold, the dispute transitions from `Raised` to `ResolvedClaimant` or `ResolvedCounterparty`.

### DisputeStatus

| Status | Meaning |
|-------------------------|-----------------------------------------------------------------|
| `Raised` | Dispute created and awaiting resolution votes. |
| `ResolvedClaimant` | Resolved in favour of the claimant. |
| `ResolvedCounterparty` | Resolved in favour of the counterparty (merchant). |
| Status | Meaning |
|------------------------|----------------------------------------------------|
| `Raised` | Dispute created and awaiting resolution votes. |
| `ResolvedClaimant` | Resolved in favour of the claimant. |
| `ResolvedCounterparty` | Resolved in favour of the counterparty (merchant). |

---

Expand All @@ -133,6 +142,16 @@ A governance process for replacing one authorised signer with another. The old s

---

## Compliance Terms

**Allowlist (Compliance)**
The set of Stellar addresses permitted to interact with protocol contracts. Maintained by the compliance contract's admin via `allow_address`, `block_address`, `allow_address_until`, and `clear_address`. The contract exposes `is_allowed` to query whether a given address is currently on the allowlist. The invoice contract checks the compliance allowlist before marking invoices as paid. An address not on the allowlist (or explicitly blocked) is rejected with an `Unauthorized` error.

**Allowlist (Token)**
See _Token Allowlist_ under Settlement Terms.

---

## Cross-Contract Workflow Summary

```
Expand Down
Loading