Skip to content

Soroban Payment Router Contract v1 #244

@techrebelgit

Description

@techrebelgit

Spike: Soroban Payment Router Contract v1

Summary

Build a Soroban smart contract template that can receive funds and distribute them across a configured list of recipients using basis points (bps).

This spike explores a reusable payout primitive that can later compose with Trustless Work escrows. For example, a Trustless Work escrow could release funds to a router contract, and the router could distribute those funds to multiple recipients such as contributors, partners, platforms, or automation services.

This is an exploratory open-source spike. The goal is to create a working contract template, document the design tradeoffs, and help new contributors understand programmable fund routing on Stellar/Soroban.


Background

Trustless Work provides escrow infrastructure for stablecoin payments. Its core system lets platforms create non-custodial escrows with roles, milestones, approvals, releases, and disputes. Contracts run on Stellar/Soroban, while apps can interact through the API, SDK, and open-source tools.

Relevant concepts:

  • Trustless Work escrows are role-based.
  • Funds are held in non-custodial smart contracts.
  • Actions are signed by configured addresses.
  • Escrow releases can send funds to a configured receiver.
  • Soroban enables programmable logic for roles, milestones, approvals, disputes, and fund release.

This spike does not modify Trustless Work escrow contracts. Instead, it explores a complementary contract template: a payout router that can act as a programmable receiver.


Problem

Many escrow-based workflows need to distribute funds to more than one party.

Examples:

  • A grant payout split between multiple contributors.
  • A marketplace payment split between seller, platform, and affiliate.
  • A project payment split between team members.
  • A revenue share flow between partners.
  • A small incentive paid to an automation service that triggers distribution.

Without a reusable router contract, each integration would need to build custom payout logic.


Objective

Create a Soroban smart contract template that:

  1. Stores a list of recipient addresses.
  2. Stores each recipient’s allocation in basis points.
  3. Validates that total allocation equals 10,000 bps.
  4. Receives funds into the contract.
  5. Allows distribute() to be called only by one of the configured recipients.
  6. Distributes the contract balance according to the configured bps.
  7. Emits events for transparency.

Basis Points Rule

Use basis points as the minimum allocation unit.

10,000 bps = 100%
1,000 bps = 10%
100 bps = 1%
1 bps = 0.01%

Example configuration:

Receiver A: 7,000 bps = 70%
Receiver B: 2,999 bps = 29.99%
Automation receiver: 1 bps = 0.01%
Total: 10,000 bps = 100%

For v1, do not support sub-bps precision.


Proposed Contract Behavior

Initialization

The contract should be initialized with:

  • admin address
  • token/asset contract address
  • list of recipients
  • allocation in bps for each recipient

The contract must validate:

  • recipient list is not empty
  • no recipient has 0 bps
  • total bps equals 10,000
  • duplicate recipients are either rejected or explicitly handled
  • token address is stored for future transfers

Suggested data structure:

struct Recipient {
    address: Address,
    bps: u32,
}

Receiving Funds

The contract should be able to hold funds of the configured token.

For v1, it is acceptable for the contract to distribute its current token balance when distribute() is called.


Distribution

Any configured recipient can call:

distribute()

The contract should:

  1. Identify the caller.
  2. Verify the caller is one of the configured recipients.
  3. Read the contract’s current balance for the configured token.
  4. Calculate each recipient’s share using bps.
  5. Transfer the calculated amount to each recipient.
  6. Handle rounding/remainder safely.
  7. Emit distribution events.

Access Control

For v1, distribute() should only be callable by one of the configured recipients.

This makes the contract recipient-triggered instead of admin-triggered.

Rationale:

  • No privileged backend signer required.
  • No external automation service is required by default.
  • Any receiver has an incentive to trigger distribution.
  • A future automation service can be added as a recipient with a small bps allocation.

Rounding / Remainder Handling

Because integer division may create a remainder, the implementation must explicitly define how leftover units are handled.

Recommended v1 approach:

  • Calculate each recipient share using integer math.
  • Track total distributed amount.
  • Send the remainder to the first configured recipient or keep it in the contract.
  • Document the chosen behavior clearly.

The contributor should explain the tradeoff in the final README.


Events

The contract should emit events for:

  • router initialized
  • funds distributed
  • recipient paid
  • unauthorized caller rejected, if applicable

Example event ideas:

router_initialized(admin, token, recipient_count)
distribution_executed(caller, total_amount)
recipient_paid(recipient, amount, bps)

Suggested Repository Structure

payment-router/
  contracts/
    router/
      src/
        lib.rs
        storage.rs
        errors.rs
        events.rs
        test.rs
      Cargo.toml
  README.md
  ARCHITECTURE.md

Deliverables

The contributor should submit:

  1. A working Soroban payment router contract.
  2. Unit tests for initialization, authorization, distribution, and rounding behavior.
  3. A README explaining how the contract works.
  4. An architecture note explaining design decisions and limitations.
  5. Example recipient configurations.
  6. Instructions for local testing.

Required Tests

At minimum, include tests for:

Initialization

  • Initializes with valid recipients and bps.
  • Rejects if total bps is less than 10,000.
  • Rejects if total bps is greater than 10,000.
  • Rejects empty recipient list.
  • Rejects recipient with 0 bps.

Authorization

  • Allows distribute() when caller is a configured recipient.
  • Rejects distribute() when caller is not a configured recipient.

Distribution

  • Distributes funds according to configured bps.
  • Handles multiple recipients.
  • Handles small balances.
  • Handles rounding/remainder behavior.
  • Does not distribute when balance is zero.

Example Scenario

A marketplace wants to split a released escrow payment between three parties:

Seller: 8,500 bps = 85%
Platform: 1,499 bps = 14.99%
Automation service: 1 bps = 0.01%

Flow:

  1. Router is deployed with the three recipients.
  2. Funds are sent to the router contract.
  3. Any configured recipient calls distribute().
  4. Router splits the balance according to bps.
  5. Each recipient receives their share.

In a future Trustless Work integration, the router contract could be configured as the receiver of an escrow release.


Out of Scope for v1

Do not include:

  • UI implementation
  • Trustless Work escrow contract changes
  • Direct Trustless Work API integration
  • Multiple token support
  • Dynamic percentage updates after initialization
  • Admin-controlled distribution
  • Off-chain webhook automation
  • Production audit guarantees
  • Mainnet deployment

Future Extensions

Possible v2 or follow-up spikes:

  1. Backend automation function that calls distribute().
  2. Trustless Work demo where escrow funds are released to the router.
  3. Support for updating recipients before funds arrive.
  4. Support for multiple assets.
  5. Support for minimum distribution thresholds.
  6. Support for recurring distributions.
  7. Support for contract-based automation services.
  8. Frontend dashboard for configuring router recipients.
  9. Indexer integration to track distributions.
  10. Audit-focused review of edge cases.

Definition of Done

This spike is complete when:

  • The contract compiles successfully.
  • The contract can be initialized with recipient addresses and bps allocations.
  • The contract validates that total bps equals 10,000.
  • The contract can hold the configured token.
  • A configured recipient can call distribute().
  • A non-recipient cannot call distribute().
  • Funds are distributed according to bps.
  • Rounding behavior is implemented and documented.
  • Unit tests cover the required cases.
  • README and architecture notes are included.
  • The contributor documents limitations and recommended next steps.

Contributor Notes

This task is intentionally scoped as a learning-oriented open-source spike.

The main goal is not just to write a contract, but to reason about how programmable payout infrastructure can compose with escrow systems.

As you work, document:

  • what was straightforward
  • what was confusing
  • what assumptions were made
  • what would need to change for production readiness
  • how this could integrate with Trustless Work in a future spike

A strong submission should help the next contributor understand both the code and the product reasoning behind it.



::contentReference[oaicite:0]{index=0}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions