Skip to content

[Revive] Add configuration to set Ethereum gas scale#10393

Merged
TorstenStueber merged 100 commits intomasterfrom
torsten/scale-eth-gas
Dec 4, 2025
Merged

[Revive] Add configuration to set Ethereum gas scale#10393
TorstenStueber merged 100 commits intomasterfrom
torsten/scale-eth-gas

Conversation

@TorstenStueber
Copy link
Copy Markdown
Contributor

@TorstenStueber TorstenStueber commented Nov 22, 2025

This PR is based on #10166 (i.e., it merges into the branch torsten/gas-fixes).

This PR adds a new configuration parameter (GasScale) to pallet-revive that allows to change the scale of the Ethereum gas and of the Ethereum gas price.

Before this PR, the Ethereum gas price is simply the next fee multiplier of pallet-transaction-payment multiplied by NativeToEthRatio. Thus, on Polkadot this is 100_000_000 when the multiplier has its default value of 1.

The required gas of a transaction is its total cost divided by the gas price, where the total cost is the sum of the transaction fee and the storage deposit.

This leads to a situation where the required gas for a transaction on revive is usually orders of magnitude larger than the required amount of gas on Ethereum. This can lead to issues with tools or systems that interact with revive and hard code expected gas amounts or upper limits of gas amounts.

Setting GasScale has two effects:

  • revive's Ethereum gas price is scaled up by the factor GasScale
  • resulting used/estimated gas amounts get scaled down by the factor GasScale.

Technical Details

Internally, revive uses exactly the same gas price and gas units as before. Only at the interface these amounts and prices get scaled by GasScale.

The actual logical changes are almost trivial and I use GasScale at only three places: in evm_base_fee to scale the gas price and in the functions from_ethereum_gas and to_ethereum_gas.

Recommended

This PR sets GasScale for the dev-node to 50_000.

This is motivated by the fact that storing a value in a contract storage slot costs DepositPerChildTrieItem + DepositPerByte * 32, which is 2_000_000_000 + 10_000_000 * 32 (= 2_320_000_000) plancks. Before this change the gas price was 1_000_000 wei, so that this
equated to 2_320_000_000 gas units. In EVM this operation requires 22_100 gas only.

Thus, GasScale would need to be about 100_000 in order for SSTORE to have similar worst case gas requirements.

Resolved Issues

This PR addresses paritytech/contract-issues#18 but we also need to find an appropriate GasScale for a mainnet installment of pallet-revive (see this comment).

athei and others added 30 commits October 30, 2025 09:49
Fix maxPriorityFee RPC

Change the EVM call opcodes to use proper gas for subcalls

Update tests-evm.yml

Update from github-actions[bot] running command 'prdoc --audience runtime_dev'

fix

fix

[pallet-revive] fix subxt submit & add debug statments (#10016)

- Fix subxt submit by default it's using
`author_submitAndWatchExtrinsic` even though we just want to fire and
forget
- Add debug instructions to log the signer & nonce of new eth
transactions when the node validate the transaction

---------

Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>

Version bumps and prdocs reordering from stable2509 (#9974)

This PR backports regular version bumps and prdocs reordering from the
stable2509 branch back to master

---------

Co-authored-by: ParityReleases <[email protected]>

Update .github/workflows/tests-evm.yml

[Release|CI/CD] Fix polkadot prod docker image (#9975)

This PR introduces a workaround to fix failing polkadot production image
flow.
The initial issue is that, for some reason, our key that used to sign
the deb `InRelease` repo noted as expired on the first `apt update` run.
But reimport of the same key fixes is it. Until the reason for this
issue is fixed, this work around helps to keep the flow working

Introduce `/cmd label` for labelling pull requests (#9915)

This allows external contributors to set label for their pull request.

Closes: #9873

Use parity-large-persistent-test for merge queue (#10025)

Investigating issue with removing persistent runners from merge queue

cc paritytech/devops#3875

pallet_revive: Lower the deposit costs for child trie items (#10027)

Fixes #9246

---------

Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>

pallet_revive: Fix incorrect `block.gaslimit` (#10026)

Fixes paritytech/contract-issues#112

---------

Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>

update tests-evm
This commit lowers the number of tests that the DT framework runs
concurrently. We have observed that with this PR if we operate with the
regular number of concurrent tests (1000) then we start to observe the
nodes not including transactions in blocks. Lowering the concurrency of
tests means that they would take longer to execute but it was observed
that we would no longer get the timeout errors if the concurrency is
lowered.

After this commit, we should no longer see the "failed to observe tx
within timeout errors" or they should be greatly reduced.
@paritytech-workflow-stopper
Copy link
Copy Markdown

All GitHub workflows were cancelled due to failure one of the required jobs.
Failed workflow url: https://github.com/paritytech/polkadot-sdk/actions/runs/19908540102
Failed job name: check-runtime-migration

@TorstenStueber TorstenStueber added this pull request to the merge queue Dec 3, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Dec 3, 2025
@TorstenStueber TorstenStueber added this pull request to the merge queue Dec 4, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Dec 4, 2025
@TorstenStueber TorstenStueber added this pull request to the merge queue Dec 4, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Dec 4, 2025
@TorstenStueber TorstenStueber added this pull request to the merge queue Dec 4, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Dec 4, 2025
@TorstenStueber TorstenStueber added this pull request to the merge queue Dec 4, 2025
github-merge-queue bot pushed a commit that referenced this pull request Dec 4, 2025
This PR is based on
#10166 (i.e., it merges
into the branch `torsten/gas-fixes`).

This PR adds a new configuration parameter (`GasScale`) to pallet-revive
that allows to change the scale of the Ethereum gas and of the Ethereum
gas price.

Before this PR, the Ethereum gas price is simply the next fee multiplier
of pallet-transaction-payment multiplied by `NativeToEthRatio`. Thus, on
Polkadot this is 100_000_000 when the multiplier has its default value
of 1.

The required gas of a transaction is its total cost divided by the gas
price, where the total cost is the sum of the transaction fee and the
storage deposit.

This leads to a situation where the required gas for a transaction on
revive is usually orders of magnitude larger than the required amount of
gas on Ethereum. This can lead to issues with tools or systems that
interact with revive and hard code expected gas amounts or upper limits
of gas amounts.

Setting `GasScale` has two effects:
- revive's Ethereum gas price is scaled up by the factor `GasScale`
- resulting used/estimated gas amounts get scaled down by the factor
`GasScale`.

## Technical Details
Internally, revive uses exactly the same gas price and gas units as
before. Only at the interface these amounts and prices get scaled by
`GasScale`.

**The actual logical changes are almost trivial and I use `GasScale` at
only three places: in `evm_base_fee` to scale the gas price and in the
functions `from_ethereum_gas` and `to_ethereum_gas`.**

## Recommended
This PR sets `GasScale` for the dev-node to 50_000.

This is motivated by the fact that storing a value in a contract storage
slot costs `DepositPerChildTrieItem + DepositPerByte * 32`, which is
`2_000_000_000 + 10_000_000 * 32` (= `2_320_000_000`) plancks. Before
this change the gas price was 1_000_000 wei, so that this
equated to 2_320_000_000 gas units. In EVM this operation requires
22_100 gas only.

Thus, `GasScale` would need to be about 100_000 in order for `SSTORE` to
have similar worst case gas requirements.

## Resolved Issues
- fixes paritytech/contract-issues#221

This PR addresses
paritytech/contract-issues#18 but we also need
to find an appropriate `GasScale` for a mainnet installment of
pallet-revive (see [this
comment](paritytech/contract-issues#18 (comment))).

---------

Co-authored-by: Alexander Theißen <[email protected]>
Co-authored-by: PG Herveou <[email protected]>
Co-authored-by: Omar Abdulla <[email protected]>
Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Dec 4, 2025
@TorstenStueber TorstenStueber added this pull request to the merge queue Dec 4, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Dec 4, 2025
@TorstenStueber TorstenStueber added this pull request to the merge queue Dec 4, 2025
Merged via the queue into master with commit b3d1f6d Dec 4, 2025
255 of 264 checks passed
@TorstenStueber TorstenStueber deleted the torsten/scale-eth-gas branch December 4, 2025 08:15
TorstenStueber added a commit that referenced this pull request Dec 6, 2025
Backport all pallet-revive related changes into `unstable2507`.

These are all the changes we want to get onto the next Kusama release.
Main changes include
- EVM backend
- Ethereum block storage
- Generalized gas mapping

The complete list of PRs in this backport is
- #9482
- #9455
- #9454
- #9501
- #9177
- #9285
- #9606
- #9414
- #9557
- #9617
- #9385
- #9679
- #9705
- #9561
- #9744
- #9736
- #9701
- #9517
- #9771
- #9683
- #9791
- #9717
- #9759
- #9823
- #9768
- #9853
- #9801
- #9780
- #9796
- #9878
- #9841
- #9670
- #9865
- #9803
- #9928
- #9818
- #9911
- #9942
- #9831
- #9945
- #9603
- #9968
- #9939
- #9991
- #9914
- #9997
- #9985
- #10016
- #10027
- #10026
- #9418
- #9988
- #10041
- #10047
- #10032
- #10065
- #10089
- #10080
- #10090
- #10106
- #10020
- #9512
- #10109
- #9699
- #10100
- #9909
- #10120
- #10146
- #10157
- #10168
- #10169
- #10160
- #10129
- #10175
- #10186
- #10192
- #10148
- #10193
- #10220
- #10233
- #10191
- #10225
- #10246
- #10239
- #10159
- #10252
- #10224
- #10267
- #10271
- #10214
- #10297
- #10290
- #10281
- #10272
- #10303
- #10336
- #10244
- #10366
- #10380
- #10383
- #10387
- #10302
- #10309
- #10427
- #10385
- #10451
- #10471
- #10166
- #10510
- #10393
- #10540
- #9587
- #10071
- #10558
- #10554
- #10325

---------

Signed-off-by: xermicus <[email protected]>
Co-authored-by: Pavlo Khrystenko <[email protected]>
Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Javier Viola <[email protected]>
Co-authored-by: Bastian Köcher <[email protected]>
Co-authored-by: Bastian Köcher <[email protected]>
Co-authored-by: pgherveou <[email protected]>
Co-authored-by: Omar <[email protected]>
Co-authored-by: 0xRVE <[email protected]>
Co-authored-by: xermicus <[email protected]>
Co-authored-by: Alexander Samusev <[email protected]>
github-merge-queue bot pushed a commit that referenced this pull request Jan 7, 2026
…n Revive (#10476)

# Description

This pull request allows for “[Nick’s
Method](https://weka.medium.com/how-to-send-ether-to-11-440-people-187e332566b7)”
style of deploying contracts which was requested in
paritytech/contract-issues#225 and was
attempted to be solved in
paritytech/contract-issues#99.

The “Nick Method” style of contract deployment is a very useful concept
to use when we wish to deploy a contract on multiple chains **with the
same address**. It allows us to do that by constructing a deployment
transaction that can be executed on any chain, thus allowing us to get
the same address for the contract’s deployment on any chain.

Additionally, this method allows for the contracts to be deployed
trustlessly, meaning that if any actor (regardless of whether they’re
honest or not) follow the same method for deploying the contract then
they’d get the same address across all chains. This allows anyone in the
Polkadot ecosystem to take existing contracts that use this method of
deployment (e.g., `Multicall3` and the ERC-1820 registry) and deploy
them on Polkadot and there’s already trust that the code is the same.

In order to be able to use the same transaction across multiple chains
transaction authors need to:

- Not include a chain id.
- Include a high gas limit so that the transaction works on (almost) all
chains.

As mentioned in
paritytech/contract-issues#225, this pattern
is already being used in a number of contracts. Most notably,
`Multicall3` and the ERC-1820 Registry.

Before this PR this method didn’t work in revive since the chain id was
an absolute must and any transaction that didn’t include a chain id
would fail with an `Invalid Transaction` error as seen below:


https://github.com/paritytech/polkadot-sdk/blob/c688963f51c55b3c2a16a00a33c4a086792a1544/substrate/frame/revive/src/evm/call.rs#L71-L76

The above implementation misses an important detail: legacy transactions
are permitted to not have the chain id set while all other non-legacy
transactions do not permit that. The models we had for legacy
transactions respected that, but we still did the chain id check for all
transaction types, which is incorrect. Therefore, this part of the code
was changed to the following:

```rust
match (tx.chain_id, tx.r#type.as_ref()) {
	(None, Some(super::Byte(TYPE_LEGACY))) => {},
	(Some(chain_id), ..) =>
		if chain_id != <T as Config>::ChainId::get().into() {
			log::debug!(target: LOG_TARGET, "Invalid chain_id {chain_id:?}");
			return Err(InvalidTransaction::Call);
		},
	(None, ..) => {
		log::debug!(target: LOG_TARGET, "Invalid chain_id None");
		return Err(InvalidTransaction::Call);
	},
}
```

The above code skips the chain id check if the transaction is of the
legacy type. Otherwise, the chain id is checked. If no chain id is
provided and the transaction is not of the legacy type then we error
out.

## Integration

Be aware that we now allow for legacy transactions to not have the chain
ID set in order to allow for “[Nick’s
Method](https://weka.medium.com/how-to-send-ether-to-11-440-people-187e332566b7)”
style of contract deployment. Non-legacy transaction continue to require
the chain id to be provided.

## Review Notes

- The main change that this PR makes can be found in the
`substrate/frame/revive/src/evm/call.rs` file allowing for legacy
contracts to not have the chain id set.
- Two new tests were added with this PR:
  - `dry_run_contract_deployment_with_nick_method_works`
  - `contract_deployment_with_nick_method_works`
- Both of the above tests test that “[Nick’s
Method](https://weka.medium.com/how-to-send-ether-to-11-440-people-187e332566b7)”
can be used to deploy the singleton factory contract provided in
[ERC-2470](https://eips.ethereum.org/EIPS/eip-2470) in a dry run and in
an `eth_transact` call.
- Note that the above tests needed to modify the transaction provided in
the ERC to update its gas limit (since the provided gas limit was too
low for our platform), **which breaks the whole idea of Nick’s Method
where the same transaction can be submitted to the same chain**. I
suspect that #10393
should fix this issue with the new gas scaling.

## Behavior On Other Chains

Since some of the comments on this PR talked about whether this is a
good idea, I wanted to add this section as justification for adding this
and also to review what other chains do with regards to unprotected
transactions.

I think that the security of this feature can be justified by:
* The fact that this feature is allowed on most other EVM chains,
including Ethereum mainnet itself.
* That metamask will fill in the chain-id automatically for users (as
seen
[here](https://docs.metamask.io/wallet/how-to/send-transactions?utm_source=chatgpt.com#chain-id)),
thus preventing users from broadcasting a re-playable transaction.
* That the default behavior of Alloy and other developer tools is to
fill in the chain ID when the developer is constructing transactions.
* That people who want to use this feature need to start their own RPC
with the `--allow-unprotected-txs` flag set

The behavior of Geth when it comes to unprotected transactions is:
* They're allowed at the protocol level for legacy transactions.
* They're disallowed at the protocol level for other transaction types.
* They're not allowed to be submitted through the Geth RPC unless the
`--rpc.allow_unprotected_txs` flag is set when Geth is started.

The above behavior matches what EIP-155 set in place for legacy
transactions.

The following is an overview of the various chains and their stance on
unprotected transactions:

* Ethereum/Geth: Allowed at the protocol level, the node must be started
with a special flag to permit it to be submitted through the RPC
([source](https://geth.ethereum.org/docs/fundamentals/command-line-options))
* Avalanche: Allowed at the protocol level, the node must be started
with a special flag to permit it to be submitted through the RPC
([source](https://build.avax.network/docs/nodes/chain-configs/subnet-evm#transaction-processing))
* Polygon: Allowed at the protocol level, the node must be started with
a special flag to permit it to be submitted through the RPC
([source](https://forum.polygon.technology/t/bor-v0-4-0-mainnet-release-indore-fork/12280))
* Arbitrum: Allowed at the protocol level, the node must be started with
a special flag to permit it to be submitted through the RPC
([source](https://pkg.go.dev/github.com/nim4/go-arbitrum/cmd/utils#:~:text=AllowUnprotectedTxs%20%3D%20%26cli.BoolFlag%7B%0A%09%09Name%3A%20%20%20%20%20%22rpc.allow%2Dunprotected%2Dtxs%22%2C%0A%09%09Usage%3A%20%20%20%20%22Allow%20for%20unprotected%20(non%20EIP155%20signed)%20transactions%20to%20be%20submitted%20via%20RPC%22%2C%0A%09%09Category%3A%20flags.APICategory%2C%0A%09%7D))

---------

Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Torsten Stüber <[email protected]>
Co-authored-by: Alexander Theißen <[email protected]>
paritytech-release-backport-bot bot pushed a commit that referenced this pull request Jan 7, 2026
…n Revive (#10476)

# Description

This pull request allows for “[Nick’s
Method](https://weka.medium.com/how-to-send-ether-to-11-440-people-187e332566b7)”
style of deploying contracts which was requested in
paritytech/contract-issues#225 and was
attempted to be solved in
paritytech/contract-issues#99.

The “Nick Method” style of contract deployment is a very useful concept
to use when we wish to deploy a contract on multiple chains **with the
same address**. It allows us to do that by constructing a deployment
transaction that can be executed on any chain, thus allowing us to get
the same address for the contract’s deployment on any chain.

Additionally, this method allows for the contracts to be deployed
trustlessly, meaning that if any actor (regardless of whether they’re
honest or not) follow the same method for deploying the contract then
they’d get the same address across all chains. This allows anyone in the
Polkadot ecosystem to take existing contracts that use this method of
deployment (e.g., `Multicall3` and the ERC-1820 registry) and deploy
them on Polkadot and there’s already trust that the code is the same.

In order to be able to use the same transaction across multiple chains
transaction authors need to:

- Not include a chain id.
- Include a high gas limit so that the transaction works on (almost) all
chains.

As mentioned in
paritytech/contract-issues#225, this pattern
is already being used in a number of contracts. Most notably,
`Multicall3` and the ERC-1820 Registry.

Before this PR this method didn’t work in revive since the chain id was
an absolute must and any transaction that didn’t include a chain id
would fail with an `Invalid Transaction` error as seen below:

https://github.com/paritytech/polkadot-sdk/blob/c688963f51c55b3c2a16a00a33c4a086792a1544/substrate/frame/revive/src/evm/call.rs#L71-L76

The above implementation misses an important detail: legacy transactions
are permitted to not have the chain id set while all other non-legacy
transactions do not permit that. The models we had for legacy
transactions respected that, but we still did the chain id check for all
transaction types, which is incorrect. Therefore, this part of the code
was changed to the following:

```rust
match (tx.chain_id, tx.r#type.as_ref()) {
	(None, Some(super::Byte(TYPE_LEGACY))) => {},
	(Some(chain_id), ..) =>
		if chain_id != <T as Config>::ChainId::get().into() {
			log::debug!(target: LOG_TARGET, "Invalid chain_id {chain_id:?}");
			return Err(InvalidTransaction::Call);
		},
	(None, ..) => {
		log::debug!(target: LOG_TARGET, "Invalid chain_id None");
		return Err(InvalidTransaction::Call);
	},
}
```

The above code skips the chain id check if the transaction is of the
legacy type. Otherwise, the chain id is checked. If no chain id is
provided and the transaction is not of the legacy type then we error
out.

## Integration

Be aware that we now allow for legacy transactions to not have the chain
ID set in order to allow for “[Nick’s
Method](https://weka.medium.com/how-to-send-ether-to-11-440-people-187e332566b7)”
style of contract deployment. Non-legacy transaction continue to require
the chain id to be provided.

## Review Notes

- The main change that this PR makes can be found in the
`substrate/frame/revive/src/evm/call.rs` file allowing for legacy
contracts to not have the chain id set.
- Two new tests were added with this PR:
  - `dry_run_contract_deployment_with_nick_method_works`
  - `contract_deployment_with_nick_method_works`
- Both of the above tests test that “[Nick’s
Method](https://weka.medium.com/how-to-send-ether-to-11-440-people-187e332566b7)”
can be used to deploy the singleton factory contract provided in
[ERC-2470](https://eips.ethereum.org/EIPS/eip-2470) in a dry run and in
an `eth_transact` call.
- Note that the above tests needed to modify the transaction provided in
the ERC to update its gas limit (since the provided gas limit was too
low for our platform), **which breaks the whole idea of Nick’s Method
where the same transaction can be submitted to the same chain**. I
suspect that #10393
should fix this issue with the new gas scaling.

## Behavior On Other Chains

Since some of the comments on this PR talked about whether this is a
good idea, I wanted to add this section as justification for adding this
and also to review what other chains do with regards to unprotected
transactions.

I think that the security of this feature can be justified by:
* The fact that this feature is allowed on most other EVM chains,
including Ethereum mainnet itself.
* That metamask will fill in the chain-id automatically for users (as
seen
[here](https://docs.metamask.io/wallet/how-to/send-transactions?utm_source=chatgpt.com#chain-id)),
thus preventing users from broadcasting a re-playable transaction.
* That the default behavior of Alloy and other developer tools is to
fill in the chain ID when the developer is constructing transactions.
* That people who want to use this feature need to start their own RPC
with the `--allow-unprotected-txs` flag set

The behavior of Geth when it comes to unprotected transactions is:
* They're allowed at the protocol level for legacy transactions.
* They're disallowed at the protocol level for other transaction types.
* They're not allowed to be submitted through the Geth RPC unless the
`--rpc.allow_unprotected_txs` flag is set when Geth is started.

The above behavior matches what EIP-155 set in place for legacy
transactions.

The following is an overview of the various chains and their stance on
unprotected transactions:

* Ethereum/Geth: Allowed at the protocol level, the node must be started
with a special flag to permit it to be submitted through the RPC
([source](https://geth.ethereum.org/docs/fundamentals/command-line-options))
* Avalanche: Allowed at the protocol level, the node must be started
with a special flag to permit it to be submitted through the RPC
([source](https://build.avax.network/docs/nodes/chain-configs/subnet-evm#transaction-processing))
* Polygon: Allowed at the protocol level, the node must be started with
a special flag to permit it to be submitted through the RPC
([source](https://forum.polygon.technology/t/bor-v0-4-0-mainnet-release-indore-fork/12280))
* Arbitrum: Allowed at the protocol level, the node must be started with
a special flag to permit it to be submitted through the RPC
([source](https://pkg.go.dev/github.com/nim4/go-arbitrum/cmd/utils#:~:text=AllowUnprotectedTxs%20%3D%20%26cli.BoolFlag%7B%0A%09%09Name%3A%20%20%20%20%20%22rpc.allow%2Dunprotected%2Dtxs%22%2C%0A%09%09Usage%3A%20%20%20%20%22Allow%20for%20unprotected%20(non%20EIP155%20signed)%20transactions%20to%20be%20submitted%20via%20RPC%22%2C%0A%09%09Category%3A%20flags.APICategory%2C%0A%09%7D))

---------

Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Torsten Stüber <[email protected]>
Co-authored-by: Alexander Theißen <[email protected]>
(cherry picked from commit 0241d9b)
paritytech-release-backport-bot bot pushed a commit that referenced this pull request Jan 7, 2026
…n Revive (#10476)

# Description

This pull request allows for “[Nick’s
Method](https://weka.medium.com/how-to-send-ether-to-11-440-people-187e332566b7)”
style of deploying contracts which was requested in
paritytech/contract-issues#225 and was
attempted to be solved in
paritytech/contract-issues#99.

The “Nick Method” style of contract deployment is a very useful concept
to use when we wish to deploy a contract on multiple chains **with the
same address**. It allows us to do that by constructing a deployment
transaction that can be executed on any chain, thus allowing us to get
the same address for the contract’s deployment on any chain.

Additionally, this method allows for the contracts to be deployed
trustlessly, meaning that if any actor (regardless of whether they’re
honest or not) follow the same method for deploying the contract then
they’d get the same address across all chains. This allows anyone in the
Polkadot ecosystem to take existing contracts that use this method of
deployment (e.g., `Multicall3` and the ERC-1820 registry) and deploy
them on Polkadot and there’s already trust that the code is the same.

In order to be able to use the same transaction across multiple chains
transaction authors need to:

- Not include a chain id.
- Include a high gas limit so that the transaction works on (almost) all
chains.

As mentioned in
paritytech/contract-issues#225, this pattern
is already being used in a number of contracts. Most notably,
`Multicall3` and the ERC-1820 Registry.

Before this PR this method didn’t work in revive since the chain id was
an absolute must and any transaction that didn’t include a chain id
would fail with an `Invalid Transaction` error as seen below:

https://github.com/paritytech/polkadot-sdk/blob/c688963f51c55b3c2a16a00a33c4a086792a1544/substrate/frame/revive/src/evm/call.rs#L71-L76

The above implementation misses an important detail: legacy transactions
are permitted to not have the chain id set while all other non-legacy
transactions do not permit that. The models we had for legacy
transactions respected that, but we still did the chain id check for all
transaction types, which is incorrect. Therefore, this part of the code
was changed to the following:

```rust
match (tx.chain_id, tx.r#type.as_ref()) {
	(None, Some(super::Byte(TYPE_LEGACY))) => {},
	(Some(chain_id), ..) =>
		if chain_id != <T as Config>::ChainId::get().into() {
			log::debug!(target: LOG_TARGET, "Invalid chain_id {chain_id:?}");
			return Err(InvalidTransaction::Call);
		},
	(None, ..) => {
		log::debug!(target: LOG_TARGET, "Invalid chain_id None");
		return Err(InvalidTransaction::Call);
	},
}
```

The above code skips the chain id check if the transaction is of the
legacy type. Otherwise, the chain id is checked. If no chain id is
provided and the transaction is not of the legacy type then we error
out.

## Integration

Be aware that we now allow for legacy transactions to not have the chain
ID set in order to allow for “[Nick’s
Method](https://weka.medium.com/how-to-send-ether-to-11-440-people-187e332566b7)”
style of contract deployment. Non-legacy transaction continue to require
the chain id to be provided.

## Review Notes

- The main change that this PR makes can be found in the
`substrate/frame/revive/src/evm/call.rs` file allowing for legacy
contracts to not have the chain id set.
- Two new tests were added with this PR:
  - `dry_run_contract_deployment_with_nick_method_works`
  - `contract_deployment_with_nick_method_works`
- Both of the above tests test that “[Nick’s
Method](https://weka.medium.com/how-to-send-ether-to-11-440-people-187e332566b7)”
can be used to deploy the singleton factory contract provided in
[ERC-2470](https://eips.ethereum.org/EIPS/eip-2470) in a dry run and in
an `eth_transact` call.
- Note that the above tests needed to modify the transaction provided in
the ERC to update its gas limit (since the provided gas limit was too
low for our platform), **which breaks the whole idea of Nick’s Method
where the same transaction can be submitted to the same chain**. I
suspect that #10393
should fix this issue with the new gas scaling.

## Behavior On Other Chains

Since some of the comments on this PR talked about whether this is a
good idea, I wanted to add this section as justification for adding this
and also to review what other chains do with regards to unprotected
transactions.

I think that the security of this feature can be justified by:
* The fact that this feature is allowed on most other EVM chains,
including Ethereum mainnet itself.
* That metamask will fill in the chain-id automatically for users (as
seen
[here](https://docs.metamask.io/wallet/how-to/send-transactions?utm_source=chatgpt.com#chain-id)),
thus preventing users from broadcasting a re-playable transaction.
* That the default behavior of Alloy and other developer tools is to
fill in the chain ID when the developer is constructing transactions.
* That people who want to use this feature need to start their own RPC
with the `--allow-unprotected-txs` flag set

The behavior of Geth when it comes to unprotected transactions is:
* They're allowed at the protocol level for legacy transactions.
* They're disallowed at the protocol level for other transaction types.
* They're not allowed to be submitted through the Geth RPC unless the
`--rpc.allow_unprotected_txs` flag is set when Geth is started.

The above behavior matches what EIP-155 set in place for legacy
transactions.

The following is an overview of the various chains and their stance on
unprotected transactions:

* Ethereum/Geth: Allowed at the protocol level, the node must be started
with a special flag to permit it to be submitted through the RPC
([source](https://geth.ethereum.org/docs/fundamentals/command-line-options))
* Avalanche: Allowed at the protocol level, the node must be started
with a special flag to permit it to be submitted through the RPC
([source](https://build.avax.network/docs/nodes/chain-configs/subnet-evm#transaction-processing))
* Polygon: Allowed at the protocol level, the node must be started with
a special flag to permit it to be submitted through the RPC
([source](https://forum.polygon.technology/t/bor-v0-4-0-mainnet-release-indore-fork/12280))
* Arbitrum: Allowed at the protocol level, the node must be started with
a special flag to permit it to be submitted through the RPC
([source](https://pkg.go.dev/github.com/nim4/go-arbitrum/cmd/utils#:~:text=AllowUnprotectedTxs%20%3D%20%26cli.BoolFlag%7B%0A%09%09Name%3A%20%20%20%20%20%22rpc.allow%2Dunprotected%2Dtxs%22%2C%0A%09%09Usage%3A%20%20%20%20%22Allow%20for%20unprotected%20(non%20EIP155%20signed)%20transactions%20to%20be%20submitted%20via%20RPC%22%2C%0A%09%09Category%3A%20flags.APICategory%2C%0A%09%7D))

---------

Co-authored-by: cmd[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Torsten Stüber <[email protected]>
Co-authored-by: Alexander Theißen <[email protected]>
(cherry picked from commit 0241d9b)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T7-smart_contracts This PR/Issue is related to smart contracts.

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[openzeppelin-revm] ERC165Checker fails with OutOfGas

5 participants