@0xsequence/guard 2.3.35 → 3.0.0-beta.2
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 +2954 -0
- package/LICENSE +0 -17
- package/README.md +1 -2
- package/dist/{declarations/src → client}/guard.gen.d.ts +47 -6
- package/dist/client/guard.gen.d.ts.map +1 -0
- package/dist/client/guard.gen.js +552 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/local.d.ts +14 -0
- package/dist/local.d.ts.map +1 -0
- package/dist/local.js +13 -0
- package/dist/sequence.d.ts +14 -0
- package/dist/sequence.d.ts.map +1 -0
- package/dist/sequence.js +44 -0
- package/dist/types.d.ts +11 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/package.json +23 -19
- package/src/{guard.gen.ts → client/guard.gen.ts} +190 -117
- package/src/index.ts +6 -2
- package/src/local.ts +23 -0
- package/src/sequence.ts +56 -0
- package/src/types.ts +27 -0
- package/test/sequence.test.ts +189 -0
- package/tsconfig.json +10 -0
- package/dist/0xsequence-guard.cjs.d.ts +0 -2
- package/dist/0xsequence-guard.cjs.dev.js +0 -768
- package/dist/0xsequence-guard.cjs.js +0 -7
- package/dist/0xsequence-guard.cjs.prod.js +0 -768
- package/dist/0xsequence-guard.esm.js +0 -758
- package/dist/declarations/src/index.d.ts +0 -2
- package/dist/declarations/src/signer.d.ts +0 -66
- package/src/signer.ts +0 -308
package/src/index.ts
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export * from './types.js'
|
|
2
|
+
export { PayloadType, SignatureType, type Signature } from './client/guard.gen.js'
|
|
3
|
+
|
|
4
|
+
export * as Client from './client/guard.gen.js'
|
|
5
|
+
export * as Sequence from './sequence.js'
|
|
6
|
+
export * as Local from './local.js'
|
package/src/local.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Address, Hex, Bytes, Secp256k1, Hash } from 'ox'
|
|
2
|
+
import * as Client from './client/guard.gen.js'
|
|
3
|
+
import * as Types from './types.js'
|
|
4
|
+
|
|
5
|
+
export class Guard implements Types.Guard {
|
|
6
|
+
public readonly address: Address.Address
|
|
7
|
+
|
|
8
|
+
constructor(private readonly privateKey: Hex.Hex) {
|
|
9
|
+
const publicKey = Secp256k1.getPublicKey({ privateKey: this.privateKey })
|
|
10
|
+
this.address = Address.fromPublicKey(publicKey)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async signPayload(
|
|
14
|
+
wallet: Address.Address,
|
|
15
|
+
chainId: number,
|
|
16
|
+
type: Client.PayloadType,
|
|
17
|
+
digest: Bytes.Bytes,
|
|
18
|
+
message: Bytes.Bytes,
|
|
19
|
+
signatures?: Client.Signature[],
|
|
20
|
+
) {
|
|
21
|
+
return Secp256k1.sign({ privateKey: this.privateKey, payload: digest })
|
|
22
|
+
}
|
|
23
|
+
}
|
package/src/sequence.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Address, Hex, Signature, Bytes, Hash } from 'ox'
|
|
2
|
+
import * as Client from './client/guard.gen.js'
|
|
3
|
+
import * as Types from './types.js'
|
|
4
|
+
|
|
5
|
+
export class Guard implements Types.Guard {
|
|
6
|
+
private readonly guard?: Client.Guard
|
|
7
|
+
public readonly address: Address.Address
|
|
8
|
+
|
|
9
|
+
constructor(hostname: string, address: Address.Address, fetch?: Client.Fetch) {
|
|
10
|
+
if (hostname && address) {
|
|
11
|
+
this.guard = new Client.Guard(hostname, fetch ?? window.fetch)
|
|
12
|
+
}
|
|
13
|
+
this.address = address
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
async signPayload(
|
|
17
|
+
wallet: Address.Address,
|
|
18
|
+
chainId: number,
|
|
19
|
+
type: Client.PayloadType,
|
|
20
|
+
digest: Bytes.Bytes,
|
|
21
|
+
message: Bytes.Bytes,
|
|
22
|
+
signatures?: Client.Signature[],
|
|
23
|
+
token?: Client.AuthToken,
|
|
24
|
+
) {
|
|
25
|
+
if (!this.guard || !this.address) {
|
|
26
|
+
throw new Error('Guard not initialized')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
const res = await this.guard.signWith({
|
|
31
|
+
signer: this.address,
|
|
32
|
+
request: {
|
|
33
|
+
chainId: chainId,
|
|
34
|
+
msg: Hex.fromBytes(digest),
|
|
35
|
+
wallet,
|
|
36
|
+
payloadType: type,
|
|
37
|
+
payloadData: Hex.fromBytes(message),
|
|
38
|
+
signatures,
|
|
39
|
+
},
|
|
40
|
+
token,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
Hex.assert(res.sig)
|
|
44
|
+
return Signature.fromHex(res.sig)
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (error instanceof Client.RequiresTOTPError) {
|
|
47
|
+
throw new Types.AuthRequiredError('TOTP')
|
|
48
|
+
}
|
|
49
|
+
if (error instanceof Client.RequiresPINError) {
|
|
50
|
+
throw new Types.AuthRequiredError('PIN')
|
|
51
|
+
}
|
|
52
|
+
console.error(error)
|
|
53
|
+
throw new Error('Error signing with guard')
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Address, Bytes, Signature } from 'ox'
|
|
2
|
+
import * as Client from './client/guard.gen.js'
|
|
3
|
+
|
|
4
|
+
export interface Guard {
|
|
5
|
+
readonly address: Address.Address
|
|
6
|
+
|
|
7
|
+
signPayload(
|
|
8
|
+
wallet: Address.Address,
|
|
9
|
+
chainId: number,
|
|
10
|
+
type: Client.PayloadType,
|
|
11
|
+
digest: Bytes.Bytes,
|
|
12
|
+
message: Bytes.Bytes,
|
|
13
|
+
signatures?: Client.Signature[],
|
|
14
|
+
token?: Client.AuthToken,
|
|
15
|
+
): Promise<Signature.Signature>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class AuthRequiredError extends Error {
|
|
19
|
+
public readonly id: 'TOTP' | 'PIN'
|
|
20
|
+
|
|
21
|
+
constructor(id: 'TOTP' | 'PIN') {
|
|
22
|
+
super('auth required')
|
|
23
|
+
this.id = id
|
|
24
|
+
this.name = 'AuthRequiredError'
|
|
25
|
+
Object.setPrototypeOf(this, AuthRequiredError.prototype)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { Guard } from '../src/sequence'
|
|
3
|
+
import { PayloadType } from '../src/client/guard.gen'
|
|
4
|
+
import { Address, Bytes, Hex } from 'ox'
|
|
5
|
+
|
|
6
|
+
// Mock fetch globally for guard API calls
|
|
7
|
+
const mockFetch = vi.fn()
|
|
8
|
+
global.fetch = mockFetch
|
|
9
|
+
|
|
10
|
+
describe('Sequence', () => {
|
|
11
|
+
describe('GuardSigner', () => {
|
|
12
|
+
let guard: Guard
|
|
13
|
+
let testWallet: Address.Address
|
|
14
|
+
let testMessage: Bytes.Bytes
|
|
15
|
+
let testMessageDigest: Bytes.Bytes
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vi.clearAllMocks()
|
|
19
|
+
guard = new Guard('https://guard.sequence.app', '0xaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeae', fetch)
|
|
20
|
+
testWallet = '0x1234567890123456789012345678901234567890' as Address.Address
|
|
21
|
+
testMessage = Bytes.fromString('Test message')
|
|
22
|
+
testMessageDigest = Bytes.fromHex('0x1234567890abcdef1234567890abcdef1234567890')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
vi.resetAllMocks()
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
describe('sign()', () => {
|
|
30
|
+
it('Should successfully sign a payload with guard service', async () => {
|
|
31
|
+
const mockSignature =
|
|
32
|
+
'0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b'
|
|
33
|
+
|
|
34
|
+
mockFetch.mockResolvedValueOnce({
|
|
35
|
+
json: async () => ({
|
|
36
|
+
sig: mockSignature,
|
|
37
|
+
}),
|
|
38
|
+
text: async () =>
|
|
39
|
+
JSON.stringify({
|
|
40
|
+
sig: mockSignature,
|
|
41
|
+
}),
|
|
42
|
+
ok: true,
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
const result = await guard.signPayload(
|
|
46
|
+
testWallet,
|
|
47
|
+
42161,
|
|
48
|
+
PayloadType.ConfigUpdate,
|
|
49
|
+
testMessageDigest,
|
|
50
|
+
testMessage,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
expect(result).toBeDefined()
|
|
54
|
+
expect(result.r).toBeDefined()
|
|
55
|
+
expect(result.s).toBeDefined()
|
|
56
|
+
expect(result.yParity).toBeDefined()
|
|
57
|
+
|
|
58
|
+
// Verify API call was made correctly
|
|
59
|
+
expect(mockFetch).toHaveBeenCalledOnce()
|
|
60
|
+
const [url, options] = mockFetch.mock.calls[0]
|
|
61
|
+
|
|
62
|
+
expect(url).toContain('/rpc/Guard/SignWith')
|
|
63
|
+
expect(options.method).toBe('POST')
|
|
64
|
+
expect(options.headers['Content-Type']).toBe('application/json')
|
|
65
|
+
|
|
66
|
+
const requestBody = JSON.parse(options.body)
|
|
67
|
+
expect(requestBody.signer).toBe('0xaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeaeae')
|
|
68
|
+
expect(requestBody.request.chainId).toBe(42161)
|
|
69
|
+
expect(requestBody.request.msg).toBe(Hex.fromBytes(testMessageDigest).toString())
|
|
70
|
+
expect(requestBody.request.payloadType).toBe(PayloadType.ConfigUpdate)
|
|
71
|
+
expect(requestBody.request.payloadData).toBe(Hex.fromBytes(testMessage).toString())
|
|
72
|
+
expect(requestBody.request.wallet).toBe(testWallet)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('Should handle custom chainId in sign request', async () => {
|
|
76
|
+
const customChainId = 1 // Ethereum mainnet
|
|
77
|
+
|
|
78
|
+
mockFetch.mockResolvedValueOnce({
|
|
79
|
+
json: async () => ({
|
|
80
|
+
sig: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b',
|
|
81
|
+
}),
|
|
82
|
+
text: async () =>
|
|
83
|
+
JSON.stringify({
|
|
84
|
+
sig: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b',
|
|
85
|
+
}),
|
|
86
|
+
ok: true,
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
await guard.signPayload(testWallet, 1, PayloadType.ConfigUpdate, testMessageDigest, testMessage)
|
|
90
|
+
|
|
91
|
+
const requestBody = JSON.parse(mockFetch.mock.calls[0][1].body)
|
|
92
|
+
expect(requestBody.request.chainId).toBe(1)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('Should throw error when guard service fails', async () => {
|
|
96
|
+
mockFetch.mockRejectedValueOnce(new Error('Network error'))
|
|
97
|
+
|
|
98
|
+
await expect(
|
|
99
|
+
guard.signPayload(testWallet, 42161, PayloadType.ConfigUpdate, testMessageDigest, testMessage),
|
|
100
|
+
).rejects.toThrow('Error signing with guard')
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('Should throw error when guard service returns invalid response', async () => {
|
|
104
|
+
mockFetch.mockResolvedValueOnce({
|
|
105
|
+
json: async () => {
|
|
106
|
+
throw new Error('Invalid JSON')
|
|
107
|
+
},
|
|
108
|
+
text: async () => {
|
|
109
|
+
throw new Error('Invalid JSON')
|
|
110
|
+
},
|
|
111
|
+
ok: true,
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
await expect(
|
|
115
|
+
guard.signPayload(testWallet, 42161, PayloadType.ConfigUpdate, testMessageDigest, testMessage),
|
|
116
|
+
).rejects.toThrow('Error signing with guard')
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('Should include proper headers and signer address in request', async () => {
|
|
120
|
+
const mockGuardAddress = '0x9876543210987654321098765432109876543210' as Address.Address
|
|
121
|
+
const customGuard = new Guard('https://guard.sequence.app', mockGuardAddress, fetch)
|
|
122
|
+
|
|
123
|
+
mockFetch.mockResolvedValueOnce({
|
|
124
|
+
json: async () => ({
|
|
125
|
+
sig: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b',
|
|
126
|
+
}),
|
|
127
|
+
text: async () =>
|
|
128
|
+
JSON.stringify({
|
|
129
|
+
sig: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1b',
|
|
130
|
+
}),
|
|
131
|
+
ok: true,
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
await customGuard.signPayload(testWallet, 42161, PayloadType.ConfigUpdate, testMessageDigest, testMessage)
|
|
135
|
+
|
|
136
|
+
const requestBody = JSON.parse(mockFetch.mock.calls[0][1].body)
|
|
137
|
+
expect(requestBody.signer).toBe(mockGuardAddress)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
describe('Error Handling', () => {
|
|
141
|
+
it('Should handle malformed guard service response', async () => {
|
|
142
|
+
mockFetch.mockResolvedValueOnce({
|
|
143
|
+
json: async () => ({
|
|
144
|
+
// Missing 'sig' field
|
|
145
|
+
error: 'Invalid request',
|
|
146
|
+
}),
|
|
147
|
+
text: async () =>
|
|
148
|
+
JSON.stringify({
|
|
149
|
+
error: 'Invalid request',
|
|
150
|
+
}),
|
|
151
|
+
ok: true,
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
await expect(
|
|
155
|
+
guard.signPayload(testWallet, 42161, PayloadType.ConfigUpdate, testMessageDigest, testMessage),
|
|
156
|
+
).rejects.toThrow('Error signing with guard')
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
it('Should handle network timeout errors', async () => {
|
|
160
|
+
mockFetch.mockImplementationOnce(
|
|
161
|
+
() => new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 100)),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
await expect(
|
|
165
|
+
guard.signPayload(testWallet, 42161, PayloadType.ConfigUpdate, testMessageDigest, testMessage),
|
|
166
|
+
).rejects.toThrow('Error signing with guard')
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
it('Should handle HTTP error responses', async () => {
|
|
170
|
+
mockFetch.mockResolvedValueOnce({
|
|
171
|
+
ok: false,
|
|
172
|
+
status: 500,
|
|
173
|
+
json: async () => ({
|
|
174
|
+
error: 'Internal server error',
|
|
175
|
+
}),
|
|
176
|
+
text: async () =>
|
|
177
|
+
JSON.stringify({
|
|
178
|
+
error: 'Internal server error',
|
|
179
|
+
}),
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
await expect(
|
|
183
|
+
guard.signPayload(testWallet, 42161, PayloadType.ConfigUpdate, testMessageDigest, testMessage),
|
|
184
|
+
).rejects.toThrow('Error signing with guard')
|
|
185
|
+
})
|
|
186
|
+
})
|
|
187
|
+
})
|
|
188
|
+
})
|
|
189
|
+
})
|
package/tsconfig.json
ADDED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
export * from "./declarations/src/index.js";
|
|
2
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMHhzZXF1ZW5jZS1ndWFyZC5janMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4vZGVjbGFyYXRpb25zL3NyYy9pbmRleC5kLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBIn0=
|