Soroban smart contract powering the Sharpy split payment protocol on Stellar. Handles invoice creation, multi-recipient fund distribution, escrow management, recurring billing, and agentic payment integration.
| Network | Contract ID | Status |
|---|---|---|
| Testnet | CCMN5OYWBWVVRIB3IDE2CCODM3CMGSMYQ7EV2UVBJ23DVIH2CL6FJRXP |
✅ Live (2026-08-11) |
| Mainnet | Coming soon | ⏳ Pending |
Recent testnet transactions demonstrating all features:
- Create Invoice #3 — Basic invoice creation with XLM
- Create Batch Invoice — Multiple invoices in one call
- Multi-recipient Payment — Split payment to multiple addresses
- Escrow Invoice — Invoice with escrow protection
- Recurring Invoice — Subscription invoice creation
Test Account: GD4Q2BH6KISIHTZWV5CSUMZC7VUBQAAXPNVSCESTUGH5WEYALMOTRS63 (View on Explorer)
graph TD
App["sharpy-app\nNext.js 14"]
SDK["@stellar-sharpy/sdk"]
RPC["Soroban RPC"]
Contract["Sharpy Contract\nSoroban · Protocol 27"]
Stellar["Stellar Network"]
App -->|"calls"| SDK
SDK -->|"simulate + submit"| RPC
RPC -->|"executes"| Contract
Contract -->|"ledger state + events"| Stellar
Stellar -->|"events"| SDK
- Multi-recipient invoices — split funds to any number of recipients in one transaction
- Split rules — Fixed, Percentage (validated ≤ 100%), Tiered (threshold-based)
- Multi-token support — one token per recipient (USDC, XLM, AQUA, yXLM)
- Recurring/subscription invoices — auto-generates next invoice on release
- Escrow protection — configurable release delay with optional arbitrator
- Escrow dispute mechanism — arbitrator can intervene before release
- Batch invoice creation — up to 10 invoices in a single transaction
- Pool payments — pay multiple invoices across different tokens in one call
- Structured events — for all lifecycle actions (created, payment, released, refunded, cancelled, escrow_funded)
- Invoice stats — funded/total/completion_bps/unique_payers via
get_invoice_stats - Full audit log — on-chain audit trail per invoice
- Admin circuit breaker — pause/unpause contract
- Payer index —
get_invoices_by_payertracks all invoices a payer touched (viapayandpool_pay) - Creator index —
get_invoices_by_creatorfor dashboard pagination - Fallback balance recovery —
claim()+get_claimable_balance()for failed recipient transfers (CEI pattern) - Storage TTL auto-extended — ~1 year on every write
- Treasury & tips —
get_treasury()+pay_with_tip()routes gratuity to treasury, excluded fromfunded - Freeze control —
freeze_invoice()/unfreeze_invoice()admin blocks/re-enablespay(frozen field) - Invoice notes —
set_invoice_notes()/get_invoice_notes()free-textInvoiceNotes { text, updated_at } - Recurring query —
get_recurring_params()exposes fullSubscriptionParams - Version query —
get_invoice_version()returns schema version (1)
| CAP | Protocol | Feature | Implementation |
|---|---|---|---|
| CAP-82 | 26 | Checked 256-bit arithmetic | Overflow-safe split calculations in _release() and get_invoice_stats() |
| CAP-78 | 26 | Limited TTL extension host functions | bump_invoice_ttl() — anyone can extend invoice storage lifetime |
| CAP-75 | 25 | Poseidon/crypto host functions | get_invoice_fingerprint() — SHA-256 tamper-evident content hash |
| Function | Description |
|---|---|
initialize(admin, treasury) |
Set admin and treasury addresses |
create_invoice(creator, recipients, amounts, tokens, deadline, options) |
Create invoice with split rules and escrow options |
create_batch(creator, invoices) |
Create up to 10 invoices in one transaction |
create_recurring(creator, recipients, amounts, token, deadline, interval, max) |
Create recurring invoice with auto-generation on release |
pay(payer, invoice_id, amount) |
Pay toward an invoice |
pool_pay(payer, payments) |
Pay multiple invoices in one call (multi-token) |
release_escrow(invoice_id) |
Release escrow-held funds after delay passes |
release(invoice_id) |
Manual release for fully funded invoice |
refund(invoice_id) |
Refund all payers after deadline passes |
cancel_invoice(caller, invoice_id) |
Creator cancels invoice and refunds payments |
dispute_release(invoice_id) |
Raise an escrow dispute before release |
resolve_dispute(invoice_id, release) |
Arbitrator resolves dispute — release or refund |
get_invoice(id) |
Read full invoice state |
get_invoice_stats(id) |
Get funded/total/completion_bps/payment_count/unique_payers |
get_invoice_fingerprint(id) |
SHA-256 tamper-evident content hash (Protocol 25/26) |
get_audit_log(id) |
Full audit trail as Vec |
get_payer_total(id, payer) |
Total amount paid by a specific address |
get_next_recurring(id) |
Next invoice ID in a recurring chain |
get_escrow_state(id) |
Current escrow/dispute state |
bump_invoice_ttl(id) |
Extend invoice storage TTL to prevent archival (Protocol 26 CAP-78) |
get_invoice_count() |
Total number of invoices ever created — O(1) global stat |
get_invoices_by_creator(creator) |
All invoice IDs created by an address |
get_invoices_by_payer(payer) |
All invoice IDs paid by a given address (payer index) |
get_claimable_balance(account, token) |
Claimable balance for account after failed transfer |
claim(account, token) |
Withdraw credited balance for account/token |
get_invoice_version(id) |
Invoice schema version (always 1) |
get_treasury() |
Treasury address set at initialize |
pay_with_tip(payer, id, amount, tip) |
Pay with gratuity routed to treasury (tip excluded from funded) |
freeze_invoice(id) / unfreeze_invoice(id) |
Admin freeze/unfreeze — blocks pay/pay_with_tip when frozen |
get_recurring_params(id) |
Full SubscriptionParams for recurring invoices (None if not recurring) |
set_invoice_notes(caller, id, text) / get_invoice_notes(id) |
Creator free-text notes InvoiceNotes { text, updated_at } |
pause / unpause |
Admin circuit breaker |
| Type | Behaviour | Example |
|---|---|---|
Fixed(amount) |
Pay exact amount regardless of funded total | Fixed(500_000_000) → always 50 USDC |
Percentage(bps) |
Pay funded * bps / 10_000 (validated ≤ 100%) |
Percentage(6000) → 60% of funded |
Tiered(threshold, bps) |
Pay percentage only if funded > threshold, else 0 |
Tiered(100_000_000, 5000) → 50% if funded > 10 USDC |
sharpy-contracts/
├── Cargo.toml # Workspace (soroban-sdk 26.1.0)
├── Makefile # Build/test/deploy commands
├── CONTRIBUTING.md
├── SECURITY.md
├── CODE_OF_CONDUCT.md
├── CHANGELOG.md
├── contracts/sharpy/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs # All contract logic (600+ lines)
│ ├── types.rs # Invoice, SplitRule, AuditEntry, etc.
│ ├── events.rs # Structured event helpers
│ └── test.rs # 92 unit tests (+7 features merged 2026-08-28)
└── .github/
├── workflows/ci.yml # Test + WASM build on every PR
└── ISSUE_TEMPLATE/ # Bug report, feature request
make test # cargo test (92 passing)
make build # build WASM
make optimize # optimize WASM with stellar contract optimize
make deploy-testnet # deploy to testnet
make deploy-mainnet # deploy to mainnet| soroban-sdk | Protocol | Status |
|---|---|---|
| 26.1.0 | 27 | ✅ Current |
| Repo | Description |
|---|---|
| sharpy-sdk | TypeScript SDK |
| sharpy-app | Next.js frontend dApp |
See CONTRIBUTING.md for setup, standards, and commit conventions.
See SECURITY.md for the vulnerability disclosure process.