Core Concept

How Round-Ups Work

Buff rounds up the total transaction value (not the fee) to the nearest increment defined by the user's plan.

The Model

Just like Acorns rounds up your coffee purchase ($3.42 → $4.00, invest $0.58), Buff rounds up your onchain transaction value.

The key difference from Acorns: each plan has its own round-up increment, so users choose how aggressively they invest.

Example: $1.52 Transaction

PlanIncrementRounds toRound-up
Seed$0.05$1.55$0.03
Sprout$0.10$1.60$0.08
Tree$0.50$2.00$0.48
Forest$1.00$2.00$0.48

Example: $1.07 Transaction

PlanIncrementRounds toRound-up
Seed$0.05$1.10$0.03
Sprout$0.10$1.10$0.03
Tree$0.50$1.50$0.43
Forest$1.00$2.00$0.93

Rules

Exact Match = Skip

If the transaction value is already an exact multiple of the increment, the round-up is $0.00 and no charge is applied.

Ceiling ($1.00)

No single round-up will ever exceed $1.00. If a very small transaction with a large increment would produce a round-up above $1.00, it gets capped. Configurable via setCeiling().

The Math

round-up.ts
typescript
1// Fixed-point arithmetic to avoid floating point errors
2const scale = 1_000_000
3const scaledValue = Math.round(txValueUsd * scale)
4const scaledRound = Math.round(roundToUsd * scale)
5const scaledRemainder = scaledValue % scaledRound
6
7// Exact match
8if (scaledRemainder === 0) return { roundUpUsd: 0, skipped: true }
9
10// Round-up amount
11const raw = (scaledRound - scaledRemainder) / scale
12
13// Cap at ceiling
14if (raw > ceiling) return { roundUpUsd: ceiling, capped: true }
15
16return { roundUpUsd: raw }
Note
Buff uses fixed-point math (scaled by 1,000,000) to avoid floating point precision errors. For example, 1.00 % 0.10 returns 0.09999... in JavaScript, but Buff correctly identifies it as an exact match.

Fee Breakdown

After calculating the round-up, Buff splits it into two parts:

PartDestinationPurpose
Round-up - Buff feeUser's Buff walletAccumulates until threshold, then swapped to target asset
Buff fee (0.25-1.00%)Buff treasuryPlatform revenue — tiered by plan
preview.ts
typescript
1const breakdown = await buff.previewFees(27.63)
2
3console.log(breakdown)
4// {
5// txValueUsd: 27.63,
6// roundToUsd: 0.10, // Sprout plan
7// roundedToUsd: 27.70, // next boundary
8// roundUpUsd: 0.07, // spare change
9// buffFeePercent: 0.75,
10// buffFeeUsd: 0.000525, // Buff takes
11// userInvestmentUsd: 0.069475, // user keeps
12// skipped: false,
13// capped: false,
14// }