Add a new token and only allow fees in alnt (#68)

Part of https://www.notion.so/Multiple-tokens-support-and-disable-transfers-for-LSTAKE-1f2a6b22d47280269f87df3fe03e8d64
- Add base token with denoms `alps` and `lps` (`1 lps = 10^18 alps`)
- Keep `alnt` as the staking token and for laconic module ops
- Accept tx fees only in `alnt`

Reviewed-on: cerc-io/laconicd#68
Co-authored-by: Prathamesh Musale <prathamesh.musale0@gmail.com>
Co-committed-by: Prathamesh Musale <prathamesh.musale0@gmail.com>
This commit is contained in:
Prathamesh Musale 2025-05-13 12:37:11 +00:00 committed by Nabarun
parent f4cf8ac744
commit 31ef3afb9a
8 changed files with 136 additions and 26 deletions

79
app/ante.go Normal file
View File

@ -0,0 +1,79 @@
package app
import (
_ "embed"
"math"
errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"git.vdb.to/cerc-io/laconicd/app/params"
)
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.50.10/x/auth/ante/validator_tx_fee.go#L15
// checkTxFeeWithValidatorMinGasPrices implements the default fee logic, where the minimum price per
// unit of gas is fixed and set by each validator, can the tx priority is computed from the gas price.
func checkTxFeeWithValidatorMinGasPrices(ctx sdk.Context, tx sdk.Tx) (sdk.Coins, int64, error) {
feeTx, ok := tx.(sdk.FeeTx)
if !ok {
return nil, 0, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx")
}
feeCoins := feeTx.GetFee()
gas := feeTx.GetGas()
// Only allow alnt as a fee token
for _, coin := range feeCoins {
if coin.Denom != params.CoinUnit {
return nil, 0, errorsmod.Wrapf(sdkerrors.ErrInvalidCoins, "invalid fee denom %s, only %s is accepted", coin.Denom, params.CoinUnit)
}
}
// Ensure that the provided fees meet a minimum threshold for the validator,
// if this is a CheckTx. This is only for local mempool purposes, and thus
// is only ran on check tx.
if ctx.IsCheckTx() {
minGasPrices := ctx.MinGasPrices()
if !minGasPrices.IsZero() {
requiredFees := make(sdk.Coins, len(minGasPrices))
// Determine the required fees by multiplying each required minimum gas
// price by the gas limit, where fee = ceil(minGasPrice * gasLimit).
glDec := sdkmath.LegacyNewDec(int64(gas))
for i, gp := range minGasPrices {
fee := gp.Amount.Mul(glDec)
requiredFees[i] = sdk.NewCoin(gp.Denom, fee.Ceil().RoundInt())
}
if !feeCoins.IsAnyGTE(requiredFees) {
return nil, 0, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, requiredFees)
}
}
}
priority := getTxPriority(feeCoins, int64(gas))
return feeCoins, priority, nil
}
// getTxPriority returns a naive tx priority based on the amount of the smallest denomination of the gas price
// provided in a transaction.
// NOTE: This implementation should be used with a great consideration as it opens potential attack vectors
// where txs with multiple coins could not be prioritize as expected.
func getTxPriority(fee sdk.Coins, gas int64) int64 {
var priority int64
for _, c := range fee {
p := int64(math.MaxInt64)
gasPrice := c.Amount.QuoRaw(gas)
if gasPrice.IsInt64() {
p = gasPrice.Int64()
}
if priority == 0 || p < priority {
priority = p
}
}
return priority
}

View File

@ -27,6 +27,7 @@ import (
"github.com/cosmos/cosmos-sdk/server/config" "github.com/cosmos/cosmos-sdk/server/config"
servertypes "github.com/cosmos/cosmos-sdk/server/types" servertypes "github.com/cosmos/cosmos-sdk/server/types"
"github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
consensuskeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper" consensuskeeper "github.com/cosmos/cosmos-sdk/x/consensus/keeper"
@ -185,6 +186,9 @@ func NewLaconicApp(
app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, make(map[string]module.AppModuleSimulation, 0)) app.sm = module.NewSimulationManagerFromAppModules(app.ModuleManager.Modules, make(map[string]module.AppModuleSimulation, 0))
app.sm.RegisterStoreDecoders() app.sm.RegisterStoreDecoders()
// set custom ante handlers
app.setCustomAnteHandler()
if err := app.Load(loadLatest); err != nil { if err := app.Load(loadLatest); err != nil {
return nil, err return nil, err
} }
@ -232,3 +236,23 @@ func (app *LaconicApp) RegisterAPIRoutes(apiSvr *api.Server, apiConfig config.AP
panic(err) panic(err)
} }
} }
// setCustomAnteHandler overwrites default ante handlers with custom ante handlers
// Reference: https://github.com/cosmos/cosmos-sdk/blob/v0.50.10/x/auth/tx/config/config.go#L149
func (app *LaconicApp) setCustomAnteHandler() {
anteHandler, err := ante.NewAnteHandler(
ante.HandlerOptions{
AccountKeeper: app.AccountKeeper,
BankKeeper: app.BankKeeper,
SignModeHandler: app.txConfig.SignModeHandler(),
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
TxFeeChecker: checkTxFeeWithValidatorMinGasPrices,
},
)
if err != nil {
panic(err)
}
// Set the AnteHandler for the app
app.SetAnteHandler(anteHandler)
}

View File

@ -10,11 +10,14 @@ import (
) )
const ( const (
CoinUnit = "lnt" // Registered token
BaseCoinUnit = "alnt" LpsCoinUnit = "lps"
LntExponent = 18 LpsBaseCoinUnit = "alps"
LpsExponent = 18
DefaultBondDenom = BaseCoinUnit // Native token, only denominated in alnt
// Used for staking, fees and laconic module ops
CoinUnit = "alnt"
// Bech32PrefixAccAddr defines the Bech32 prefix of an account's address. // Bech32PrefixAccAddr defines the Bech32 prefix of an account's address.
Bech32PrefixAccAddr = "laconic" Bech32PrefixAccAddr = "laconic"
@ -39,12 +42,11 @@ func init() {
} }
func RegisterDenoms() { func RegisterDenoms() {
err := sdk.RegisterDenom(CoinUnit, math.LegacyOneDec()) err := sdk.RegisterDenom(LpsCoinUnit, math.LegacyOneDec())
if err != nil { if err != nil {
panic(err) panic(err)
} }
err = sdk.RegisterDenom(LpsBaseCoinUnit, math.LegacyNewDecWithPrec(1, LpsExponent))
err = sdk.RegisterDenom(BaseCoinUnit, math.LegacyNewDecWithPrec(1, LntExponent))
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -21,7 +21,6 @@ import (
"github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/cosmos/cosmos-sdk/crypto/keyring"
"github.com/cosmos/cosmos-sdk/server" "github.com/cosmos/cosmos-sdk/server"
serverconfig "github.com/cosmos/cosmos-sdk/server/config" serverconfig "github.com/cosmos/cosmos-sdk/server/config"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/types/module"
"github.com/cosmos/cosmos-sdk/types/tx/signing" "github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/x/auth/tx" "github.com/cosmos/cosmos-sdk/x/auth/tx"
@ -29,6 +28,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/auth/types" "github.com/cosmos/cosmos-sdk/x/auth/types"
"git.vdb.to/cerc-io/laconicd/app" "git.vdb.to/cerc-io/laconicd/app"
"git.vdb.to/cerc-io/laconicd/app/params"
"git.vdb.to/cerc-io/laconicd/gql" "git.vdb.to/cerc-io/laconicd/gql"
) )
@ -100,7 +100,7 @@ func NewRootCmd() *cobra.Command {
// overwrite the minimum gas price from the app configuration // overwrite the minimum gas price from the app configuration
srvCfg := serverconfig.DefaultConfig() srvCfg := serverconfig.DefaultConfig()
srvCfg.MinGasPrices = fmt.Sprintf("0%s", sdk.DefaultBondDenom) srvCfg.MinGasPrices = fmt.Sprintf("0%s", params.CoinUnit)
// overwrite the block timeout // overwrite the block timeout
cmtCfg := cmtcfg.DefaultConfig() cmtCfg := cmtcfg.DefaultConfig()

View File

@ -1,11 +1,14 @@
#!/bin/bash #!/bin/bash
set -e
KEY="alice" KEY="alice"
CHAINID=${CHAINID:-"laconic_9000-1"} CHAINID=${CHAINID:-"laconic_9000-1"}
MONIKER=${MONIKER:-"localtestnet"} MONIKER=${MONIKER:-"localtestnet"}
KEYRING=${KEYRING:-"test"} KEYRING=${KEYRING:-"test"}
DENOM=${DENOM:-"alnt"} DENOM=${DENOM:-"alnt"}
STAKING_AMOUNT=${STAKING_AMOUNT:-"1000000000000000"} BALANCE=${BALANCE:-"1000000000000000000000000000000"} # 10^32 alnt
STAKING_AMOUNT=${STAKING_AMOUNT:-"1000000000000000"} # 10^15 alnt
MIN_GAS_PRICE=${MIN_GAS_PRICE:-"0.001"} MIN_GAS_PRICE=${MIN_GAS_PRICE:-"0.001"}
LOGLEVEL=${LOGLEVEL:-"info"} LOGLEVEL=${LOGLEVEL:-"info"}
@ -126,11 +129,9 @@ if [ "$1" == "clean" ] || [ ! -d "$HOME/.laconicd/data" ]; then
fi fi
# Allocate genesis accounts (cosmos formatted addresses) # Allocate genesis accounts (cosmos formatted addresses)
# 10^30 alnt | 10^12 lnt laconicd genesis add-genesis-account $KEY ${BALANCE}${DENOM} --keyring-backend $KEYRING
laconicd genesis add-genesis-account $KEY 1000000000000000000000000000000$DENOM --keyring-backend $KEYRING
# Sign genesis transaction # Sign genesis transaction
# 10^15 alnt
laconicd genesis gentx $KEY $STAKING_AMOUNT$DENOM --keyring-backend $KEYRING --chain-id $CHAINID laconicd genesis gentx $KEY $STAKING_AMOUNT$DENOM --keyring-backend $KEYRING --chain-id $CHAINID
# Collect genesis tx # Collect genesis tx

View File

@ -5,11 +5,12 @@ import (
fmt "fmt" fmt "fmt"
sdkmath "cosmossdk.io/math" sdkmath "cosmossdk.io/math"
"git.vdb.to/cerc-io/laconicd/app/params"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
) )
// DefaultMaxBondAmountTokens are the default parameter values. // DefaultMaxBondAmountTokens are the default parameter values.
var DefaultMaxBondAmountTokens = sdkmath.NewInt(1000000000000) // 10^12 alnt var DefaultMaxBondAmountTokens = sdkmath.NewInt(1000000000000) // 10^12
func NewParams(maxBondAmount sdk.Coin) Params { func NewParams(maxBondAmount sdk.Coin) Params {
return Params{MaxBondAmount: maxBondAmount} return Params{MaxBondAmount: maxBondAmount}
@ -17,7 +18,7 @@ func NewParams(maxBondAmount sdk.Coin) Params {
// DefaultParams returns default module parameters // DefaultParams returns default module parameters
func DefaultParams() Params { func DefaultParams() Params {
return NewParams(sdk.NewCoin(sdk.DefaultBondDenom, DefaultMaxBondAmountTokens)) return NewParams(sdk.NewCoin(params.CoinUnit, DefaultMaxBondAmountTokens))
} }
// Validate checks that the parameters have valid values // Validate checks that the parameters have valid values

View File

@ -15,6 +15,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"git.vdb.to/cerc-io/laconicd/app/params"
auctiontypes "git.vdb.to/cerc-io/laconicd/x/auction" auctiontypes "git.vdb.to/cerc-io/laconicd/x/auction"
registrytypes "git.vdb.to/cerc-io/laconicd/x/registry" registrytypes "git.vdb.to/cerc-io/laconicd/x/registry"
"git.vdb.to/cerc-io/laconicd/x/registry/helpers" "git.vdb.to/cerc-io/laconicd/x/registry/helpers"
@ -295,7 +296,7 @@ func (k Keeper) createAuthority(ctx sdk.Context, name string, owner string, isRo
moduleParams.AuthorityAuctionCommitFee, moduleParams.AuthorityAuctionCommitFee,
moduleParams.AuthorityAuctionRevealFee, moduleParams.AuthorityAuctionRevealFee,
moduleParams.AuthorityAuctionMinimumBid, moduleParams.AuthorityAuctionMinimumBid,
sdk.NewCoin(sdk.DefaultBondDenom, math.NewInt(0)), sdk.NewCoin(params.CoinUnit, math.NewInt(0)),
0, 0,
ownerAddress, ownerAddress,
) )

View File

@ -6,26 +6,28 @@ import (
sdkmath "cosmossdk.io/math" sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
"git.vdb.to/cerc-io/laconicd/app/params"
) )
// Default parameter values. // Default parameter values.
var ( var (
// DefaultRecordRent is the default record rent for 1 time period (see expiry time). // DefaultRecordRent is the default record rent for 1 time period (see expiry time).
DefaultRecordRent = sdkmath.NewInt(1000000) // 10^6 alnt DefaultRecordRent = sdkmath.NewInt(1000000) // 10^6
// DefaultRecordExpiryTime is the default record expiry time (1 year). // DefaultRecordExpiryTime is the default record expiry time (1 year).
DefaultRecordExpiryTime = time.Hour * 24 * 365 DefaultRecordExpiryTime = time.Hour * 24 * 365
DefaultAuthorityRent = sdkmath.NewInt(1000000) // 10^6 alnt DefaultAuthorityRent = sdkmath.NewInt(1000000) // 10^6
DefaultAuthorityExpiryTime = time.Hour * 24 * 365 DefaultAuthorityExpiryTime = time.Hour * 24 * 365
DefaultAuthorityGracePeriod = time.Hour * 24 * 2 DefaultAuthorityGracePeriod = time.Hour * 24 * 2
DefaultAuthorityAuctionEnabled = false DefaultAuthorityAuctionEnabled = false
DefaultCommitsDuration = time.Hour * 24 DefaultCommitsDuration = time.Hour * 24
DefaultRevealsDuration = time.Hour * 24 DefaultRevealsDuration = time.Hour * 24
DefaultCommitFee = sdkmath.NewInt(1000000) // 10^6 alnt DefaultCommitFee = sdkmath.NewInt(1000000) // 10^6
DefaultRevealFee = sdkmath.NewInt(1000000) // 10^6 alnt DefaultRevealFee = sdkmath.NewInt(1000000) // 10^6
DefaultMinimumBid = sdkmath.NewInt(5000000) // 5 * 10^6 alnt DefaultMinimumBid = sdkmath.NewInt(5000000) // 5 * 10^6
) )
// NewParams creates a new Params instance // NewParams creates a new Params instance
@ -60,13 +62,13 @@ func NewParams(
// DefaultParams returns a default set of parameters. // DefaultParams returns a default set of parameters.
func DefaultParams() Params { func DefaultParams() Params {
return NewParams( return NewParams(
sdk.NewCoin(sdk.DefaultBondDenom, DefaultRecordRent), DefaultRecordExpiryTime, sdk.NewCoin(params.CoinUnit, DefaultRecordRent), DefaultRecordExpiryTime,
sdk.NewCoin(sdk.DefaultBondDenom, DefaultAuthorityRent), sdk.NewCoin(params.CoinUnit, DefaultAuthorityRent),
DefaultAuthorityExpiryTime, DefaultAuthorityGracePeriod, DefaultAuthorityAuctionEnabled, DefaultCommitsDuration, DefaultAuthorityExpiryTime, DefaultAuthorityGracePeriod, DefaultAuthorityAuctionEnabled, DefaultCommitsDuration,
DefaultRevealsDuration, DefaultRevealsDuration,
sdk.NewCoin(sdk.DefaultBondDenom, DefaultCommitFee), sdk.NewCoin(params.CoinUnit, DefaultCommitFee),
sdk.NewCoin(sdk.DefaultBondDenom, DefaultRevealFee), sdk.NewCoin(params.CoinUnit, DefaultRevealFee),
sdk.NewCoin(sdk.DefaultBondDenom, DefaultMinimumBid), sdk.NewCoin(params.CoinUnit, DefaultMinimumBid),
) )
} }