Skip to content

feat(forge): implement vm.signTypedData cheatcode #10330

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

Draft
wants to merge 21 commits into
base: master
Choose a base branch
from

Conversation

Haxry
Copy link
Contributor

@Haxry Haxry commented Apr 17, 2025

Motivation

Solution

closes #10281

PR Checklist

  • Added Tests
  • Added Documentation
  • Breaking changes

@Haxry Haxry requested a review from zerosnacks as a code owner April 17, 2025 14:07
@Haxry Haxry marked this pull request as draft April 17, 2025 14:10
@grandizzy
Copy link
Collaborator

@Haxry pls don't introduce ethers dep bit use alloy. Also pls add a test for the new cheatcodes and make sure priv key is redacted in traces, you can add here

"signDelegation" | "signAndAttachDelegation" => {

Thanks

@Haxry
Copy link
Contributor Author

Haxry commented Apr 19, 2025

@grandizzy
plz review !
are the changes ok ?
I'll add the test too !

@grandizzy
Copy link
Collaborator

@grandizzy plz review ! are the changes ok ? I'll add the test too !

yep, lgtm! I run cargo cheats and pushed commit to fix CI, would be great to have a test and then can be merged, maybe @aviggiano have a solidity test to use here?

@grandizzy grandizzy changed the title implemented vm.signTypedData feat(forge): implement vm.signTypedData cheatcode Apr 22, 2025
@grandizzy grandizzy added T-feature Type: feature C-forge Command: forge labels Apr 22, 2025
@aviggiano
Copy link

Hey
Thanks for implementing this feature. I'll test it on my project today

@grandizzy
Copy link
Collaborator

Hey Thanks for implementing this feature. I'll test it on my project today

Awesome, thanks. Mind that the PR wasn't merged yet so you'll need to build locally and test, was wondering if you could provide a sample for input / output to include as a test.

@Haxry
Copy link
Contributor Author

Haxry commented Apr 22, 2025

@aviggiano plz provide a sample test case to include in tests

@aviggiano
Copy link

aviggiano commented Apr 22, 2025

@Haxry I hope this example can help:

https://github.com/aviggiano/foundry-vm-signtypeddata/blob/db023ea33bfa4979a59a5fa00bfd85204a56e987/script/SignTypedData.s.sol#L23-L46

This is the current setup to sign with EIP712:

forge script script/SignTypedData.s.sol --rpc-url base -vvvvv --ffi

You should have a Ledger setup and unlocked. It will prompt you to "blind sign" using cast:

    ├─ [0] VM::ffi(["cast", "wallet", "sign", "--ledger", "--mnemonic-derivation-path", "m/44'/60'/0'/0/0", "--data", "{\"domain\":{\"chainId\":\"8453\",\"verifyingContract\":\"0xF3a292Dda3F524EA20b5faF2EE0A1c4abA665e4F\"},\"message\":{\"to\":\"0x4200000000000000000000000000000000000006\",\"value\":\"0\",\"data\":\"0x2e1a7d4d0000000000000000000000000000000000000000000000000000000000000000\",\"operation\":0,\"baseGas\":\"0\",\"gasPrice\":\"0\",\"gasToken\":\"0x0000000000000000000000000000000000000000\",\"refundReceiver\":\"0x0000000000000000000000000000000000000000\",\"nonce\":1,\"safeTxGas\":\"0\"},\"primaryType\":\"SafeTx\",\"types\":{\"SafeTx\":[{\"name\":\"to\",\"type\":\"address\"},{\"name\":\"value\",\"type\":\"uint256\"},{\"name\":\"data\",\"type\":\"bytes\"},{\"name\":\"operation\",\"type\":\"uint8\"},{\"name\":\"safeTxGas\",\"type\":\"uint256\"},{\"name\":\"baseGas\",\"type\":\"uint256\"},{\"name\":\"gasPrice\",\"type\":\"uint256\"},{\"name\":\"gasToken\",\"type\":\"address\"},{\"name\":\"refundReceiver\",\"type\":\"address\"},{\"name\":\"nonce\",\"type\":\"uint256\"}]}}"])

The goal is to replace this FFI call to cast with vm.signTypedData

@grandizzy
Copy link
Collaborator

@Haxry I hope this example can help:

https://github.com/aviggiano/foundry-vm-signtypeddata/blob/db023ea33bfa4979a59a5fa00bfd85204a56e987/script/SignTypedData.s.sol#L23-L46

Confirm this works with the current cheatcode which requires a private key, one difference though is that the cheatcode returns v, r, s, of sig but I guess that's OK @aviggiano ?

I think we need also a cheatcode that would enable signing with the ledger and not by receiving priv key, @Haxry @aviggiano wdyt? thanks

@aviggiano
Copy link

aviggiano commented Apr 23, 2025

@grandizzy returning v, r, s is OK,

but requiring the private key is not sufficient for my use case, since the biggest motivation for this feature is being able to sign transactions with ledger.

So yeah, it would be important to have a cheatcode that does not require the private key.

@0xrusowsky
Copy link
Contributor

@Haxry any updates here? to bring the PR to the finish line you simply have to:

  • add a cheatcode variant which doesn't require the pk, and which outputs the digest
  • add uni tests

it's been a month since the last update, so if you are too busy i can take over

@jenpaff jenpaff moved this to In Progress in Foundry May 27, 2025
@0xrusowsky
Copy link
Contributor

0xrusowsky commented May 27, 2025

@Haxry i finally took over cause we want to integrate this feature asap, as 2 other PRs that implement eip712-related cheatcodes will be merged soon. Regardless of that, thanks for your contribution!

fyi, i've named the cheatcode that produces the digest vm.eip712HashTypedData so that it follows the same naming convention as the other two:

the vm.signTypedData hasn't been renamed, as it already follows the naming standard of the "crypto" cheatcodes

@aviggiano please lmk if this is what u needed

@0xrusowsky 0xrusowsky marked this pull request as ready for review May 27, 2025 18:54
@@ -503,11 +503,12 @@ impl CallTraceDecoder {

Some(decoded.iter().map(format_token).collect())
}
"signDelegation" | "signAndAttachDelegation" => {
"signDelegation" | "signAndAttachDelegation" | "signTypedData" => {
Copy link
Contributor

Choose a reason for hiding this comment

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

@grandizzy i noticed that signDelegation and signAndAttachDelegation are under the Scripting group, however signTypedData is under Crypto.

does it matter? should we change its group perhaps?

@aviggiano
Copy link

@0xrusowsky thanks

I still don't see how I can sign without a private key, though

My goal is to use EIP-712 with ledger. How would I accomplish this with the current cheatcode?

@0xrusowsky 0xrusowsky marked this pull request as draft May 27, 2025 20:53
@0xrusowsky
Copy link
Contributor

0xrusowsky commented May 27, 2025

@0xrusowsky thanks

I still don't see how I can sign without a private key, though

My goal is to use EIP-712 with ledger. How would I accomplish this with the current cheatcode?

my bad, i should have read more carefully the previous convos that u had... now i see that u want to get rid of the FFI dependency

@0xrusowsky
Copy link
Contributor

0xrusowsky commented May 27, 2025

@aviggiano if the cheatcode were to simply perfom the ffi call to cast under the hood, and adopt this API:

    /// Signs human-readable typed data `jsonData` using a hardware wallet.
    ///
    /// This cheatcode performs a foreign function call to `cast` to abstract away complexity of
    /// manually building the calldata. Because of that, it requires the FFI cheatcode to be enabled.
    ///
    /// # Params:
    ///
    /// * jsonData: the typed data must follow the EIP-712 standard.
    /// * walletType: the hardware wallet type, either "ledger" or "trezor".
    /// * walletArgs: the wallet derivation path or the mnemonic index.
    function signTypedData(string calldata jsonData, string calldata walletType, string calldata walletArgs)
        external pure returns (uint8 v, bytes32 r,bytes32 s);

would it solve your issues? note that u'd still have to enable the FFI flag

@aviggiano
Copy link

I think this solves my problem, thanks

@0xrusowsky 0xrusowsky marked this pull request as ready for review May 28, 2025 14:42
@0xrusowsky 0xrusowsky marked this pull request as draft May 28, 2025 17:45
@0xrusowsky
Copy link
Contributor

@aviggiano unfortunately, the ledger support will have to wait a little bit more.

after discussing it with @zerosnacks and @grandizzy, we've agreed that my proposal is not optimal due to the inherent risks of executing FFI calls on the terminal (even if it's a call to cast in this case).

Instead, we will think how to come around the limitation that forces cheatcodes to be synchronous. However, this requires some design and exploration work.

@0xrusowsky 0xrusowsky moved this from In Progress to Blocked in Foundry May 28, 2025
@bowd
Copy link

bowd commented Jun 2, 2025

Ah damn so the async part is the source of complexity here. I was also building something on top of @aviggiano 's lib and the ffi limitation screws with my usecase a bit more. I wanted to have the option of both signing and broadcasting from the ledger in the same script and that's currently not possible with this solution, because if forge connects to the ledger, the ffi call to cast won't run.

Any intution on how likely it would be to make async cheatcodes a reality?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-forge Command: forge T-feature Type: feature
Projects
Status: Blocked
Development

Successfully merging this pull request may close these issues.

feat(cheatcodes): add vm.signTypedData cheatcode to Foundry
5 participants