AnteHandler
The AnteHandler decorators are provided for the app - Dchain to use the Abstract Account to decide if a transaction
has the correct authentication data to be executed. Previously the default is to check that the signature data is indeed
valid and from the correct key.
The Proxy smart accounts implements Sudo entry_points to allow the process from the AnteHandler to check if the
valid credential is provided.
Overview
The abstractaccount module provides three main decorators that integrate with Dchain's transaction processing pipeline:
- SetAbstractAccountPubKeyDecorator: Sets NilPubKey for accounts implementing AbstractAccountI
- BeforeTxDecorator: Validates credentials by calling the account contract before transaction execution
- AfterTxDecorator: Placeholder for post-transaction hooks (currently not implemented)
These decorators work together to enable custom authentication logic for abstract accounts while maintaining compatibility with standard SDK accounts.
Decorators
1. SetAbstractAccountPubKeyDecorator
Location: x/abstractaccount/ante.go:51-109
This decorator runs after the standard SetPubKeyDecorator and handles the special case where accounts implement the
AbstractAccountI interface.
Purpose: Set a NilPubKey for abstract accounts that don't have a public key set yet.
Flow:
- Extract signers from the transaction
- For each signer, retrieve the account from the account keeper
- Check if the account implements
AbstractAccountIinterface - If yes, check if
IsAbstractAccount()returns true - If the account doesn't have a pubkey set, create and set a
NilPubKey - Save the updated account to the account keeper
Why NilPubKey?
Abstract accounts don't use traditional cryptographic signatures for authentication. Instead, they rely on smart
contract logic. The NilPubKey is a special public key type that:
- Returns the account address when
Address()is called - Panics if
VerifySignature()is called (should never happen for abstract accounts) - Prevents nil pointer panics during transaction signing data preparation
Code Reference:
func (sapkd SetAbstractAccountPubKeyDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
// Loop through each signer
for _, signer := range signers {
acc, err := authante.GetSignerAcc(ctx, sapkd.ak, signer)
// Check if account implements AbstractAccountI
absAccI, ok := acc.(types.AbstractAccountI)
if !ok {
continue
}
// Check if it's actually an abstract account
if !absAccI.IsAbstractAccount() {
continue
}
// Set NilPubKey if not already set
if acc.GetPubKey() == nil {
nilPubKey := vestingtypes.NewNilPubKey(acc.GetAddress())
err = acc.SetPubKey(nilPubKey)
sapkd.ak.SetAccount(ctx, acc)
}
}
return next(ctx, tx, simulate)
}
2. BeforeTxDecorator
Location: x/abstractaccount/ante.go:112-231
This is the main decorator that implements custom authentication for abstract accounts.
Purpose: Validate transaction credentials by invoking the account contract's BeforeTx sudo entry point.
Flow:
-
Determine if this is an Abstract Account Transaction:
- Transaction must have exactly one signer and one signature
- The signer must implement
AbstractAccountIandIsAbstractAccount()must return true - If not, delegate to the standard
SigVerificationDecorator
-
Store Signer Address: Save the abstract account address to module store for potential use in PostHandler
-
Verify Sequence Number: Check that the signature sequence matches the account sequence to prevent replay attacks
-
Prepare Contract Sudo Message:
- Convert transaction messages to
[]Anyformat - Extract sign bytes and credentials from the signature data
- Build the
BeforeTxsudo message
- Convert transaction messages to
-
Invoke Account Contract: Call the account contract's sudo entry point with gas limit specified in module params
-
Continue to Next Decorator: If contract execution succeeds, proceed with transaction execution
Credentials vs Signatures:
The decorator uses the term "credentials" instead of "signatures" because abstract accounts can use any form of authentication proof, not just cryptographic signatures. Examples include:
- WebAuthn signatures (passkeys)
- Zero-knowledge proofs
- Multi-factor authentication tokens
- Time-based one-time passwords (TOTP)
Sudo Message Format:
{
"before_tx": {
"msgs": [...], // Transaction messages as Any types
"tx_bytes": "...", // Sign bytes for the transaction
"cred_bytes": "...", // Credential bytes (e.g., signature)
"simulate": false // Whether this is a simulation
}
}
Code Reference:
func (d BeforeTxDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
// Check if this is an abstract account transaction
isAbstractAccountTx, signerAcc, sig, err := IsAbstractAccountTx(ctx, tx, d.ak)
if !isAbstractAccountTx {
// Delegate to standard verification
svd := authante.NewSigVerificationDecorator(d.ak, d.signModeHandler)
return svd.AnteHandle(ctx, tx, simulate, next)
}
// Save signer address for posthandler
err = d.aak.SetSignerAddress(ctx, signerAcc.GetAddress())
// Check sequence number
if sig.Sequence != signerAcc.GetSequence() {
return ctx, sdkerrors.ErrWrongSequence
}
// Prepare sudo message
sudoMsgBytes, err := json.Marshal(&types.AccountSudoMsg{
BeforeTx: &types.BeforeTx{
Msgs: msgAnys,
TxBytes: signBytes,
CredBytes: sigBytes,
Simulate: simulate,
},
})
// Invoke account contract with gas limit
if err := utils.SudoWithGasLimit(ctx, d.aak.ContractKeeper(), signerAcc.GetAddress(), sudoMsgBytes, params.MaxGasBefore); err != nil {
return ctx, err
}
return next(ctx, tx, simulate)
}
3. AfterTxDecorator
Location: x/abstractaccount/ante.go:233-251
This decorator is a PostDecorator that runs after transaction execution.
Purpose: Placeholder for future post-transaction hooks for abstract accounts.
Current Implementation: Currently not implemented - simply passes through to the next handler.
Potential Future Uses:
- Post-transaction validation
- Event emission for abstract account transactions
- Plugin execution hooks
- State update notifications
Code Reference:
func (d AfterTxDecorator) PostHandle(ctx sdk.Context, tx sdk.Tx, simulate, success bool, next sdk.PostHandler) (sdk.Context, error) {
// Currently we only support BeforeTx
return next(ctx, tx, simulate, success)
}
Helper Functions
IsAbstractAccountTx
Location: x/abstractaccount/ante.go:255-295
Determines whether a transaction qualifies as an abstract account transaction.
Criteria:
- Transaction has exactly one signer
- Transaction has exactly one signature
- The signer account implements
AbstractAccountIinterface - The account's
IsAbstractAccount()method returns true
Returns:
bool: Whether this is an abstract account transactionsdk.AccountI: The signer account (if applicable)*txsigning.SignatureV2: The signature data (if applicable)error: Any error encountered
SigVerificationGasConsumer
Location: x/abstractaccount/ante.go:35-47
Custom gas consumer for signature verification that handles NilPubKey specially.
Behavior:
- For
NilPubKey: Does not consume gas (gas is consumed during contract execution) - For other public keys: Delegates to the default SDK gas consumer
Code Reference:
func SigVerificationGasConsumer(meter storetypes.GasMeter, sig txsigning.SignatureV2, params authtypes.Params) error {
switch sig.PubKey.(type) {
case *vestingtypes.NilPubKey:
return nil // No gas consumed for NilPubKey
default:
return authante.DefaultSigVerificationGasConsumer(meter, sig, params)
}
}
Integration with App
The decorators are typically integrated into the ante handler chain in app/ante.go:
anteDecorators := []sdk.AnteDecorator{
// ... other decorators
authante.NewSetPubKeyDecorator(accountKeeper),
abstractaccount.NewSetAbstractAccountPubKeyDecorator(accountKeeper),
// ... other decorators
abstractaccount.NewBeforeTxDecorator(abstractAccountKeeper, accountKeeper, signModeHandler),
// ... other decorators
}
The AfterTxDecorator is integrated into the post handler chain:
postDecorators := []sdk.PostDecorator{
abstractaccount.NewAfterTxDecorator(abstractAccountKeeper),
}
Gas Limits
The module parameters include a MaxGasBefore setting that limits the gas consumption during the BeforeTx contract
execution. This prevents abstract accounts from consuming excessive gas during authentication, which could be used for
denial-of-service attacks.
Error Handling
If the contract execution in BeforeTxDecorator fails (e.g., invalid credentials, contract error), the ante handler
returns an error and the transaction is rejected. Common failure scenarios:
- Invalid credentials provided
- Contract execution error
- Gas limit exceeded
- Sequence number mismatch
- Account not found