Skip to content

Conversation

@alexggh
Copy link
Contributor

@alexggh alexggh commented Oct 2, 2025

Needed for: paritytech/foundry-polkadot#334.

In foundry-polkadot we need the ability to be able to manipulate the msg.sender and the tx.origin that a solidity contract sees cheatcode documentation, plus the ability to mock calls and functions.

Currently all create/call methods use the bare_instantiate/bare_call to run things in pallet-revive, the caller then normally gets set automatically, based on what is the call stack, but for forge test we need to be able to manipulate, so that we can set it to custom values.

Additionally, for delegate_call, bare_call is used, so there is no way to specify we are dealing with a delegate call, so the call is not working correcly.

For both this paths, we need a way to inject this information into the execution environment, hence I added an optional hooks interface that we implement from foundry cheatcodes for prank and mock functionality.

TODO

  • Add tests to make sure the hooks functionality does not regress.

@alexggh alexggh force-pushed the alexggh/inject_state branch from 690e4aa to 3cba3a8 Compare October 8, 2025 11:11
Signed-off-by: Alexandru Gheorghe <[email protected]>
@alexggh alexggh force-pushed the alexggh/inject_state branch from 3cba3a8 to a57b755 Compare October 8, 2025 11:30
Signed-off-by: Alexandru Gheorghe <[email protected]>
Signed-off-by: Alexandru Gheorghe <[email protected]>
@alexggh alexggh marked this pull request as ready for review October 8, 2025 15:51
@alexggh alexggh changed the title pallet-revive: add mechanism to manipulate caller and delegate_calls pallet-revive: add interface to implement mocks and pranks Oct 8, 2025
@alexggh
Copy link
Contributor Author

alexggh commented Oct 8, 2025

@athei @smiasojed changed a bit the approach instead of injecting data I added a mocks hooks trait that we then implement in foundry codebase here: https://github.com/paritytech/foundry-polkadot/pull/334/files#diff-9e3b7cd881b3ef657db58bf38a66549a3b651f550caa7d623f8d4b20a813ea79R73, let me know what you think.

@paritytech-workflow-stopper
Copy link

All GitHub workflows were cancelled due to failure one of the required jobs.
Failed workflow url: https://github.com/paritytech/polkadot-sdk/actions/runs/18350472696
Failed job name: test-linux-stable-no-try-runtime


/// Convert a weight to a gas value.
fn evm_gas_from_weight(weight: Weight) -> U256 {
pub fn evm_gas_from_weight(weight: Weight) -> U256 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is that used from outside the crate?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Signed-off-by: Alexandru Gheorghe <[email protected]>
... to help with foundry-polkadot implementation for etch cheatcode

Signed-off-by: Alexandru Gheorghe <[email protected]>
Signed-off-by: Alexandru Gheorghe <[email protected]>
Signed-off-by: Alexandru Gheorghe <[email protected]>
@alexggh alexggh requested a review from athei October 10, 2025 10:06
@smiasojed
Copy link
Contributor

Should Self::transfer_from_origin be called from the fn run when we are mocking the call?

@smiasojed
Copy link
Contributor

Should we charge deposit / gas if the call is mocked

@alexggh
Copy link
Contributor Author

alexggh commented Oct 21, 2025

Should Self::transfer_from_origin be called from the fn run when we are mocking the call?
Should we charge deposit / gas if the call is mocked

Good points will have to look in to it, the existing mock/prank testsuite seems to be working well, so probably we are doing the right thing here.

@alexggh
Copy link
Contributor Author

alexggh commented Oct 21, 2025

Should we charge deposit / gas if the call is mocked

Looked a bit in foundry and it seems that mocked calls don't consume any gas, https://github.com/paritytech/foundry-polkadot/blob/9dd622a57199cbce23603874bda1d656b5b6e3ec/crates/cheatcodes/src/inspector.rs#L1039, so I guess we need to make it the same here.

@alexggh
Copy link
Contributor Author

alexggh commented Oct 21, 2025

Should we charge deposit / gas if the call is mocked

Looked a bit in foundry and it seems that mocked calls don't consume any gas, https://github.com/paritytech/foundry-polkadot/blob/9dd622a57199cbce23603874bda1d656b5b6e3ec/crates/cheatcodes/src/inspector.rs#L1039, so I guess we need to make it the same here.

Did some digging a bit and this PR is actually doing the right thingm because we don't actually execute the contract and we just use the mocked values, no gas and storage deposit is recorded in the frame, so we don't charge anything for mocked calls and that is the exact same behaviour as mainline foundry.

@smiasojed
Copy link
Contributor

smiasojed commented Oct 23, 2025

Should Self::transfer_from_origin be called from the fn run when we are mocking the call?
Should we charge deposit / gas if the call is mocked

Good points will have to look in to it, the existing mock/prank testsuite seems to be working well, so probably we are doing the right thing here.

I think that transfer will happen when:

  1. External call to contract (EOA → Contract)
  2. Contract calling another contract (Contract A → Contract B)
  3. Contract instantiation (Deploy with value)

Transfer will NOT happen when:

  1. EOA→EOA (plain account to plain account)
  2. EOA→non-existent address

Is it intentional?

@alexggh
Copy link
Contributor Author

alexggh commented Oct 23, 2025

Should Self::transfer_from_origin be called from the fn run when we are mocking the call?
Should we charge deposit / gas if the call is mocked

Good points will have to look in to it, the existing mock/prank testsuite seems to be working well, so probably we are doing the right thing here.

I think that transfer will happen when:

  1. External call to contract (EOA → Contract)
  2. Contract calling another contract (Contract A → Contract B)
  3. Contract instantiation (Deploy with value)

Transfer will NOT happen when:

  1. EOA→EOA (plain account to plain account)
  2. EOA→non-existent address

Is it intentional?

Alright, I see your point, I did some investigations and foundry mainline doesn't do any transfer of value when calls are mocked, so I will make that consistent for our implementation as well.

@alexggh
Copy link
Contributor Author

alexggh commented Oct 27, 2025

/cmd prdoc --audience node_dev --bump minor

@alexggh alexggh added the T7-smart_contracts This PR/Issue is related to smart contracts. label Oct 27, 2025
Copy link
Contributor

@smiasojed smiasojed left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks

Signed-off-by: Alexandru Gheorghe <[email protected]>
Signed-off-by: Alexandru Gheorghe <[email protected]>
@alexggh alexggh enabled auto-merge October 28, 2025 12:10
@alexggh alexggh added this pull request to the merge queue Oct 28, 2025
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 28, 2025
@alexggh alexggh added this pull request to the merge queue Oct 28, 2025
Merged via the queue into master with commit 74ac323 Oct 28, 2025
236 of 237 checks passed
@alexggh alexggh deleted the alexggh/inject_state branch October 28, 2025 13:53
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

None yet

Development

Successfully merging this pull request may close these issues.

6 participants