Repository navigation
Keep contract burn_tokens working after the tokenfactory removal - #4549
Conversation
Contracts that issued factory denoms redeem by sending the retired tokenfactory burn_tokens custom message: the Silo and Eris liquid staking hubs burn iSEI and ampSEI when they submit an unbonding batch, and the Wormhole token translator burns when bridging out. Without the module that message ends the whole execution with "Unknown Sei Wasm Message". LegacyBurnTokensHandler serves it ahead of the SDK message handler by burning from the calling contract's own balance through the Wasm module account, the same operation BankMsg::Burn already performs, and only for denoms of the form factory/<contract>/<subdenom>. Creating, minting, admin changes and metadata stay removed, and direct MsgBurn transactions are still refused. Co-authored-by: Cursor <cursoragent@cursor.com>
|
The latest Buf updates on your PR. Results from workflow Buf / buf (pull_request).
|
Codecov Report❌ Patch coverage is
❌ Your patch check has failed because the patch coverage (31.25%) is below the target coverage (50.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #4549 +/- ##
==========================================
- Coverage 57.56% 57.54% -0.02%
==========================================
Files 2132 2133 +1
Lines 167272 167288 +16
==========================================
- Hits 96286 96263 -23
- Misses 70981 71020 +39
Partials 5 5
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
@seidroid review |
There was a problem hiding this comment.
Adds a message handler that keeps the retired tokenfactory burn_tokens Wasm message working: a contract can burn its own balance of factory/\<contract>/\<subdenom> denoms through the Wasm module account, and the handler runs first in CustomMessageHandler's chain. The burn uses the same transfer-then-burn path as wasmd's BankMsg::Burn handler, the Wasm module account has Burner permission, the prefix check ends in / so one address cannot match a longer one, and other messages still fall through as ErrUnknownMsg, so nothing blocks; the one note is a panic on malformed input (tree checked at bf78b7c; tests not run because no Go toolchain was available; codex's reading reported nothing, which agrees except for that note).
Non-blocking
1 finding on the changed lines, as inline comments.
seidroid review · decision approve · session f772c3ba10f444a09031f34f84dff0ca · turn resp_claude_1bc698d91a0a75202387082019e6700a · item e3a055812c3558b896ff0cc1ac415bfb
Findings: 0 blocking | 1 non-blocking | 1 posted inline
Co-authored-by: Cursor <cursoragent@cursor.com>
Coin.Validate dereferences the amount, so a burn_tokens message whose amount has a denom but no amount panicked instead of returning ErrInvalidCoins, and Wasm dispatch re-raises that panic past any reply_on_error submessage. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@seidroid review |
There was a problem hiding this comment.
Since my last review the handler checks amount.Amount.IsNil() before Validate(), a test now covers a missing amount, and a CHANGELOG entry was added; this fixes the panic I raised, and printing the nil amount in the error message is also safe. No other findings: codex's reading also reported nothing, which agrees, and the tree I checked is at merge commit 5653a44.
seidroid review · decision approve · session f772c3ba10f444a09031f34f84dff0ca · turn resp_claude_847e9e036376bec1b83def354679dc74 · item f5f5fddb871c504d8c9846bab3d16dea
Findings: 0 blocking | 0 non-blocking | 0 posted inline
PR SummaryMedium Risk Overview Adds Reviewed by Cursor Bugbot for commit ae97af9. Bugbot is set up for automated code reviews on this repo. Configure here. |
#4536 removed tokenfactory behind v6.8, including the
burn_tokenscustom Wasm message, and contracts that issuedfactory/denoms redeem them by sending exactly that message. From the upgrade height those redemptions fail with "Unknown Sei Wasm Message". The Silo and Eris liquid staking hubs on pacific-1 burn the queued iSEI and ampSEI insubmit_batch, so no unbonding batch could be submitted and the queued tokens would stay locked in the hubs; iSEI alone is backed by about 3.6M SEI. The Wormhole token translator, which has no admin and so can never be migrated, issues 90 livefactory/denoms through the same bindings.The removed module had no burn logic of its own beyond an admin check.
CustomEncoderturnedburn_tokensinto aMsgBurnsent by the contract (EncodeTokenFactoryBurn),msgServer.Burnrequired that sender to be the admin stored for the denom (Burn), andburnFrommoved the coins into the tokenfactory module account withSendCoinsFromAccountToModuleand destroyed them withBankKeeper.BurnCoins(burnFrom).MsgBurnhad no way to name another address, so a burn only ever took from the sender's own balance.LegacyBurnTokensHandlermakes the same two bank calls through the Wasm module account, which already holds Burner permission becauseBankMsg::Burnburns through it the same way, and it refuses missing, zero or invalid amounts. The admin check becomes the creator embedded in the denom: onlyfactory/<contract>/<subdenom>denoms whose<contract>is the caller are accepted, so no tokenfactory store or keeper is needed, and because any contract can already burn coins it holds withBankMsg::Burn, the handler grants no new capability. It runs first inCustomMessageHandler's chain becauseCustomEncoderrefusesburn_tokenswith an error that ends the chain. Creating, minting, admin changes, metadata, queries and allowlists stay removed and directMsgBurntransactions are still refused at CheckTx, so the hubs stop issuing and wind down instead of breaking. Asking each issuer to switch toBankMsg::Burnwould cover the two hubs but not immutable contracts such as the translator.The handler cannot change how other contracts execute. Every message that is not
burn_tokens, including bank, staking, Wasm, stargate andcall_evm, getsErrUnknownMsgwithout touching state and continues down the chain exactly as before. The burned balance is always the calling contract's own, because wasmd supplies the contract address rather than the message, and the prefix ends in/, so no contract can burn another contract's or a user's tokens, includingfactory/tokens a pool merely holds. The transfer and the burn run inside the sub-message's cache context inDispatchSubmessages, so a failure reverts both, and the bank operations are gas-metered. The remaining edges are deliberate: a hand-built message carrying bothburn_tokensandcall_evmis treated as a burn, the same precedence the removed encoder had; a burn lowers the denom's bank supply, which readers such as the ERC20 pointer'stotalSupplysee, as they did before v6.8; two burns of the same denom in one block write the same supply and module balance keys, so OCC re-executes one, while burns of different denoms share no keys; and every other custom message gets one extra JSON decode that is not gas-metered, next to the oneCustomEncoderalready does.This has to ship in the same release as #4536: a v6.8 cut from main without it breaks those redemptions at the upgrade height. Two differences from the removed module are visible: the bank
burnevent names the Wasm module account as the burner and has noburn_from_address, and authority follows the creator rather than the stored admin, so a contract that handed admin rights away can burn its own holdings again, as it already could withBankMsg::Burn, while an admin that did not create a denom cannot, which is already the case after #4536. Validated with thewasmbindingtests and by running the pacific-1 Silo and Eris hub bytecode from v6.7 to v6.8, both in the offline upgrade harness and on a four-validator docker cluster upgraded through governance: with this change both hubs burn, unbond and pay out the queued SEI with every validator agreeing on the app hash, and without it bothsubmit_batchcalls fail. That cross-version test is not included because it needs the two hub binaries (about 1.3 MB) checked in as test data.