wUSDv
A Token-2022 token whose every unit is a share of the USDv the wUSDv program holds, worth more USDv as Solomon Labs' rewards are released to holders at one price for wrapping and unwrapping.
wUSDv carries Solomon Labs' USDv yield into places USDv itself earns nothing. One wUSDv is a share of the USDv the wUSDv program holds. Solomon pays rewards to the program's yield account, and the program releases them to every share, so a wUSDv balance never changes while each unit becomes worth more USDv. Anyone may wrap or unwrap at any time, without permission, and use wUSDv anywhere a Token-2022 token goes.
How the price works
A share's price is the program's assets divided by the share count. Wrapping buys shares at that price, and unwrapping sells them at that price. Both round in favour of the shares that stay, so no sequence of wraps, unwraps and payments within one moment returns more USDv than went in.
The assets are the USDv the program holds, less everything not yet released. A payment from Solomon raises the balance at once but the price only gradually: it releases linearly over a release window fixed at launch, counted from when it arrived. Someone who wraps just before a payment and unwraps just after it earns only the seconds they held. Payments that arrive within the same fourteenth of a window share one release schedule, so the program needs at most fifteen of them.
Releases are capped. The release cap, a yearly rate between 5% and 20% of the assets set when the program is initialised, limits how fast income becomes price. Income beyond what the cap allows over one window waits in a backlog, which drains only at the part of the cap that the scheduled releases leave unused. A large donation, or Solomon catching up on missed payments, therefore raises the price at the cap's pace rather than at once.
When someone unwraps, they give up their share of the income still releasing. That share moves to the backlog, so those who stay never collect it at once: it releases at the cap's spare room, which can be sooner than the payment it came from would have released, but never faster than the cap.
The window must be at least as long as the longest gap between Solomon's payments, late payments included. A shorter window lets a well-timed entrant collect part of a payment without having held through it. Measurements behind these choices, from the design's simulator, are in the program's property tests.
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.
The missing USDv is taken first from the backlog, then from the income still releasing, and only then from the price, where the program records it as a loss. While the program is frozen, USDv that arrives repays that loss straight to the price. Nobody can enter or leave in the meantime, so a repayment reaches exactly the holders who bore the loss, at once.
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. If a loss leaves the shares worth nothing, anything that arrives goes straight to the price, so restitution can never be locked away.
Instructions
Wrap
Moves USDv from any account the signer owns or is delegated, and mints the shares it buys to any wUSDv account. The program first books any payment that has arrived, so the price is current. It counts what actually reached its account, not what was asked. A wrap is refused while the program is frozen or wrapping is paused, when it would pass the deposit limit, 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. When the destination requires a memo on incoming transfers, the program logs one first. 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.
Checkpoint and quote
checkpoint books whatever the program's two USDv accounts show: a payment, a donation, USDv
taken from either account, or a frozen account.
Anyone may call it, and it never moves tokens. 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 wUSDv 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, so USDv leaves the principal account only through an unwrap. Solomon pays rewards on the two accounts' combined balance, so rewards compound without being moved.
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.
Initialize
Opens the program once. It creates the USDv accounts, the wUSDv mint and the account that holds the seed: 1 USDv wrapped at launch, whose shares no instruction can move. The seed keeps the share count above zero, so the first depositor cannot set the price. It also writes wUSDv's name, symbol and URI into the mint, where wallets read them; governance can replace them later. Only the bootstrap key built into the program may run it.
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 between 5% and 20%, set the deposit limit, replace the guardian, unfreeze the program, and replace wUSDv'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 the moment that pause began, so a resume can never be queued ahead of a pause, and one queued before a later pause cannot end it; an unfreeze is bound to its freeze the same way. 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 wUSDv, or change the release window.
The program upgrade authority
The upgrade authority can replace the program, as for every Umbra 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, up to sixteen per transfer, so a hook set later does not stop it; a hook that needed more would stop wrapping and unwrapping until the program was upgraded.
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.
- CPI Guard. An account with Token-2022's CPI Guard on wraps or unwraps through a delegate.
Reference
| Code | Error | Meaning |
|---|---|---|
| 0 | UnsupportedInstructionVersion | Returned when the instruction envelope's version is not the one this build supports. |
| 1 | UnknownInstruction | Returned when the instruction tag has no handler. |
| 2 | MismatchingAccountKeys | The instruction supplied a number of accounts the handler does not name. Every destructure binds an exact arity, so this is returned for too many accounts as well as too few. |
| 3 | InvalidPayload | Returned when the payload has the wrong length. |
| 4 | NotOwnedByProgram | Returned when an account this program must own is owned by something else. |
| 5 | PdaMismatch | Returned when an account is not the address its seeds derive. |
| 6 | InvalidSystemProgram | Returned when the account in the System-program slot is not the System program. |
| 7 | InvalidTokenProgram | Returned when the account in the token-program slot is not Token-2022. |
| 8 | InvalidAssociatedTokenProgram | Returned when the account in the associated-token-program slot is not that program. |
| 9 | InvalidMemoProgram | Returned when the account in the memo-program slot is not the Memo program. |
| 10 | AlreadyInitialized | Returned when the target account already exists, so creating it would overwrite state. |
| 11 | NotInitialized | Returned when the wrapper's state account is absent, wrongly sized, or not active. |
| 12 | Unauthorized | Returned when the signer is not the key this instruction requires. |
| 13 | WrongUsdvMint | Returned when the mint passed as USDv is not the USDv mint this program serves. |
| 14 | WrongUsdvDecimals | Returned when the USDv mint does not have the decimals this program was built for. |
| 15 | WrongVaultAccount | Returned when an account is not the principal account, yield account, wUSDv mint or locked account the wrapper recorded at initialize. |
| 16 | TokenAccountMalformed | Returned when a token account's data is too short to be one. |
| 17 | InvalidReleaseWindow | Returned when the release window is not a whole number of slots or is out of bounds. |
| 18 | CapOutOfRange | Returned when the release cap is outside the accepted bounds. |
| 19 | TvlCapBelowSeed | Returned when the TVL cap is below the seed, which would refuse every wrap. |
| 20 | InvalidMetadata | Returned when the token name or symbol is empty, or a metadata field is not UTF-8. |
| 21 | SeedNotReceived | Returned when the seed transfer did not deliver exactly the seed to the principal account. |
| 22 | WrapsPaused | Wrapping is paused. Unwrapping still works. |
| 23 | NoAssets | wUSDv is worth nothing right now, so a deposit cannot be priced. |
| 24 | TvlCapExceeded | The deposit would take the wrapper past its deposit limit. |
| 25 | NothingMinted | The deposit is worth less than one wUSDv raw unit. |
| 26 | SharesBelowMinimum | The deposit buys fewer wUSDv than the minimum you set. |
| 27 | SharesOverflow | The wUSDv supply would overflow. |
| 28 | SharesOutOfRange | The amount of wUSDv to unwrap is zero or more than can be redeemed. |
| 29 | NothingPaid | The wUSDv unwrapped is worth less than one USDv raw unit. |
| 30 | PayoutBelowMinimum | The unwrap pays less USDv than the minimum you set. |
| 31 | SourceIsVault | Returned when a wrap names the wrapper's own principal or yield account as its source. |
| 32 | DestinationIsVault | Returned when an unwrap names the wrapper's own principal or yield account as its destination. |
| 33 | HookAccountsMismatch | Returned when the payload splits more transfer-hook accounts than the instruction carries. |
| 34 | TooManyHookAccounts | Returned when one transfer carries more transfer-hook accounts than this program forwards. |
| 35 | UnknownOperation | Returned when a proposal or authority record governs another operation than the one the instruction acts on. |
| 36 | TimelockNotElapsed | Returned when a proposal has not yet reached its `execute_after_slot`. |
| 37 | DelayOverflow | Returned when `current_slot + delay_slots` would overflow a `u64` while queueing a proposal. |
| 38 | ProposalExpired | Returned when a proposal matured but was not executed inside its grace window, so it expired. |
| 39 | GovernanceDelayBelowMinimum | Returned when a governance delay is below the minimum the timelock enforces. |
| 40 | EpochExhausted | Returned when a record's epoch has reached its maximum, so it cannot advance. |
| 41 | WrongAccount | Returned when an account is not a live governance record or pending proposal of this program. |
| 42 | WrongRentRecipient | Returned when the rent recipient is not the account that paid the proposal's rent. |
| 43 | TypedProposeRequired | Returned when the generic propose is asked to queue a reassignment, which must go through propose_reassign_authority so it waits every delay it shortens. |
| 44 | ProposalVoided | Returned when the proposal was voided after it was queued: its operation's authority changed, its delay was raised, or its queue was voided. Anyone may close it. |
| 45 | ProgramFrozen | The wrapper is frozen: USDv left its accounts other than through an unwrap, or one of its accounts is frozen. Nothing can be wrapped or unwrapped until governance unfreezes it. |
| 46 | NotFrozen | Returned when an unfreeze finds the wrapper not frozen. |
| 47 | LossOutstanding | Returned when an unfreeze that writes nothing off finds a loss still outstanding. |
| 48 | ProgramAccountFrozen | Returned when an unfreeze finds one of the wrapper's USDv accounts still frozen. |
libs/typescript/errors/error-manifest.json.Last updated