Stellar & Soroban
This page documents the on-chain Soroban contract that powers Skyhitz: storage layout, public methods, units, thresholds, and the reward math (APR and earnings). It also clarifies when user actions grant equity shares vs. when they are micro‑spends that only boost escrow/APR.
Storage & Types
Contract state per entry:
id: string
tvl: i128
(stroops)escrow: i128
(stroops)apr: i128
(percent integer)shares: Map<address, i128>
(stroops)withdrawn_earnings: Map<address, i128>
Global keys (DataKey
):
Index
: Vector of all entry idsEntries(id)
: TheEntry
for a given idNetwork
: "testnet" | "public" (affects native token address)Admin
: Address authorized to perform administration calls
Precision constants:
- Lumens to stroops: 1 XLM = 10,000,000 stroops
- Internal proportional math scale:
SCALE = 1_000_000
(6 decimals)
Key calls:
invest(user, id, amount i128)
: amount in stroops (1 XLM = 10,000,000)claim_earnings(user, id) -> i128
: returns claimed amount (stroops)
Administration and helpers:
set_entry(entry)
: Admin only. Upserts an entry and appends to indexremove_entry(id)
: Admin only. Removes entry and prunes indexget_entry(id) -> Entry
: Read the current on-chain entryversion() -> u32
: Contract version markerinit(admin, network, ids[])
: One-time initializer (admin/network/index)upgrade(new_wasm_hash)
: Admin only. Code upgradeget_xlm_address()
: Resolve native token address for current network
Partition on mining (off-chain logic then on-chain calls):
- Escrow:
min(1 XLM * topAPR%, 0.3 XLM)
- Remaining: split 50/50 — issuer payment (payment op), user equity invest (contract)
Notes:
Public Methods – Detailed
set_entry(entry: Entry)
- Auth: Admin
- Effect: Persists/overwrites the entry by id, appends id to
Index
- Use: Bootstrapping entries or administrative correction
remove_entry(id: String)
- Auth: Admin
- Effect: Deletes
Entries(id)
and rebuildsIndex
without it - Use: Hard removal when an entry is de-listed. Pairs with off-chain R2/Algolia cleanup
get_entry(id: String) -> Entry
- Read-only state accessor. Panics if not found
- Returned fields are integers (
i128
) in stroops or percent forapr
version() -> u32
- Bumped when meaningful changes occur; helpful for client compatibility
init(admin: Address, network: String, ids: Vec<String>)
- Auth: First call only (guards against re-init)
- Effect: Sets admin/network; optionally seeds new zeroed entries for provided ids and adds them to
Index
upgrade(new_wasm_hash: BytesN<32>)
- Auth: Admin
- Effect: Hot‑swap the contract code to the provided WASM hash
invest(user: Address, id: String, amount: i128)
- Units:
amount
is in stroops - Threshold:
download_amount = 3_000_000
stroops (0.3 XLM) - Flow:
user.require_auth()
- Load or lazily create
Entry
forid
- If
amount > 3_000_000
, user receives equity shares andtvl += amount
- Always
escrow += amount
- Recompute
apr
withget_apr(entry)
- Persist and call
transfer(user -> contract, amount)
using the native token client
Implications:
-
Equity vs Micro‑spend: The comparison is strictly
>
(greater than) the 0.3 XLM threshold.- If
amount > 0.3 XLM
: equity shares are credited (user ownership increases) and TVL increases - If
amount <= 0.3 XLM
: NO shares are credited; only escrow increases
- If
-
Micro‑spend actions in Skyhitz (stream end, like, download) are configured at or below 0.3 XLM, so they do not grant shares but DO increase escrow. Because APR is a function of
(escrow - tvl) / tvl
, these micro‑spends can raise APR, benefiting equity holders.
claim_earnings(user: Address, id: String) -> i128
- Auth:
user
- Definitions:
- Earnings pool:
max(escrow - tvl, 0)
- User fraction:
user_shares / tvl
(computed with 6‑dec scale) - Claimable:
user_fraction * earnings_pool - withdrawn_earnings[user]
- Earnings pool:
- Flow:
- Verify user has non‑zero shares
- Compute earnings pool and scaled fraction
- Subtract already withdrawn amount, clamp at 0
- Update
withdrawn_earnings[user] += claimable
- Reduce
escrow -= claimable
- Recompute
apr
transfer(contract -> user, claimable)
- Return
claimable
APR Calculation
APR is computed in‑contract as a simple percent derived from excess escrow:
if tvl == 0 or escrow <= tvl:
apr = 0
else:
apr = ((escrow - tvl) * 100) / tvl
- Internally the contract uses
SCALE = 1_000_000
to avoid precision loss when dividing, but the result is effectively an integer percent. - Off‑chain we surface this APR in Algolia and use
ratingReplicaIndex
to determine the highest APR for the mining partition.
When do users get shares?
- Equity (shares) is granted only when a single
invest
call usesamount > 0.3 XLM
. - Micro‑spends (≤ 0.3 XLM):
- Stream completion spend
- Like spend
- Download spend
- These increase
escrow
but notshares
ortvl
.
Mining nuances
Mining an external track makes two invest
calls:
- Escrow invest (capped by
min(1 XLM * topAPR%/100, 0.3 XLM)
): may or may not exceed the threshold. It's typically ≤ 0.3 XLM and thus does not credit shares. - Equity invest with half of the remaining amount: this is typically > 0.3 XLM and credits shares to the miner.
Between those, a separate Stellar payment sends the other half to the issuer (off‑chain payment, not a contract call). Only the two invest
calls interact with the contract state.
- Always convert lumens to stroops (1e7) when calling contract
- Read-only previews avoid unnecessary invocations