Skip to content

testing: Validate full escrow flow using native XLM as trustline #83

@armandocodecr

Description

@armandocodecr

The Trustless Work escrow contract uses a trustline field to specify which Stellar asset is locked and transferred during the escrow lifecycle. To date, all testing has been done exclusively with USDC.

The contract is asset-agnostic — it accepts any valid Stellar Asset Contract (SAC) address as the trustline. However, native XLM has not been tested and behaves differently from issued assets:

  • XLM does not require a changeTrust operation before receiving funds.
  • Its SAC address must be resolved via CLI before using it (see Step 2).
  • XLM uses 7 decimal places: 1 XLM = 10_000_000 stroops. All amount values in the contract are in the asset's smallest unit.

This issue tracks the community effort to validate the complete escrow lifecycle end-to-end on Testnet using native XLM, interacting directly with the deployed contract via the Stellar CLI — no API or dApp required.


Goal

Confirm that all escrow contract functions work correctly when XLM is used as the trustline asset, and surface any edge cases or errors specific to XLM.


Prerequisites

Install and configure everything before starting.

1. Install Rust and the wasm32 target

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup target add wasm32v1-none

2. Install the Stellar CLI

# macOS/Linux via Homebrew
brew install stellar-cli

# or via cargo
cargo install --locked stellar-cli --features opt

3. Configure Testnet

stellar network add \
  --global testnet \
  --rpc-url https://soroban-testnet.stellar.org:443 \
  --network-passphrase "Test SDF Network ; September 2015"

4. Create and fund test identities

You need at least two identities. We'll use alice (service provider / receiver) and bob (approver / release signer / platform / dispute resolver).

stellar keys generate --global alice --network testnet
stellar keys generate --global bob --network testnet

# Fund both accounts via Friendbot
stellar keys fund alice --network testnet
stellar keys fund bob --network testnet

Check their public keys with:

stellar keys address alice
stellar keys address bob

Step-by-Step Tasks

Step 1 — Clone the repo and build the contract

git clone https://github.com/Trustless-Work/Trustless-Work-Smart-Escrow.git
cd Trustless-Work-Smart-Escrow
stellar contract build

This generates the WASM at target/wasm32v1-none/release/escrow.wasm.


Step 2 — Resolve the native XLM SAC address

The native XLM asset has a Soroban contract representation. Resolve it with:

stellar contract id asset \
  --asset native \
  --network testnet \
  --source alice

Save this address — you will use it as the trustline.address in the next step.

# Example: store it in a variable
XLM_SAC=$(stellar contract id asset --asset native --network testnet --source alice)
echo $XLM_SAC

Step 3 — Deploy the escrow contract

# Upload the WASM and get its hash
WASM_HASH=$(stellar contract install \
  --network testnet \
  --source alice \
  --wasm target/wasm32v1-none/release/escrow.wasm)

echo "WASM hash: $WASM_HASH"

# Deploy a new contract instance
CONTRACT_ID=$(stellar contract deploy \
  --wasm-hash $WASM_HASH \
  --source alice \
  --network testnet)

echo "Contract ID: $CONTRACT_ID"

Save CONTRACT_ID — every subsequent command uses it.


Step 4 — Initialize the escrow

Replace <ALICE_ADDRESS> and <BOB_ADDRESS> with the outputs of stellar keys address alice/bob. Replace <XLM_SAC> with the value from Step 2.

stellar contract invoke \
  --id $CONTRACT_ID \
  --source alice \
  --network testnet \
  -- \
  initialize_escrow \
  --escrow_properties '{
    "engagement_id": "xlm-test-001",
    "title": "XLM Escrow Test",
    "description": "Testing native XLM as trustline",
    "amount": "20000000",
    "platform_fee": 300,
    "receiver_memo": "0",
    "roles": {
      "approver": "<BOB_ADDRESS>",
      "service_provider": "<ALICE_ADDRESS>",
      "platform_address": "<BOB_ADDRESS>",
      "release_signer": "<BOB_ADDRESS>",
      "dispute_resolver": "<BOB_ADDRESS>",
      "receiver": "<ALICE_ADDRESS>"
    },
    "milestones": [
      {
        "description": "Milestone 1: Setup",
        "status": "Pending",
        "evidence": "",
        "approved": false
      },
      {
        "description": "Milestone 2: Delivery",
        "status": "Pending",
        "evidence": "",
        "approved": false
      }
    ],
    "flags": {
      "disputed": false,
      "released": false,
      "resolved": false
    },
    "trustline": {
      "address": "<XLM_SAC>"
    }
  }'

amount: 20000000 = 2 XLM in stroops. platform_fee: 300 = 3% in basis points.


Step 5 — Fund the escrow

Alice sends 2 XLM to the contract. The amount must match what was set in initialize_escrow.

stellar contract invoke \
  --id $CONTRACT_ID \
  --source alice \
  --network testnet \
  -- \
  fund_escrow \
  --signer <ALICE_ADDRESS> \
  --expected_escrow '{
    "engagement_id": "xlm-test-001",
    "title": "XLM Escrow Test",
    "description": "Testing native XLM as trustline",
    "amount": "20000000",
    "platform_fee": 300,
    "receiver_memo": "0",
    "roles": {
      "approver": "<BOB_ADDRESS>",
      "service_provider": "<ALICE_ADDRESS>",
      "platform_address": "<BOB_ADDRESS>",
      "release_signer": "<BOB_ADDRESS>",
      "dispute_resolver": "<BOB_ADDRESS>",
      "receiver": "<ALICE_ADDRESS>"
    },
    "milestones": [
      {"description": "Milestone 1: Setup", "status": "Pending", "evidence": "", "approved": false},
      {"description": "Milestone 2: Delivery", "status": "Pending", "evidence": "", "approved": false}
    ],
    "flags": {"disputed": false, "released": false, "resolved": false},
    "trustline": {"address": "<XLM_SAC>"}
  }' \
  --amount 20000000

Verify the contract holds the XLM:

stellar contract invoke \
  --id $CONTRACT_ID \
  --source alice \
  --network testnet \
  -- \
  get_escrow

Step 6 — Update milestone status

Alice (service provider) marks each milestone as completed and provides evidence.

# Milestone 0
stellar contract invoke \
  --id $CONTRACT_ID \
  --source alice \
  --network testnet \
  -- \
  change_milestone_status \
  --milestone_index 0 \
  --new_status "Completed" \
  --new_evidence "https://github.com/your-repo/pull/1" \
  --service_provider <ALICE_ADDRESS>

# Milestone 1
stellar contract invoke \
  --id $CONTRACT_ID \
  --source alice \
  --network testnet \
  -- \
  change_milestone_status \
  --milestone_index 1 \
  --new_status "Completed" \
  --new_evidence "https://github.com/your-repo/pull/2" \
  --service_provider <ALICE_ADDRESS>

Step 7 — Approve milestones

Bob (approver) approves each milestone.

# Approve milestone 0
stellar contract invoke \
  --id $CONTRACT_ID \
  --source bob \
  --network testnet \
  -- \
  approve_milestone \
  --milestone_index 0 \
  --approver <BOB_ADDRESS>

# Approve milestone 1
stellar contract invoke \
  --id $CONTRACT_ID \
  --source bob \
  --network testnet \
  -- \
  approve_milestone \
  --milestone_index 1 \
  --approver <BOB_ADDRESS>

Step 8 — Release funds

Bob (release signer) triggers the release. The contract will split:

  • 0.30% → Trustless Work protocol address
  • 3% → Bob (platform fee)
  • Remainder → Alice (receiver)
stellar contract invoke \
  --id $CONTRACT_ID \
  --source bob \
  --network testnet \
  -- \
  release_funds \
  --release_signer <BOB_ADDRESS> \
  --trustless_work_address <TRUSTLESS_WORK_PROTOCOL_ADDRESS>

The Trustless Work protocol address for Testnet can be found in the official documentation.


Step 9 (Optional) — Test the dispute flow

This step is optional but highly valuable. Start fresh with a new deployed contract and funded escrow, then:

# 1. Open a dispute (alice or bob, but NOT the dispute_resolver)
stellar contract invoke \
  --id $CONTRACT_ID \
  --source alice \
  --network testnet \
  -- \
  dispute_escrow \
  --signer <ALICE_ADDRESS>

# 2. Resolve the dispute — distribute the full balance between parties
#    The sum of all values in distributions MUST equal the contract's XLM balance
stellar contract invoke \
  --id $CONTRACT_ID \
  --source bob \
  --network testnet \
  -- \
  resolve_dispute \
  --dispute_resolver <BOB_ADDRESS> \
  --trustless_work_address <TRUSTLESS_WORK_PROTOCOL_ADDRESS> \
  --distributions '{"<ALICE_ADDRESS>": "12000000", "<BOB_ADDRESS>": "8000000"}'

Evidence Submission

When commenting on this issue to report your results, paste the following template filled with your real data:

## XLM Flow Testing — Evidence Report

**Tester:** @your-github-handle  
**Date:** YYYY-MM-DD  
**Network:** Stellar Testnet  
**Contract ID:** C...  
**XLM SAC address used:** C...  

### Transaction Hashes

| Step | Function | TX Hash | Stellar Expert Link |
|---|---|---|---|
| 3 | Deploy | `...` | [link](https://stellar.expert/explorer/testnet/tx/...) |
| 4 | `initialize_escrow` | `...` | [link](...) |
| 5 | `fund_escrow` | `...` | [link](...) |
| 6a | `change_milestone_status` (0) | `...` | [link](...) |
| 6b | `change_milestone_status` (1) | `...` | [link](...) |
| 7a | `approve_milestone` (0) | `...` | [link](...) |
| 7b | `approve_milestone` (1) | `...` | [link](...) |
| 8 | `release_funds` | `...` | [link](...) |
| 9 (opt) | `dispute_escrow` | `...` | [link](...) |
| 9 (opt) | `resolve_dispute` | `...` | [link](...) |

### Fee Verification (release_funds)

| Recipient | Expected (stroops) | Received (stroops) |
|---|---|---|
| Alice (receiver) | `20000000 - 60000 - 600000 = 19340000` | `?` |
| Trustless Work (0.30%) | `60000` | `?` |
| Bob / platform (3%) | `600000` | `?` |

### Observations

[Describe any errors, unexpected behavior, or XLM-specific issues encountered.]

Acceptance Criteria

  • Steps 1–8 completed successfully with no contract errors.
  • Fee amounts in the evidence table match the expected values.
  • All transaction hashes are verifiable on Stellar Expert Testnet.
  • Evidence report posted as a comment in this issue using the template above.

Metadata

Metadata

Assignees

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