Repository navigation
ic402 v2.18.0
Minor — a refund the close could not make is reported, and its record is kept until the payer
recovers it. Requested by EngramX. No PaymentResult tag change; no stored type changed
(StableSession, StableGatewayState and STABLE_SCHEMA_VERSION are unchanged).
Added
sessionCounts().refundOwed. When an ICP close's refund leg fails (refused or rejected by the
ledger), the session ends#closedwith the payer's remainder in its escrow subaccount. When the
payer closed,closeSessionreturned#settlementFailed("Refund leg …"); when the expiry sweep
closed, the timer dropped that result andsessionCounts()counted the sessionclosedlike a clean
close, so nobody knew.refundOwednow counts these sessions untilrecoverEscrowleaves one fee
or less in the escrow (a partial recovery keeps it counted). The escrow balance is computed from the
close, not queried: an escrow subaccount is derivable and anyone can top it up. The example's
health().sessionsgains the field, soexample.didand the client IDL gainrefundOwed : nat
(additive; existing decoders ignore it).refundOwedToStable()/loadRefundOwed(data)on the Gateway, and theIc402.RefundOwedtype.
The owed refunds are kept outsideStableSession, so persist them in their own stable variable
and restore them afterloadStable(README,docs/upgrade-safety.md; the example does). The new
variable is upgrade-compatible for an existing persistent actor. Unpersisted, the count restarts at 0
on upgrade, as before 2.18.0.
Fixed
- An owed refund could be stranded for good.
gcClosedSessionsdeleted a#closedrecord 24 h
after its last activity even when its refund had failed, and the record is what authorizes
recoverEscrow: after that, the remainder sat in the escrow with no way left to move it. A session
whose close reported a failed refund leg (from either close path) now keeps its record until the
payer has recovered it.
Not changed
- A session forced out of
#closingwithforceResolveSession, or a close a force landed on, is not
recorded: the library cannot know whether its refund ran, and a wrong entry could never clear. It
keeps the usual 24 h window from the force (runbook §3). - Not retroactive: a refund that failed before the canister ran 2.18.0 is not tracked (the stored
session record does not say whether its refund succeeded, and escrow balances are never queried). - The owed figure uses the ledger fee at the close; if the fee changes before recovery, recover the
fullbalance − current feein one call.
Changed (toolchain, merged since 2.17.7)
- pnpm 9.15.0 → 10.34.6, with
@icp-sdk/icp-cliandesbuildinstall scripts ignored explicitly
(they only validate an optional platform binary; both work without them). - icp CLI 1.6.0 everywhere (it was 1.0.2 locally and 1.1.0 in CI).
- moc 1.11.0 → 1.16.1 and wasmtime 44.0.0 → 48.0.5. The 108
.vals()calls moc 1.16 flags (M0269)
are now.values()(available since moc 0.14, so consumers on moc 1.x are unaffected). An in-place
upgrade of a 1.11.0-built example holding state to the 1.16.1 build was rehearsed: state preserved,
the replica suite green afterwards.