> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usespoke.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Reward math and edge cases

> Calculate full paths, short paths, caps, integer remainders, and zero-recipient purchases exactly.

Amounts below are integer **base units** unless explicitly marked SDUSD. SDUSD has six decimals: `1,000,000` base units equals `1.000000 SDUSD`.

## Calculate the sale reward

```text theme={null}
rawReward = floor(priceBaseUnits × commissionBps / 10,000)
reward = min(rawReward, perSaleCap)
```

The contract then checks path eligibility, available reward escrow, campaign status, and liability capacity. A preview is not a reservation: those conditions can change before the transaction executes.

For a price of `20,000,000`, a rate of `1,000` bps, and a per-sale cap of `1,500,000`, the percentage calculation produces `2,000,000`, so the cap limits the reward to `1,500,000` base units, or **1.5 SDUSD**.

## Full four-person path

Use nearest-first weights `[5000, 2500, 1500, 1000]`.

| Reward | Nearest | Parent | Next ancestor | Fourth recipient |
| -: | -: | -: | -: | -: |
| 1000 | 500 | 250 | 150 | 100 |
| 1001 | 501 | 250 | 150 | 100 |
| 1 | 1 | 0 | 0 | 0 |

The more distant allocations are rounded down. The nearest eligible contributor receives the remaining base units so that allocations sum exactly to the reserved reward. Tiny rewards can produce zero amounts at some levels; percentage displays must not imply fractional base units are paid.

## Two people with renormalization

Only the first two weights, `5000` and `2500`, are eligible. Normalize them to 10,000 basis points. The implementation gives the rounding remainder to the nearest weight, yielding `[6667, 3333]`.

For a reward of `1000`, the allocations are `[667, 333]`. For `1,000,000`, they are `[666700, 333300]`. The implementation first normalizes weights at basis-point precision, so do not replace it with a floating-point two-thirds calculation and expect identical base units.

## Two people with return-unused

The original eligible weights total `7500` bps. For a reward of `1000`, reserve `750`, allocate `[500, 250]`, and leave `250` unallocated in available rewards.

For a reward of `1001`, reserve `floor(1001 × 7500 / 10000) = 750`; allocate `[500, 250]`; the remaining `251` is unallocated. The split still conserves the reserved amount exactly.

## One person or a direct purchase

With renormalization, one eligible contributor receives the whole reward. With return-unused, that contributor receives the amount permitted by their original weight, subject to integer rounding.

A direct purchase uses node ID zero and has no eligible recipients. Its reserved distributor reward is zero; this is different from a one-person path.

## Invalid paths are rejected

The selected path must belong to the campaign and satisfy the contract's eligibility checks. An ineligible buyer or merchant in paid ancestry is not silently removed to produce a more convenient split. Refresh a rejected checkout and explain the actual issue rather than presenting a shortened fabricated path.

## Use the shared implementation

Repository contributors can use `computeReward`, `normalizeWeights`, `allocatePolicyReward`, and `simulatePolicy` from `@spoke/protocol`. The contract's `PolicyMath` is the settlement implementation. The shared test vectors and contract tests are the comparison point; keep all calculations in integers.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.