Skip to content

Commit

Permalink
EIP-7251: return fee from getter and add usage example
Browse files Browse the repository at this point in the history
The get operation is meant to be used by contracts to compute the exact amount of ether
required to add a request. It does not return the fee directly though, but it
returns the count of 'excess requests' instead. The caller has to compute the fee
themselves by applying the fee formula.

I think this is not great. The fee logic, while reasonably straightforward, is an
implementation detail of the contract. Duplicating it into caller contracts could lead to
a mismatch in the computed values, and it's not necessary. I propose we change the
system contract to return the fee directly. This contract change has also been submitted
in this PR: lightclient/sys-asm#33

The Rationale section of the EIP also had some outdated text about returning fee overage
to the caller. The contract does not return overage, so I am removing that section here,
and adding recommendations & example code for calling the contract.
  • Loading branch information
fjl committed Nov 7, 2024
1 parent fd89c8f commit 240c198
Showing 1 changed file with 61 additions and 37 deletions.
98 changes: 61 additions & 37 deletions EIPS/eip-7251.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,9 @@ def fake_exponential(factor: int, numerator: int, denominator: int) -> int:
return output // denominator
```

##### Excess Consolidation Requests Getter
##### Fee Getter

```python
def get_excess_consolidation_requests():
count = sload(CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS, EXCESS_CONSOLIDATION_REQUESTS_STORAGE_SLOT)
return count
```
When the input to the contract is length zero, interpret this as a get request for the current fee, i.e. the contract returns the result of `get_fee()`.

##### System Call

Expand Down Expand Up @@ -233,28 +229,6 @@ eq
push1 0xcf
jumpi
calldatasize
iszero
iszero
push1 0x28
jumpi
push0
sload
push0
mstore
push1 0x20
push0
return
jumpdest
calldatasize
push1 0x60
eq
iszero
push2 0x019a
jumpi
push1 0x11
push0
sload
Expand All @@ -276,7 +250,7 @@ push0
dup3
gt
iszero
push1 0x80
push1 0x68
jumpi
dup2
Expand All @@ -294,14 +268,35 @@ push1 0x01
add
swap2
swap1
push1 0x65
push1 0x4d
jump
jumpdest
swap1
swap4
swap1
div
calldatasize
push1 0x60
eq
push1 0x84
jumpi
calldatasize
push2 0x019a
jumpi
callvalue
push2 0x019a
jumpi
push0
mstore
push1 0x20
push0
return
jumpdest
callvalue
lt
push2 0x019a
Expand Down Expand Up @@ -386,11 +381,8 @@ eq
push2 0x0129
jumpi
dup1
push1 0x74
mul
dup4
dup3
dup2
add
push1 0x04
mul
Expand All @@ -412,12 +404,15 @@ swap1
push1 0x01
add
sload
swap3
dup5
push1 0x74
mul
swap4
push1 0x60
shl
dup5
mstore
swap1
swap2
dup4
push1 0x14
add
Expand Down Expand Up @@ -472,8 +467,8 @@ jumpi
pop
push0
jumpdest
push1 0x01
sload
push1 0x01
Expand Down Expand Up @@ -621,6 +616,35 @@ Sync committee selection is also already weighted by effective balance, so this

This proposal maintains the activation and exit churn invariants limiting active weight instead of validator count. Balance top-ups are now handled explicitly, being subject to the same activation queue as full deposits.

### Fee Overpayment

Calls to the system contract require a fee payment defined by the current contract state. Overpaid fees are not returned to the caller. It is not generally possible to compute the exact required fee amount ahead of time. When adding a withdrawal request from a contract, the contract can perform a read operation to check for the current fee and then pay exactly the required amount. Here is an example in Solidity:

```
function addConsolidation(bytes memory srcPubkey, bytes memory targetPubkey) private {
assert(srcPubkey.length == 48);
assert(targetPubkey.length == 48);
// Read current fee from the contract.
(bool readOK, bytes memory feeData) = ConsolidationsContract.staticcall('');
if (!readOK) {
revert('reading fee failed');
}
uint256 fee = uint256(bytes32(feeData));
// Add the request.
bytes memory callData = bytes.concat(srcPubkey, targetPubkey);
(bool writeOK,) = ConsolidationsContract.call{value: fee}(callData);
if (!writeOK) {
revert('adding request failed');
}
}
```

Note: the system contract uses the EVM `CALLER` operation (Solidity: `msg.sender`) to get the address used in the consolidation request, i.e. the address that calls the system contract must match the 0x01 withdrawal credential recorded in the beacon state.

Using an EOA to request consolidations will always result in overpayment of fees. There is no way for an EOA to use a wrapper contract to request a consolidation. And even if a way existed, the gas cost of returning the overage would likely be higher than the overage itself. If requesting consolidations from an EOA to the system contract is desired, we recommend that users perform transaction simulations to estimate a reasonable fee amount to sent.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).

0 comments on commit 240c198

Please sign in to comment.