@pay-skill/sdk 0.1.1 → 0.1.3
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/README.md +154 -154
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +2 -2
- package/dist/auth.js.map +1 -1
- package/dist/client.d.ts +8 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +106 -54
- package/dist/client.js.map +1 -1
- package/dist/eip3009.d.ts +24 -0
- package/dist/eip3009.d.ts.map +1 -0
- package/dist/eip3009.js +56 -0
- package/dist/eip3009.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +35 -26
- package/dist/models.d.ts.map +1 -1
- package/dist/signer.d.ts +1 -1
- package/dist/signer.d.ts.map +1 -1
- package/dist/signer.js +1 -2
- package/dist/signer.js.map +1 -1
- package/dist/wallet.d.ts.map +1 -1
- package/dist/wallet.js +6 -3
- package/dist/wallet.js.map +1 -1
- package/jsr.json +12 -0
- package/package.json +48 -44
- package/src/auth.ts +200 -200
- package/src/client.ts +644 -644
- package/src/eip3009.ts +79 -79
- package/src/index.ts +51 -51
- package/src/models.ts +77 -77
- package/src/ows-signer.ts +223 -223
- package/src/signer.ts +147 -147
- package/src/wallet.ts +445 -445
- package/tests/test_auth_rejection.ts +154 -154
- package/tests/test_crypto.ts +251 -251
- package/tests/test_e2e.ts +158 -158
- package/tests/test_ows_integration.ts +92 -92
- package/tests/test_ows_signer.ts +365 -365
- package/tests/test_validation.ts +66 -66
package/src/ows-signer.ts
CHANGED
|
@@ -1,223 +1,223 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OWS (Open Wallet Standard) signer adapter.
|
|
3
|
-
*
|
|
4
|
-
* Wraps the @open-wallet-standard/core FFI module to implement the Pay
|
|
5
|
-
* Signer interface. OWS handles encrypted key storage + policy-gated signing;
|
|
6
|
-
* this adapter translates between Pay's (domain, types, value) EIP-712
|
|
7
|
-
* calling convention and OWS's JSON string convention.
|
|
8
|
-
*
|
|
9
|
-
* Priority: Pay's own CLI signer is #1, OWS is #2. OWS only activates when
|
|
10
|
-
* OWS_WALLET_ID is set AND @open-wallet-standard/core is installed. If the
|
|
11
|
-
* env var is set but the package is missing, creation fails loud with
|
|
12
|
-
* install instructions.
|
|
13
|
-
*
|
|
14
|
-
* Usage:
|
|
15
|
-
* const signer = await OwsSigner.create({ walletId: "pay-my-agent" });
|
|
16
|
-
* const wallet = new Wallet({ signer });
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import type { Signer } from "./signer.js";
|
|
20
|
-
|
|
21
|
-
/** Subset of @open-wallet-standard/core we call at runtime. */
|
|
22
|
-
interface OwsModule {
|
|
23
|
-
getWallet(nameOrId: string, vaultPath?: string): {
|
|
24
|
-
id: string;
|
|
25
|
-
name: string;
|
|
26
|
-
accounts: Array<{
|
|
27
|
-
chainId: string;
|
|
28
|
-
address: string;
|
|
29
|
-
derivationPath: string;
|
|
30
|
-
}>;
|
|
31
|
-
createdAt: string;
|
|
32
|
-
};
|
|
33
|
-
signTypedData(
|
|
34
|
-
wallet: string,
|
|
35
|
-
chain: string,
|
|
36
|
-
typedDataJson: string,
|
|
37
|
-
passphrase?: string,
|
|
38
|
-
index?: number,
|
|
39
|
-
vaultPath?: string,
|
|
40
|
-
): { signature: string; recoveryId?: number };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** EIP-712 domain fields. */
|
|
44
|
-
export interface TypedDataDomain {
|
|
45
|
-
name?: string;
|
|
46
|
-
version?: string;
|
|
47
|
-
chainId?: number | bigint;
|
|
48
|
-
verifyingContract?: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/** EIP-712 type definitions. */
|
|
52
|
-
export interface TypedDataTypes {
|
|
53
|
-
[typeName: string]: Array<{ name: string; type: string }>;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/** Options for {@link OwsSigner.create}. */
|
|
57
|
-
export interface OwsSignerOptions {
|
|
58
|
-
/** OWS wallet name or UUID (e.g. "pay-my-agent"). */
|
|
59
|
-
walletId: string;
|
|
60
|
-
/** Chain name - only used for logging, not passed to OWS. Default: "base". */
|
|
61
|
-
chain?: string;
|
|
62
|
-
/** OWS API key token (passed as passphrase to OWS signing calls). */
|
|
63
|
-
owsApiKey?: string;
|
|
64
|
-
/** @internal Inject OWS module for testing - bypasses dynamic import. */
|
|
65
|
-
_owsModule?: unknown;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Signer backed by the Open Wallet Standard.
|
|
70
|
-
*
|
|
71
|
-
* - Keys live in OWS's encrypted vault (~/.ows/wallets/), never in env vars.
|
|
72
|
-
* - Signing calls go through OWS FFI, which evaluates policy rules before signing.
|
|
73
|
-
* - Use the static {@link create} factory - the constructor is private because
|
|
74
|
-
* address resolution requires a synchronous FFI call that must happen before
|
|
75
|
-
* sign() can work.
|
|
76
|
-
*/
|
|
77
|
-
export class OwsSigner implements Signer {
|
|
78
|
-
readonly #walletId: string;
|
|
79
|
-
readonly #address: string;
|
|
80
|
-
readonly #owsApiKey: string | undefined;
|
|
81
|
-
readonly #ows: OwsModule;
|
|
82
|
-
|
|
83
|
-
private constructor(
|
|
84
|
-
walletId: string,
|
|
85
|
-
address: string,
|
|
86
|
-
owsModule: OwsModule,
|
|
87
|
-
owsApiKey?: string,
|
|
88
|
-
) {
|
|
89
|
-
this.#walletId = walletId;
|
|
90
|
-
this.#address = address;
|
|
91
|
-
this.#ows = owsModule;
|
|
92
|
-
this.#owsApiKey = owsApiKey;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Create an OwsSigner by resolving the wallet address from OWS.
|
|
97
|
-
*
|
|
98
|
-
* Lazily imports @open-wallet-standard/core so the module is only required
|
|
99
|
-
* when OWS is actually used (keeps it an optional peer dependency).
|
|
100
|
-
*/
|
|
101
|
-
static async create(options: OwsSignerOptions): Promise<OwsSigner> {
|
|
102
|
-
let owsModule: OwsModule;
|
|
103
|
-
if (options._owsModule) {
|
|
104
|
-
owsModule = options._owsModule as OwsModule;
|
|
105
|
-
} else {
|
|
106
|
-
try {
|
|
107
|
-
// Dynamic string prevents TypeScript from statically resolving this
|
|
108
|
-
// optional peer dependency at compile time.
|
|
109
|
-
const moduleName = "@open-wallet-standard/core";
|
|
110
|
-
owsModule = (await import(moduleName)) as unknown as OwsModule;
|
|
111
|
-
} catch {
|
|
112
|
-
throw new Error(
|
|
113
|
-
"OWS_WALLET_ID is set but @open-wallet-standard/core is not installed. " +
|
|
114
|
-
"Install it with: npm install @open-wallet-standard/core",
|
|
115
|
-
);
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const walletInfo = owsModule.getWallet(options.walletId);
|
|
120
|
-
const evmAccount = walletInfo.accounts.find(
|
|
121
|
-
(a) => a.chainId === "evm" || a.chainId.startsWith("eip155:"),
|
|
122
|
-
);
|
|
123
|
-
if (!evmAccount) {
|
|
124
|
-
throw new Error(
|
|
125
|
-
`No EVM account found in OWS wallet '${options.walletId}'. ` +
|
|
126
|
-
`Available chains: ${walletInfo.accounts.map((a) => a.chainId).join(", ") || "none"}.`,
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return new OwsSigner(
|
|
131
|
-
options.walletId,
|
|
132
|
-
evmAccount.address,
|
|
133
|
-
owsModule,
|
|
134
|
-
options.owsApiKey,
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
get address(): string {
|
|
139
|
-
return this.#address;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
sign(_hash: Uint8Array): Uint8Array {
|
|
143
|
-
throw new Error(
|
|
144
|
-
"OwsSigner does not support raw hash signing. " +
|
|
145
|
-
"OWS only supports EIP-712 typed data signing. " +
|
|
146
|
-
"Use CliSigner or RawKeySigner for operations that require sign().",
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Sign EIP-712 typed data via OWS FFI.
|
|
152
|
-
*
|
|
153
|
-
* This is the primary signing method for OWS. Pay's auth module should
|
|
154
|
-
* call this instead of sign() when an OwsSigner is detected.
|
|
155
|
-
*/
|
|
156
|
-
async signTypedData(
|
|
157
|
-
domain: TypedDataDomain,
|
|
158
|
-
types: TypedDataTypes,
|
|
159
|
-
value: Record<string, unknown>,
|
|
160
|
-
): Promise<string> {
|
|
161
|
-
// 1. Derive primaryType: first key in types that is NOT "EIP712Domain".
|
|
162
|
-
const primaryType =
|
|
163
|
-
Object.keys(types).filter((k) => k !== "EIP712Domain")[0] ?? "Request";
|
|
164
|
-
|
|
165
|
-
// 2. Build EIP712Domain type array dynamically from domain object fields.
|
|
166
|
-
const eip712DomainType: Array<{ name: string; type: string }> = [];
|
|
167
|
-
if (domain.name !== undefined)
|
|
168
|
-
eip712DomainType.push({ name: "name", type: "string" });
|
|
169
|
-
if (domain.version !== undefined)
|
|
170
|
-
eip712DomainType.push({ name: "version", type: "string" });
|
|
171
|
-
if (domain.chainId !== undefined)
|
|
172
|
-
eip712DomainType.push({ name: "chainId", type: "uint256" });
|
|
173
|
-
if (domain.verifyingContract !== undefined)
|
|
174
|
-
eip712DomainType.push({
|
|
175
|
-
name: "verifyingContract",
|
|
176
|
-
type: "address",
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
// 3. Assemble full EIP-712 typed data structure.
|
|
180
|
-
const fullTypedData = {
|
|
181
|
-
types: { EIP712Domain: eip712DomainType, ...types },
|
|
182
|
-
primaryType,
|
|
183
|
-
domain,
|
|
184
|
-
message: value,
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
// 4. Serialize - handle BigInt values.
|
|
188
|
-
const json = JSON.stringify(fullTypedData, (_key, v) =>
|
|
189
|
-
typeof v === "bigint" ? v.toString() : (v as unknown),
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
// 5. Call OWS FFI. Chain is always "evm" for EVM signing.
|
|
193
|
-
const result = this.#ows.signTypedData(
|
|
194
|
-
this.#walletId,
|
|
195
|
-
"evm",
|
|
196
|
-
json,
|
|
197
|
-
this.#owsApiKey,
|
|
198
|
-
);
|
|
199
|
-
|
|
200
|
-
// 6. Concatenate r+s+v into 65-byte Ethereum signature.
|
|
201
|
-
const sig = result.signature.startsWith("0x")
|
|
202
|
-
? result.signature.slice(2)
|
|
203
|
-
: result.signature;
|
|
204
|
-
|
|
205
|
-
// If OWS already returns r+s+v (130 hex chars), use as-is.
|
|
206
|
-
if (sig.length === 130) {
|
|
207
|
-
return `0x${sig}`;
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
// Otherwise it's r+s (128 hex chars), append v.
|
|
211
|
-
const v = (result.recoveryId ?? 0) + 27;
|
|
212
|
-
return `0x${sig}${v.toString(16).padStart(2, "0")}`;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/** Prevent wallet ID / key leakage in serialization. */
|
|
216
|
-
toJSON(): Record<string, string> {
|
|
217
|
-
return { address: this.#address, walletId: this.#walletId };
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
[Symbol.for("nodejs.util.inspect.custom")](): string {
|
|
221
|
-
return `OwsSigner { address: '${this.#address}', wallet: '${this.#walletId}' }`;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* OWS (Open Wallet Standard) signer adapter.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the @open-wallet-standard/core FFI module to implement the Pay
|
|
5
|
+
* Signer interface. OWS handles encrypted key storage + policy-gated signing;
|
|
6
|
+
* this adapter translates between Pay's (domain, types, value) EIP-712
|
|
7
|
+
* calling convention and OWS's JSON string convention.
|
|
8
|
+
*
|
|
9
|
+
* Priority: Pay's own CLI signer is #1, OWS is #2. OWS only activates when
|
|
10
|
+
* OWS_WALLET_ID is set AND @open-wallet-standard/core is installed. If the
|
|
11
|
+
* env var is set but the package is missing, creation fails loud with
|
|
12
|
+
* install instructions.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* const signer = await OwsSigner.create({ walletId: "pay-my-agent" });
|
|
16
|
+
* const wallet = new Wallet({ signer });
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { Signer } from "./signer.js";
|
|
20
|
+
|
|
21
|
+
/** Subset of @open-wallet-standard/core we call at runtime. */
|
|
22
|
+
interface OwsModule {
|
|
23
|
+
getWallet(nameOrId: string, vaultPath?: string): {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
accounts: Array<{
|
|
27
|
+
chainId: string;
|
|
28
|
+
address: string;
|
|
29
|
+
derivationPath: string;
|
|
30
|
+
}>;
|
|
31
|
+
createdAt: string;
|
|
32
|
+
};
|
|
33
|
+
signTypedData(
|
|
34
|
+
wallet: string,
|
|
35
|
+
chain: string,
|
|
36
|
+
typedDataJson: string,
|
|
37
|
+
passphrase?: string,
|
|
38
|
+
index?: number,
|
|
39
|
+
vaultPath?: string,
|
|
40
|
+
): { signature: string; recoveryId?: number };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** EIP-712 domain fields. */
|
|
44
|
+
export interface TypedDataDomain {
|
|
45
|
+
name?: string;
|
|
46
|
+
version?: string;
|
|
47
|
+
chainId?: number | bigint;
|
|
48
|
+
verifyingContract?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** EIP-712 type definitions. */
|
|
52
|
+
export interface TypedDataTypes {
|
|
53
|
+
[typeName: string]: Array<{ name: string; type: string }>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Options for {@link OwsSigner.create}. */
|
|
57
|
+
export interface OwsSignerOptions {
|
|
58
|
+
/** OWS wallet name or UUID (e.g. "pay-my-agent"). */
|
|
59
|
+
walletId: string;
|
|
60
|
+
/** Chain name - only used for logging, not passed to OWS. Default: "base". */
|
|
61
|
+
chain?: string;
|
|
62
|
+
/** OWS API key token (passed as passphrase to OWS signing calls). */
|
|
63
|
+
owsApiKey?: string;
|
|
64
|
+
/** @internal Inject OWS module for testing - bypasses dynamic import. */
|
|
65
|
+
_owsModule?: unknown;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Signer backed by the Open Wallet Standard.
|
|
70
|
+
*
|
|
71
|
+
* - Keys live in OWS's encrypted vault (~/.ows/wallets/), never in env vars.
|
|
72
|
+
* - Signing calls go through OWS FFI, which evaluates policy rules before signing.
|
|
73
|
+
* - Use the static {@link create} factory - the constructor is private because
|
|
74
|
+
* address resolution requires a synchronous FFI call that must happen before
|
|
75
|
+
* sign() can work.
|
|
76
|
+
*/
|
|
77
|
+
export class OwsSigner implements Signer {
|
|
78
|
+
readonly #walletId: string;
|
|
79
|
+
readonly #address: string;
|
|
80
|
+
readonly #owsApiKey: string | undefined;
|
|
81
|
+
readonly #ows: OwsModule;
|
|
82
|
+
|
|
83
|
+
private constructor(
|
|
84
|
+
walletId: string,
|
|
85
|
+
address: string,
|
|
86
|
+
owsModule: OwsModule,
|
|
87
|
+
owsApiKey?: string,
|
|
88
|
+
) {
|
|
89
|
+
this.#walletId = walletId;
|
|
90
|
+
this.#address = address;
|
|
91
|
+
this.#ows = owsModule;
|
|
92
|
+
this.#owsApiKey = owsApiKey;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Create an OwsSigner by resolving the wallet address from OWS.
|
|
97
|
+
*
|
|
98
|
+
* Lazily imports @open-wallet-standard/core so the module is only required
|
|
99
|
+
* when OWS is actually used (keeps it an optional peer dependency).
|
|
100
|
+
*/
|
|
101
|
+
static async create(options: OwsSignerOptions): Promise<OwsSigner> {
|
|
102
|
+
let owsModule: OwsModule;
|
|
103
|
+
if (options._owsModule) {
|
|
104
|
+
owsModule = options._owsModule as OwsModule;
|
|
105
|
+
} else {
|
|
106
|
+
try {
|
|
107
|
+
// Dynamic string prevents TypeScript from statically resolving this
|
|
108
|
+
// optional peer dependency at compile time.
|
|
109
|
+
const moduleName = "@open-wallet-standard/core";
|
|
110
|
+
owsModule = (await import(moduleName)) as unknown as OwsModule;
|
|
111
|
+
} catch {
|
|
112
|
+
throw new Error(
|
|
113
|
+
"OWS_WALLET_ID is set but @open-wallet-standard/core is not installed. " +
|
|
114
|
+
"Install it with: npm install @open-wallet-standard/core",
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const walletInfo = owsModule.getWallet(options.walletId);
|
|
120
|
+
const evmAccount = walletInfo.accounts.find(
|
|
121
|
+
(a) => a.chainId === "evm" || a.chainId.startsWith("eip155:"),
|
|
122
|
+
);
|
|
123
|
+
if (!evmAccount) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`No EVM account found in OWS wallet '${options.walletId}'. ` +
|
|
126
|
+
`Available chains: ${walletInfo.accounts.map((a) => a.chainId).join(", ") || "none"}.`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return new OwsSigner(
|
|
131
|
+
options.walletId,
|
|
132
|
+
evmAccount.address,
|
|
133
|
+
owsModule,
|
|
134
|
+
options.owsApiKey,
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
get address(): string {
|
|
139
|
+
return this.#address;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
sign(_hash: Uint8Array): Uint8Array {
|
|
143
|
+
throw new Error(
|
|
144
|
+
"OwsSigner does not support raw hash signing. " +
|
|
145
|
+
"OWS only supports EIP-712 typed data signing. " +
|
|
146
|
+
"Use CliSigner or RawKeySigner for operations that require sign().",
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Sign EIP-712 typed data via OWS FFI.
|
|
152
|
+
*
|
|
153
|
+
* This is the primary signing method for OWS. Pay's auth module should
|
|
154
|
+
* call this instead of sign() when an OwsSigner is detected.
|
|
155
|
+
*/
|
|
156
|
+
async signTypedData(
|
|
157
|
+
domain: TypedDataDomain,
|
|
158
|
+
types: TypedDataTypes,
|
|
159
|
+
value: Record<string, unknown>,
|
|
160
|
+
): Promise<string> {
|
|
161
|
+
// 1. Derive primaryType: first key in types that is NOT "EIP712Domain".
|
|
162
|
+
const primaryType =
|
|
163
|
+
Object.keys(types).filter((k) => k !== "EIP712Domain")[0] ?? "Request";
|
|
164
|
+
|
|
165
|
+
// 2. Build EIP712Domain type array dynamically from domain object fields.
|
|
166
|
+
const eip712DomainType: Array<{ name: string; type: string }> = [];
|
|
167
|
+
if (domain.name !== undefined)
|
|
168
|
+
eip712DomainType.push({ name: "name", type: "string" });
|
|
169
|
+
if (domain.version !== undefined)
|
|
170
|
+
eip712DomainType.push({ name: "version", type: "string" });
|
|
171
|
+
if (domain.chainId !== undefined)
|
|
172
|
+
eip712DomainType.push({ name: "chainId", type: "uint256" });
|
|
173
|
+
if (domain.verifyingContract !== undefined)
|
|
174
|
+
eip712DomainType.push({
|
|
175
|
+
name: "verifyingContract",
|
|
176
|
+
type: "address",
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// 3. Assemble full EIP-712 typed data structure.
|
|
180
|
+
const fullTypedData = {
|
|
181
|
+
types: { EIP712Domain: eip712DomainType, ...types },
|
|
182
|
+
primaryType,
|
|
183
|
+
domain,
|
|
184
|
+
message: value,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// 4. Serialize - handle BigInt values.
|
|
188
|
+
const json = JSON.stringify(fullTypedData, (_key, v) =>
|
|
189
|
+
typeof v === "bigint" ? v.toString() : (v as unknown),
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
// 5. Call OWS FFI. Chain is always "evm" for EVM signing.
|
|
193
|
+
const result = this.#ows.signTypedData(
|
|
194
|
+
this.#walletId,
|
|
195
|
+
"evm",
|
|
196
|
+
json,
|
|
197
|
+
this.#owsApiKey,
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
// 6. Concatenate r+s+v into 65-byte Ethereum signature.
|
|
201
|
+
const sig = result.signature.startsWith("0x")
|
|
202
|
+
? result.signature.slice(2)
|
|
203
|
+
: result.signature;
|
|
204
|
+
|
|
205
|
+
// If OWS already returns r+s+v (130 hex chars), use as-is.
|
|
206
|
+
if (sig.length === 130) {
|
|
207
|
+
return `0x${sig}`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Otherwise it's r+s (128 hex chars), append v.
|
|
211
|
+
const v = (result.recoveryId ?? 0) + 27;
|
|
212
|
+
return `0x${sig}${v.toString(16).padStart(2, "0")}`;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Prevent wallet ID / key leakage in serialization. */
|
|
216
|
+
toJSON(): Record<string, string> {
|
|
217
|
+
return { address: this.#address, walletId: this.#walletId };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
[Symbol.for("nodejs.util.inspect.custom")](): string {
|
|
221
|
+
return `OwsSigner { address: '${this.#address}', wallet: '${this.#walletId}' }`;
|
|
222
|
+
}
|
|
223
|
+
}
|