NimbusProtocol
Architecture

uUSDv

A Token-2022 token whose every unit is a share of the USDv the uUSDv program holds, worth more USDv as Solomon Labs' rewards are released to holders at one price for wrapping and unwrapping.

uUSDv carries Solomon Labs' USDv yield into places USDv itself earns nothing. One uUSDv is a share of the USDv the uUSDv program holds. Solomon pays rewards to the program, and the program releases them to every share, so a uUSDv balance never changes while each unit becomes worth more USDv. Anyone may wrap or unwrap at any time, without permission and without a fee, and use uUSDv anywhere a Token-2022 token goes.

Nimbus never holds the USDv. The program does, and its rules below decide who can move it.

Addresses

  • Program: uUSDvQ2s5f8LirnG3Qqn6YEBkYTFJT8av2aaPjoUsmH
  • uUSDv mint: uUSDvgstJR56s4qLHtMRsP2jhTHsJrBaY4EUfBpkKAa

The mint is an address the program derives from its own id, so no other mint can pass for uUSDv. uUSDv has the same six decimals as USDv.

How the price works

Every wrap and unwrap uses one price: the program's assets divided by the number of uUSDv shares.

unreleased=rewards still releasing+backlogassets=cash−unreleasedprice=assetsshares\begin{aligned} \text{unreleased} &= \text{rewards still releasing} + \text{backlog} \\ \text{assets} &= \text{cash} - \text{unreleased} \\ \text{price} &= \frac{\text{assets}}{\text{shares}} \end{aligned}

Here cash is the USDv in the program's two accounts.

The assets are the USDv the program holds, less every reward it has not yet released. A reward raises the program's balance the moment it arrives, but it raises the price only as it is released.

Wrapping

A wrap deposits USDv and mints the shares it buys at the current price:

shares minted=⌊deposit×sharesassets⌋\text{shares minted} = \left\lfloor \frac{\text{deposit} \times \text{shares}}{\text{assets}} \right\rfloor

The program counts the USDv that actually reached its account, never more than the amount asked, so a token with a transfer fee cannot buy shares it did not pay for.

Unwrapping

An unwrap burns shares and pays the USDv they are worth:

USDv paid=⌊shares burned×assetsshares⌋\text{USDv paid} = \left\lfloor \frac{\text{shares burned} \times \text{assets}}{\text{shares}} \right\rfloor

Rounding

Both results are rounded down, and the rewards still releasing are counted in the program's favour on each side: rounded down when a wrap prices a deposit and up when an unwrap prices a payout. A wrap therefore never buys a share for less than an unwrap pays for it, and no sequence of wraps and unwraps returns more USDv than went in. What rounding costs is at most a few raw units, millionths of a USDv.

The starting price

The program opens holding a seed of 1 USDv, wrapped into 1,000,000 raw shares that no instruction can ever unwrap. One uUSDv starts at exactly one USDv, and the seed keeps the share count above zero for good, so the first depositor cannot set the price by being alone.

How rewards are released

The release window

Each reward releases in a straight line over the release window, starting when the program books it:

still releasing(t)=reward×end−twindow,end=arrival+window\text{still releasing}(t) = \text{reward} \times \frac{\text{end} - t}{\text{window}}, \qquad \text{end} = \text{arrival} + \text{window}

Halfway through the window, half of the reward has reached the price. At the end of the window all of it has.

The window is set once, at launch, and can never be changed. It must be between 14 hours and 70 days, in whole multiples of 14 seconds. It should be at least as long as the longest gap between Solomon's payments, late payments included: with a shorter window, a reward could finish releasing before the next one arrives, and someone who wrapped just before a payment would collect a share of it after holding for only part of the period it pays for.

Release schedules

The program divides time into periods one fourteenth of a window long and keeps one release schedule per period, so at most fifteen run at once. Rewards that arrive within the same period join one schedule. Its start moves to the average arrival time of everything in it, weighted by amount and rounded later, so joining releases nothing early. Rounding it earlier would release a sliver at once, which someone holding most shares could collect in the same transaction.

The release cap

How fast rewards turn into price is capped. The release cap is a yearly rate, between 5% and 20% of the assets, chosen at launch and adjustable by governance within that range.

cap rate=assets×cap1 yearcap over window=assets×cap×window1 yearroom=cap over window−rewards still releasing\begin{aligned} \text{cap rate} &= \frac{\text{assets} \times \text{cap}}{1\ \text{year}} \\ \text{cap over window} &= \frac{\text{assets} \times \text{cap} \times \text{window}}{1\ \text{year}} \\ \text{room} &= \text{cap over window} - \text{rewards still releasing} \end{aligned}

When a reward arrives, up to room of it starts releasing at once in the current schedule. The rest waits in the backlog. The backlog drains only at whatever part of the cap rate the schedules leave unused:

backlog drain rate=max⁡(0, cap rate−the schedules’ combined release rate)\text{backlog drain rate} = \max\bigl(0,\ \text{cap rate} - \text{the schedules' combined release rate}\bigr)

A schedule filled to the full room releases exactly at the cap rate, so while it runs the backlog waits, and once it ends the backlog drains at the full cap. The cap rate is recalculated from the assets every time the program books anything.

The cap exists so that no single event moves the price at once: a large donation, Solomon catching up on missed payments, or a repayment after a loss all raise the price at the cap's pace. That pace also limits what anyone can gain by wrapping just before such an event.

A worked example

Take a program holding 1,000,000 USDv of assets, a 10% cap and a 14-day window.

  • The cap rate is 100,000 USDv a year, about 273.97 USDv a day, and the cap over one window is about 3,835.62 USDv.
  • A 2,000 USDv reward fits inside the room, so all of it releases over the 14 days, about 142.86 USDv a day. A holder with 1% of the shares throughout gains 20 USDv. A holder with 1% who unwraps after 7 days gains 10 USDv.
  • A 5,000 USDv reward exceeds the room. About 3,835.62 USDv releases over the 14 days at the full cap, and about 1,164.38 USDv waits in the backlog. When the schedule ends, the backlog drains at the cap rate, so the whole reward has reached the price after about 18 days.

Figures are rounded, and the cap rate grows slightly as rewards raise the assets.

Joining and leaving

Leaving forfeits only rewards still releasing

When someone unwraps, every release schedule shrinks in proportion to the shares that remain. The leaver's part of the rewards still releasing moves to the backlog, where it drains at the cap's spare room to everyone who stays. The leaver keeps everything already released.

An exit does not move the price for anyone else. The payout comes out of the assets in exact proportion to the shares burned, and the forfeited rewards stay in the unreleased total, only moving from the schedules to the backlog:

price after=assets−USDv paidshares−shares burned=price before\text{price after} = \frac{\text{assets} - \text{USDv paid}}{\text{shares} - \text{shares burned}} = \text{price before}

Joining buys no rewards already in flight

A wrap prices against the assets, which exclude everything still releasing. A newcomer pays the price released so far and gains only as the rewards still in flight release while they hold.

Edge cases

Unwrapping before any reward is released

You get back what you wrapped and nothing more. With no reward released in between, the price at unwrap is the price you wrapped at. Before any reward has ever been released, that price is exactly one, and a wrap followed by an unwrap returns the same number of raw units. Once rewards have moved the price, a round trip with nothing released in between returns what you wrapped, less at most a few raw units of rounding.

Wrapping just before a reward and unwrapping just after

You earn only for the seconds you held. The reward reaches the price in a straight line over the window, so an unwrap moments after it arrives collects almost nothing of it, and the rest goes to those who stay.

Leaving halfway through a release

You keep your share of the half already released. Your share of the other half goes to the backlog and reaches the holders who stay.

A reward while nobody else holds uUSDv

It releases to the shares that exist, which may be only the seed. The seed's shares can never be unwrapped, so what accrues to them stays in the program for good and adds to the price of every later share in proportion.

A large donation or a catch-up payment

Anyone may send USDv straight to the program's principal or yield account. The next booking counts it as a reward. It releases at the cap's pace, partly through the backlog when it is larger than the room, never at once.

USDv sent anywhere else

USDv sent to any other token account, including one that names a program authority as its owner, is not counted, and no instruction can move it.

Very small wraps and unwraps

A wrap worth less than one raw share is refused, and so is an unwrap that would pay less than one raw unit of USDv.

Slippage limits

Every wrap names the fewest shares it accepts and every unwrap the least USDv it accepts. Either is refused, with nothing moved, if the price moved past that limit before it landed.

The deposit limit

A wrap is refused if it would take the program's balance past the deposit limit. Governance can lower the limit below the current balance: that refuses new wraps until unwraps bring the balance under it, and never blocks an unwrap.

Paused wraps

The guardian can pause wrapping. A pause never touches unwrapping, and only governance can resume wrapping.

When USDv itself is paused

If Solomon pauses USDv, no USDv can move, so neither wraps nor unwraps can run until USDv resumes.

When every share is worth nothing

After a loss that leaves the assets at zero, wraps are refused, because no price exists to buy at. Any USDv that arrives then goes straight to the price instead of releasing, so a repayment can never be locked away behind a cap that releases a share of nothing.

Destinations that require a memo

When the account an unwrap pays into requires a memo on incoming transfers, the program sends the memo uUSDv unwrap first.

Wallets with CPI Guard

An account with Token-2022's CPI Guard on must wrap or unwrap through a delegate.

The freeze

Only an unwrap moves USDv out of the program's two accounts, and the program records each account's balance after every wrap and unwrap. Before doing anything else, every wrap, unwrap and checkpoint compares those records with the real balances. If either account holds less than it should, even by one raw unit and even when the other account rose by as much, USDv has left without an unwrap, which only Solomon's powers over USDv could do. The program then freezes: nothing can be wrapped or unwrapped until governance unfreezes it. Solomon freezing either account freezes the program the same way. Permanent counters record every unit that ever left each account without an unwrap.

A refused wrap or unwrap cannot record anything, because a failed instruction changes nothing. The next one finds the same shortfall and is refused too, and the first checkpoint records the freeze for good. Anyone may call a checkpoint.

Who bears a loss

The missing USDv is taken in this order:

  1. from the backlog;
  2. then from every release schedule, in proportion;
  3. only then from the price, where the program records it as a loss.

Rewards not yet released absorb a loss before any holder's principal does.

Repayment

While the program is frozen, USDv that arrives repays the recorded loss straight to the price, before anything else. Nobody can enter or leave in the meantime, so a repayment reaches exactly the holders who bore the loss, at once. Anything beyond the loss is ordinary income and releases under the cap.

Unfreezing

Governance unfreezes the program through the timelock. Its proposal names the freeze it ends and when that freeze began, so every new outflow is a new freeze that an earlier proposal cannot end. An unfreeze is refused while either account is still frozen, and while a loss is still outstanding, unless the proposal writes the rest off. A write-off declares the loss final: USDv that arrives afterwards releases like any other income, because with wrapping open an instant repayment could be timed by a newcomer.

Instructions

Wrap

Moves USDv from any account the signer owns or is delegated, and mints the shares it buys to any uUSDv account. The program first books any reward that has arrived, so the price is current. A wrap is refused while the program is frozen or wrapping is paused, when it would pass the deposit limit, when it is worth less than one share, or when it buys fewer shares than the caller's minimum.

Unwrap

Burns shares from an account the signer owns or is delegated, and pays the USDv they are worth to any USDv account outside the program. The payout comes from the yield account and, for any part it cannot cover, the principal account. Neither the guardian nor governance can stop an unwrap. Only a freeze does, and only USDv leaving the program's accounts, or Solomon freezing one of them, causes one. An unwrap is also refused when it pays less than one raw unit, less than the caller's minimum, or would reach into the seed.

Checkpoint and quote

checkpoint books whatever the program's two USDv accounts show: a reward, a donation, USDv taken from either account, or a frozen account. Anyone may call it, and it never moves tokens. Wraps and unwraps run the same booking first, so a checkpoint only brings the books up to date between them.

quote returns what a checkpoint would leave, without writing anything: the assets an unwrap prices against, the share count, and the assets a wrap prices against. A lending market or any other integrator should price uUSDv with quote, never with a market price, which a flash loan can move.

The two USDv accounts

The program holds USDv in two accounts and counts their combined balance. Wraps pay into the principal account and Solomon pays rewards into the yield account. No instruction moves USDv from one to the other. Unwraps pay from the yield account first, so wrapped USDv leaves the principal account only once the yield account is empty. Solomon pays rewards on the two accounts' combined balance, so rewards compound without being moved.

Initialize

Opens the program once. It creates the USDv accounts, the uUSDv mint and the account that holds the seed, and sets the release window, the release cap, the deposit limit and the guardian. It also writes uUSDv's name, symbol and URI into the mint, where wallets read them. Only the bootstrap key built into the program may run it.

Launch settings

SettingAllowed valuesWho can change it later
Release window14 hours to 70 days, in multiples of 14 secondsNobody
Release cap5% to 20% of the assets a yearGovernance
Deposit limit1 USDv or moreGovernance
GuardianAny keyGovernance
Name, symbol and URIUp to 32, 10 and 200 bytesGovernance

Who controls what

The guardian

One key may pause wrapping, at once. A pause never touches unwrapping, so it can slow money coming in but never trap money going out.

Governance

Through the governance timelock, governance may resume wrapping, set the release cap within its range, set the deposit limit, replace the guardian, unfreeze the program, and replace uUSDv's name, symbol and URI. Each proposal names the value it replaces, so it executes only against the state it was made for. A resume names the pause it ends and when that pause began, so a resume can never be queued ahead of a pause, and one queued before a later pause cannot end it. A resume ends only the guardian's pause, never a freeze. A metadata update names the count of earlier updates, so it never lands on metadata that changed after it was proposed.

Governance cannot move USDv, mint or burn uUSDv, or change the release window.

The program upgrade authority

The upgrade authority can replace the program, as for every Nimbus program.

Solomon

USDv is Solomon's token, and its mint keeps powers the program cannot override. Solomon can pause USDv, freeze an account, move tokens through its permanent delegate, or set a transfer hook. A paused USDv stops wrapping and unwrapping until it resumes. A clawback from either of the program's accounts, or freezing either of them, freezes the program until governance unfreezes it. The program forwards the accounts a transfer hook needs, so a hook set later does not stop it.

The program relies on Solomon paying rewards on the principal and yield accounts' combined balance, into the yield account, and on Solomon never using its permanent delegate on the program's accounts or freezing them, acting against a thief's own addresses instead.

Limits

  • Stolen USDv. Wrapping is instant and unscreened, so a thief can wrap stolen USDv and unwrap clean USDv. Holders are protected only by Solomon acting against the thief's own addresses, as for every pool of USDv.
  • Missed payments. When Solomon misses payments and then catches up, someone who wraps just before the catch-up shares in it. The cap bounds how much.
  • Changes in Solomon's schedule or rate. When the yield still releasing drops at once, because Solomon pays more often, cuts its rate or resumes after a pause, whoever holds across the change receives a one-time share of it. This comes out of yield, never out of USDv deposited.
  • Large exits and entries. A holder who leaves gives up their share of the yield still releasing; in steady flows this balances what entrants inherit.

Reference

Last updated

On this page