Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,18 @@ The package does not construct messages or hashes, manage RPC clients, send tran

## Status

Pre-release. The API may change before the first tag.
Initial pre-release. The API may change before v1.0.0.

Pin a commit if you use this before a release:
Install the latest tagged release:

```bash
go get github.com/yermakovsa/erc6492-go@<commit>
```
go get github.com/yermakovsa/erc6492-go@v0.1.0
````

After a tagged release is available:
Pin a commit if you need an unreleased change:

```bash
go get github.com/yermakovsa/erc6492-go@v0.1.0
go get github.com/yermakovsa/erc6492-go@<commit>
```

## API shape
Expand Down Expand Up @@ -182,6 +182,15 @@ if result.Valid {
}
```

## Examples

Runnable examples are available in [`examples`](examples).

- [`examples/eoa`](examples/eoa) verifies a known EOA signature locally.
- [`examples/eip1271`](examples/eip1271) verifies a known EIP-1271 signature against a fixture contract on Sepolia.

The examples use caller-supplied final hashes. They do not build messages, hash typed data, send transactions, or deploy contracts.

## Testing

```bash
Expand Down
10 changes: 10 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Examples

Small examples for using `erc6492-go`.

The examples verify caller-supplied final hashes. They do not build messages, hash typed data, send transactions, or deploy contracts.

## Available examples

- [`eoa`](./eoa) verifies a known EOA signature locally.
- [`eip1271`](./eip1271) verifies a known EIP-1271 signature against a fixture contract on Sepolia.
58 changes: 58 additions & 0 deletions examples/eip1271/ExampleEIP1271Wallet.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/// @notice Tiny EIP-1271 fixture wallet. Not for production use.
contract ExampleEIP1271Wallet {
bytes4 public constant EIP1271_MAGIC_VALUE = 0x1626ba7e;

bytes4 public constant INVALID_SIGNATURE_VALUE = 0xffffffff;

/// @dev secp256k1n / 2, used to reject malleable signatures.
uint256 public constant SECP256K1_HALF_ORDER = 0x7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0;

address public immutable owner;

error ZeroOwner();

constructor(address owner_) {
if (owner_ == address(0)) {
revert ZeroOwner();
}

owner = owner_;
}

/// @notice Validates a 65-byte r || s || v signature against the exact input hash.
/// @dev No prefixing, typed-data hashing, or v = 0/1 normalization.
function isValidSignature(bytes32 hash, bytes calldata signature) external view returns (bytes4) {
if (signature.length != 65) {
return INVALID_SIGNATURE_VALUE;
}

bytes32 r;
bytes32 s;
uint8 v;

assembly {
r := calldataload(signature.offset)
s := calldataload(add(signature.offset, 0x20))
v := byte(0, calldataload(add(signature.offset, 0x40)))
}

if (uint256(s) > SECP256K1_HALF_ORDER) {
return INVALID_SIGNATURE_VALUE;
}

if (v != 27 && v != 28) {
return INVALID_SIGNATURE_VALUE;
}

address recovered = ecrecover(hash, v, r, s);

if (recovered == owner) {
return EIP1271_MAGIC_VALUE;
}

return INVALID_SIGNATURE_VALUE;
}
}
35 changes: 35 additions & 0 deletions examples/eip1271/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# EIP-1271 example

This example verifies a known EIP-1271 signature against a small fixture contract deployed on Sepolia.

It uses a final `bytes32` hash directly. It does not hash messages, build typed data, send transactions, or deploy anything.

## Fixture

- Chain: Sepolia
- Contract: `0x01719b210cca35ee34f46007daed7fb359086f91`
- Verified source: https://sepolia.etherscan.io/address/0x01719b210cca35ee34f46007daed7fb359086f91#code
- Owner: `0x3c002f761491bea2b25A8321490f9ca7A87B4DCf`
- Hash: `0x3dec0f6a98cd6082f478ae1d655bf12eb7c2c52be60e011c91a5ae1f62670b5c`
- Expected result: `0x1626ba7e`

`ExampleEIP1271Wallet.sol` is included here only so the fixture contract is easy to inspect.

The tested Solidity fixture repo is:

https://github.com/yermakovsa/example-eip1271-wallet

## Run

From the repository root:

```bash
RPC_URL="<your Sepolia RPC URL>" go run ./examples/eip1271
````

Expected output:

```text
valid: true
method: eip1271
```
42 changes: 42 additions & 0 deletions examples/eip1271/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package main

import (
"context"
"fmt"
"log"
"os"
"time"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"

erc6492 "github.com/yermakovsa/erc6492-go"
)

func main() {
rpcURL := os.Getenv("RPC_URL")
if rpcURL == "" {
log.Fatal("RPC_URL is required")
}

ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
defer cancel()

client, err := ethclient.DialContext(ctx, rpcURL)
if err != nil {
log.Fatalf("connect rpc: %v", err)
}
defer client.Close()

wallet := common.HexToAddress("0x01719b210cca35ee34f46007daed7fb359086f91")
hash := common.HexToHash("0x3dec0f6a98cd6082f478ae1d655bf12eb7c2c52be60e011c91a5ae1f62670b5c")
signature := common.FromHex("0xae49e3481e4a9f5c59d78d3e47efd9cc5975e0c0678243202846eb9e43230e02425fe69fd882ccec3a76e13d25a542b58a33ef24c5a8a8186778d7e13c87ca671c")

result, err := erc6492.VerifyEIP1271(ctx, client, wallet, hash, signature)
if err != nil {
log.Fatalf("verify eip1271: %v", err)
}

fmt.Printf("valid: %t\n", result.Valid)
fmt.Printf("method: %s\n", result.Method)
}
20 changes: 20 additions & 0 deletions examples/eoa/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# EOA example

This example verifies a known EOA signature against a final hash.

It runs locally and does not use RPC.

## Run

From the repository root:

```bash
go run ./examples/eoa
````

Expected output:

```text
valid: true
method: eoa
```
24 changes: 24 additions & 0 deletions examples/eoa/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

import (
"fmt"
"log"

"github.com/ethereum/go-ethereum/common"

erc6492 "github.com/yermakovsa/erc6492-go"
)

func main() {
signer := common.HexToAddress("0x6B6aD336c4016653885CeCa2C11Cf6742843298F")
hash := common.HexToHash("0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
signature := common.FromHex("0xd6e50e39ab346b4d8a924608953758e52cb7f81944ee5dbae3e00531eadea82c202706b219d251633c65338b701f05d2a61f633b22f2d34b2cca10dd24742c451c")

result, err := erc6492.VerifyEOA(signer, hash, signature)
if err != nil {
log.Fatalf("verify eoa: %v", err)
}

fmt.Printf("valid: %t\n", result.Valid)
fmt.Printf("method: %s\n", result.Method)
}
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,22 @@ go 1.24.0
require github.com/ethereum/go-ethereum v1.15.11

require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/VictoriaMetrics/fastcache v1.13.0 // indirect
github.com/bits-and-blooms/bitset v1.20.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/consensys/gnark-crypto v0.18.1 // indirect
github.com/crate-crypto/go-eth-kzg v1.5.0 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/ethereum/c-kzg-4844/v2 v2.1.6 // indirect
github.com/ethereum/go-verkle v0.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/uint256 v1.3.2 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
Expand Down
Loading
Loading