Concept · Scale-ratio math

Splits without decryption

When the contract can't see operands, percentages turn into fixed-denominator integers. Scale-ratio is the SDK's bigint primitive for that.

Splitting an encrypted balance — say, transferring 1/3 of Mira's vesting to a new recipient — looks easy in plaintext: multiply by 1/3. Inside FHEVM, the contract can't see the operands. It can compute FHE.mul(handle, plaintext), but only on integer plaintexts. So the SDK encodes the ratio as a fixed-denominator scaled numerator and lets the contract do the on-chain multiply.

The denominator#

FHE_SPLIT_DENOMINATOR is a public constant (currently 10^18). Any ratio gets scaled by this denominator and rounded down before reaching the contract. The contract performs FHE.mul(handle, scaledNum) / FHE_SPLIT_DENOMINATOR inside its own computation.

The helper#

components/split.ts
ts
import { scaleRatio, FHE_SPLIT_DENOMINATOR } from "@tokenops/sdk/fhe";

// 33.3...% split: 1/3 of the original vesting moves to the new recipient.
const ratio = scaleRatio({ numerator: 1n, denominator: 3n });
// ratio.scaledNumerator === FHE_SPLIT_DENOMINATOR / 3 (rounded down)

await manager.splitVesting({
  vestingId,
  newRecipient,
  ratio: ratio.scaledNumerator,
});

When ratios fail#

Ratios > 1 throw at the SDK boundary — scaleRatio({ numerator: 4n, denominator: 3n }) is a programming error, not a runtime decision. The split-vesting contract also rejects ratios > 1e18 to refuse over-allocation even if the caller bypasses the SDK.

See also