Skip to content

Commit 7ca5485

Browse files
Add eip-6492 on chain validation (#341)
* Add EIP-6492 tests * Add onchain EIP-6492 validation
1 parent eedaece commit 7ca5485

File tree

11 files changed

+358
-6
lines changed

11 files changed

+358
-6
lines changed

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ start-testchain-anvil:
4646
start-testchain-anvil-verbose:
4747
cd ./testutil/testchain && pnpm start:anvil:verbose
4848

49+
# Fork a chain: make start-testchain-fork FORK_URL=https://nodes.sequence.app/polygon-zkevm
50+
FORK_URL ?= https://nodes.sequence.app/polygon-zkevm
51+
start-testchain-fork:
52+
cd ./testutil/testchain && FORK_URL="$(FORK_URL)" pnpm start:anvil:fork -- "$(FORK_URL)"
53+
54+
start-testchain-fork-verbose:
55+
cd ./testutil/testchain && FORK_URL="$(FORK_URL)" pnpm start:anvil:fork:verbose -- "$(FORK_URL)"
56+
4957
check-testchain-running:
5058
@curl http://localhost:8545 -H"Content-type: application/json" -X POST -d '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' --write-out '%{http_code}' --silent --output /dev/null | grep 200 > /dev/null \
5159
|| { echo "*****"; echo "Oops! testchain is not running. Please run 'make start-testchain' in another terminal."; echo "*****"; exit 1; }

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ For documentation on sequence, please see our [docs](https://docs.sequence.xyz)
1414
2. `make start-testchain` -- starts the test ethereum chain (id 1337)
1515
3. (in a separate terminal) `make test` -- runs test suite
1616

17+
To run tests against a forked chain instead: `make start-testchain-fork` (default fork URL is Polygon ZK EVM). Override with `make start-testchain-fork FORK_URL=https://your-rpc-url`.
18+
1719

1820
## Testing
1921

auth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ func EIP6492ValidateSignature() ethauth.ValidatorFunc {
101101
return false, "", fmt.Errorf("sig is invalid: %w", err)
102102
}
103103

104-
isValid, err := eip6492.ValidateEIP6492Offchain(ctx, provider, signer, hash, sig, nil)
104+
isValid, err := eip6492.ValidateEIP6492(ctx, provider, signer, hash, sig, nil)
105105
if err != nil {
106106
return false, "", fmt.Errorf("failed to validate: %w", err)
107107
}

core/v2/v2.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1036,7 +1036,7 @@ func (l *signatureTreeDynamicSignatureLeaf) recover(ctx context.Context, payload
10361036
signature := l.signature
10371037

10381038
if provider != nil {
1039-
isValid, err := eip6492.ValidateEIP6492Offchain(ctx, provider, l.address, payload.Digest().Hash, signature, nil)
1039+
isValid, err := eip6492.ValidateEIP6492(ctx, provider, l.address, payload.Digest().Hash, signature, nil)
10401040
if err != nil {
10411041
return nil, nil, fmt.Errorf("unable to validate signature for %v: %w", l.address, err)
10421042
}

core/v3/v3.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1122,7 +1122,7 @@ func (l *signatureTreeSignatureERC1271Leaf) recover(ctx context.Context, payload
11221122
signature := l.Signature
11231123

11241124
if provider != nil {
1125-
isValid, err := eip6492.ValidateEIP6492Offchain(ctx, provider, l.Address, payload.Digest().Hash, signature, nil)
1125+
isValid, err := eip6492.ValidateEIP6492(ctx, provider, l.Address, payload.Digest().Hash, signature, nil)
11261126
if err != nil {
11271127
return nil, nil, fmt.Errorf("unable to validate ERC-1271 signature: %w", err)
11281128
}

eip6492_live_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package sequence_test
2+
3+
import (
4+
"context"
5+
"strings"
6+
"testing"
7+
8+
"github.com/0xsequence/ethkit/go-ethereum/accounts"
9+
"github.com/0xsequence/ethkit/go-ethereum/common"
10+
"github.com/0xsequence/go-sequence"
11+
"github.com/0xsequence/go-sequence/lib/eip6492"
12+
"github.com/stretchr/testify/require"
13+
)
14+
15+
// These tests require a live testchain (make start-testchain or make start-testchain-fork).
16+
// They create a Sequence wallet, sign a message, wrap in EIP-6492, and validate via
17+
// ValidateEIP6492Offchain or ValidateEIP6492Onchain. Onchain tests skip if the validator
18+
// is not deployed at EIP_6492_ADDRESS on the chain.
19+
20+
func TestEIP6492Live_ValidateSequenceWalletMessage_Offchain(t *testing.T) {
21+
// V2 counterfactual wallet on testchain
22+
wallet, err := testChain.V2DummySequenceWallet(10, true)
23+
require.NoError(t, err)
24+
25+
message := []byte("hello world!")
26+
_, eip191Message := accounts.TextAndHash(message)
27+
28+
sig, err := wallet.SignMessage([]byte(eip191Message))
29+
require.NoError(t, err)
30+
31+
wrapped, err := sequence.EIP6492Signature(sig, wallet.GetWalletConfig())
32+
require.NoError(t, err)
33+
34+
digest := common.BytesToHash(accounts.TextHash(message))
35+
ctx := context.Background()
36+
37+
valid, err := eip6492.ValidateEIP6492Offchain(ctx, testChain.Provider, wallet.Address(), digest, wrapped, nil)
38+
require.NoError(t, err)
39+
require.True(t, valid, "EIP-6492 offchain validation should succeed for Sequence wallet message signature")
40+
}
41+
42+
func TestEIP6492Live_ValidateSequenceWalletMessage_ViaIsValidMessageSignature(t *testing.T) {
43+
// Same flow as above but via the public API to ensure full path works
44+
wallet, err := testChain.V2DummySequenceWallet(11, true)
45+
require.NoError(t, err)
46+
47+
message := []byte("eip6492 live test")
48+
_, eip191Message := accounts.TextAndHash(message)
49+
50+
sig, err := wallet.SignMessage([]byte(eip191Message))
51+
require.NoError(t, err)
52+
53+
wrapped, err := sequence.EIP6492Signature(sig, wallet.GetWalletConfig())
54+
require.NoError(t, err)
55+
56+
isValid, err := sequence.IsValidMessageSignature(
57+
wallet.Address(),
58+
message,
59+
wrapped,
60+
testChain.ChainID(),
61+
testChain.Provider,
62+
nil,
63+
)
64+
require.NoError(t, err)
65+
require.True(t, isValid, "IsValidMessageSignature should succeed for EIP-6492 wrapped Sequence wallet signature")
66+
}
67+
68+
func TestEIP6492Live_ValidateDeployedSequenceWallet_Offchain(t *testing.T) {
69+
// Deploy the wallet first, then validate (tests both counterfactual deploy path and already-deployed path)
70+
wallet, err := testChain.V2DummySequenceWallet(12, false)
71+
require.NoError(t, err)
72+
73+
message := []byte("deployed wallet sign")
74+
_, eip191Message := accounts.TextAndHash(message)
75+
76+
sig, err := wallet.SignMessage([]byte(eip191Message))
77+
require.NoError(t, err)
78+
79+
wrapped, err := sequence.EIP6492Signature(sig, wallet.GetWalletConfig())
80+
require.NoError(t, err)
81+
82+
digest := common.BytesToHash(accounts.TextHash(message))
83+
ctx := context.Background()
84+
85+
valid, err := eip6492.ValidateEIP6492Offchain(ctx, testChain.Provider, wallet.Address(), digest, wrapped, nil)
86+
require.NoError(t, err)
87+
require.True(t, valid, "EIP-6492 offchain validation should succeed for deployed Sequence wallet")
88+
}
89+
90+
func TestEIP6492Live_ValidateSequenceWalletMessage_Onchain(t *testing.T) {
91+
// Same as offchain test but via pre-deployed validator at EIP_6492_ADDRESS.
92+
// Skips if the validator is not deployed on this chain.
93+
wallet, err := testChain.V2DummySequenceWallet(13, true)
94+
require.NoError(t, err)
95+
96+
message := []byte("hello world onchain!")
97+
_, eip191Message := accounts.TextAndHash(message)
98+
99+
sig, err := wallet.SignMessage([]byte(eip191Message))
100+
require.NoError(t, err)
101+
102+
wrapped, err := sequence.EIP6492Signature(sig, wallet.GetWalletConfig())
103+
require.NoError(t, err)
104+
105+
digest := common.BytesToHash(accounts.TextHash(message))
106+
ctx := context.Background()
107+
108+
valid, err := eip6492.ValidateEIP6492Onchain(ctx, testChain.Provider, wallet.Address(), digest, wrapped, nil)
109+
if err != nil && strings.Contains(err.Error(), "returned no data") {
110+
t.Skipf("EIP-6492 validator not deployed on this chain: %v", err)
111+
}
112+
require.NoError(t, err)
113+
require.True(t, valid, "EIP-6492 onchain validation should succeed for Sequence wallet message signature")
114+
}
115+
116+
func TestEIP6492Live_ValidateDeployedSequenceWallet_Onchain(t *testing.T) {
117+
// Deployed wallet, validated via onchain contract. Skips if validator not deployed.
118+
wallet, err := testChain.V2DummySequenceWallet(14, false)
119+
require.NoError(t, err)
120+
121+
message := []byte("deployed wallet onchain sign")
122+
_, eip191Message := accounts.TextAndHash(message)
123+
124+
sig, err := wallet.SignMessage([]byte(eip191Message))
125+
require.NoError(t, err)
126+
127+
wrapped, err := sequence.EIP6492Signature(sig, wallet.GetWalletConfig())
128+
require.NoError(t, err)
129+
130+
digest := common.BytesToHash(accounts.TextHash(message))
131+
ctx := context.Background()
132+
133+
valid, err := eip6492.ValidateEIP6492Onchain(ctx, testChain.Provider, wallet.Address(), digest, wrapped, nil)
134+
if err != nil && strings.Contains(err.Error(), "returned no data") {
135+
t.Skipf("EIP-6492 validator not deployed on this chain: %v", err)
136+
}
137+
require.NoError(t, err)
138+
require.True(t, valid, "EIP-6492 onchain validation should succeed for deployed Sequence wallet")
139+
}

lib/eip6492/eip6492.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ contract ValidateSigOffchain {
187187

188188
const EIP_6492_OFFCHAIN_DEPLOY_CODE = "0x608060405234801561001057600080fd5b5060405161124a38038061124a83398101604081905261002f91610124565b600060405161003d906100dd565b604051809103906000f080158015610059573d6000803e3d6000fd5b5090506000816001600160a01b0316638f0684308686866040518463ffffffff1660e01b815260040161008e939291906101fb565b6020604051808303816000875af11580156100ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100d19190610244565b9050806000526001601ff35b610fdc8061026e83390190565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561011b578181015183820152602001610103565b50506000910152565b60008060006060848603121561013957600080fd5b83516001600160a01b038116811461015057600080fd5b6020850151604086015191945092506001600160401b038082111561017457600080fd5b818601915086601f83011261018857600080fd5b81518181111561019a5761019a6100ea565b604051601f8201601f19908116603f011681019083821181831017156101c2576101c26100ea565b816040528281528960208487010111156101db57600080fd5b6101ec836020830160208801610100565b80955050505050509250925092565b60018060a01b0384168152826020820152606060408201526000825180606084015261022e816080850160208701610100565b601f01601f191691909101608001949350505050565b60006020828403121561025657600080fd5b8151801515811461026657600080fd5b939250505056fe608060405234801561001057600080fd5b50610fbc806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806376be4cea1161005057806376be4cea146100a65780638f068430146100b957806398ef1ed8146100cc57600080fd5b80631c6453271461006c5780633d787b6314610093575b600080fd5b61007f61007a366004610ad4565b6100df565b604051901515815260200160405180910390f35b61007f6100a1366004610ad4565b61023d565b61007f6100b4366004610b3e565b61031e565b61007f6100c7366004610ad4565b6108e1565b61007f6100da366004610ad4565b61096e565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061012890889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610181575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261017e91810190610c45565b60015b610232573d8080156101af576040519150601f19603f3d011682016040523d82523d6000602084013e6101b4565b606091505b508051600181900361022757816000815181106101d3576101d3610c69565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167f0100000000000000000000000000000000000000000000000000000000000000149250610235915050565b600092505050610235565b90505b949350505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906102879088908890889088906001908990600401610bc3565b6020604051808303816000875af19250505080156102e0575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526102dd91810190610c45565b60015b610232573d80801561030e576040519150601f19603f3d011682016040523d82523d6000602084013e610313565b606091505b506000915050610235565b600073ffffffffffffffffffffffffffffffffffffffff87163b6060827f64926492649264926492649264926492649264926492649264926492649264928888610369602082610c98565b610375928b9290610cd8565b61037e91610d02565b1490508015610484576000606089828a610399602082610c98565b926103a693929190610cd8565b8101906103b39190610e18565b955090925090508415806103c45750865b1561047d576000808373ffffffffffffffffffffffffffffffffffffffff16836040516103f19190610eb2565b6000604051808303816000865af19150503d806000811461042e576040519150601f19603f3d011682016040523d82523d6000602084013e610433565b606091505b50915091508161047a57806040517f9d0d6e2d0000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b60405180910390fd5b50505b50506104be565b87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509294505050505b80806104ca5750600083115b156106bb576040517f1626ba7e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b1690631626ba7e90610523908c908690600401610f2b565b602060405180830381865afa92505050801561057a575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261057791810190610f44565b60015b61060f573d8080156105a8576040519150601f19603f3d011682016040523d82523d6000602084013e6105ad565b606091505b50851580156105bc5750600084115b156105db576105d08b8b8b8b8b600161031e565b9450505050506108d7565b806040517f6f2a95990000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f1626ba7e000000000000000000000000000000000000000000000000000000001480158161065f575086155b801561066b5750600085115b1561068b5761067f8c8c8c8c8c600161031e565b955050505050506108d7565b841580156106965750825b80156106a0575087155b156106af57806000526001601ffd5b94506108d79350505050565b6041871461074b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f5369676e617475726556616c696461746f72237265636f7665725369676e657260448201527f3a20696e76616c6964207369676e6174757265206c656e6774680000000000006064820152608401610471565b600061075a6020828a8c610cd8565b61076391610d02565b90506000610775604060208b8d610cd8565b61077e91610d02565b905060008a8a604081811061079557610795610c69565b919091013560f81c915050601b81148015906107b557508060ff16601c14155b15610842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f5369676e617475726556616c696461746f723a20696e76616c6964207369676e60448201527f617475726520762076616c7565000000000000000000000000000000000000006064820152608401610471565b6040805160008152602081018083528e905260ff831691810191909152606081018490526080810183905273ffffffffffffffffffffffffffffffffffffffff8e169060019060a0016020604051602081039080840390855afa1580156108ad573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff161496505050505050505b9695505050505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061092b9088908890889088906001908990600401610bc3565b6020604051808303816000875af115801561094a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102329190610c45565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906109b790889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610a10575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610a0d91810190610c45565b60015b610232573d808015610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b606091505b5080516001819003610a6257816000815181106101d3576101d3610c69565b8082fd5b73ffffffffffffffffffffffffffffffffffffffff81168114610a8857600080fd5b50565b60008083601f840112610a9d57600080fd5b50813567ffffffffffffffff811115610ab557600080fd5b602083019150836020828501011115610acd57600080fd5b9250929050565b60008060008060608587031215610aea57600080fd5b8435610af581610a66565b935060208501359250604085013567ffffffffffffffff811115610b1857600080fd5b610b2487828801610a8b565b95989497509550505050565b8015158114610a8857600080fd5b60008060008060008060a08789031215610b5757600080fd5b8635610b6281610a66565b955060208701359450604087013567ffffffffffffffff811115610b8557600080fd5b610b9189828a01610a8b565b9095509350506060870135610ba581610b30565b91506080870135610bb581610b30565b809150509295509295509295565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015260a060408201528360a0820152838560c0830137600060c085830181019190915292151560608201529015156080820152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909101019392505050565b600060208284031215610c5757600080fd5b8151610c6281610b30565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b81810381811115610cd2577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b92915050565b60008085851115610ce857600080fd5b83861115610cf557600080fd5b5050820193919092039150565b80356020831015610cd2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112610d7e57600080fd5b813567ffffffffffffffff80821115610d9957610d99610d3e565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715610ddf57610ddf610d3e565b81604052838152866020858801011115610df857600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600060608486031215610e2d57600080fd5b8335610e3881610a66565b9250602084013567ffffffffffffffff80821115610e5557600080fd5b610e6187838801610d6d565b93506040860135915080821115610e7757600080fd5b50610e8486828701610d6d565b9150509250925092565b60005b83811015610ea9578181015183820152602001610e91565b50506000910152565b60008251610ec4818460208701610e8e565b9190910192915050565b60008151808452610ee6816020860160208601610e8e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c626020830184610ece565b8281526040602082015260006102356040830184610ece565b600060208284031215610f5657600080fd5b81517fffffffff0000000000000000000000000000000000000000000000000000000081168114610c6257600080fdfea26469706673582212201a72aed4b15ffb05b6502997a9bb655992e06590bd26b336dfbb153d7ff6f34b64736f6c63430008120033"
189189
const EIP_6492_SUFFIX = "0x6492649264926492649264926492649264926492649264926492649264926492"
190+
const EIP_6492_ADDRESS = "0x0000000044dE9306b161ddE8D58682054e70c766"
190191

191192
var EIP6492MagicBytes = hexutil.MustDecode(EIP_6492_SUFFIX)
192193

@@ -213,6 +214,28 @@ func DecodeEIP6492Signature(signature []byte) (common.Address, []byte, []byte, e
213214
return create2Factory, factoryCalldata, sigToValidate, nil
214215
}
215216

217+
func ValidateEIP6492(
218+
ctx context.Context,
219+
provider *ethrpc.Provider,
220+
signer common.Address,
221+
hash common.Hash,
222+
signature []byte,
223+
block *big.Int,
224+
) (bool, error) {
225+
validatorAddr := common.HexToAddress(EIP_6492_ADDRESS)
226+
code, err := provider.CodeAt(ctx, validatorAddr, block)
227+
if err == nil && len(code) > 0 {
228+
// Validate using the deployed EIP-6492 validator
229+
valid, onchainErr := ValidateEIP6492Onchain(ctx, provider, signer, hash, signature, block)
230+
if onchainErr == nil && valid {
231+
return true, nil
232+
}
233+
}
234+
235+
// Fall back to offchain validation
236+
return ValidateEIP6492Offchain(ctx, provider, signer, hash, signature, block)
237+
}
238+
216239
func ValidateEIP6492Offchain(
217240
ctx context.Context,
218241
provider *ethrpc.Provider,
@@ -252,3 +275,45 @@ func ValidateEIP6492Offchain(
252275
expectedResult := hexutil.MustDecode("0x01")
253276
return bytes.Equal(result, expectedResult), nil
254277
}
278+
279+
// ValidateEIP6492Onchain validates an EIP-6492 signature by calling the pre-deployed
280+
// UniversalSigValidator at EIP_6492_ADDRESS and invoking isValidSigWithSideEffects.
281+
// It takes the same arguments as ValidateEIP6492Offchain but uses the onchain contract
282+
// instead of deployment via constructor.
283+
func ValidateEIP6492Onchain(
284+
ctx context.Context,
285+
provider *ethrpc.Provider,
286+
signer common.Address,
287+
hash common.Hash,
288+
signature []byte,
289+
block *big.Int,
290+
) (bool, error) {
291+
calldata, err := ethcoder.AbiEncodeMethodCalldata(
292+
"isValidSigWithSideEffects(address,bytes32,bytes)",
293+
[]interface{}{signer, hash, signature},
294+
)
295+
if err != nil {
296+
return false, err
297+
}
298+
299+
contractAddr := common.HexToAddress(EIP_6492_ADDRESS)
300+
msg := ethereum.CallMsg{
301+
To: &contractAddr,
302+
Data: calldata,
303+
}
304+
305+
result, err := provider.CallContract(ctx, msg, block)
306+
if err != nil {
307+
return false, err
308+
}
309+
310+
if len(result) == 0 {
311+
return false, fmt.Errorf("contract call returned no data: validator may not be deployed at %s or call reverted", EIP_6492_ADDRESS)
312+
}
313+
314+
var valid bool
315+
if err := ethcoder.AbiDecoder([]string{"bool"}, result, []interface{}{&valid}); err != nil {
316+
return false, err
317+
}
318+
return valid, nil
319+
}

0 commit comments

Comments
 (0)