README
¶
devgas
The devgas
module of Nibiru Chain shares contract execution fees with smart contract
developers.
This aims to increase the adoption of Nibiru by offering CosmWasm smart contract developers a direct source of income based on usage. Developers can register their smart contracts and every time someone interacts with a registered smart contract, the contract deployer or their assigned withdrawal account receives a part of the transaction fees.
Table of Contents
- Register a Contract Withdrawal Address
- Update a Contract's Withdrawal Address
- Concepts
- State
- State Transitions
- Register Fee Share
- TxMsgs - devgas
MsgRegisterFeeShare
- Ante
- Handling
- Events
- Module Parameters
- Enable FeeShare Module
- Clients
- Command Line Interface
- gRPC Queries
Register a Contract Withdrawal Address
nibid tx devgas register [contract_bech32] [withdraw_bech32] --from [key]
Registers the withdrawal address for the given contract.
Register Args
contract_bech32 (string, required)
: The bech32 address of the contract whose
interaction fees will be shared.
withdraw_bech32 (string, required)
: The bech32 address where the interaction
fees will be sent every block.
Description
This command registers the withdrawal address for the given contract. Any time a user interacts with your contract, the funds will be sent to the withdrawal address. It can be any valid address, such as a DAO, normal account, another contract, or a multi-sig.
Permissions
This command can only be run by the admin of the contract. If there is no admin, then it can only be run by the contract creator.
Exceptions
-
withdraw_bech32
can not be the community pool (distribution) address. This is a limitation of the way the SDK handles this module account -
For contracts created or administered by a contract factory, the withdrawal address can only be the same as the contract address. This can be registered by anyone, but it's unchangeable. This is helpful for SubDAOs or public goods to save fees in the treasury.
If you create a contract like this, it's best to create an execution method for withdrawing fees to an account. To do this, you'll need to save the withdrawal address in the contract's state before uploading a non-migratable contract.
Update a Contract's Withdrawal Address
This can be changed at any time so long as you are still the admin or creator of a contract with the command:
nibid tx devgas update [contract] [new_withdraw_address]
Update Exception
This can not be done if the contract was created from or is administered by another contract (a contract factory). There is not currently a way for a contract to change its own withdrawal address directly.
Concepts
FeeShare
The DevGas (x/devgas
) module is a revenue-per-gas model, which allows
developers to get paid for deploying their decentralized applications (dApps)
on Nibiru. This helps developers to generate revenue every time a user
invokes their contracts to execute a transaction on the chain.
This registration is permissionless to sign up for and begin earning fees from.
By default, 50% of all gas fees for Execute Messages are shared. This
can be changed by governance and implemented by the x/devgas
module.
Registration
Developers register their contract applications to gain their cut of fees per execution. Any contract can be registered by a developer by submitting a signed transaction. After the transaction is executed successfully, the developer will start receiving a portion of the transaction fees paid when a user interacts with the registered contract. The developer can have the funds sent to their wallet, a DAO, or any other wallet address on the Nibiru.
::: tip NOTE: If your contract is part of a development project, please ensure that the deployer of the contract (or the factory/DAO that deployed the contract) is an account that is owned by that project. This avoids the situation, that an individual deployer who leaves your project could become malicious. :::
Fee Distribution
As described above, developers will earn a portion of the transaction fee after registering their contracts. To understand how transaction fees are distributed, we will look at the following in detail:
- The transactions eligible are only Wasm Execute Txs (
MsgExecuteContract
).
WASM Transaction Fees
Users pay transaction fees to pay to interact with smart contracts on Nibiru.
When a transaction is executed, the entire fee amount (gas limit * gas price
)
is sent to the FeeCollector
module account during the Cosmos SDK
AnteHandler
execution.
After this step, the FeeCollector
sends 50% of the funds and splits them
between contracts that were executed on the transaction. If the fees paid are
not accepted by governance, there is no payout to the developers (for example,
niche base tokens) for tax purposes. If a user sends a message and it does not
interact with any contracts (ex: bankSend), then the entire fee is sent to the
FeeCollector
as expected.
State
The x/devgas
module keeps the following objects in the state:
State Object | Description | Key | Value | Store |
---|---|---|---|---|
FeeShare |
Fee split bytecode | []byte{1} + []byte(contract_address) |
[]byte{feeshare} |
KV |
DeployerFeeShares |
Contract by deployer address bytecode | []byte{2} + []byte(deployer_address) + []byte(contract_address) |
[]byte{1} |
KV |
FeeSharesByWithdrawer |
Contract by withdraw address bytecode | []byte{3} + []byte(withdraw_address) + []byte(contract_address) |
[]byte{1} |
KV |
State: FeeShare
A FeeShare
defines an instance that organizes fee distribution conditions for
the owner of a given smart contract
type FeeShare struct {
// contract_address is the bech32 address of a registered contract in string form
ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"`
// deployer_address is the bech32 address of message sender. It must be the
// same as the contracts admin address.
DeployerAddress string `protobuf:"bytes,2,opt,name=deployer_address,json=deployerAddress,proto3" json:"deployer_address,omitempty"`
// withdrawer_address is the bech32 address of account receiving the
// transaction fees.
WithdrawerAddress string `protobuf:"bytes,3,opt,name=withdrawer_address,json=withdrawerAddress,proto3" json:"withdrawer_address,omitempty"`
}
State: ContractAddress
ContractAddress
defines the contract address that has been registered for fee distribution.
DeployerAddress
A DeployerAddress
is the admin address for a registered contract.
WithdrawerAddress
The WithdrawerAddress
is the address that receives transaction fees for a registered contract.
Genesis State
The x/devgas
module's GenesisState
defines the state necessary for initializing the chain from a previously exported height. It contains the module parameters and the fee share for registered contracts:
// GenesisState defines the module's genesis state.
type GenesisState struct {
// module parameters
Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"`
// active registered contracts for fee distribution
FeeShares []FeeShare `protobuf:"bytes,2,rep,name=feeshares,json=feeshares,proto3" json:"feeshares"`
}
State Transitions
The x/devgas
module allows for three types of state transitions:
RegisterFeeShare
, UpdateFeeShare
and CancelFeeShare
. The logic for
distributing transaction fees is handled through the Ante
handler.
Register Fee Share
A developer registers a contract for receiving transaction fees by defining the contract address and the withdrawal address for fees to be paid too. If this is not set, the developer can not get income from the contract. This is opt-in for tax purposes. When registering for fees to be paid, you MUST be the admin of said wasm contract. The withdrawal address can be the same as the contract's address if you so choose.
- User submits a
RegisterFeeShare
to register a contract address, along with a withdrawal address that they would like to receive the fees to - Check if the following conditions pass:
x/devgas
module is enabled via Governance- the contract was not previously registered
- deployer has a valid account (it has done at least one transaction)
- the contract address exists
- the deployer signing the transaction is the admin of the contract
- the contract is already deployed
- Store an instance of the provided share.
All transactions sent to the registered contract occurring after registration
will have their fees distributed to the developer, according to the global
DeveloperShares
parameter in governance.
Update Fee Split
A developer updates the withdraw address for a registered contract, defining the contract address and the new withdraw address.
- The user submits a
UpdateFeeShare
- Check if the following conditions pass:
x/devgas
module is enabled- the contract is registered
- the signer of the transaction is the same as the contract admin per the WasmVM
- Update the fee with the new withdrawal address.
After this update, the developer receives the fees on the new withdrawal address.
Cancel Fee Split
A developer cancels receiving fees for a registered contract, defining the contract address.
- The user submits a
CancelFeeShare
- Check if the following conditions pass:
x/devgas
module is enabled- the contract is registered
- the signer of the transaction is the same as the contract admin per the WasmVM
- Remove share from storage
The developer no longer receives fees from transactions sent to this contract. All fees go to the community.
TxMsgs - devgas
This section defines the sdk.Msg
concrete types that result in the state
transitions defined on the previous section.
MsgRegisterFeeShare
Defines a transaction signed by a developer to register a contract for transaction fee distribution. The sender must be an EOA that corresponds to the contract deployer address.
type MsgRegisterFeeShare struct {
// contract_address in bech32 format
ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"`
// deployer_address is the bech32 address of message sender. It must be the
// same the contract's admin address
DeployerAddress string `protobuf:"bytes,2,opt,name=deployer_address,json=deployerAddress,proto3" json:"deployer_address,omitempty"`
// withdrawer_address is the bech32 address of account receiving the
// transaction fees
WithdrawerAddress string `protobuf:"bytes,3,opt,name=withdrawer_address,json=withdrawerAddress,proto3" json:"withdrawer_address,omitempty"`
}
The message content stateless validation fails if:
- Contract bech32 address is invalid
- Deployer bech32 address is invalid
- Withdraw bech32 address is invalid
MsgUpdateFeeShare
Defines a transaction signed by a developer to update the withdraw address of a contract registered for transaction fee distribution. The sender must be the admin of the contract.
type MsgUpdateFeeShare struct {
// contract_address in bech32 format
ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"`
// deployer_address is the bech32 address of message sender. It must be the
// same the contract's admin address
DeployerAddress string `protobuf:"bytes,2,opt,name=deployer_address,json=deployerAddress,proto3" json:"deployer_address,omitempty"`
// withdrawer_address is the bech32 address of account receiving the
// transaction fees
WithdrawerAddress string `protobuf:"bytes,3,opt,name=withdrawer_address,json=withdrawerAddress,proto3" json:"withdrawer_address,omitempty"`
}
The message content stateless validation fails if:
- Contract bech32 address is invalid
- Deployer bech32 address is invalid
- Withdraw bech32 address is invalid
MsgCancelFeeShare
Defines a transaction signed by a developer to remove the information for a registered contract. Transaction fees will no longer be distributed to the developer for this smart contract. The sender must be an admin that corresponds to the contract.
type MsgCancelFeeShare struct {
// contract_address in bech32 format
ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"`
// deployer_address is the bech32 address of message sender. It must be the
// same the contract's admin address
DeployerAddress string `protobuf:"bytes,2,opt,name=deployer_address,json=deployerAddress,proto3" json:"deployer_address,omitempty"`
}
The message content stateless validation fails if:
- Contract bech32 address is invalid
- Contract bech32 address is zero
- Deployer bech32 address is invalid
Ante
The fees module uses the ante handler to distribute fees between developers and the community.
Handling
An Ante Decorator executes custom logic after each
successful WasmExecuteMsg transaction. All fees paid by a user for transaction
execution are sent to the FeeCollector
module account during the
AnteHandler
execution before being redistributed to the registered contract
developers.
If the x/devgas
module is disabled or the Wasm Execute Msg transaction
targets an unregistered contract, the handler returns nil
, without performing
any actions. In this case, 100% of the transaction fees remain in the
FeeCollector
module, to be distributed elsewhere.
If the x/devgas
module is enabled and a Wasm Execute Msg transaction
targets a registered contract, the handler sends a percentage of the
transaction fees (paid by the user) to the withdraw address set for that
contract.
- The user submits an Execute transaction (
MsgExecuteContract
) to a smart contract and the transaction is executed successfully - Check if
- fees module is enabled
- the smart contract is registered to receive fee split
- Calculate developer fees according to the
DeveloperShares
parameter. - Check what fees governance allows to be paid in
- Check which contracts the user executed that also have been registered.
- Calculate the total amount of fees to be paid to the developer(s). If multiple, split the 50% between all registered withdrawal addresses.
- Distribute the remaining amount in the
FeeCollector
to validators according to the SDK Distribution Scheme.
Events
The x/devgas
module emits the following events:
Event: Register Fee Split
Type | Attribute Key | Attribute Value |
---|---|---|
register_feeshare |
"contract" |
{msg.ContractAddress} |
register_feeshare |
"sender" |
{msg.DeployerAddress} |
register_feeshare |
"withdrawer_address" |
{msg.WithdrawerAddress} |
Event: Update Fee Split
Type | Attribute Key | Attribute Value |
---|---|---|
update_feeshare |
"contract" |
{msg.ContractAddress} |
update_feeshare |
"sender" |
{msg.DeployerAddress} |
update_feeshare |
"withdrawer_address" |
{msg.WithdrawerAddress} |
Event: Cancel Fee Split
Type | Attribute Key | Attribute Value |
---|---|---|
cancel_feeshare |
"contract" |
{msg.ContractAddress} |
cancel_feeshare |
"sender" |
{msg.DeployerAddress} |
Module Parameters
The fee Split module contains the following parameters:
Key | Type | Default Value |
---|---|---|
EnableFeeShare |
bool | true |
DeveloperShares |
sdk.Dec | 50% |
AllowedDenoms |
[]string{} | []string(nil) |
Enable FeeShare Module
The EnableFeeShare
parameter toggles all state transitions in the module.
When the parameter is disabled, it will prevent any transaction fees from being
distributed to contract deplorers and it will disallow contract registrations,
updates or cancellations.
Developer Shares Amount
The DeveloperShares
parameter is the percentage of transaction fees that are
sent to the contract deplorers.
Allowed Denominations
The AllowedDenoms
parameter is used to specify which fees coins will be paid
to contract developers. If this is empty, all fees paid will be split. If not,
only fees specified here will be paid out to the withdrawal address.
Clients
Command Line Interface
Find below a list of nibid
commands added with the x/devgas
module. You
can obtain the full list by using the nibid -h
command. A CLI command can
look like this:
nibid query feeshare params
Queries
Command | Subcommand | Description |
---|---|---|
query feeshare |
params |
Get devgas params |
query feeshare |
contract |
Get the devgas for a given contract |
query feeshare |
contracts |
Get all feeshares |
query feeshare |
deployer-contracts |
Get all feeshares of a given deployer |
query feeshare |
withdrawer-contracts |
Get all feeshares of a given withdrawer |
Transactions
Command | Subcommand | Description |
---|---|---|
tx feeshare |
register |
Register a contract for receiving devgas |
tx feeshare |
update |
Update the withdraw address for a contract |
tx feeshare |
cancel |
Remove the devgas for a contract |
gRPC Queries
Verb | Method | Description |
---|---|---|
gRPC |
nibiru.devgas.v1.Query/Params |
Get devgas params |
gRPC |
nibiru.devgas.v1.Query/FeeShare |
Get the devgas for a given contract |
gRPC |
nibiru.devgas.v1.Query/FeeShares |
Get all feeshares |
gRPC |
nibiru.devgas.v1.Query/DeployerFeeShares |
Get all feeshares of a given deployer |
gRPC |
nibiru.devgas.v1.Query/FeeSharesByWithdrawer |
Get all feeshares of a given withdrawer |
GET |
/nibiru.devgas/v1/params |
Get devgas params |
GET |
/nibiru.devgas/v1/feeshares/{contract_address} |
Get the devgas for a given contract |
GET |
/nibiru.devgas/v1/feeshares |
Get all feeshares |
GET |
/nibiru.devgas/v1/feeshares/{deployer_address} |
Get all feeshares of a given deployer |
GET |
/nibiru.devgas/v1/feeshares/{withdraw_address} |
Get all feeshares of a given withdrawer |
gRPC Transactions
Verb | Method | Description |
---|---|---|
gRPC |
nibiru.devgas.v1.Msg/RegisterFeeShare |
Register a contract for receiving devgas |
gRPC |
nibiru.devgas.v1.Msg/UpdateFeeShare |
Update the withdraw address for a contract |
gRPC |
nibiru.devgas.v1.Msg/CancelFeeShare |
Remove the devgas for a contract |
POST |
/nibiru.devgas/v1/tx/register_feeshare |
Register a contract for receiving devgas |
POST |
/nibiru.devgas/v1/tx/update_feeshare |
Update the withdraw address for a contract |
POST |
/nibiru.devgas/v1/tx/cancel_feeshare |
Remove the devgas for a contract |
Credits: Evmos and Juno
"This module is a heavily modified fork of evmos/x/revenue" - Juno Network
This module is a heavily modified fork of Juno's heavily modified fork. 🙃
Documentation
¶
Index ¶
- Constants
- func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState
- func InitGenesis(ctx sdk.Context, k keeper.Keeper, data types.GenesisState)
- type AppModule
- func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock)
- func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate
- func (am AppModule) ExportGenesis(ctx sdk.Context, cdc sdkcodec.JSONCodec) json.RawMessage
- func (AppModule) GenerateGenesisState(simState *module.SimulationState)
- func (am AppModule) InitGenesis(ctx sdk.Context, cdc sdkcodec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate
- func (AppModule) Name() string
- func (am AppModule) QuerierRoute() string
- func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry)
- func (am AppModule) RegisterServices(cfg module.Configurator)
- func (AppModule) RegisterStoreDecoder(sdk.StoreDecoderRegistry)
- func (AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation
- type AppModuleBasic
- func (AppModuleBasic) ConsensusVersion() uint64
- func (AppModuleBasic) DefaultGenesis(cdc sdkcodec.JSONCodec) json.RawMessage
- func (AppModuleBasic) GetQueryCmd() *cobra.Command
- func (AppModuleBasic) GetTxCmd() *cobra.Command
- func (AppModuleBasic) Name() string
- func (b AppModuleBasic) RegisterGRPCGatewayRoutes(c sdkclient.Context, serveMux *runtime.ServeMux)
- func (AppModuleBasic) RegisterInterfaces(interfaceRegistry sdkcodectypes.InterfaceRegistry)
- func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *sdkcodec.LegacyAmino)
- func (b AppModuleBasic) ValidateGenesis(cdc sdkcodec.JSONCodec, _ sdkclient.TxEncodingConfig, bz json.RawMessage) error
Constants ¶
const ConsensusVersion = 1
ConsensusVersion defines the current module consensus version.
Variables ¶
This section is empty.
Functions ¶
func ExportGenesis ¶
ExportGenesis export module state
func InitGenesis ¶
InitGenesis import module genesis
Types ¶
type AppModule ¶
type AppModule struct { AppModuleBasic // contains filtered or unexported fields }
AppModule implements the AppModule interface for the fees module.
func NewAppModule ¶
func NewAppModule( k keeper.Keeper, ak authkeeper.AccountKeeper, ss exported.Subspace, ) AppModule
NewAppModule creates a new AppModule Object
func (AppModule) BeginBlock ¶
func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock)
BeginBlock executes all ABCI BeginBlock logic respective to the fees module.
func (AppModule) EndBlock ¶
func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate
EndBlock executes all ABCI EndBlock logic respective to the fee-share module. It returns no validator updates.
func (AppModule) ExportGenesis ¶
ExportGenesis returns the fees module's exported genesis state as raw JSON bytes.
func (AppModule) GenerateGenesisState ¶
func (AppModule) GenerateGenesisState(simState *module.SimulationState)
GenerateGenesisState implements module.AppModuleSimulation.
func (AppModule) InitGenesis ¶
func (am AppModule) InitGenesis(ctx sdk.Context, cdc sdkcodec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate
InitGenesis performs the fees module's genesis initialization. It returns no validator updates.
func (AppModule) QuerierRoute ¶
QuerierRoute returns the module's query routing key.
func (AppModule) RegisterInvariants ¶
func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry)
RegisterInvariants registers the fees module's invariants.
func (AppModule) RegisterServices ¶
func (am AppModule) RegisterServices(cfg module.Configurator)
RegisterServices registers a GRPC query service to respond to the module-specific GRPC queries.
func (AppModule) RegisterStoreDecoder ¶
func (AppModule) RegisterStoreDecoder(sdk.StoreDecoderRegistry)
RegisterStoreDecoder implements module.AppModuleSimulation.
func (AppModule) WeightedOperations ¶
func (AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation
WeightedOperations implements module.AppModuleSimulation.
type AppModuleBasic ¶
type AppModuleBasic struct{}
AppModuleBasic type for the fees module
func (AppModuleBasic) ConsensusVersion ¶
func (AppModuleBasic) ConsensusVersion() uint64
ConsensusVersion returns the consensus state-breaking version for the module.
func (AppModuleBasic) DefaultGenesis ¶
func (AppModuleBasic) DefaultGenesis(cdc sdkcodec.JSONCodec) json.RawMessage
DefaultGenesis returns default genesis state as raw bytes for the fees module.
func (AppModuleBasic) GetQueryCmd ¶
func (AppModuleBasic) GetQueryCmd() *cobra.Command
GetQueryCmd returns the fees module's root query command.
func (AppModuleBasic) GetTxCmd ¶
func (AppModuleBasic) GetTxCmd() *cobra.Command
GetTxCmd returns the root tx command for the fees module.
func (AppModuleBasic) Name ¶
func (AppModuleBasic) Name() string
Name returns the fees module's name.
func (AppModuleBasic) RegisterGRPCGatewayRoutes ¶
func (b AppModuleBasic) RegisterGRPCGatewayRoutes( c sdkclient.Context, serveMux *runtime.ServeMux, )
RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the fees module.
func (AppModuleBasic) RegisterInterfaces ¶
func (AppModuleBasic) RegisterInterfaces( interfaceRegistry sdkcodectypes.InterfaceRegistry, )
RegisterInterfaces registers interfaces and implementations of the fees module.
func (AppModuleBasic) RegisterLegacyAminoCodec ¶
func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *sdkcodec.LegacyAmino)
RegisterLegacyAminoCodec performs a no-op as the fees do not support Amino encoding.
func (AppModuleBasic) ValidateGenesis ¶
func (b AppModuleBasic) ValidateGenesis( cdc sdkcodec.JSONCodec, _ sdkclient.TxEncodingConfig, bz json.RawMessage, ) error
ValidateGenesis performs genesis state validation for the fees module.