-
Notifications
You must be signed in to change notification settings - Fork 902
[ENG-127] Fee distribution module spec #481
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
804bb78
Distribution module spec
loredanacirstea 3d8125b
x/fees - update spec
loredanacirstea 6f34b97
Merge branch 'main' into loredana/ENG-127-fee-distribution-spec
loredanacirstea 2e32ea0
Apply suggestions from code review
loredanacirstea 363e8b6
x/fees spec - tables align left (PR review)
loredanacirstea 4c16755
x/fees - adding a "Future Improvements" section
loredanacirstea a91508e
x/fees spec - small fix
loredanacirstea f9b2acd
Update x/fees/spec/09_improvements.md
fedekunze File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<!-- | ||
order: 1 | ||
--> | ||
|
||
# Concepts | ||
|
||
## EOA | ||
|
||
An Externally Owned Account ([EOA](https://ethereum.org/en/whitepaper/#ethereum-accounts)) is an account controlled by a private key, that can sign transactions. | ||
|
||
## Deployer Address | ||
|
||
The EOA address that deployed the smart contract being registered for fee distribution. | ||
|
||
## Withdraw Address | ||
|
||
The address set by a contract deployer to receive transaction fees for a registered smart contract. If not set, it defaults to the deployer’s address. | ||
|
||
## Developer | ||
|
||
The entity that has control over the deployer account. | ||
|
||
## Registration of a Contract | ||
|
||
Any contract can be registered by a developer by submitting a signed transaction. The signer of this transaction must match the address of the deployer of the contract in order for the registration to succeed. 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. | ||
|
||
### Fee Distribution | ||
|
||
As described above, developers will earn a portion of the transaction fee after they register their contracts. The transactions eligible are only EVM transactions (`MsgEthereumTx`). Cosmos SDK transactions are not eligible at this time. | ||
|
||
#### EVM Transaction Fees | ||
|
||
When a transaction is executed, the entire fee amount `gasLimit * gasPrice` is sent to the `FeeCollector` Module Account during the `AnteHandler` execution. After the EVM executes the transaction, the user receives a refund of `(gasLimit - gasUsed) * gasPrice`. | ||
|
||
Therefore, the user only pays for the execution: `txFee = gasUsed * gasPrice`. This is the transaction fee distributed between developers and validators, in accordance with the `x/fees` module parameters: `DeveloperShares`, `ValidatorShares`. This distribution is handled through the `PostTxProcessing` [Hook](./05_hooks.md). | ||
|
||
### Address Derivation | ||
|
||
When registering a smart contract, the deployer provides an array of nonces, used to [derive the contract’s address](https://github.com/ethereum/go-ethereum/blob/d8ff53dfb8a516f47db37dbc7fd7ad18a1e8a125/crypto/crypto.go#L107-L111). The smart contract can be directly deployed by the deployer's EOA or created through one or more [factory](https://en.wikipedia.org/wiki/Factory_method_pattern) pattern smart contracts. | ||
|
||
If `MyContract` is deployed directly by `DeployerEOA`, in a transaction sent with nonce `5`, then the array of nonces is `[5]`. | ||
|
||
If the contract was created by a smart contract, through the `CREATE` opcode, we need to provide all the nonces from the creation path. Let's take the example of `DeployerEOA` deploying a `Factory0` smart contract with nonce `5`. Then, `DeployerEOA` sends a transaction to `Factory0` through which a `Factory1` smart contract is created. Let us assume `Factory1` is the second contract created by `Factory0` - the nonce is `1`. Then, `DeployerEOA` sends a transaction to the `Factory1` contract, through which `MyContract` is created. Let us assume this is the first contract created by `Factory1` - the nonce is `0`. We now have an address derivation path of `DeployerEOA` -> `Factory0` -> `Factory1` -> `MyContract`. To be able to verify that `DeployerEOA` can register `MyContract`, we need to provide the following nonces: `[5, 1, 0]`. | ||
|
||
::: tip | ||
**Note**: Even if `MyContract` is created from `Factory1` through a transaction sent by an account different from `DeployerEOA`, only `DeployerEOA` can register `MyContract`. | ||
::: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<!-- | ||
order: 2 | ||
--> | ||
|
||
# State | ||
|
||
## State Objects | ||
|
||
The `x/fees` module keeps the following objects in state: | ||
|
||
| State Object | Description | Key | Value | Store | | ||
| :------------------ | :-------------------------- | :------------------------------------- | :--------------------------- | :---- | | ||
| `DeployerAddress` | Deployer address bytecode | `[]byte{1} + []byte(contract_address)` | `[]byte{deployer_address}` | KV | | ||
| `WithdrawAddress` | Withdraw address bytecode | `[]byte{2} + []byte(contract_address)` | `[]byte{withdraw_address}` | KV | | ||
| `ContractAddresses` | Contract addresses bytecode | `[]byte{3} + []byte(deployer_address)` | `[]byte{contract_addresses}` | KV | | ||
|
||
### DeployerAddress | ||
|
||
Deployer address for a registered contract. | ||
|
||
### WithdrawAddress | ||
|
||
Address that will receive transaction fees for a registered contract. | ||
|
||
### ContractAddresses | ||
|
||
Slice of contract addresses registered by a developer. | ||
|
||
## Genesis State | ||
|
||
The `x/fees` module's `GenesisState` defines the state necessary for initializing the chain from a previous exported height. It contains the module parameters and the fee information for registered contracts: | ||
|
||
```go | ||
// 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 | ||
DevFeeInfos []DevFeeInfo `protobuf:"bytes,2,rep,name=dev_fee_infos,json=devFeeInfos,proto3" json:"dev_fee_infos"` | ||
} | ||
``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
<!-- | ||
order: 3 | ||
--> | ||
|
||
# State Transitions | ||
|
||
The `x/fees` module allows for three types of state transitions: `RegisterDevFeeInfo`, `UpdateDevFeeInfo` and `CancelDevFeeInfo`. The logic for *distributing transaction fees*, is handled through [Hooks](./05_hooks.md). | ||
|
||
### Fee Info Registration | ||
|
||
A developer registers a contract for receiving transaction fees, defining the contract address, an array of nonces for deriving the contract address from the EOA address deploying the contract and an optional withdraw address for sending the fees. | ||
|
||
If the withdraw address is not set, the fees will be sent to the deployer address by default. | ||
|
||
1. User submits a `RegisterDevFeeInfo` to register a contract address, along with a withdraw address that they would like to receive the fees to | ||
2. The following checks must pass: | ||
1. `x/fees` module is enabled | ||
2. the contract was not previously registered | ||
3. deployer has a valid account (it has done at least one transaction) and is not a smart contract | ||
4. an account corresponding to the contract address exists, with a non-empty bytecode | ||
5. contract address can be derived from the deployer’s address and provided nonces using the `CREATE` operation | ||
3. An instance of the provided fee information is stored | ||
4. All transactions sent to the registered contract occurring after registration will have their fees distributed to the developer, according to the global `DeveloperShares` parameter | ||
|
||
### Fee Info Update | ||
|
||
A developer updates the withdraw address for a registered contract, defining the contract address and the new withdraw address. | ||
|
||
1. User submits a `UpdateDevFeeInfo` | ||
2. The following checks must pass: | ||
1. `x/fees` module is enabled | ||
2. the contract is registered | ||
3. the signer of the transaction is the same as the contract deployer | ||
3. The fee information is updated with the new withdraw address. | ||
4. The developer receives the fees from transactions occurring after this update, on the new withdraw address | ||
|
||
### Fee Info Cancel | ||
|
||
A developer cancels receiving fees for a registered contract, defining the contract address. | ||
|
||
1. User submits a `CancelDevFeeInfo` | ||
2. The following checks must pass: | ||
1. `x/fees` module is enabled | ||
2. the contract is registered | ||
3. the signer of the transaction is the same as the contract deployer | ||
3. The fee information is removed from storage | ||
4. The developer no longer receives fees from transactions sent to this contract |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
<!-- | ||
order: 4 | ||
--> | ||
|
||
# Transactions | ||
|
||
This section defines the `sdk.Msg` concrete types that result in the state transitions defined on the previous section. | ||
|
||
### `MsgRegisterDevFeeInfo` | ||
|
||
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. | ||
|
||
```go | ||
type MsgRegisterDevFeeInfo struct { | ||
// contract hex address | ||
ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` | ||
// bech32 address of message sender, must be the same as the origin EOA | ||
// sending the transaction which deploys the contract | ||
DeployerAddress string `protobuf:"bytes,2,opt,name=deployer_address,json=deployerAddress,proto3" json:"deployer_address,omitempty"` | ||
// bech32 address of account receiving the transaction fees | ||
WithdrawAddress string `protobuf:"bytes,3,opt,name=withdraw_address,json=withdrawAddress,proto3" json:"withdraw_address,omitempty"` | ||
// array of nonces from the address path, where the last nonce is | ||
// the nonce that determines the contract's address - it can be an EOA nonce | ||
// or a factory contract nonce | ||
Nonces []uint64 `protobuf:"varint,4,rep,packed,name=nonces,proto3" json:"nonces,omitempty"` | ||
} | ||
``` | ||
|
||
The message content stateless validation fails if: | ||
|
||
- Contract address is invalid | ||
- Contract address is zero | ||
- Deployer address is invalid | ||
- Withdraw address is invalid | ||
- Nonces array is empty | ||
|
||
### `MsgUpdateDevFeeInfo` | ||
|
||
Defines a transaction signed by a developer to update the withdraw address of a contract registered for transaction fee distribution. The sender must be an EOA that corresponds to the contract deployer address. | ||
|
||
```go | ||
type MsgUpdateDevFeeInfo struct { | ||
// contract hex address | ||
ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` | ||
// deployer bech32 address | ||
DeployerAddress string `protobuf:"bytes,2,opt,name=deployer_address,json=deployerAddress,proto3" json:"deployer_address,omitempty"` | ||
// new withdraw bech32 address for receiving the transaction fees | ||
WithdrawAddress string `protobuf:"bytes,3,opt,name=withdraw_address,json=withdrawAddress,proto3" json:"withdraw_address,omitempty"` | ||
} | ||
``` | ||
|
||
The message content stateless validation fails if: | ||
|
||
- Contract address is invalid | ||
- Contract address is zero | ||
- Deployer address is invalid | ||
- Withdraw address is invalid | ||
- Withdraw address is same as deployer address | ||
|
||
### `MsgCancelDevFeeInfo` | ||
|
||
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 EOA that corresponds to the contract deployer address. | ||
|
||
```go | ||
type MsgCancelDevFeeInfo struct { | ||
// contract hex address | ||
ContractAddress string `protobuf:"bytes,1,opt,name=contract_address,json=contractAddress,proto3" json:"contract_address,omitempty"` | ||
// deployer bech32 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 address is invalid | ||
- Contract address is zero | ||
- Deployer address is invalid |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<!-- | ||
order: 5 | ||
--> | ||
|
||
# Hooks | ||
|
||
The fees module implements one transaction hook, from the `x/evm` module. | ||
|
||
## EVM Hook | ||
|
||
An [EVM hook](https://evmos.dev/modules/evm/06_hooks.html) executes custom lo 9E7A gic after each successful EVM transaction. | ||
|
||
All fees paid by a user for transaction execution are sent to the `FeeCollector` Module Account during the `AnteHandler` execution. | ||
|
||
If the `x/fees` module is disabled or the EVM transaction targets an unregistered contract, the EVM hook returns `nil`, without performing any actions. In this case, 100% of the transaction fees remain in the `FeeCollector` module, to be distributed to the block proposer. | ||
|
||
If the `x/fees` module is enabled, the EVM hook sends a percentage of the fees paid by the user for a transaction to a registered contract, to the withdraw address set for that contract, or to the contract deployer. | ||
|
||
1. User submits an EVM transaction to a smart contract that has been registered to receive fees and the transaction is finished successfully. | ||
2. The EVM hook’s `PostTxProcessing` method is called on the fees module. It is passed the initial transaction message, that includes the gas price paid by the user and the transaction receipt, which includes the gas used by the transaction. The hook calculates | ||
|
||
```go | ||
devFees := receipt.GasUsed * msg.GasPrice * params.DeveloperShares | ||
``` | ||
|
||
and sends these dev fees from the `FeeCollector` (Cosmos SDK `auth` module account) to the registered withdraw address for that contract. The remaining amount in the `FeeCollector` is allocated towards the [SDK Distribution Scheme](https://docs.cosmos.network/master/modules/distribution/03_begin_block.html#the-distribution-scheme). If there is no withdraw address, fees are sent to contract deployer's address. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<!-- | ||
order: 6 | ||
--> | ||
|
||
# Events | ||
|
||
The `x/fees` module emits the following events: | ||
|
||
## Register Contract Fee Info | ||
|
||
| Type | Attribute Key | Attribute Value | | ||
| :---------------------- | :---------------------| :--------------------------------- | | ||
| `register_dev_fee_info` | `"contract"` | `{msg.ContractAddress}` | | ||
| `register_dev_fee_info` | `"sender"` | `{msg.DeployerAddress}` | | ||
| `register_dev_fee_info` | `"withdraw_address"` | `{msg.WithdrawAddress}` | | ||
|
||
## Update Contract Fee Info | ||
|
||
| Type | Attribute Key | Attribute Value | | ||
| :--------------------- | :---------------------------- | :-------------------------- | | ||
| `update_dev_fee_info` | `"contract"` | `{msg.ContractAddress}` | | ||
| `update_dev_fee_info` | `"sender"` | `{msg.DeployerAddress}` | | ||
| `update_dev_fee_info` | `"withdraw_address"` | `{msg.WithdrawAddress}` | | ||
|
||
## Cancel Contract Fee Info | ||
|
||
| Type | Attribute Key | Attribute Value | | ||
| :--------------------- | :---------------------------- | :-------------------------- | | ||
| `cancel_dev_fee_info` | `"contract"` | `{msg.ContractAddress}` | | ||
| `cancel_dev_fee_info` | `"sender"` | `{msg.DeployerAddress}` | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<!-- | ||
order: 7 | ||
--> | ||
|
||
# Parameters | ||
|
||
The fees module contains the following parameters: | ||
|
||
| Key | Type | Default Value | | ||
| :------------------------- | :------------ | :---------------------------- | | ||
| `EnableFees` | bool | `true` | | ||
| `DeveloperShares` | sdk.Dec | `50%` | | ||
| `ValidatorShares` | sdk.Dec | `50%` | | ||
| `AddrDerivationCostCreate` | uint64 | `50` | | ||
|
||
## Enable Fee Module | ||
|
||
The `EnableFees` parameter toggles all state transitions in the module. When the parameter is disabled, it will prevent any transaction fees from being distributed to contract deployers and it will disallow contract registrations, updates or cancellations. | ||
|
||
### Developer Shares Amount | ||
|
||
The `DeveloperShares` parameter is the percentage of transaction fees that is sent to the contract deployers. | ||
|
||
### Validator Shares Amount | ||
|
||
The `ValidatorShares` parameter is the percentage of transaction fees that is sent to the block proposer. | ||
|
||
### AddrDerivationCostCreate | ||
|
||
The `AddrDerivationCostCreate` parameter is the gas value charged for performing an address derivation in the contract registration process. Because we allow an unlimited number of nonces to be given for deriving the smart contract address, a flat gas fee is charged for each address derivation iteration. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
<!-- | ||
order: 8 | ||
--> | ||
|
||
# Clients | ||
|
||
## CLI | ||
|
||
Find below a list of `evmosd` commands added with the `x/fees` module. You can obtain the full list by using the `evmosd -h` command. A CLI command can look like this: | ||
|
||
```bash | ||
evmosd query fees params | ||
``` | ||
|
||
### Queries | ||
|
||
| Command | Subcommand | Description | | ||
| :------------- | :------------ | :----------------------------- | | ||
| `query` `fees` | `params` | Get fees params | | ||
| `query` `fees` | `fee-info` | Get registered fee info | | ||
| `query` `fees` | `fee-infos` | Get all registered fee infos | | ||
|
||
### Transactions | ||
|
||
| Command | Subcommand | Description | | ||
| :---------- | :-------------- | :----------------------------------------- | | ||
| `tx` `fees` | `register-fee` | Register a contract for receiving fees | | ||
| `tx` `fees` | `update-fee` | Update the withdraw address for a contract | | ||
| `tx` `fees` | `cancel-fee` | Remove the fee info for a contract | | ||
|
||
## gRPC | ||
|
||
### Queries | ||
|
||
| Verb | Method | Description | | ||
| :----- | :------------------------------------------- | :------------------------------------------ | | ||
| `gRPC` | `evmos.fees.v1.Query/Params` | Get fees params | | ||
| `gRPC` | `evmos.fees.v1.Query/DevFeeInfo` | Get registered fee info | | ||
| `gRPC` | `evmos.fees.v1.Query/DevFeeInfos` | Get all registered fee infos | | ||
| `gRPC` | `evmos.fees.v1.Query/DevFeeInfosPerDeployer` | Get all registered fee infos for a deployer | | ||
| `GET` | `/evmos/fees/v1/params` | Get fees params | | ||
| `GET` | `/evmos/fees/v1/fees/{contract_address}` | Get registered fee info | | ||
| `GET` | `/evmos/fees/v1/fees` | Get all registered fee infos | | ||
| `GET` | `/evmos/fees/v1/fees/{deployer_address}` | Get all registered fee infos for a deployer | | ||
|
||
### Transactions | ||
|
||
| Verb | Method | Description | | ||
| :----- | :---------------------------------------- | :----------------------------------------- | | ||
| `gRPC` | `evmos.fees.v1.Msg/RegisterDevFeeInfo` | Register a contract for receiving fees | | ||
| `gRPC` | `evmos.fees.v1.Msg/UpdateDevFeeInfo` | Update the withdraw address for a contract | | ||
| `gRPC` | `evmos.fees.v1.Msg/CancelDevFeeInfo` | Remove the fee info for a contract | | ||
| `POST` | `/evmos/fees/v1/tx/register_dev_fee_info` | Register a contract for receiving fees | | ||
| `POST` | `/evmos/fees/v1/tx/update_dev_fee_info` | Update the withdraw address for a contract | | ||
| `POST` | `/evmos/fees/v1/tx/cancel_dev_fee_info` | Remove the fee info for a contract | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<!-- | ||
order: 9 | ||
--> | ||
|
||
# Future Improvements | ||
fedekunze marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
- The fee distribution registration could be extended to register the withdrawal address to the owner of the contract according to [EIP173](https://eips.ethereum.org/EIPS/eip-173). | ||
- Extend the supported message types for the transaction fee distribution to Cosmos transactions that interact with the EVM (eg: ERC20 module, IBC transactions). | ||
- Distribute fees for internal transaction calls to other registered contracts. At this time, we only send transaction fees to the deployer of the smart contract represented by the `to` field of the transaction request (`MyContract`). We do not distribute fees to smart contracts called internally by `MyContract`. | ||
- `CREATE2` opcode support for address derivation. When registering a smart contract, we verify that its address is derived from the deployer’s address. At this time, we only support the derivation path using the `CREATE` opcode, which accounts for most cases. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.