@0xsequence/wallet-primitives 0.0.0-20250520201059
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +5 -0
- package/CHANGELOG.md +7 -0
- package/LICENSE +202 -0
- package/dist/address.d.ts +5 -0
- package/dist/address.d.ts.map +1 -0
- package/dist/address.js +7 -0
- package/dist/address.js.map +1 -0
- package/dist/attestation.d.ts +24 -0
- package/dist/attestation.d.ts.map +1 -0
- package/dist/attestation.js +77 -0
- package/dist/attestation.js.map +1 -0
- package/dist/config.d.ts +85 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +381 -0
- package/dist/config.js.map +1 -0
- package/dist/constants.d.ts +173 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +31 -0
- package/dist/constants.js.map +1 -0
- package/dist/context.d.ts +9 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +8 -0
- package/dist/context.js.map +1 -0
- package/dist/erc-6492.d.ts +19 -0
- package/dist/erc-6492.d.ts.map +1 -0
- package/dist/erc-6492.js +64 -0
- package/dist/erc-6492.js.map +1 -0
- package/dist/extensions/index.d.ts +9 -0
- package/dist/extensions/index.d.ts.map +1 -0
- package/dist/extensions/index.js +7 -0
- package/dist/extensions/index.js.map +1 -0
- package/dist/extensions/passkeys.d.ts +31 -0
- package/dist/extensions/passkeys.d.ts.map +1 -0
- package/dist/extensions/passkeys.js +224 -0
- package/dist/extensions/passkeys.js.map +1 -0
- package/dist/extensions/recovery.d.ts +310 -0
- package/dist/extensions/recovery.d.ts.map +1 -0
- package/dist/extensions/recovery.js +444 -0
- package/dist/extensions/recovery.js.map +1 -0
- package/dist/generic-tree.d.ts +14 -0
- package/dist/generic-tree.d.ts.map +1 -0
- package/dist/generic-tree.js +34 -0
- package/dist/generic-tree.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/network.d.ts +15 -0
- package/dist/network.d.ts.map +1 -0
- package/dist/network.js +24 -0
- package/dist/network.js.map +1 -0
- package/dist/payload.d.ts +108 -0
- package/dist/payload.d.ts.map +1 -0
- package/dist/payload.js +627 -0
- package/dist/payload.js.map +1 -0
- package/dist/permission.d.ts +73 -0
- package/dist/permission.d.ts.map +1 -0
- package/dist/permission.js +188 -0
- package/dist/permission.js.map +1 -0
- package/dist/session-config.d.ts +113 -0
- package/dist/session-config.d.ts.map +1 -0
- package/dist/session-config.js +554 -0
- package/dist/session-config.js.map +1 -0
- package/dist/session-signature.d.ts +24 -0
- package/dist/session-signature.d.ts.map +1 -0
- package/dist/session-signature.js +141 -0
- package/dist/session-signature.js.map +1 -0
- package/dist/signature.d.ts +108 -0
- package/dist/signature.d.ts.map +1 -0
- package/dist/signature.js +1079 -0
- package/dist/signature.js.map +1 -0
- package/dist/utils.d.ts +45 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +100 -0
- package/dist/utils.js.map +1 -0
- package/eslint.config.mjs +4 -0
- package/package.json +27 -0
- package/src/address.ts +19 -0
- package/src/attestation.ts +114 -0
- package/src/config.ts +521 -0
- package/src/constants.ts +39 -0
- package/src/context.ts +16 -0
- package/src/erc-6492.ts +97 -0
- package/src/extensions/index.ts +14 -0
- package/src/extensions/passkeys.ts +283 -0
- package/src/extensions/recovery.ts +542 -0
- package/src/generic-tree.ts +55 -0
- package/src/index.ts +15 -0
- package/src/network.ts +37 -0
- package/src/payload.ts +825 -0
- package/src/permission.ts +252 -0
- package/src/session-config.ts +681 -0
- package/src/session-signature.ts +197 -0
- package/src/signature.ts +1398 -0
- package/src/utils.ts +114 -0
- package/tsconfig.json +10 -0
package/src/config.ts
ADDED
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
import { Address, Bytes, Hash, Hex } from 'ox'
|
|
2
|
+
import {
|
|
3
|
+
isRawConfig,
|
|
4
|
+
isRawNestedLeaf,
|
|
5
|
+
isRawSignerLeaf,
|
|
6
|
+
isSignedSapientSignerLeaf,
|
|
7
|
+
isSignedSignerLeaf,
|
|
8
|
+
RawConfig,
|
|
9
|
+
RawTopology,
|
|
10
|
+
SignatureOfSapientSignerLeaf,
|
|
11
|
+
SignatureOfSignerLeaf,
|
|
12
|
+
} from './signature.js'
|
|
13
|
+
|
|
14
|
+
export type SignerLeaf = {
|
|
15
|
+
type: 'signer'
|
|
16
|
+
address: Address.Address
|
|
17
|
+
weight: bigint
|
|
18
|
+
signed?: boolean
|
|
19
|
+
signature?: SignatureOfSignerLeaf
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export type SapientSignerLeaf = {
|
|
23
|
+
type: 'sapient-signer'
|
|
24
|
+
address: Address.Address
|
|
25
|
+
weight: bigint
|
|
26
|
+
imageHash: Hex.Hex
|
|
27
|
+
signed?: boolean
|
|
28
|
+
signature?: SignatureOfSapientSignerLeaf
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type SubdigestLeaf = {
|
|
32
|
+
type: 'subdigest'
|
|
33
|
+
digest: Hex.Hex
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type AnyAddressSubdigestLeaf = {
|
|
37
|
+
type: 'any-address-subdigest'
|
|
38
|
+
digest: Hex.Hex
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type NestedLeaf = {
|
|
42
|
+
type: 'nested'
|
|
43
|
+
tree: Topology
|
|
44
|
+
weight: bigint
|
|
45
|
+
threshold: bigint
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export type NodeLeaf = Hex.Hex
|
|
49
|
+
|
|
50
|
+
export type Node = [Topology, Topology]
|
|
51
|
+
|
|
52
|
+
export type Leaf = SignerLeaf | SapientSignerLeaf | SubdigestLeaf | AnyAddressSubdigestLeaf | NestedLeaf | NodeLeaf
|
|
53
|
+
|
|
54
|
+
export type Topology = Node | Leaf
|
|
55
|
+
|
|
56
|
+
export type Config = {
|
|
57
|
+
threshold: bigint
|
|
58
|
+
checkpoint: bigint
|
|
59
|
+
topology: Topology
|
|
60
|
+
checkpointer?: Address.Address
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function isSignerLeaf(cand: any): cand is SignerLeaf {
|
|
64
|
+
return typeof cand === 'object' && cand !== null && cand.type === 'signer'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function isSapientSignerLeaf(cand: any): cand is SapientSignerLeaf {
|
|
68
|
+
return typeof cand === 'object' && cand !== null && cand.type === 'sapient-signer'
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function isSubdigestLeaf(cand: any): cand is SubdigestLeaf {
|
|
72
|
+
return typeof cand === 'object' && cand !== null && cand.type === 'subdigest'
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function isAnyAddressSubdigestLeaf(cand: any): cand is AnyAddressSubdigestLeaf {
|
|
76
|
+
return typeof cand === 'object' && cand !== null && cand.type === 'any-address-subdigest'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function isNodeLeaf(cand: any): cand is NodeLeaf {
|
|
80
|
+
return Hex.validate(cand) && cand.length === 66
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function isNestedLeaf(cand: any): cand is NestedLeaf {
|
|
84
|
+
return typeof cand === 'object' && cand !== null && cand.type === 'nested'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function isNode(cand: any): cand is Node {
|
|
88
|
+
return Array.isArray(cand) && cand.length === 2 && isTopology(cand[0]) && isTopology(cand[1])
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function isConfig(cand: any): cand is Config {
|
|
92
|
+
return typeof cand === 'object' && 'threshold' in cand && 'checkpoint' in cand && 'topology' in cand
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function isLeaf(cand: Topology): cand is Leaf {
|
|
96
|
+
return (
|
|
97
|
+
isSignerLeaf(cand) ||
|
|
98
|
+
isSapientSignerLeaf(cand) ||
|
|
99
|
+
isSubdigestLeaf(cand) ||
|
|
100
|
+
isAnyAddressSubdigestLeaf(cand) ||
|
|
101
|
+
isNodeLeaf(cand) ||
|
|
102
|
+
isNestedLeaf(cand)
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function isTopology(cand: any): cand is Topology {
|
|
107
|
+
return isNode(cand) || isLeaf(cand)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function getSigners(configuration: Config | Topology): {
|
|
111
|
+
signers: Address.Address[]
|
|
112
|
+
sapientSigners: { address: Address.Address; imageHash: Hex.Hex }[]
|
|
113
|
+
isComplete: boolean
|
|
114
|
+
} {
|
|
115
|
+
const signers = new Set<Address.Address>()
|
|
116
|
+
const sapientSigners = new Set<{ address: Address.Address; imageHash: Hex.Hex }>()
|
|
117
|
+
|
|
118
|
+
let isComplete = true
|
|
119
|
+
|
|
120
|
+
const scan = (topology: Topology) => {
|
|
121
|
+
if (isNode(topology)) {
|
|
122
|
+
scan(topology[0])
|
|
123
|
+
scan(topology[1])
|
|
124
|
+
} else if (isSignerLeaf(topology)) {
|
|
125
|
+
if (topology.weight) {
|
|
126
|
+
signers.add(topology.address)
|
|
127
|
+
}
|
|
128
|
+
} else if (isSapientSignerLeaf(topology)) {
|
|
129
|
+
sapientSigners.add({ address: topology.address, imageHash: topology.imageHash })
|
|
130
|
+
} else if (isNodeLeaf(topology)) {
|
|
131
|
+
isComplete = false
|
|
132
|
+
} else if (isNestedLeaf(topology)) {
|
|
133
|
+
if (topology.weight) {
|
|
134
|
+
scan(topology.tree)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
scan(isConfig(configuration) ? configuration.topology : configuration)
|
|
140
|
+
return { signers: Array.from(signers), sapientSigners: Array.from(sapientSigners), isComplete }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function findSignerLeaf(
|
|
144
|
+
configuration: Config | Topology,
|
|
145
|
+
address: Address.Address,
|
|
146
|
+
): SignerLeaf | SapientSignerLeaf | undefined {
|
|
147
|
+
if (isConfig(configuration)) {
|
|
148
|
+
return findSignerLeaf(configuration.topology, address)
|
|
149
|
+
} else if (isNode(configuration)) {
|
|
150
|
+
return findSignerLeaf(configuration[0], address) || findSignerLeaf(configuration[1], address)
|
|
151
|
+
} else if (isSignerLeaf(configuration)) {
|
|
152
|
+
if (Address.isEqual(configuration.address, address)) {
|
|
153
|
+
return configuration
|
|
154
|
+
}
|
|
155
|
+
} else if (isSapientSignerLeaf(configuration)) {
|
|
156
|
+
if (Address.isEqual(configuration.address, address)) {
|
|
157
|
+
return configuration
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return undefined
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export function getWeight(
|
|
164
|
+
topology: RawTopology | RawConfig | Config,
|
|
165
|
+
canSign: (signer: SignerLeaf | SapientSignerLeaf) => boolean,
|
|
166
|
+
): { weight: bigint; maxWeight: bigint } {
|
|
167
|
+
topology = isRawConfig(topology) || isConfig(topology) ? topology.topology : topology
|
|
168
|
+
|
|
169
|
+
if (isSignedSignerLeaf(topology)) {
|
|
170
|
+
return { weight: topology.weight, maxWeight: topology.weight }
|
|
171
|
+
} else if (isSignerLeaf(topology)) {
|
|
172
|
+
return { weight: 0n, maxWeight: canSign(topology) ? topology.weight : 0n }
|
|
173
|
+
} else if (isRawSignerLeaf(topology)) {
|
|
174
|
+
return { weight: topology.weight, maxWeight: topology.weight }
|
|
175
|
+
} else if (isSignedSapientSignerLeaf(topology)) {
|
|
176
|
+
return { weight: topology.weight, maxWeight: topology.weight }
|
|
177
|
+
} else if (isSapientSignerLeaf(topology)) {
|
|
178
|
+
return { weight: 0n, maxWeight: canSign(topology) ? topology.weight : 0n }
|
|
179
|
+
} else if (isSubdigestLeaf(topology)) {
|
|
180
|
+
return { weight: 0n, maxWeight: 0n }
|
|
181
|
+
} else if (isAnyAddressSubdigestLeaf(topology)) {
|
|
182
|
+
return { weight: 0n, maxWeight: 0n }
|
|
183
|
+
} else if (isRawNestedLeaf(topology)) {
|
|
184
|
+
const { weight, maxWeight } = getWeight(topology.tree, canSign)
|
|
185
|
+
return {
|
|
186
|
+
weight: weight >= topology.threshold ? topology.weight : 0n,
|
|
187
|
+
maxWeight: maxWeight >= topology.threshold ? topology.weight : 0n,
|
|
188
|
+
}
|
|
189
|
+
} else if (isNodeLeaf(topology)) {
|
|
190
|
+
return { weight: 0n, maxWeight: 0n }
|
|
191
|
+
} else {
|
|
192
|
+
const [left, right] = [getWeight(topology[0], canSign), getWeight(topology[1], canSign)]
|
|
193
|
+
return { weight: left.weight + right.weight, maxWeight: left.maxWeight + right.maxWeight }
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function hashConfiguration(topology: Topology | Config): Bytes.Bytes {
|
|
198
|
+
if (isConfig(topology)) {
|
|
199
|
+
let root = hashConfiguration(topology.topology)
|
|
200
|
+
root = Hash.keccak256(Bytes.concat(root, Bytes.padLeft(Bytes.fromNumber(topology.threshold), 32)))
|
|
201
|
+
root = Hash.keccak256(Bytes.concat(root, Bytes.padLeft(Bytes.fromNumber(topology.checkpoint), 32)))
|
|
202
|
+
root = Hash.keccak256(
|
|
203
|
+
Bytes.concat(
|
|
204
|
+
root,
|
|
205
|
+
Bytes.padLeft(Bytes.fromHex(topology.checkpointer ?? '0x0000000000000000000000000000000000000000'), 32),
|
|
206
|
+
),
|
|
207
|
+
)
|
|
208
|
+
return root
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (isSignerLeaf(topology)) {
|
|
212
|
+
return Hash.keccak256(
|
|
213
|
+
Bytes.concat(
|
|
214
|
+
Bytes.fromString('Sequence signer:\n'),
|
|
215
|
+
Bytes.fromHex(topology.address),
|
|
216
|
+
Bytes.padLeft(Bytes.fromNumber(topology.weight), 32),
|
|
217
|
+
),
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (isSapientSignerLeaf(topology)) {
|
|
222
|
+
return Hash.keccak256(
|
|
223
|
+
Bytes.concat(
|
|
224
|
+
Bytes.fromString('Sequence sapient config:\n'),
|
|
225
|
+
Bytes.fromHex(topology.address),
|
|
226
|
+
Bytes.padLeft(Bytes.fromNumber(topology.weight), 32),
|
|
227
|
+
Bytes.padLeft(Bytes.fromHex(topology.imageHash), 32),
|
|
228
|
+
),
|
|
229
|
+
)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (isSubdigestLeaf(topology)) {
|
|
233
|
+
return Hash.keccak256(Bytes.concat(Bytes.fromString('Sequence static digest:\n'), Bytes.fromHex(topology.digest)))
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (isAnyAddressSubdigestLeaf(topology)) {
|
|
237
|
+
return Hash.keccak256(
|
|
238
|
+
Bytes.concat(Bytes.fromString('Sequence any address subdigest:\n'), Bytes.fromHex(topology.digest)),
|
|
239
|
+
)
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (isNodeLeaf(topology)) {
|
|
243
|
+
return Bytes.fromHex(topology)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (isNestedLeaf(topology)) {
|
|
247
|
+
return Hash.keccak256(
|
|
248
|
+
Bytes.concat(
|
|
249
|
+
Bytes.fromString('Sequence nested config:\n'),
|
|
250
|
+
hashConfiguration(topology.tree),
|
|
251
|
+
Bytes.padLeft(Bytes.fromNumber(topology.threshold), 32),
|
|
252
|
+
Bytes.padLeft(Bytes.fromNumber(topology.weight), 32),
|
|
253
|
+
),
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (isNode(topology)) {
|
|
258
|
+
return Hash.keccak256(Bytes.concat(hashConfiguration(topology[0]), hashConfiguration(topology[1])))
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
throw new Error('Invalid topology')
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function flatLeavesToTopology(leaves: Leaf[]): Topology {
|
|
265
|
+
if (leaves.length === 0) {
|
|
266
|
+
throw new Error('Cannot create topology from empty leaves')
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (leaves.length === 1) {
|
|
270
|
+
return leaves[0]!
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (leaves.length === 2) {
|
|
274
|
+
return [leaves[0]!, leaves[1]!]
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return [
|
|
278
|
+
flatLeavesToTopology(leaves.slice(0, leaves.length / 2)),
|
|
279
|
+
flatLeavesToTopology(leaves.slice(leaves.length / 2)),
|
|
280
|
+
]
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function configToJson(config: Config): string {
|
|
284
|
+
return JSON.stringify({
|
|
285
|
+
threshold: config.threshold.toString(),
|
|
286
|
+
checkpoint: config.checkpoint.toString(),
|
|
287
|
+
topology: encodeTopology(config.topology),
|
|
288
|
+
checkpointer: config.checkpointer,
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function configFromJson(json: string): Config {
|
|
293
|
+
const parsed = JSON.parse(json)
|
|
294
|
+
return {
|
|
295
|
+
threshold: BigInt(parsed.threshold),
|
|
296
|
+
checkpoint: BigInt(parsed.checkpoint),
|
|
297
|
+
checkpointer: parsed.checkpointer,
|
|
298
|
+
topology: decodeTopology(parsed.topology),
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function encodeTopology(top: Topology): any {
|
|
303
|
+
if (isNode(top)) {
|
|
304
|
+
return [encodeTopology(top[0]), encodeTopology(top[1])]
|
|
305
|
+
} else if (isSignerLeaf(top)) {
|
|
306
|
+
return {
|
|
307
|
+
type: 'signer',
|
|
308
|
+
address: top.address,
|
|
309
|
+
weight: top.weight.toString(),
|
|
310
|
+
}
|
|
311
|
+
} else if (isSapientSignerLeaf(top)) {
|
|
312
|
+
return {
|
|
313
|
+
type: 'sapient-signer',
|
|
314
|
+
address: top.address,
|
|
315
|
+
weight: top.weight.toString(),
|
|
316
|
+
imageHash: top.imageHash,
|
|
317
|
+
}
|
|
318
|
+
} else if (isSubdigestLeaf(top)) {
|
|
319
|
+
return {
|
|
320
|
+
type: 'subdigest',
|
|
321
|
+
digest: top.digest,
|
|
322
|
+
}
|
|
323
|
+
} else if (isAnyAddressSubdigestLeaf(top)) {
|
|
324
|
+
return {
|
|
325
|
+
type: 'any-address-subdigest',
|
|
326
|
+
digest: top.digest,
|
|
327
|
+
}
|
|
328
|
+
} else if (isNodeLeaf(top)) {
|
|
329
|
+
return top
|
|
330
|
+
} else if (isNestedLeaf(top)) {
|
|
331
|
+
return {
|
|
332
|
+
type: 'nested',
|
|
333
|
+
tree: encodeTopology(top.tree),
|
|
334
|
+
weight: top.weight.toString(),
|
|
335
|
+
threshold: top.threshold.toString(),
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
throw new Error('Invalid topology')
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function decodeTopology(obj: any): Topology {
|
|
343
|
+
if (Array.isArray(obj)) {
|
|
344
|
+
if (obj.length !== 2) {
|
|
345
|
+
throw new Error('Invalid node structure in JSON')
|
|
346
|
+
}
|
|
347
|
+
return [decodeTopology(obj[0]), decodeTopology(obj[1])]
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (typeof obj === 'string') {
|
|
351
|
+
return obj as Hex.Hex
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
switch (obj.type) {
|
|
355
|
+
case 'signer':
|
|
356
|
+
return {
|
|
357
|
+
type: 'signer',
|
|
358
|
+
address: obj.address,
|
|
359
|
+
weight: BigInt(obj.weight),
|
|
360
|
+
}
|
|
361
|
+
case 'sapient-signer':
|
|
362
|
+
return {
|
|
363
|
+
type: 'sapient-signer',
|
|
364
|
+
address: obj.address,
|
|
365
|
+
weight: BigInt(obj.weight),
|
|
366
|
+
imageHash: obj.imageHash,
|
|
367
|
+
}
|
|
368
|
+
case 'subdigest':
|
|
369
|
+
return {
|
|
370
|
+
type: 'subdigest',
|
|
371
|
+
digest: obj.digest,
|
|
372
|
+
}
|
|
373
|
+
case 'any-address-subdigest':
|
|
374
|
+
return {
|
|
375
|
+
type: 'any-address-subdigest',
|
|
376
|
+
digest: obj.digest,
|
|
377
|
+
}
|
|
378
|
+
case 'nested':
|
|
379
|
+
return {
|
|
380
|
+
type: 'nested',
|
|
381
|
+
tree: decodeTopology(obj.tree),
|
|
382
|
+
weight: BigInt(obj.weight),
|
|
383
|
+
threshold: BigInt(obj.threshold),
|
|
384
|
+
}
|
|
385
|
+
default:
|
|
386
|
+
throw new Error('Invalid type in topology JSON')
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export type SignerSignature<T> = [T] extends [Promise<unknown>]
|
|
391
|
+
? never
|
|
392
|
+
: MaybePromise<T> | { signature: Promise<T>; onSignerSignature?: SignerSignatureCallback; onCancel?: CancelCallback }
|
|
393
|
+
|
|
394
|
+
export function normalizeSignerSignature<T>(signature: SignerSignature<T>): {
|
|
395
|
+
signature: Promise<T>
|
|
396
|
+
onSignerSignature?: SignerSignatureCallback
|
|
397
|
+
onCancel?: CancelCallback
|
|
398
|
+
} {
|
|
399
|
+
if (signature instanceof Promise) {
|
|
400
|
+
return { signature }
|
|
401
|
+
} else if (
|
|
402
|
+
typeof signature === 'object' &&
|
|
403
|
+
signature &&
|
|
404
|
+
'signature' in signature &&
|
|
405
|
+
signature.signature instanceof Promise
|
|
406
|
+
) {
|
|
407
|
+
return signature as ReturnType<typeof normalizeSignerSignature>
|
|
408
|
+
} else {
|
|
409
|
+
return { signature: Promise.resolve(signature) as Promise<T> }
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
export type SignerErrorCallback = (signer: SignerLeaf | SapientSignerLeaf, error: unknown) => void
|
|
414
|
+
|
|
415
|
+
type SignerSignatureCallback = (topology: RawTopology) => void
|
|
416
|
+
type CancelCallback = (success: boolean) => void
|
|
417
|
+
type MaybePromise<T> = T | Promise<T>
|
|
418
|
+
|
|
419
|
+
export function mergeTopology(a: Topology, b: Topology): Topology {
|
|
420
|
+
if (isNode(a) && isNode(b)) {
|
|
421
|
+
return [mergeTopology(a[0], b[0]), mergeTopology(a[1], b[1])]
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (isNode(a) && !isNode(b)) {
|
|
425
|
+
if (!isNodeLeaf(b)) {
|
|
426
|
+
throw new Error('Topology mismatch: cannot merge node with non-node that is not a node leaf')
|
|
427
|
+
}
|
|
428
|
+
const hb = hashConfiguration(b)
|
|
429
|
+
if (!Bytes.isEqual(hb, hashConfiguration(a))) {
|
|
430
|
+
throw new Error('Topology mismatch: node hash does not match')
|
|
431
|
+
}
|
|
432
|
+
return a
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (!isNode(a) && isNode(b)) {
|
|
436
|
+
if (!isNodeLeaf(a)) {
|
|
437
|
+
throw new Error('Topology mismatch: cannot merge node with non-node that is not a node leaf')
|
|
438
|
+
}
|
|
439
|
+
const ha = hashConfiguration(a)
|
|
440
|
+
if (!Bytes.isEqual(ha, hashConfiguration(b))) {
|
|
441
|
+
throw new Error('Topology mismatch: node hash does not match')
|
|
442
|
+
}
|
|
443
|
+
return b
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
return mergeLeaf(a as Leaf, b as Leaf)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function mergeLeaf(a: Leaf, b: Leaf): Leaf {
|
|
450
|
+
if (isNodeLeaf(a) && isNodeLeaf(b)) {
|
|
451
|
+
if (!Hex.isEqual(a, b)) {
|
|
452
|
+
throw new Error('Topology mismatch: different node leaves')
|
|
453
|
+
}
|
|
454
|
+
return a
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (isNodeLeaf(a) && !isNodeLeaf(b)) {
|
|
458
|
+
const hb = hashConfiguration(b)
|
|
459
|
+
if (!Bytes.isEqual(hb, Bytes.fromHex(a))) {
|
|
460
|
+
throw new Error('Topology mismatch: node leaf hash does not match')
|
|
461
|
+
}
|
|
462
|
+
return b
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (!isNodeLeaf(a) && isNodeLeaf(b)) {
|
|
466
|
+
const ha = hashConfiguration(a)
|
|
467
|
+
if (!Bytes.isEqual(ha, Bytes.fromHex(b))) {
|
|
468
|
+
throw new Error('Topology mismatch: node leaf hash does not match')
|
|
469
|
+
}
|
|
470
|
+
return a
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
if (isSignerLeaf(a) && isSignerLeaf(b)) {
|
|
474
|
+
if (a.address !== b.address || a.weight !== b.weight) {
|
|
475
|
+
throw new Error('Topology mismatch: signer fields differ')
|
|
476
|
+
}
|
|
477
|
+
if (!!a.signed !== !!b.signed || !!a.signature !== !!b.signature) {
|
|
478
|
+
throw new Error('Topology mismatch: signer signature fields differ')
|
|
479
|
+
}
|
|
480
|
+
return a
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (isSapientSignerLeaf(a) && isSapientSignerLeaf(b)) {
|
|
484
|
+
if (a.address !== b.address || a.weight !== b.weight || a.imageHash !== b.imageHash) {
|
|
485
|
+
throw new Error('Topology mismatch: sapient signer fields differ')
|
|
486
|
+
}
|
|
487
|
+
if (!!a.signed !== !!b.signed || !!a.signature !== !!b.signature) {
|
|
488
|
+
throw new Error('Topology mismatch: sapient signature fields differ')
|
|
489
|
+
}
|
|
490
|
+
return a
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (isSubdigestLeaf(a) && isSubdigestLeaf(b)) {
|
|
494
|
+
if (!Bytes.isEqual(Bytes.fromHex(a.digest), Bytes.fromHex(b.digest))) {
|
|
495
|
+
throw new Error('Topology mismatch: subdigest fields differ')
|
|
496
|
+
}
|
|
497
|
+
return a
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
if (isAnyAddressSubdigestLeaf(a) && isAnyAddressSubdigestLeaf(b)) {
|
|
501
|
+
if (!Bytes.isEqual(Bytes.fromHex(a.digest), Bytes.fromHex(b.digest))) {
|
|
502
|
+
throw new Error('Topology mismatch: any-address-subdigest fields differ')
|
|
503
|
+
}
|
|
504
|
+
return a
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (isNestedLeaf(a) && isNestedLeaf(b)) {
|
|
508
|
+
if (a.weight !== b.weight || a.threshold !== b.threshold) {
|
|
509
|
+
throw new Error('Topology mismatch: nested leaf fields differ')
|
|
510
|
+
}
|
|
511
|
+
const mergedTree = mergeTopology(a.tree, b.tree)
|
|
512
|
+
return {
|
|
513
|
+
type: 'nested',
|
|
514
|
+
weight: a.weight,
|
|
515
|
+
threshold: a.threshold,
|
|
516
|
+
tree: mergedTree,
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
throw new Error('Topology mismatch: incompatible leaf types')
|
|
521
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Abi, Address, Hex } from 'ox'
|
|
2
|
+
|
|
3
|
+
export const DEFAULT_CREATION_CODE: Hex.Hex =
|
|
4
|
+
'0x603e600e3d39601e805130553df33d3d34601c57363d3d373d363d30545af43d82803e903d91601c57fd5bf3'
|
|
5
|
+
|
|
6
|
+
export const DefaultFactory: Address.Address = '0xe068ec288d8b4Aaf7F7FC028Ce0797a7a353EF2d'
|
|
7
|
+
export const DefaultStage1: Address.Address = '0x302608CcdCc540761A0ec89C9d8Fa195dc8049C6'
|
|
8
|
+
export const DefaultStage2: Address.Address = '0x80cF586AFaCb3Cae77d84aFEBcC92382eDCF3A02'
|
|
9
|
+
export const DefaultGuest: Address.Address = '0x75e19AA6241D84C290658131857824B4eeF10dfF'
|
|
10
|
+
export const DefaultSessionManager: Address.Address = '0x81Fa4b986f958CB02A3A6c10aa38056dCd701941'
|
|
11
|
+
|
|
12
|
+
// ERC1271
|
|
13
|
+
export const IS_VALID_SIGNATURE = Abi.from([
|
|
14
|
+
'function isValidSignature(bytes32 _hash, bytes memory _signature) public view returns (bytes4 magicValue)',
|
|
15
|
+
])[0]
|
|
16
|
+
|
|
17
|
+
// Factory
|
|
18
|
+
export const DEPLOY = Abi.from([
|
|
19
|
+
'function deploy(address _mainModule, bytes32 _salt) public payable returns (address _contract)',
|
|
20
|
+
])[0]
|
|
21
|
+
|
|
22
|
+
// Stage1Module
|
|
23
|
+
export const GET_IMPLEMENTATION = Abi.from(['function getImplementation() external view returns (address)'])[0]
|
|
24
|
+
|
|
25
|
+
// Stage2Module
|
|
26
|
+
export const IMAGE_HASH = Abi.from(['function imageHash() external view returns (bytes32)'])[0]
|
|
27
|
+
export const READ_NONCE = Abi.from(['function readNonce(uint256 _space) public view returns (uint256)'])[0]
|
|
28
|
+
export const EXECUTE = Abi.from(['function execute(bytes calldata _payload, bytes calldata _signature) external'])[0]
|
|
29
|
+
export const UPDATE_IMAGE_HASH = Abi.from(['function updateImageHash(bytes32 _imageHash) external'])[0]
|
|
30
|
+
|
|
31
|
+
// Sapient
|
|
32
|
+
export const RECOVER_SAPIENT_SIGNATURE = Abi.from([
|
|
33
|
+
'function recoverSapientSignature((uint8 kind,bool noChainId,(address to,uint256 value,bytes data,uint256 gasLimit,bool delegateCall,bool onlyFallback,uint256 behaviorOnError)[] calls,uint256 space,uint256 nonce,bytes message,bytes32 imageHash,bytes32 digest,address[] parentWallets) calldata _payload, bytes calldata _signature) external view returns (bytes32)',
|
|
34
|
+
])[0]
|
|
35
|
+
|
|
36
|
+
// SapientCompact
|
|
37
|
+
export const RECOVER_SAPIENT_SIGNATURE_COMPACT = Abi.from([
|
|
38
|
+
'function recoverSapientSignatureCompact(bytes32 _digest, bytes calldata _signature) external view returns (bytes32)',
|
|
39
|
+
])[0]
|
package/src/context.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Address, Hex } from 'ox'
|
|
2
|
+
import { DEFAULT_CREATION_CODE, DefaultFactory, DefaultStage1, DefaultStage2 } from './constants.js'
|
|
3
|
+
|
|
4
|
+
export type Context = {
|
|
5
|
+
factory: Address.Address
|
|
6
|
+
stage1: Address.Address
|
|
7
|
+
stage2: Address.Address
|
|
8
|
+
creationCode: Hex.Hex
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const Dev1: Context = {
|
|
12
|
+
factory: DefaultFactory,
|
|
13
|
+
stage1: DefaultStage1,
|
|
14
|
+
stage2: DefaultStage2,
|
|
15
|
+
creationCode: DEFAULT_CREATION_CODE,
|
|
16
|
+
}
|
package/src/erc-6492.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { AbiFunction, AbiParameters, Address, Bytes, Hex, Provider } from 'ox'
|
|
2
|
+
import { WrappedSignature } from 'ox/erc6492'
|
|
3
|
+
import { DEPLOY } from './constants.js'
|
|
4
|
+
import { Context } from './context.js'
|
|
5
|
+
|
|
6
|
+
const EIP_6492_OFFCHAIN_DEPLOY_CODE =
|
|
7
|
+
'0x608060405234801561001057600080fd5b5060405161124a38038061124a83398101604081905261002f91610124565b600060405161003d906100dd565b604051809103906000f080158015610059573d6000803e3d6000fd5b5090506000816001600160a01b0316638f0684308686866040518463ffffffff1660e01b815260040161008e939291906101fb565b6020604051808303816000875af11580156100ad573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100d19190610244565b9050806000526001601ff35b610fdc8061026e83390190565b634e487b7160e01b600052604160045260246000fd5b60005b8381101561011b578181015183820152602001610103565b50506000910152565b60008060006060848603121561013957600080fd5b83516001600160a01b038116811461015057600080fd5b6020850151604086015191945092506001600160401b038082111561017457600080fd5b818601915086601f83011261018857600080fd5b81518181111561019a5761019a6100ea565b604051601f8201601f19908116603f011681019083821181831017156101c2576101c26100ea565b816040528281528960208487010111156101db57600080fd5b6101ec836020830160208801610100565b80955050505050509250925092565b60018060a01b0384168152826020820152606060408201526000825180606084015261022e816080850160208701610100565b601f01601f191691909101608001949350505050565b60006020828403121561025657600080fd5b8151801515811461026657600080fd5b939250505056fe608060405234801561001057600080fd5b50610fbc806100206000396000f3fe608060405234801561001057600080fd5b50600436106100675760003560e01c806376be4cea1161005057806376be4cea146100a65780638f068430146100b957806398ef1ed8146100cc57600080fd5b80631c6453271461006c5780633d787b6314610093575b600080fd5b61007f61007a366004610ad4565b6100df565b604051901515815260200160405180910390f35b61007f6100a1366004610ad4565b61023d565b61007f6100b4366004610b3e565b61031e565b61007f6100c7366004610ad4565b6108e1565b61007f6100da366004610ad4565b61096e565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061012890889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610181575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261017e91810190610c45565b60015b610232573d8080156101af576040519150601f19603f3d011682016040523d82523d6000602084013e6101b4565b606091505b508051600181900361022757816000815181106101d3576101d3610c69565b6020910101517fff00000000000000000000000000000000000000000000000000000000000000167f0100000000000000000000000000000000000000000000000000000000000000149250610235915050565b600092505050610235565b90505b949350505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906102879088908890889088906001908990600401610bc3565b6020604051808303816000875af19250505080156102e0575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682019092526102dd91810190610c45565b60015b610232573d80801561030e576040519150601f19603f3d011682016040523d82523d6000602084013e610313565b606091505b506000915050610235565b600073ffffffffffffffffffffffffffffffffffffffff87163b6060827f64926492649264926492649264926492649264926492649264926492649264928888610369602082610c98565b610375928b9290610cd8565b61037e91610d02565b1490508015610484576000606089828a610399602082610c98565b926103a693929190610cd8565b8101906103b39190610e18565b955090925090508415806103c45750865b1561047d576000808373ffffffffffffffffffffffffffffffffffffffff16836040516103f19190610eb2565b6000604051808303816000865af19150503d806000811461042e576040519150601f19603f3d011682016040523d82523d6000602084013e610433565b606091505b50915091508161047a57806040517f9d0d6e2d0000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b60405180910390fd5b50505b50506104be565b87878080601f0160208091040260200160405190810160405280939291908181526020018383808284376000920191909152509294505050505b80806104ca5750600083115b156106bb576040517f1626ba7e00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8b1690631626ba7e90610523908c908690600401610f2b565b602060405180830381865afa92505050801561057a575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016820190925261057791810190610f44565b60015b61060f573d8080156105a8576040519150601f19603f3d011682016040523d82523d6000602084013e6105ad565b606091505b50851580156105bc5750600084115b156105db576105d08b8b8b8b8b600161031e565b9450505050506108d7565b806040517f6f2a95990000000000000000000000000000000000000000000000000000000081526004016104719190610f18565b7fffffffff0000000000000000000000000000000000000000000000000000000081167f1626ba7e000000000000000000000000000000000000000000000000000000001480158161065f575086155b801561066b5750600085115b1561068b5761067f8c8c8c8c8c600161031e565b955050505050506108d7565b841580156106965750825b80156106a0575087155b156106af57806000526001601ffd5b94506108d79350505050565b6041871461074b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152603a60248201527f5369676e617475726556616c696461746f72237265636f7665725369676e657260448201527f3a20696e76616c6964207369676e6174757265206c656e6774680000000000006064820152608401610471565b600061075a6020828a8c610cd8565b61076391610d02565b90506000610775604060208b8d610cd8565b61077e91610d02565b905060008a8a604081811061079557610795610c69565b919091013560f81c915050601b81148015906107b557508060ff16601c14155b15610842576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602d60248201527f5369676e617475726556616c696461746f723a20696e76616c6964207369676e60448201527f617475726520762076616c7565000000000000000000000000000000000000006064820152608401610471565b6040805160008152602081018083528e905260ff831691810191909152606081018490526080810183905273ffffffffffffffffffffffffffffffffffffffff8e169060019060a0016020604051602081039080840390855afa1580156108ad573d6000803e3d6000fd5b5050506020604051035173ffffffffffffffffffffffffffffffffffffffff161496505050505050505b9695505050505050565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea9061092b9088908890889088906001908990600401610bc3565b6020604051808303816000875af115801561094a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102329190610c45565b6040517f76be4cea00000000000000000000000000000000000000000000000000000000815260009030906376be4cea906109b790889088908890889088908190600401610bc3565b6020604051808303816000875af1925050508015610a10575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610a0d91810190610c45565b60015b610232573d808015610a3e576040519150601f19603f3d011682016040523d82523d6000602084013e610a43565b606091505b5080516001819003610a6257816000815181106101d3576101d3610c69565b8082fd5b73ffffffffffffffffffffffffffffffffffffffff81168114610a8857600080fd5b50565b60008083601f840112610a9d57600080fd5b50813567ffffffffffffffff811115610ab557600080fd5b602083019150836020828501011115610acd57600080fd5b9250929050565b60008060008060608587031215610aea57600080fd5b8435610af581610a66565b935060208501359250604085013567ffffffffffffffff811115610b1857600080fd5b610b2487828801610a8b565b95989497509550505050565b8015158114610a8857600080fd5b60008060008060008060a08789031215610b5757600080fd5b8635610b6281610a66565b955060208701359450604087013567ffffffffffffffff811115610b8557600080fd5b610b9189828a01610a8b565b9095509350506060870135610ba581610b30565b91506080870135610bb581610b30565b809150509295509295509295565b73ffffffffffffffffffffffffffffffffffffffff8716815285602082015260a060408201528360a0820152838560c0830137600060c085830181019190915292151560608201529015156080820152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016909101019392505050565b600060208284031215610c5757600080fd5b8151610c6281610b30565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b81810381811115610cd2577f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b92915050565b60008085851115610ce857600080fd5b83861115610cf557600080fd5b5050820193919092039150565b80356020831015610cd2577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff602084900360031b1b1692915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082601f830112610d7e57600080fd5b813567ffffffffffffffff80821115610d9957610d99610d3e565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715610ddf57610ddf610d3e565b81604052838152866020858801011115610df857600080fd5b836020870160208301376000602085830101528094505050505092915050565b600080600060608486031215610e2d57600080fd5b8335610e3881610a66565b9250602084013567ffffffffffffffff80821115610e5557600080fd5b610e6187838801610d6d565b93506040860135915080821115610e7757600080fd5b50610e8486828701610d6d565b9150509250925092565b60005b83811015610ea9578181015183820152602001610e91565b50506000910152565b60008251610ec4818460208701610e8e565b9190910192915050565b60008151808452610ee6816020860160208601610e8e565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c626020830184610ece565b8281526040602082015260006102356040830184610ece565b600060208284031215610f5657600080fd5b81517fffffffff0000000000000000000000000000000000000000000000000000000081168114610c6257600080fdfea26469706673582212201a72aed4b15ffb05b6502997a9bb655992e06590bd26b336dfbb153d7ff6f34b64736f6c63430008120033'
|
|
8
|
+
|
|
9
|
+
export function deploy<T extends Bytes.Bytes | Hex.Hex>(
|
|
10
|
+
deployHash: T,
|
|
11
|
+
context: Context,
|
|
12
|
+
): { to: Address.Address; data: T } {
|
|
13
|
+
const encoded = AbiFunction.encodeData(DEPLOY, [context.stage1, Hex.from(deployHash)])
|
|
14
|
+
|
|
15
|
+
switch (typeof deployHash) {
|
|
16
|
+
case 'object':
|
|
17
|
+
return { to: context.factory, data: Hex.toBytes(encoded) as T }
|
|
18
|
+
case 'string':
|
|
19
|
+
return { to: context.factory, data: encoded as T }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function wrap<T extends Bytes.Bytes | Hex.Hex>(
|
|
24
|
+
signature: T,
|
|
25
|
+
{ to, data }: { to: Address.Address; data: Bytes.Bytes | Hex.Hex },
|
|
26
|
+
): T {
|
|
27
|
+
const encoded = Hex.concat(
|
|
28
|
+
AbiParameters.encode(
|
|
29
|
+
[{ type: 'address' }, { type: 'bytes' }, { type: 'bytes' }],
|
|
30
|
+
[to, Hex.from(data), Hex.from(signature)],
|
|
31
|
+
),
|
|
32
|
+
WrappedSignature.magicBytes,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
switch (typeof signature) {
|
|
36
|
+
case 'object':
|
|
37
|
+
return Hex.toBytes(encoded) as T
|
|
38
|
+
case 'string':
|
|
39
|
+
return encoded as T
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function decode<T extends Bytes.Bytes | Hex.Hex>(
|
|
44
|
+
signature: T,
|
|
45
|
+
): { signature: T; erc6492?: { to: Address.Address; data: T } } {
|
|
46
|
+
switch (typeof signature) {
|
|
47
|
+
case 'object':
|
|
48
|
+
if (
|
|
49
|
+
Bytes.toHex(signature.subarray(-WrappedSignature.magicBytes.slice(2).length / 2)) ===
|
|
50
|
+
WrappedSignature.magicBytes
|
|
51
|
+
) {
|
|
52
|
+
const [to, data, decoded] = AbiParameters.decode(
|
|
53
|
+
[{ type: 'address' }, { type: 'bytes' }, { type: 'bytes' }],
|
|
54
|
+
signature.subarray(0, -WrappedSignature.magicBytes.slice(2).length / 2),
|
|
55
|
+
)
|
|
56
|
+
return { signature: Hex.toBytes(decoded) as T, erc6492: { to, data: Hex.toBytes(data) as T } }
|
|
57
|
+
} else {
|
|
58
|
+
return { signature }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
case 'string':
|
|
62
|
+
if (signature.endsWith(WrappedSignature.magicBytes.slice(2))) {
|
|
63
|
+
try {
|
|
64
|
+
const [to, data, decoded] = AbiParameters.decode(
|
|
65
|
+
[{ type: 'address' }, { type: 'bytes' }, { type: 'bytes' }],
|
|
66
|
+
signature.slice(0, -WrappedSignature.magicBytes.slice(2).length) as Hex.Hex,
|
|
67
|
+
)
|
|
68
|
+
return { signature: decoded as T, erc6492: { to, data: data as T } }
|
|
69
|
+
} catch {
|
|
70
|
+
return { signature }
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
return { signature }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function isValid(
|
|
79
|
+
address: Address.Address,
|
|
80
|
+
messageHash: Bytes.Bytes | Hex.Hex,
|
|
81
|
+
encodedSignature: Bytes.Bytes | Hex.Hex,
|
|
82
|
+
provider: Provider.Provider,
|
|
83
|
+
): Promise<boolean> {
|
|
84
|
+
// Validate off chain with ERC-6492
|
|
85
|
+
const validationCallData: Hex.Hex = AbiParameters.encode(AbiParameters.from('address, bytes32, bytes'), [
|
|
86
|
+
address,
|
|
87
|
+
Hex.from(messageHash),
|
|
88
|
+
Hex.from(encodedSignature),
|
|
89
|
+
])
|
|
90
|
+
const callData = Hex.concat(EIP_6492_OFFCHAIN_DEPLOY_CODE, validationCallData)
|
|
91
|
+
return provider
|
|
92
|
+
.request({
|
|
93
|
+
method: 'eth_call',
|
|
94
|
+
params: [{ data: callData }, 'latest'],
|
|
95
|
+
})
|
|
96
|
+
.then((result) => parseInt(result, 16) === 1)
|
|
97
|
+
}
|