8000 [ENG-127] Fee distribution module spec by loredanacirstea · Pull Request #481 · evmos/evmos · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[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 8 commits into from
Apr 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions x/fees/spec/01_concepts.md
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`.
:::
41 changes: 41 additions & 0 deletions x/fees/spec/02_state.md
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"`
}
```
47 changes: 47 additions & 0 deletions x/fees/spec/03_state_transitions.md
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
77 changes: 77 additions & 0 deletions x/fees/spec/04_transactions.md
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
26 changes: 26 additions & 0 deletions x/fees/spec/05_hooks.md
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.
30 changes: 30 additions & 0 deletions x/fees/spec/06_events.md
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}` |
30 changes: 30 additions & 0 deletions x/fees/spec/07_parameters.md
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.
55 changes: 55 additions & 0 deletions x/fees/spec/08_clients.md
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 |
10 changes: 10 additions & 0 deletions x/fees/spec/09_improvements.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!--
order: 9
-->

# Future Improvements

- 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.
Loading
0