abstractionkit 0.3.3 → 0.3.5
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/CHANGELOG.md +79 -0
- package/README.md +12 -0
- package/dist/index.cjs +694 -154
- package/dist/index.d.cts +55 -49
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +55 -49
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +694 -154
- package/dist/index.mjs +692 -156
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,84 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.5
|
|
4
|
+
|
|
5
|
+
### New Features
|
|
6
|
+
|
|
7
|
+
- **EIP-712 typed-data signing for `Simple7702Account` / `Simple7702AccountV09`**: `signUserOperationWithSigner` now accepts `signTypedData`-only signers (JSON-RPC wallets, viem `WalletClient`) in addition to existing `signHash` signers. The v0.8/v0.9 `userOpHash` IS the EIP-712 digest of `PackedUserOperation` under the EntryPoint's domain, so both schemes produce signatures that validate against the same hash. Throws on EntryPoint v0.7 (different signing scheme). Adds `getUserOperationEip712TypedData(userOp, chainId)` on `BaseSimple7702Account` as the lower-level escape hatch for integrators driving `signTypedData` with their own primitive (HSM, MPC, custom wallet abstraction).
|
|
8
|
+
```ts
|
|
9
|
+
// Path A: signTypedData-only signer
|
|
10
|
+
const signer = {
|
|
11
|
+
address: eoaAddress,
|
|
12
|
+
signTypedData: async (td) => walletClient.signTypedData(td),
|
|
13
|
+
};
|
|
14
|
+
userOp.signature = await account.signUserOperationWithSigner(userOp, signer, chainId);
|
|
15
|
+
|
|
16
|
+
// Path B: drive signTypedData yourself
|
|
17
|
+
const td = account.getUserOperationEip712TypedData(userOp, chainId);
|
|
18
|
+
userOp.signature = await wallet.signTypedData(td.domain, td.types, td.message);
|
|
19
|
+
```
|
|
20
|
+
- **WebAuthn pubkey JSON helpers + assertion normalizer** (3 new exports from package root):
|
|
21
|
+
- `pubkeyCoordinatesToJson(pubkey)` / `pubkeyCoordinatesFromJson(input)`: bigint-safe JSON round-trip for `{ x, y }` coordinates. Hex on the wire, canonical `{ x: bigint, y: bigint }` after parse. `fromJson` accepts a JSON string or a pre-parsed object, and either hex or decimal string coords.
|
|
22
|
+
- `webauthnSignatureFromAssertion(response)`: turns a structural assertion shape (browser `AuthenticatorAssertionResponse`, `ox/WebAuthnP256` sign output, or `@simplewebauthn/browser`) into the `WebauthnSignatureData` that `fromSafeWebauthn` and `createWebAuthnSignature` already accept. Replaces the ~13-line parser pipeline every Safe-passkeys consumer was writing in their `getAssertion` callback.
|
|
23
|
+
- **`fromSafeWebauthn` adapter**: package-root factory that produces an `ExternalSigner` from a WebAuthn credential, ready to pass into `safe.signUserOperationWithSigners(op, [signer], chainId)`. Hides three Safe-specific concerns: address routing (the WebAuthn shared signer for the deployment UserOp, the deterministic verifier-proxy address derived from `(x, y)` afterward), the `type: "contract"` tag, and the Safe-specific signature encoding. Required `accountClass` parameter (the same Safe subclass used at `initializeNewAccount`) sources the Passkey module defaults — `SafeAccountV0_2_0` / `SafeAccountV0_3_0` for v0.2.0 (FCL P256), `SafeMultiChainSigAccountV1` for v0.2.1 (Daimo P256 + RIP-7951). Picking the wrong class would derive an address that isn't an on-chain owner and the bundler would reject with a generic "Invalid UserOp signature" (`GS026` on-chain), so the param is required to surface this choice at compile time. Caller supplies a `getAssertion(challenge: Uint8Array) => Promise<WebauthnSignatureData>` callback that runs `navigator.credentials.get(...)` (browser) or an equivalent native bridge — the SDK doesn't import `navigator` itself, so the adapter stays environment-agnostic. Pass `expectedSigners: [{ x, y }]` to `createUserOperation` so the bundler estimates verification gas against the WebAuthn dummy signature (~400 bytes) instead of the EOA dummy (~65 bytes); without it, the real signed UserOp is rejected at submit. The `FromSafeWebauthnParams` and `WebauthnAssertionFetcher` types are also exported from the package root.
|
|
24
|
+
```ts
|
|
25
|
+
import { fromSafeWebauthn, SafeAccountV0_3_0 } from "abstractionkit";
|
|
26
|
+
|
|
27
|
+
let userOperation = await safe.createUserOperation(
|
|
28
|
+
transactions, nodeUrl, bundlerUrl,
|
|
29
|
+
{ expectedSigners: [{ x, y }] },
|
|
30
|
+
);
|
|
31
|
+
const signer = fromSafeWebauthn({
|
|
32
|
+
publicKey: { x, y },
|
|
33
|
+
isInit: userOperation.nonce === 0n,
|
|
34
|
+
accountClass: SafeAccountV0_3_0, // SafeMultiChainSigAccountV1 for multi-chain
|
|
35
|
+
getAssertion: async (challenge) => {
|
|
36
|
+
const assertion = await navigator.credentials.get({
|
|
37
|
+
publicKey: { challenge, rpId, allowCredentials, userVerification },
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
authenticatorData: assertion.response.authenticatorData,
|
|
41
|
+
clientDataFields: extractClientDataFields(assertion.response),
|
|
42
|
+
rs: extractSignature(assertion.response),
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
userOperation.signature = await safe.signUserOperationWithSigners(
|
|
47
|
+
userOperation, [signer], chainId,
|
|
48
|
+
);
|
|
49
|
+
```
|
|
50
|
+
- **`ExternalSigner.type` field** (`"ecdsa" | "contract"`, optional, defaults to `"ecdsa"`). When `"contract"`, the signer's signature is encoded as a dynamic-length EIP-1271 contract-signature segment instead of a raw 65-byte ECDSA blob. Lets a single `signUserOperationWithSigners([...])` call mix ECDSA owners and contract-signature owners (WebAuthn, smart-contract owners) in the same Safe multisig batch. Account-agnostic: ignored by non-Safe accounts that don't model contract signatures. `fromSafeWebauthn` sets this internally.
|
|
51
|
+
|
|
52
|
+
### Breaking Changes
|
|
53
|
+
|
|
54
|
+
- **`UserOperationToSignWithOverrides.overrides` is split into `options` and `webAuthnSignatureOverrides`.** The previous kitchen-sink `overrides` field carried both per-call signing options (timing, multi-chain, module address) and WebAuthn-specific encoding overrides (verifier addresses, init flag); these now live on dedicated fields. Affects callers of `SafeMultiChainSigAccountV1.signUserOperations` and `signUserOperationsWithSigners`. Migration:
|
|
55
|
+
```ts
|
|
56
|
+
// Before
|
|
57
|
+
await safe.signUserOperations(
|
|
58
|
+
[{
|
|
59
|
+
userOperation, chainId, validAfter, validUntil,
|
|
60
|
+
overrides: { isInit: true, webAuthnSharedSigner, safe4337ModuleAddress },
|
|
61
|
+
}],
|
|
62
|
+
[pk],
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// After
|
|
66
|
+
await safe.signUserOperations(
|
|
67
|
+
[{
|
|
68
|
+
userOperation, chainId, validAfter, validUntil,
|
|
69
|
+
options: { safe4337ModuleAddress },
|
|
70
|
+
webAuthnSignatureOverrides: { isInit: true, webAuthnSharedSigner },
|
|
71
|
+
}],
|
|
72
|
+
[pk],
|
|
73
|
+
);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 0.3.4
|
|
77
|
+
|
|
78
|
+
### New Features
|
|
79
|
+
|
|
80
|
+
- **`SafeAccount.isDeployed(accountAddress, nodeRpcUrl)`**: static method that checks whether a Safe account is already deployed on-chain. Returns `true` when `accountAddress` has non-empty bytecode, `false` otherwise. Useful for branching between `new SafeAccountV0_3_0(address)` (existing account) and `SafeAccountV0_3_0.initializeNewAccount([owners])` (counterfactual) without inspecting `eth_getCode` manually.
|
|
81
|
+
|
|
3
82
|
## 0.3.3
|
|
4
83
|
|
|
5
84
|
### New Features
|
package/README.md
CHANGED
|
@@ -242,6 +242,18 @@ const newAccount = SafeAccountV0_3_0.initializeNewAccount(["0xOwnerAddress"]);
|
|
|
242
242
|
// First UserOp will deploy it automatically
|
|
243
243
|
```
|
|
244
244
|
|
|
245
|
+
When you have a stored Safe address and need to pick between the two paths at runtime, use `isDeployed` to avoid emitting redundant factory data (and the `AA10 sender already constructed` error) on subsequent UserOps:
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { SafeAccountV0_3_0 } from "abstractionkit";
|
|
249
|
+
|
|
250
|
+
const smartAccount = (await SafeAccountV0_3_0.isDeployed(safeAddress, nodeRpc))
|
|
251
|
+
? new SafeAccountV0_3_0(safeAddress)
|
|
252
|
+
: SafeAccountV0_3_0.initializeNewAccount(owners);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
`isDeployed` only checks for bytecode at the address. It does not verify that the deployed code is a Safe or that its on-chain owners match a given set. If your owners may have changed, compare `createAccountAddress(owners)` against the stored address yourself before calling `initializeNewAccount`.
|
|
256
|
+
|
|
245
257
|
### Calibur 7702: delegate an EOA and send a transfer
|
|
246
258
|
|
|
247
259
|
`Calibur7702Account` is Uniswap's EIP-7702 smart account. It upgrades a regular EOA in place so the same address becomes a programmable smart account on EntryPoint v0.8.
|