-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy patheip712.go
More file actions
161 lines (136 loc) · 4.51 KB
/
eip712.go
File metadata and controls
161 lines (136 loc) · 4.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package gocalibur
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)
// EIP-712 type strings for Calibur
const (
CallType = "Call(address to,uint256 value,bytes data)"
BatchedCallType = "BatchedCall(Call[] calls,bool revertOnFailure)" + CallType
SignedBatchedCallType = "SignedBatchedCall(BatchedCall batchedCall,uint256 nonce,bytes32 keyHash,address executor,uint256 deadline)" + BatchedCallType
)
// EIP712Domain represents the EIP-712 domain for Calibur.
type EIP712Domain struct {
Name string
Version string
ChainID *big.Int
VerifyingContract common.Address
Salt [32]byte
}
// CaliburDomain returns the EIP-712 domain for a Calibur-delegated account.
// For EIP-7702, verifyingContract is the user's address, and salt is derived from the Calibur contract.
func CaliburDomain(chainID *big.Int, userAddr common.Address, caliburAddr common.Address) EIP712Domain {
// Salt is the Calibur address padded to 32 bytes (left-padded)
var salt [32]byte
copy(salt[12:], caliburAddr.Bytes())
return EIP712Domain{
Name: "Calibur",
Version: "1.0.0",
ChainID: chainID,
VerifyingContract: userAddr,
Salt: salt,
}
}
// DomainSeparator computes the EIP-712 domain separator.
func (d EIP712Domain) DomainSeparator() [32]byte {
typeHash := crypto.Keccak256([]byte("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"))
nameHash := crypto.Keccak256([]byte(d.Name))
versionHash := crypto.Keccak256([]byte(d.Version))
data := append(typeHash, nameHash...)
data = append(data, versionHash...)
data = append(data, common.LeftPadBytes(d.ChainID.Bytes(), 32)...)
data = append(data, common.LeftPadBytes(d.VerifyingContract.Bytes(), 32)...)
data = append(data, d.Salt[:]...)
hash := crypto.Keccak256(data)
var result [32]byte
copy(result[:], hash)
return result
}
// HashCall computes the EIP-712 struct hash for a Call.
func HashCall(call Call) [32]byte {
typeHash := keccak256([]byte(CallType))
value := call.Value
if value == nil {
value = big.NewInt(0)
}
dataHash := keccak256(call.Data)
return keccak256(
typeHash[:],
common.LeftPadBytes(call.To.Bytes(), 32),
common.LeftPadBytes(value.Bytes(), 32),
dataHash[:],
)
}
// HashBatchedCall computes the EIP-712 struct hash for a BatchedCall.
func HashBatchedCall(batch BatchedCall) [32]byte {
typeHash := keccak256([]byte(BatchedCallType))
// Hash the array of calls
callHashes := make([]byte, 0, len(batch.Calls)*32)
for _, call := range batch.Calls {
h := HashCall(call)
callHashes = append(callHashes, h[:]...)
}
callsHash := keccak256(callHashes)
// revertOnFailure as uint256 (0 or 1)
revertBytes := make([]byte, 32)
if batch.RevertOnFailure {
revertBytes[31] = 1
}
return keccak256(
typeHash[:],
callsHash[:],
revertBytes,
)
}
// HashSignedBatchedCall computes the EIP-712 struct hash for a SignedBatchedCall.
func HashSignedBatchedCall(signed SignedBatchedCall) [32]byte {
typeHash := keccak256([]byte(SignedBatchedCallType))
batchedCallHash := HashBatchedCall(signed.BatchedCall)
nonce := signed.Nonce
if nonce == nil {
nonce = big.NewInt(0)
}
deadline := signed.Deadline
if deadline == nil {
deadline = big.NewInt(0)
}
return keccak256(
typeHash[:],
batchedCallHash[:],
common.LeftPadBytes(nonce.Bytes(), 32),
signed.KeyHash[:],
common.LeftPadBytes(signed.Executor.Bytes(), 32),
common.LeftPadBytes(deadline.Bytes(), 32),
)
}
// ComputeDigest computes the EIP-712 digest for signing a SignedBatchedCall.
// This is the hash that should be signed using secp256k1, P-256, or WebAuthn.
//
// Usage:
//
// digest := gocalibur.ComputeDigest(domain, signedCall)
// // For secp256k1: sig, _ := crypto.Sign(digest[:], privateKey)
// // For P-256: r, s, _ := gocalibur.SignP256(privateKey, digest[:])
// // For WebAuthn: use digest as the challenge
func ComputeDigest(domain EIP712Domain, signed SignedBatchedCall) [32]byte {
domainSeparator := domain.DomainSeparator()
structHash := HashSignedBatchedCall(signed)
// EIP-712 message: "\x19\x01" || domainSeparator || structHash
return keccak256(
[]byte{0x19, 0x01},
domainSeparator[:],
structHash[:],
)
}
// keccak256 computes keccak256 hash of concatenated inputs.
func keccak256(data ...[]byte) [32]byte {
var allData []byte
for _, d := range data {
allData = append(allData, d...)
}
hash := crypto.Keccak256(allData)
var result [32]byte
copy(result[:], hash)
return result
}