deposits
gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/deposits on onyx-1 testnet
realmapplicationescrowlive on onyx-1
Hold a refundable security deposit against agreed conditions.
Settles the deposit on conditions set up front, including splitting one pot between two parties who disagree about it.
In detail curated
REFUNDABLE SECURITY DEPOSITS WITH CONDITION-GATED FORFEITURE -- the one money shape the portfolio never had: ONE POT SPLITTING PARTIALLY between two rival claimants by a conditional rule, and the first realm where the DEPOSITOR'S OWN money is at risk of partial loss. A holder opens an agreement naming a depositor, an exact amount, a term and a FORFEIT CEILING (the most the holder may ever claim); the depositor reads all of it, the named arbiter included, and consents by FUNDING; after the term the holder may file a claim up to the ceiling; the depositor may accept it, dispute it to the arbiter, or let it stand; whatever is not awarded is REFUNDED. It is none of the shipped shapes: crowdfund is all-or-nothing, auction is winner-takes, loans is principal-out for a larger total back, reimbursements is approver-gated to a single claimant -- none of them SPLITS. CUSTODY -- Held == locked + contested + feeledger.Liabilities(), three buckets rather than two, with locked and contested DISJOINT: a dispute moves the amount out of one and into the other in the same transaction, so 'what is the arbiter deciding over' is a readable number rather than an inference. Settlement is PULL, never push. The fee applies to the AWARD ONLY -- a fully-refunded deposit costs the depositor nothing, which is what a security deposit has to do to be worth posting. FIVE routes reach settled and THREE are permissionless (SettleUndisputed, ReleaseUnclaimed, LapseDispute), so no terminal state depends on a party choosing to act. The consent chain award <= claimAmount <= forfeitCeiling <= amount holds on every route and nothing raises either bound after funding. Audit PROCEED, 0 RED / 0 material YELLOW, 3 GREEN / 3 REFUTED; G2's party-disjointness guard on SetFeeRecipient was REFUSED on evidence rather than added (a global over an unbounded growing set can only be snapshot-checked, and a guard any later transaction walks through reads as an invariant without being one). 56-case mutation sweep FULLY COVERED; 25-test suite green. ITS BYTE-MATCH FAILED: see source.divergence.
Use it
A realm holds state. Reading it is a free query; changing it is a transaction you sign yourself.
Read it — no key, no account, no GNOT
gnokey query vm/qeval \
--data 'gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/deposits.Contested()' \
--remote https://rpc.onyx.testnets.gno.land:443Contested is read-only, so this is a query rather than a transaction: it costs nothing, signs nothing and needs no funded account. Every exported function is listed under the API below; swap the name and the arguments to read any of the others.
Change its state
Every state change is a transaction you sign with your own key — this site never asks for one and holds none. You do not have to hand-write the transaction: gnoweb generates the exact gnokey maketx call command for any of this realm's 19 crossing functions, with the argument names filled in.
Read the known limitations further down this page first. They record what was not verified on chain, which is usually the part that matters.
Import it from another realm
import "gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/deposits"The path is the address: there is no package manager, nothing to add to a manifest and no version to pin, so the package you compile against is the one the chain runs. It differs per chain because the namespace in it is the deploying account's.
Deployments
onyx-1 testnet onyx-1
RenderedSourceCall builderState
chain-attested| Import path | gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/deposits |
|---|---|
| Namespace | g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3 |
| Deployed at height | 491,920 |
| Deploy transaction | 24e191b395274422a2c9a6b40c1d48dca4fd32607da4f17770569b57486f6905 look it up on the RPC |
| Deployer | g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3 |
| Gas used | 51,078,826 |
| Storage | not attested per package on this chain — see the limitations below |
| Files on chain | deposits.gno gnomod.toml |
| Deployed bytes | deposits.gno — 38,013 bytes |
| sha256 | f098e3cd2d1e75a01dfa5f134c8ebe675e09e7e92edd79d7841767ac3861d986 |
| Realm address | g1rtphf7raf5zjcvpupze8xwkdq7pcra8whhve4s |
Do not take the hash above on trust. $download returns the bytes onyx-1 is actually running; this command fetches them and prints their digest, which should equal the one in the table:
curl -sS 'https://onyx.testnets.gno.land/r/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/deposits$download&file=deposits.gno' | shasum -a 256Expected: f098e3cd2d1e75a01dfa5f134c8ebe675e09e7e92edd79d7841767ac3861d986 — 38,013 bytes. Use curl: the gno.land edges answer Python's default user-agent with HTTP 403.
API
chain-derived 54 exported functions.
Every function below deep-links to gnoweb's call builder, which generates a ready-to-run gnokey maketx call for it:
AcceptAdminAcceptClaimAgreementsOfAmountArbiterDeadlineOfAwardCancelUnfundedClaimClaimAllClaimAmountClaimDeadlineClaimReasonClaimableOfContestedCreatedHeightDisputeDisputeDeadlineOfDisputeReasonFeeBpsFeeBpsOfFeesAccruedFileClaimForfeitCeilingFundFundDeadlineFundedHeightHasAgreementHeightHeldIsClaimableIsDisputableIsSettledLapseDisputeLiabilitiesLockedMaxClaimableMemoNumAgreementsOpenAgreementPendingValveRefundReleaseReleaseUnclaimedRenderResolveDisputeSetFeeBpsSetFeeRecipientSettleUndisputedSettledHeightStatusSweepDenomTermEndTransferAdminWithdrawFees
Overview
Package deposits is a refundable security deposit with condition-gated, partially-forfeitable settlement.
The shape
A HOLDER (landlord, service provider, equipment lessor) opens an agreement naming a DEPOSITOR, an exact amount, a term, and a FORFEIT CEILING — the most the holder may ever claim out of the deposit. The depositor reads all of it, including the named arbiter, and consents by FUNDING. At the end of the term the holder may file a claim up to the ceiling; the depositor may accept it, dispute it to the arbiter, or let it stand. Whatever is not awarded to the holder is refunded to the depositor.
That makes this the first realm in the portfolio where ONE POT SPLITS PARTIALLY BETWEEN TWO RIVAL CLAIMANTS by a conditional rule, and the first where the depositor's own money is at risk of partial loss. `crowdfund` is all-or-nothing, `auction` is winner-takes, `loans` is principal-out / larger-back, `reimbursements` is approver-gated to a single claimant. See DISCOVERY.md for the §7 gate and the five candidates it killed.
Lifecycle
OpenAgreement -> open (nothing held; depositor has not consented) Fund -> funded (depositor attaches the exact amount) FileClaim -> claimed (holder, after term end, within the claim window) Dispute -> disputed (depositor, within the dispute window) ResolveDispute -> settled (arbiter awards 0..claim to the holder)
Five routes reach `settled`, and THREE OF THEM ARE PERMISSIONLESS, so no terminal state depends on a party acting:
AcceptClaim depositor agrees award = claim SettleUndisputed ANYONE, dispute window up award = claim ReleaseUnclaimed ANYONE, claim window up award = 0 (holder never claimed) LapseDispute ANYONE, arbiter silent award = 0 (claim unsubstantiated) Release holder waives early award = 0
Custody
Coins are held by the realm from Fund until settlement, then become PULL-CLAIMABLE through `p/feeledger`. Nothing is ever pushed: a settlement credits balances and the parties `Claim` them, which removes the grief and reentrancy surface a push settlement would have.
The conservation invariant carries one bucket more than the sibling realms:
Held == locked + contested + feeledger.Liabilities()
`locked` and `contested` are DISJOINT — a disputed deposit moves out of one and into the other in the same transaction. The split exists because a contested deposit is the one balance whose destination is not yet decided by this realm's own rules; keeping it separate makes "what is the arbiter deciding over" a readable number rather than an inference.
The fee
The protocol fee applies ONLY to the portion AWARDED to the holder. A refund to the depositor is returned in full at fee 0, matching `auction`'s rule that a refund is not a fee event. A fully-refunded deposit therefore costs the depositor nothing.
What the depositor is trusting, stated plainly
- The FORFEIT CEILING is the depositor's hard protection, and they consent to it by funding. Nothing can take more than the ceiling.
- If NO ARBITER is named, an unaccepted claim STANDS once the dispute window passes. That is a legitimate configuration — a deposit with a low ceiling and no arbiter — and the depositor sees `Arbiter() == ""` before funding.
- If an arbiter IS named, `LapseDispute` awards the contested amount to the DEPOSITOR. A disappeared arbiter cannot strand funds, and the holder — who chose the arbiter — bears the cost of that choice failing.
- Residual, not fixed here: a hostile depositor can dispute a legitimate claim to delay settlement by one dispute window. The delay is bounded and costs the depositor the same wait; it is disclosed rather than mitigated.
- The realm settles MONEY. Whether the damage was real is off-chain; this realm binds the ceiling, the windows, and the arbiter's authority.
Scope
Single-denom (ugnot), single depositor per agreement, flat ceiling. Graduated forfeiture over damage severity (`p/tiers`) is a deliberate v2 candidate, not an omission — see DISCOVERY.md §5.
Imports
chainchain/runtimechain/runtime/unsafegno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/coiniogno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/feeledgergno.land/p/nt/avl/v0gno.land/p/nt/markdown/sanitize/v0strconv
Constants and variables
Agreement status values. Settled and Cancelled are terminal.
const (
DepositOpen = "open" // created; the depositor has not funded
DepositFunded = "funded" // funded; the term is running or the claim window is open
DepositClaimed = "claimed" // the holder filed a claim; the depositor may accept or dispute
DepositDisputed = "disputed" // the depositor disputed; the arbiter decides
DepositSettled = "settled" // split credited; both parties pull
DepositCancelled = "cancelled" // never funded; nothing moved
)
Input bounds.
const (
// MinDeposit is the dust floor; MaxDeposit keeps the split, the ceiling and
// the fee far from int64 overflow. The depositor must ATTACH the deposit, so
// the real ceiling is their balance.
MinDeposit = int64(10_000)
MaxDeposit = int64(1_000_000_000_000) // 1,000,000 GNOT
// MinClaim is the dust floor for a forfeit ceiling and for any claim filed
// against it. A ceiling below it would make FileClaim unsatisfiable.
MinClaim = int64(1_000)
// Term and window bounds, in blocks. The window floors keep a counterparty
// from being given a deadline they cannot physically meet; the ceilings keep
// height arithmetic far from overflow.
MinTerm = int64(10)
MaxTerm = int64(7_500_000)
MinWindow = int64(10)
MaxWindow = int64(7_500_000)
// FundWindow is how long an unfunded agreement stays fundable. After it,
// Fund refuses and CancelUnfunded is permissionless, so an abandoned offer
// cannot occupy a holder's slot forever.
FundWindow = int64(1_000_000)
// MaxMemoLen and MaxReasonLen bound the stored free text.
MaxMemoLen = 200
MaxReasonLen = 200
// MaxAgreementsPerHolder bounds one holder's stored agreements. A terminal
// agreement does NOT free the slot: a settled deposit is a permanent record
// both parties and an auditor may need to read.
MaxAgreementsPerHolder = 50
// RenderLimit bounds every rendered list — an unbounded Render was YELLOW in
// several prior audits in this portfolio.
RenderLimit = 20
)
Denom is the only asset this realm accepts.
const Denom = "ugnot"
MaxFeeBps is the hard protocol-fee cap: 1000 bps = 10%. The fee applies only to the portion AWARDED to the holder, never to a refund.
const MaxFeeBps = int64(1000)
Functions
AcceptAdmin
func AcceptAdmin(cur realm)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asAcceptAdmin(cross(cur), ...).
AcceptClaim
func AcceptClaim(cur realm, id int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asAcceptClaim(cross(cur), ...).
AcceptClaim settles at the claimed amount. Depositor only, while a claim stands. Accepting early is allowed — the dispute window is a deadline for disputing, not a waiting period.
AgreementsOf
func AgreementsOf(holder address) int64
Amount
func Amount(id int64) int64
ArbiterDeadlineOf
func ArbiterDeadlineOf(id int64) int64
Award
func Award(id int64) int64
CancelUnfunded
func CancelUnfunded(cur realm, id int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asCancelUnfunded(cross(cur), ...).
CancelUnfunded abandons an agreement the depositor never funded. Either party may cancel at any time — refusing to fund is the depositor's right and withdrawing the offer is the holder's — and ANYONE may cancel once the fund deadline has passed, so a dead offer cannot hold a slot forever. No coins have ever been held, so nothing moves.
Claim
func Claim(cur realm, amount int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asClaim(cross(cur), ...).
Claim sends amount ugnot of the caller's claimable balance — refunds and awards from settled agreements — to the caller.
ClaimAll
func ClaimAll(cur realm)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asClaimAll(cross(cur), ...).
ClaimAll sends the caller's entire claimable balance to the caller.
ClaimAmount
func ClaimAmount(id int64) int64
ClaimDeadline
func ClaimDeadline(id int64) int64
ClaimReason
func ClaimReason(id int64) string
ClaimableOf
func ClaimableOf(a address) int64
Contested
func Contested() int64
CreatedHeight
func CreatedHeight(id int64) int64
Dispute
func Dispute(cur realm, id int64, reason string)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asDispute(cross(cur), ...).
Dispute sends a standing claim to the arbiter. Depositor only, before the dispute deadline, and only if an arbiter was named — with no arbiter there is nobody to decide, and the depositor saw that before funding. The deposit moves from `locked` into `contested`; no coins move.
DisputeDeadlineOf
func DisputeDeadlineOf(id int64) int64
DisputeDeadlineOf is the last height at which the depositor may Dispute a standing claim; ArbiterDeadlineOf is the last height at which the arbiter's ruling pre-empts LapseDispute. Both are 0 until the relevant stage is live.
DisputeReason
func DisputeReason(id int64) string
FeeBps
func FeeBps() int64
FeeBpsOf
func FeeBpsOf(id int64) int64
FeesAccrued
func FeesAccrued() int64
FileClaim
func FileClaim(cur realm, id, amount int64, reason string)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asFileClaim(cross(cur), ...).
FileClaim claims `amount` of the deposit against the ceiling. Holder only, only once, only after the term has ended and before the claim window closes. `amount` must be in [MinClaim, forfeitCeiling]. The depositor may then accept it, dispute it to the arbiter, or let it stand.
ForfeitCeiling
func ForfeitCeiling(id int64) int64
Fund
func Fund(cur realm, id int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asFund(cross(cur), ...).
Fund attaches the exact deposit and starts the term. The named depositor only; the attached amount must equal the agreement's amount exactly, and the fund deadline must not have passed. Funding IS the depositor's consent to the ceiling, the windows and the arbiter, all of which were readable beforehand.
FundDeadline
func FundDeadline(id int64) int64
FundedHeight
func FundedHeight(id int64) int64
HasAgreement
func HasAgreement(id int64) bool
Height
func Height() int64
Held
func Held() int64
IsClaimable
func IsClaimable(id int64) bool
IsClaimable reports whether FileClaim would be accepted at this height.
IsDisputable
func IsDisputable(id int64) bool
IsDisputable reports whether Dispute would be accepted at this height.
IsSettled
func IsSettled(id int64) bool
LapseDispute
func LapseDispute(cur realm, id int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asLapseDispute(cross(cur), ...).
LapseDispute refunds the whole deposit because the arbiter did not rule before their deadline. Anyone may call it. The award is 0, not the claim: an unsubstantiated claim fails. The holder chose the arbiter and bears the cost of that choice failing — which is also why a disappeared arbiter can never strand a deposit.
Liabilities
func Liabilities() int64
Locked
func Locked() int64
MaxClaimable
func MaxClaimable(id int64) int64
MaxClaimable is the most the holder could still take out of this agreement. It is the ceiling while the deposit is funded, the filed claim once one stands, and 0 once the agreement is terminal.
Memo
func Memo(id int64) string
NumAgreements
func NumAgreements() int64
OpenAgreement
func OpenAgreement(cur realm, depositor address, amount, forfeitCeiling, termBlocks, claimWindow, disputeWindow int64, arbiter address, memo string) int64
Crossing function. Callable from a transaction via
MsgCall, and from another realm asOpenAgreement(cross(cur), ...).
OpenAgreement offers a deposit agreement. The caller becomes the holder and names the `depositor`, the exact `amount` they must attach, the `termBlocks` the deposit is held for, the `claimWindow` the holder has to file a claim after the term ends, the `disputeWindow` the depositor has to dispute one (and the arbiter then has to rule), and the `forfeitCeiling` — the most the holder may ever claim, in [MinClaim, amount]. `arbiter` may be the zero address, in which case a claim cannot be disputed; it may not be either party. Nothing is held until the depositor Funds, which is how they consent to all of it. The current protocol fee is snapshotted in. Returns the id.
PendingValve
func PendingValve(id int64) string
PendingValve names the permissionless call this agreement is ready for, or "" when none is. It is the one view a keeper needs.
Refund
func Refund(id int64) int64
Refund is the amount the depositor gets back, known only once settled.
Release
func Release(cur realm, id int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asRelease(cross(cur), ...).
Release returns the whole deposit to the depositor early. Holder only, while the agreement is funded and unclaimed. It is the holder waiving the claim they have not yet filed — the one route to settlement that needs no clock.
ReleaseUnclaimed
func ReleaseUnclaimed(cur realm, id int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asReleaseUnclaimed(cross(cur), ...).
ReleaseUnclaimed refunds the whole deposit because the holder never filed a claim before the window closed. Anyone may call it.
Render
func Render(path string) string
ResolveDispute
func ResolveDispute(cur realm, id, awardToHolder int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asResolveDispute(cross(cur), ...).
ResolveDispute rules on a disputed claim. Arbiter only. `awardToHolder` is in [0, the claimed amount] — the arbiter may reduce a claim or deny it outright but never award more than was claimed, which is itself capped by the ceiling the depositor consented to. No deadline is enforced here: a late ruling is better than a stranded deposit, and `LapseDispute` is the race that bounds it.
SetFeeBps
func SetFeeBps(cur realm, bps int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asSetFeeBps(cross(cur), ...).
SetFeeRecipient
func SetFeeRecipient(cur realm, recipient address)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asSetFeeRecipient(cross(cur), ...).
SetFeeRecipient names the address that may withdraw the accrued fee pot.
It deliberately does NOT enforce disjointness from agreement parties, in contrast to OpenAgreement, which refuses an arbiter equal to either party. The two rules are not comparable, and the asymmetry is the point:
- The arbiter rule is per-agreement and checked at the moment the agreement is created, so it is an invariant of that agreement for life.
- The fee recipient is one global over an unbounded, growing set of agreements. A check here could only test a snapshot, and the very next OpenAgreement could name the sitting recipient as holder or depositor. A guard that any later transaction can walk through is worse than no guard: it reads as an invariant and is not one.
Nothing is at stake in the overlap, which is why the honest answer is a documented omission rather than a decorative check. The fee is derived only from the holder's award inside ledger.MustDeposit, never from the refund, so a recipient who is also a holder collects the same coins by two routes instead of more coins. Solvency is unaffected in either direction: feeledger accounts user credit and the fee pot as separate buckets, both are inside ledger.Liabilities(), and SweepDenom's reserve is locked + contested + ledger.Liabilities() — so an overlapping recipient cannot make the swept surplus overstate what the realm is free to release.
SettleUndisputed
func SettleUndisputed(cur realm, id int64)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asSettleUndisputed(cross(cur), ...).
SettleUndisputed settles at the claimed amount because the depositor neither accepted nor disputed before the dispute window closed. Anyone may call it.
SettledHeight
func SettledHeight(id int64) int64
Status
func Status(id int64) string
SweepDenom
func SweepDenom(cur realm, denom string)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asSweepDenom(cross(cur), ...).
SweepDenom recovers out-of-band coins to the fee recipient. For the ledger denom the reserve is locked + contested + Liabilities() — held deposits, contested deposits, claimable refunds, awards and the fee pot are all structurally unreachable.
TermEnd
func TermEnd(id int64) int64
TransferAdmin
func TransferAdmin(cur realm, successor address)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asTransferAdmin(cross(cur), ...).
WithdrawFees
func WithdrawFees(cur realm)
Crossing function. Callable from a transaction via
MsgCall, and from another realm asWithdrawFees(cross(cur), ...).
Doc text is reproduced as vm/qdoc returns it. The node markdown-escapes doc comments, so a bracket or angle bracket may carry a backslash the committed source does not have. The source itself is at source and in this repository.
Dependencies
chain-attested| Imports | chain, chain/runtime, chain/runtime/unsafe, gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/coinio, gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/feeledger, gno.land/p/nt/avl/v0, gno.land/p/nt/markdown/sanitize/v0, strconv |
|---|---|
| First-party dependencies | gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/coinio, gno.land/p/g1ut6uspuh73e02yauxpmyt8g3wwddaq8utagvm3/feeledger |
Known limitations
Recorded by the people who built and deployed it. This list is deliberately not empty where honesty costs something.
curated- THE BYTE-MATCH FAILED. source.divergence carries the full measurement. The deployed realm is behaviourally the audited realm (25 --- PASS on the chain's own bytes with an identical per-test gas signature), but the clean byte-match claim is NOT available for this package and never will be -- the live doc comment is one word-wrap away from the commit forever.
- THE REALM SETTLES MONEY, NOT FACTS. Whether the damage was real is off-chain; what the realm binds is the ceiling, the windows and the arbiter's authority.
- A HOSTILE DEPOSITOR CAN DISPUTE A LEGITIMATE CLAIM to delay settlement by one dispute window. The delay is bounded and costs the depositor the same wait. Disclosed rather than mitigated.
- WITH NO ARBITER NAMED, AN UNACCEPTED CLAIM STANDS once the dispute window passes -- a legitimate configuration (low ceiling, no arbiter) that the depositor sees as Arbiter(id) == "" before funding. With an arbiter named, LapseDispute awards the contested amount to the DEPOSITOR.
- FLAT CEILING, NOT GRADUATED. Forfeiture scaled over damage severity (p/tiers, as r/latefees does for overdue duration) is a v2 candidate; the reason it is not in v1 is the Amendment H payload bound, not an architectural objection.
- SINGLE-DENOM (ugnot), ONE DEPOSITOR PER AGREEMENT.
- A TERMINAL AGREEMENT KEEPS ITS PER-HOLDER SLOT, bounded by MaxAgreementsPerHolder=50 over a holder's lifetime rather than over their open set (monotone by design -- a settled deposit is a permanent record both parties and an auditor may need to read).
- SetFeeRecipient DELIBERATELY DOES NOT ENFORCE DISJOINTNESS from the parties, unlike the arbiter rule. AUDIT.md G2 refuses the guard on evidence; the reasoning is in the function's doc comment.
- THE LIVE RUN COVERED ONE ROUTE OF FIVE: fund -> claim -> dispute -> arbiter award -> both pulls, at a 250-bps fee. AcceptClaim, SettleUndisputed, ReleaseUnclaimed, LapseDispute, Release, CancelUnfunded, the no-arbiter configuration, SweepDenom's reserve and the two-step admin transfer are suite- and sweep-covered only.
- FOUR KEYS, STILL ONE PRINCIPAL FOR THREE ROLES: holder, admin and fee recipient were all g1ut6usp..., so the admin/fee-recipient separation is suite-only. The depositor and the arbiter were genuinely separate keys and the arbiter was neither party.
- storage_bytes and storage_deposit are null for the AL56 reason in storage_measurement_note; the figures ARE chain-attested (70,755 B / 7,075,500ugnot at 491924). Do not read the nulls as an unmeasured deploy.
Source
| Source file | onyx/r/deposits/deposits.gno at commit c2e10d17ffbf in the project repository (not public — the digest command above is the check that needs no repository) |
|---|---|
| Matches the deployed bytes | no — UNINTENDED divergence: the deploy did not place the committed bytes on chain |
| Why | BYTE-MATCH FAILED, and the divergence is 10 bytes in two '//' comment lines. Same length (38,013 B both sides): offsets 3677-3683 and 3685-3687, lines 71-72 of the package doc comment's LapseDispute trust bullet, where the inline gno_addpkg emitter re-wrapped the word 'who' across a line break -- a word and a space trading places, which is why the length is preserved. The committed file is NOT edited to match the chain: that would be the comparison deleting itself. The divergence is proven INERT by running the chain's own bytes in a standalone gnowork workspace against the committed test file: 25 --- PASS / 0 --- FAIL, gno lint silent, gofmt -l clean, and the full 75-line test+gas signature IDENTICAL to the committed bytes' (a RenderLimit 20->19 negative control moves it, so the comparison is sensitive). CAUSE: gno_addpkg takes inline files[].body strings and no path, so the emitted string is the real subject; the two-emission gas-reference control agreed to the byte because MsgAddPackage gas is dominated by stored byte count and this edit moved zero bytes -- a gas-reference control is structurally blind to any constant-length edit. Realm bytes are immutable after addpkg, so this is permanent. |