Skip to content

Repository files navigation

Sharpy — Advanced Split Payment Contract

Soroban Rust Tests License Version Demo

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.

image

Deployments

Network Contract ID Status
Testnet CAEWQX36RLGP2WY6ACOREDJEIGELYV3HWWUPGV3CJMC27OWGQWZHTH6T Live (2026-09-04, 184-test build)
Mainnet Coming soon Pending

Live Transaction Examples

Recent testnet transactions demonstrating all features:

Test Account: GD4Q2BH6KISIHTZWV5CSUMZC7VUBQAAXPNVSCESTUGH5WEYALMOTRS63 (View on Explorer)


Internal architecture

Contributor-oriented storage keys, state machines, and events: ARCHITECTURE.md.

Architecture

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
Loading

Features

  • 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 indexget_invoices_by_payer tracks all invoices a payer touched (via pay and pool_pay)
  • Creator indexget_invoices_by_creator for dashboard pagination
  • Fallback balance recoveryclaim() + get_claimable_balance() for failed recipient transfers (CEI pattern)
  • Storage TTL auto-extended — ~1 year on every write
  • Treasury & tipsget_treasury() + pay_with_tip() routes gratuity to treasury, excluded from funded
  • Freeze controlfreeze_invoice()/unfreeze_invoice() admin blocks/re-enables pay (frozen field)
  • Invoice notesset_invoice_notes()/get_invoice_notes() free-text InvoiceNotes { text, updated_at }
  • Invoice tags
  • Archivalarchive_invoice/unarchive_invoice/is_archived terminal invoice archiving
  • Streaming paymentscreate_stream/withdraw_vested/cancel_stream/top_up_stream cliff-gated linear vesting StreamingState
  • Composable routingset_route/get_route/resolve_route pass-through hop to another invoice ComposableRoute (self-route and 2-cycle rejected)
  • Tranche releaserelease_tranche/get_released_bps partial release in basis points TrancheState (capped at 100%)
  • Whitelist gatingset_whitelist/get_whitelist/add_whitelisted_payer/remove_whitelisted_payer creator-managed payer allowlist enforced in pay
  • Protocol feeset_protocol_fee/get_protocol_fee/preview_fee admin-set bps cut FeeConfig (pure preview, no release-path change)
  • Approval flowset_approval_config/approve_invoice/get_approval_state multi-sig prep
  • Invoice templatescreate_template/get_template reusable configs InvoiceTemplate
  • Recurring pausepause_recurring/resume_recurring/is_recurring_paused
  • Discount configset/get_discount DiscountConfig { discount_bps, updated_at }
  • Invoice metadataset/get_invoice_metadata InvoiceMetadata { entries, updated_at }
  • Deadline extensionextend_deadline(caller, id, new_deadline) creator can push deadline forward
  • Batch refundrefund_batch(caller, ids) refund up to 10 deadline-passed invoices in one tx
  • Extra memoset_invoice_memo_ext()/get_invoice_memo_ext() creator memo InvoiceExtraMemo { memo, updated_at } (256 chars) — set_invoice_tags()/get_invoice_tags() categorized InvoiceTags { tags, updated_at } (max 10, 32 chars each)
  • Recurring queryget_recurring_params() exposes full SubscriptionParams
  • Version queryget_invoice_version() returns schema version (1)

Protocol 25/26 Features

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

Contract Functions

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 }
set_invoice_tags(caller, id, tags) / get_invoice_tags(id) Creator tags InvoiceTags { tags, updated_at } (10 max)
create_stream(id, recipient, amount, start, end, cliff) / withdraw_vested(id, recipient) / cancel_stream(id, recipient) / top_up_stream(id, recipient, additional) Cliff-gated linear vesting schedule per invoice
set_route(caller, id, target) / get_route(id) / resolve_route(id) Pass-through hop to another invoice (one level)
release_tranche(caller, id, bps) / get_released_bps(id) Partial release accounting, cumulative cap 10_000 bps
set_whitelist(caller, id, payers) / get_whitelist(id) / add_whitelisted_payer / remove_whitelisted_payer Payer allowlist enforced by pay (absent = open)
set_protocol_fee(bps, collector) / get_protocol_fee() / preview_fee(amount) Admin bps fee with pure preview query
pause / unpause Admin circuit breaker

Split Rules

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

Project Structure

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                  # 184 unit tests (streaming, routing, tranche, whitelist, fee-module)
└── .github/
    ├── workflows/ci.yml             # Test + WASM build on every PR
    └── ISSUE_TEMPLATE/              # Bug report, feature request

Build & Test

make test           # cargo test (184 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

Protocol Compatibility

soroban-sdk Protocol Status
26.1.0 27 Current

Related Repos

Repo Description
sharpy-sdk TypeScript SDK
sharpy-app Next.js frontend dApp

Contributing

See CONTRIBUTING.md for setup, standards, and commit conventions.

Security

See SECURITY.md for the vulnerability disclosure process.

License

MIT

About

Soroban smart contract for advanced split payments on Stellar — recurring invoices, escrow, and batch operations

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages