-
Notifications
You must be signed in to change notification settings - Fork 16
Expand file tree
/
Copy pathadapters.ts
More file actions
170 lines (161 loc) · 5.83 KB
/
Copy pathadapters.ts
File metadata and controls
170 lines (161 loc) · 5.83 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
162
163
164
165
166
167
168
169
170
import { privateKeyToAddress, signHash, signTypedData } from "../ethereUtils";
import type { ExternalSigner, TypedData } from "./types";
// Structural types for well-known signers. NO imports from viem / ethers
// at the type level (beyond the already-present ethers runtime dep used by
// fromPrivateKey); these shapes match their public APIs so users can pass
// an instance directly.
/**
* Shape matching viem's `PrivateKeyAccount` / `LocalAccount`.
*
* @remarks Requires viem >= 2.0 (the `sign({ hash })` method was added in
* the 2.0 account refactor; viem 1.x errors structurally).
* @internal Pass concrete viem instances to {@link fromViem}. For wrapper
* typing, use `Parameters<typeof fromViem>[0]`.
*/
export interface ViemLocalAccountLike {
address: `0x${string}`;
sign: (args: { hash: `0x${string}` }) => Promise<`0x${string}`>;
signTypedData: (args: {
domain: TypedData["domain"];
types: Record<string, Array<{ name: string; type: string }>>;
primaryType: string;
message: Record<string, unknown>;
}) => Promise<`0x${string}`>;
}
/**
* Minimal shape required by {@link fromViemWalletClient}: `account.address`
* read structurally, `signTypedData` cast locally inside the adapter because
* viem's const generics can't be reproduced without re-exporting viem's
* types. Runtime call shape is stable across viem 2.x.
*
* @remarks Requires viem >= 2.0.
* @internal Pass concrete `WalletClient` instances to {@link fromViemWalletClient}.
*/
export interface ViemWalletClientLike {
account?: { address: `0x${string}` } | undefined;
signTypedData: unknown;
}
/**
* Internal shape that viem's `signTypedData` conforms to at runtime.
* Used only inside {@link fromViemWalletClient}.
*/
type ViemSignTypedDataCall = (args: {
account: { address: `0x${string}` } | `0x${string}`;
domain: TypedData["domain"];
types: TypedData["types"];
primaryType: string;
message: Record<string, unknown>;
}) => Promise<`0x${string}`>;
/**
* Shape matching ethers `Wallet` / `HDNodeWallet`. Parameter types
* deliberately widen ethers' `TypedDataDomain` / `TypedDataField[]` so the
* interface doesn't import from ethers while still accepting a Wallet
* instance without casts.
*
* @remarks Requires ethers >= 6.0 (ethers 5.x used private `_signTypedData`).
* @internal Pass concrete `Wallet` / `HDNodeWallet` instances to {@link fromEthersWallet}.
*/
export interface EthersWalletLike {
address: string;
signingKey: {
sign: (hash: string) => { serialized: string };
};
signTypedData: (
domain: {
name?: string;
version?: string;
chainId?: number | bigint;
verifyingContract?: string;
salt?: string;
},
types: Record<string, Array<{ name: string; type: string }>>,
message: Record<string, unknown>,
) => Promise<string>;
}
/**
* Build an ExternalSigner from a raw private-key hex string. Supports both raw-hash
* and typed-data signing, delegated to the internal `ethereUtils` helpers
* ({@link signHash}, {@link signTypedData}) — no extra packages needed.
* If you already hold a viem Account or ethers Wallet, use {@link fromViem}
* or {@link fromEthersWallet} instead.
*
* @example
* import { fromPrivateKey } from "abstractionkit";
* const signer = fromPrivateKey(process.env.PRIVATE_KEY!);
* userOp.signature = await safe.signUserOperationWithSigners(userOp, [signer], chainId);
*/
export function fromPrivateKey(privateKey: string): ExternalSigner<unknown> {
return {
address: privateKeyToAddress(privateKey),
signHash: (hash) => signHash(privateKey, hash).serialized,
signTypedData: (td) => signTypedData(privateKey, td.domain, td.types, td.message),
};
}
/**
* Adapt a viem Local Account (e.g. `privateKeyToAccount(pk)`) to an ExternalSigner.
* Supports both raw-hash and typed-data signing.
*
* @remarks Requires viem >= 2.0.
*/
export function fromViem(account: ViemLocalAccountLike): ExternalSigner<unknown> {
return {
address: account.address,
signHash: (hash) => account.sign({ hash }),
signTypedData: (td) =>
account.signTypedData({
domain: td.domain,
types: td.types,
primaryType: td.primaryType,
message: td.message,
}),
};
}
/**
* Adapt a viem `WalletClient` to an ExternalSigner. Only typed-data signing is
* exposed, because `WalletClient` drives browser/JSON-RPC wallets which
* can't sign raw hashes. Requires the client to have been constructed with
* an `account`; for local accounts, prefer `fromViem` so you also get
* raw-hash fallback.
*
* @remarks Requires viem >= 2.0.
*/
export function fromViemWalletClient(client: ViemWalletClientLike): ExternalSigner<unknown> {
if (!client.account) {
throw new Error(
"fromViemWalletClient: client has no `account` configured. " +
"Construct with `createWalletClient({ account, transport, chain })`.",
);
}
// Capture the full account object: passing just the address would force
// viem to route to `eth_signTypedData_v4` (fails on HTTP transports),
// whereas the object may carry local signing methods.
const account = client.account;
const signTypedData = client.signTypedData as ViemSignTypedDataCall;
return {
address: account.address,
signTypedData: (td) =>
signTypedData({
account,
domain: td.domain,
types: td.types,
primaryType: td.primaryType,
message: td.message,
}),
};
}
/**
* Adapt an ethers `Wallet` / `HDNodeWallet` to an ExternalSigner. Supports both
* raw-hash and typed-data signing.
*
* @remarks Requires ethers >= 6.0.
*/
export function fromEthersWallet(wallet: EthersWalletLike): ExternalSigner<unknown> {
// ethers types `address` as plain `string`; at runtime it's always
// checksummed 0x-prefixed hex.
return {
address: wallet.address as `0x${string}`,
signHash: async (hash) => wallet.signingKey.sign(hash).serialized as `0x${string}`,
signTypedData: async (td) =>
(await wallet.signTypedData(td.domain, td.types, td.message)) as `0x${string}`,
};
}