Background
PerformVaultFeeTransfer previously added OutstandingAumFee with Coin.Add, which panics on a denom mismatch, so a malformed stored coin (e.g. {denom:"", amount:0}) could break fee reconciliation. That panic vector was eliminated in 2edbd03 (#210): the keeper now adds raw Ints with SafeAdd and rebuilds the coin from vault.PaymentDenom.
However, the malformed state itself is still representable — validation and state migration were never tightened to match.
Remaining work
-
Tighten VaultAccount.Validate() — types/vault.go:294-296 short-circuits the denom check for empty-denom coins:
if (v.OutstandingAumFee.Denom != "" || (!v.OutstandingAumFee.Amount.IsNil() && !v.OutstandingAumFee.Amount.IsZero())) && v.OutstandingAumFee.Denom != v.PaymentDenom {
A coin of {denom:"", amount:0} still passes validation. Tighten so the denom must match PaymentDenom (or the coin must be fully unset/nil).
-
Migration normalization (optional) — no migration normalizes existing OutstandingAumFee state; MigrateVaultAccountPaymentDenomDefaults (keeper/migrations.go:40-59) normalizes only PaymentDenom. Consider normalizing empty-denom zero fees to sdk.NewCoin(PaymentDenom, 0).
-
Regression test — add a test importing/validating a vault with OutstandingAumFee = {denom:"", amount:0} to lock in the chosen behavior.
Risk
Residual runtime risk is nil — the coin is rebuilt from PaymentDenom at the only use site — so this is defensive hardening and state hygiene, not a live bug.
Refs: #210
Background
PerformVaultFeeTransferpreviously addedOutstandingAumFeewithCoin.Add, which panics on a denom mismatch, so a malformed stored coin (e.g.{denom:"", amount:0}) could break fee reconciliation. That panic vector was eliminated in2edbd03(#210): the keeper now adds rawInts withSafeAddand rebuilds the coin fromvault.PaymentDenom.However, the malformed state itself is still representable — validation and state migration were never tightened to match.
Remaining work
Tighten
VaultAccount.Validate()—types/vault.go:294-296short-circuits the denom check for empty-denom coins:A coin of
{denom:"", amount:0}still passes validation. Tighten so the denom must matchPaymentDenom(or the coin must be fully unset/nil).Migration normalization (optional) — no migration normalizes existing
OutstandingAumFeestate;MigrateVaultAccountPaymentDenomDefaults(keeper/migrations.go:40-59) normalizes onlyPaymentDenom. Consider normalizing empty-denom zero fees tosdk.NewCoin(PaymentDenom, 0).Regression test — add a test importing/validating a vault with
OutstandingAumFee = {denom:"", amount:0}to lock in the chosen behavior.Risk
Residual runtime risk is nil — the coin is rebuilt from
PaymentDenomat the only use site — so this is defensive hardening and state hygiene, not a live bug.Refs: #210