kokio-sdk 0.1.4 → 1.0.0

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.
Files changed (84) hide show
  1. package/README.md +185 -1
  2. package/dist/esm/admin/config-admin.js +85 -0
  3. package/dist/esm/admin/interface/deviceWalletClass.js +34 -0
  4. package/dist/esm/admin/interface/deviceWalletFactoryClass.js +57 -0
  5. package/dist/esm/admin/interface/eSIMWalletClass.js +31 -0
  6. package/dist/esm/admin/interface/eSIMWalletFactoryClass.js +22 -0
  7. package/dist/esm/admin/interface/lazyWalletRegistryClass.js +33 -0
  8. package/dist/esm/admin/interface/registryClass.js +43 -0
  9. package/dist/esm/config.js +4 -0
  10. package/dist/esm/interface/deviceWalletClass.js +1 -13
  11. package/dist/esm/interface/eSIMWalletClass.js +2 -8
  12. package/dist/esm/interface/eSIMWalletFactoryClass.js +2 -8
  13. package/dist/esm/interface/lazyWalletRegistryClass.js +2 -11
  14. package/dist/esm/logic/P256Verifier.js +8 -15
  15. package/dist/esm/logic/account-kit/createSmartAccount.js +76 -24
  16. package/dist/esm/logic/admin/deviceWallet.eoa.js +42 -0
  17. package/dist/esm/logic/admin/deviceWalletFactory.eoa.js +133 -0
  18. package/dist/esm/logic/admin/eSIMWallet.eoa.js +30 -0
  19. package/dist/esm/logic/admin/eSIMWalletFactory.eoa.js +43 -0
  20. package/dist/esm/logic/admin/lazyWalletRegistry.eoa.js +62 -0
  21. package/dist/esm/logic/admin/reads/deviceWallet.reads.js +45 -0
  22. package/dist/esm/logic/admin/reads/deviceWalletFactory.reads.js +87 -0
  23. package/dist/esm/logic/admin/reads/eSIMWallet.reads.js +44 -0
  24. package/dist/esm/logic/admin/reads/eSIMWalletFactory.reads.js +32 -0
  25. package/dist/esm/logic/admin/reads/lazyWalletRegistry.reads.js +62 -0
  26. package/dist/esm/logic/admin/reads/registry.reads.js +136 -0
  27. package/dist/esm/logic/admin/registry.eoa.js +23 -0
  28. package/dist/esm/logic/constants.js +31 -71
  29. package/dist/esm/logic/deviceWallet.js +28 -91
  30. package/dist/esm/logic/deviceWalletFactory.js +21 -30
  31. package/dist/esm/logic/eSIMWallet.js +25 -57
  32. package/dist/esm/logic/eSIMWalletFactory.js +17 -50
  33. package/dist/esm/logic/errors.js +118 -0
  34. package/dist/esm/logic/lazyWalletRegistry.js +12 -72
  35. package/dist/esm/logic/utils.js +3 -3
  36. package/dist/types/abis/BeaconProxy.d.ts +58 -2
  37. package/dist/types/abis/DeviceWallet.d.ts +702 -2
  38. package/dist/types/abis/DeviceWalletFactory.d.ts +634 -2
  39. package/dist/types/abis/ESIMWallet.d.ts +375 -2
  40. package/dist/types/abis/ESIMWalletFactory.d.ts +355 -2
  41. package/dist/types/abis/LazyWalletRegistry.d.ts +565 -2
  42. package/dist/types/abis/P256Verifier.d.ts +56 -2
  43. package/dist/types/abis/Registry.d.ts +623 -2
  44. package/dist/types/abis/RegistryHelper.d.ts +295 -2
  45. package/dist/types/admin/config-admin.d.ts +55 -0
  46. package/dist/types/admin/interface/deviceWalletClass.d.ts +17 -0
  47. package/dist/types/admin/interface/deviceWalletFactoryClass.d.ts +25 -0
  48. package/dist/types/admin/interface/eSIMWalletClass.d.ts +17 -0
  49. package/dist/types/admin/interface/eSIMWalletFactoryClass.d.ts +10 -0
  50. package/dist/types/admin/interface/lazyWalletRegistryClass.d.ts +14 -0
  51. package/dist/types/admin/interface/registryClass.d.ts +17 -0
  52. package/dist/types/config.d.ts +2 -0
  53. package/dist/types/interface/P256VerifierClass.d.ts +2 -1
  54. package/dist/types/interface/deviceWalletClass.d.ts +24 -28
  55. package/dist/types/interface/deviceWalletFactoryClass.d.ts +27 -26
  56. package/dist/types/interface/eSIMWalletClass.d.ts +2 -4
  57. package/dist/types/interface/eSIMWalletFactoryClass.d.ts +24 -26
  58. package/dist/types/interface/lazyWalletRegistryClass.d.ts +1 -6
  59. package/dist/types/interface/smartAccountClass.d.ts +23 -23
  60. package/dist/types/logic/P256Verifier.d.ts +2 -1
  61. package/dist/types/logic/account-kit/createSmartAccount.d.ts +38 -1
  62. package/dist/types/logic/admin/deviceWallet.eoa.d.ts +12 -0
  63. package/dist/types/logic/admin/deviceWalletFactory.eoa.d.ts +33 -0
  64. package/dist/types/logic/admin/eSIMWallet.eoa.d.ts +14 -0
  65. package/dist/types/logic/admin/eSIMWalletFactory.eoa.d.ts +13 -0
  66. package/dist/types/logic/admin/lazyWalletRegistry.eoa.d.ts +18 -0
  67. package/dist/types/logic/admin/reads/deviceWallet.reads.d.ts +16 -0
  68. package/dist/types/logic/admin/reads/deviceWalletFactory.reads.d.ts +26 -0
  69. package/dist/types/logic/admin/reads/eSIMWallet.reads.d.ts +15 -0
  70. package/dist/types/logic/admin/reads/eSIMWalletFactory.reads.d.ts +10 -0
  71. package/dist/types/logic/admin/reads/lazyWalletRegistry.reads.d.ts +20 -0
  72. package/dist/types/logic/admin/reads/registry.reads.d.ts +34 -0
  73. package/dist/types/logic/admin/registry.eoa.d.ts +7 -0
  74. package/dist/types/logic/deviceWallet.d.ts +1 -5
  75. package/dist/types/logic/deviceWalletFactory.d.ts +5 -4
  76. package/dist/types/logic/eSIMWallet.d.ts +1 -3
  77. package/dist/types/logic/eSIMWalletFactory.d.ts +2 -4
  78. package/dist/types/logic/errors.d.ts +63 -0
  79. package/dist/types/logic/lazyWalletRegistry.d.ts +1 -6
  80. package/dist/types/logic/utils.d.ts +8 -1
  81. package/dist/types/types.d.ts +1 -1
  82. package/package.json +12 -3
  83. package/dist/esm/interface/utilsClass.js +0 -13
  84. package/dist/types/interface/utilsClass.d.ts +0 -2567
@@ -1,6 +1,7 @@
1
1
  import { createSmartAccountClient, getEntryPoint, toSmartContractAccount, split, erc7677Middleware } from "@aa-sdk/core";
2
- import { http, encodeFunctionData, encodePacked, encodeAbiParameters, parseAbiParameters, getContract, concat, keccak256, getContractAddress, getAddress, hashMessage, stringToBytes, toHex, hexToBytes, bytesToHex } from "viem";
2
+ import { http, encodeFunctionData, encodePacked, encodeAbiParameters, parseAbiParameters, getContract, concat, keccak256, getContractAddress, getAddress, hashMessage, toHex, hashTypedData, hexToBytes, bytesToHex } from "viem";
3
3
  import { _getChainSpecificConstants, ZERO, SIGNATURE_VALIDITY_SECONDS } from "../constants.js";
4
+ import { CounterfactualMismatchError } from "../errors.js";
4
5
  import { _remove0x } from "../utils.js";
5
6
  import { DeviceWallet, DeviceWalletFactory } from "../../abis/index.js";
6
7
  import { isoBase64URL } from "@simplewebauthn/server/helpers";
@@ -15,6 +16,22 @@ var AuthenticatorTransport;
15
16
  AuthenticatorTransport["hybrid"] = "hybrid";
16
17
  AuthenticatorTransport["internal"] = "internal";
17
18
  })(AuthenticatorTransport || (AuthenticatorTransport = {}));
19
+ /**
20
+ * BeaconProxy creation bytecode, used to compute the CREATE2 counterfactual
21
+ * DeviceWallet address off-chain (initCode = creationCode ++ abi.encode(beacon, initData)).
22
+ *
23
+ * PINNED - this MUST byte-for-byte match the BeaconProxy the on-chain
24
+ * DeviceWalletFactory deploys, or the computed address will diverge from the
25
+ * deployed one. Source of truth:
26
+ * OpenZeppelin Contracts v5.0.0 - proxy/beacon/BeaconProxy.sol
27
+ * compiled by Hardhat (smart-contract-suite `artifacts/@openzeppelin/contracts/
28
+ * proxy/beacon/BeaconProxy.sol/BeaconProxy.json`), solc 0.8.25+commit.b61c2a91,
29
+ * optimizer { enabled: true, runs: 200 }, viaIR: true.
30
+ * NOTE: the Foundry `out/` artifact (different optimizer settings) produces a
31
+ * DIFFERENT bytecode - do not swap it in without re-verifying the counterfactual.
32
+ * `_assertCounterfactualMatchesOnChain` guards against drift at runtime.
33
+ */
34
+ export const BEACON_PROXY_CREATION_CODE = "0x60a06040908082526104a8803803809161001982856102ae565b8339810182828203126101e95761002f826102e7565b60208084015191939091906001600160401b0382116101e9570182601f820112156101e957805190610060826102fb565b9361006d875195866102ae565b8285528383830101116101e957829060005b83811061029a57505060009184010152823b1561027a577fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5080546001600160a01b0319166001600160a01b038581169182179092558551635c60da1b60e01b8082529194928482600481895afa91821561026f57600092610238575b50813b1561021f5750508551847f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e600080a282511561020057508290600487518096819382525afa9283156101f5576000936101b3575b5091600080848461019096519101845af4903d156101aa573d610174816102fb565b90610181885192836102ae565b8152600081943d92013e610316565b505b6080525161012e908161037a82396080518160180152f35b60609250610316565b92508183813d83116101ee575b6101ca81836102ae565b810103126101e9576000806101e1610190956102e7565b945050610152565b600080fd5b503d6101c0565b85513d6000823e3d90fd5b9350505050346102105750610192565b63b398979f60e01b8152600490fd5b8751634c9c8ce360e01b81529116600482015260249150fd5b9091508481813d8311610268575b61025081836102ae565b810103126101e957610261906102e7565b90386100fb565b503d610246565b88513d6000823e3d90fd5b8351631933b43b60e21b81526001600160a01b0384166004820152602490fd5b81810183015186820184015284920161007f565b601f909101601f19168101906001600160401b038211908210176102d157604052565b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036101e957565b6001600160401b0381116102d157601f01601f191660200190565b9061033d575080511561032b57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580610370575b61034e575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561034656fe60806040819052635c60da1b60e01b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811560a9576000916054575b5060da565b905060203d60201160a3575b601f8101601f191682019167ffffffffffffffff831181841017608d576088926040520160b5565b38604f565b634e487b7160e01b600052604160045260246000fd5b503d6060565b6040513d6000823e3d90fd5b602090607f19011260d5576080516001600160a01b038116810360d55790565b600080fd5b6000808092368280378136915af43d82803e1560f4573d90f35b3d90fdfea264697066735822122099ba460fd62b3e22c737d15959887e6cae3498f3495d31e43e2bcf1283aec7d264736f6c63430008190033";
18
35
  /*
19
36
  ** Stamp is client-side authentication. Since the passkeys are one the user's mobile device
20
37
  ** react-native-passkey helps fetch passkey for the user (provided credentialId, rpId).
@@ -22,7 +39,10 @@ var AuthenticatorTransport;
22
39
  */
23
40
  export const _stamp = async (credentialId, rpId, payload) => {
24
41
  const signingOptions = {
25
- challenge: isoBase64URL.fromBuffer(hexToBytes(payload)),
42
+ // `Uint8Array.from` gives a fresh ArrayBuffer-backed view, matching the
43
+ // `Uint8Array<ArrayBuffer>` that `fromBuffer` expects (viem's `hexToBytes`
44
+ // is typed over the wider `ArrayBufferLike`).
45
+ challenge: isoBase64URL.fromBuffer(Uint8Array.from(hexToBytes(payload))),
26
46
  allowCredentials: [{
27
47
  id: credentialId,
28
48
  type: "public-key",
@@ -119,7 +139,7 @@ const _encodeBatchExecute = async (txs) => {
119
139
  args: [new_txs]
120
140
  });
121
141
  };
122
- const _getAccountInitCode = async (client, deviceUniqueIdentifier, deviceWalletOwnerKey, salt) => {
142
+ export const _getAccountInitCode = async (client, deviceUniqueIdentifier, deviceWalletOwnerKey, salt) => {
123
143
  // To send with user operations
124
144
  const chainID = await client.getChainId();
125
145
  const rpcURL = client.transport.url;
@@ -131,7 +151,7 @@ const _getAccountInitCode = async (client, deviceUniqueIdentifier, deviceWalletO
131
151
  });
132
152
  return values.factoryAddresses.DEVICE_WALLET_FACTORY.concat(_remove0x(callData));
133
153
  };
134
- const getInitCodeHash = async (client, deviceUniqueIdentifier, deviceWalletOwnerKey) => {
154
+ export const getInitCodeHash = async (client, deviceUniqueIdentifier, deviceWalletOwnerKey) => {
135
155
  const chainID = await client.getChainId();
136
156
  const rpcURL = client.transport.url;
137
157
  const values = _getChainSpecificConstants(chainID, rpcURL);
@@ -144,7 +164,7 @@ const getInitCodeHash = async (client, deviceUniqueIdentifier, deviceWalletOwner
144
164
  address: deviceWalletFactoryAddress,
145
165
  client
146
166
  });
147
- const beacon = await deviceWalletFactory.read.beacon([]);
167
+ const beacon = await deviceWalletFactory.read.beacon();
148
168
  // Encode the DeviceWallet.init with the init params
149
169
  const deviceWalletInitData = encodeFunctionData({
150
170
  abi: DeviceWallet,
@@ -156,14 +176,13 @@ const getInitCodeHash = async (client, deviceUniqueIdentifier, deviceWalletOwner
156
176
  eSIMWalletFactoryAddress
157
177
  ]
158
178
  });
159
- const beaconProxyBytecode = "0x60a06040908082526104a8803803809161001982856102ae565b8339810182828203126101e95761002f826102e7565b60208084015191939091906001600160401b0382116101e9570182601f820112156101e957805190610060826102fb565b9361006d875195866102ae565b8285528383830101116101e957829060005b83811061029a57505060009184010152823b1561027a577fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5080546001600160a01b0319166001600160a01b038581169182179092558551635c60da1b60e01b8082529194928482600481895afa91821561026f57600092610238575b50813b1561021f5750508551847f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e600080a282511561020057508290600487518096819382525afa9283156101f5576000936101b3575b5091600080848461019096519101845af4903d156101aa573d610174816102fb565b90610181885192836102ae565b8152600081943d92013e610316565b505b6080525161012e908161037a82396080518160180152f35b60609250610316565b92508183813d83116101ee575b6101ca81836102ae565b810103126101e9576000806101e1610190956102e7565b945050610152565b600080fd5b503d6101c0565b85513d6000823e3d90fd5b9350505050346102105750610192565b63b398979f60e01b8152600490fd5b8751634c9c8ce360e01b81529116600482015260249150fd5b9091508481813d8311610268575b61025081836102ae565b810103126101e957610261906102e7565b90386100fb565b503d610246565b88513d6000823e3d90fd5b8351631933b43b60e21b81526001600160a01b0384166004820152602490fd5b81810183015186820184015284920161007f565b601f909101601f19168101906001600160401b038211908210176102d157604052565b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036101e957565b6001600160401b0381116102d157601f01601f191660200190565b9061033d575080511561032b57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580610370575b61034e575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561034656fe60806040819052635c60da1b60e01b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811560a9576000916054575b5060da565b905060203d60201160a3575b601f8101601f191682019167ffffffffffffffff831181841017608d576088926040520160b5565b38604f565b634e487b7160e01b600052604160045260246000fd5b503d6060565b6040513d6000823e3d90fd5b602090607f19011260d5576080516001600160a01b038116810360d55790565b600080fd5b6000808092368280378136915af43d82803e1560f4573d90f35b3d90fdfea264697066735822122099ba460fd62b3e22c737d15959887e6cae3498f3495d31e43e2bcf1283aec7d264736f6c63430008190033";
160
179
  // Encode BeaconProxy constructor args
161
180
  const beaconProxyConstructorArgs = encodeAbiParameters(parseAbiParameters("address, bytes"), [beacon, deviceWalletInitData]);
162
181
  // Compute initCode
163
- const initCode = concat([beaconProxyBytecode, beaconProxyConstructorArgs]);
182
+ const initCode = concat([BEACON_PROXY_CREATION_CODE, beaconProxyConstructorArgs]);
164
183
  return keccak256(initCode);
165
184
  };
166
- const getCounterFactualAddress = async (client, deviceUniqueIdentifier, deviceWalletOwnerKey, salt) => {
185
+ export const getCounterFactualAddress = async (client, deviceUniqueIdentifier, deviceWalletOwnerKey, salt) => {
167
186
  const chainID = await client.getChainId();
168
187
  const rpcURL = client.transport.url;
169
188
  const values = _getChainSpecificConstants(chainID, rpcURL);
@@ -179,7 +198,41 @@ const getCounterFactualAddress = async (client, deviceUniqueIdentifier, deviceWa
179
198
  });
180
199
  return getAddress(create2Address);
181
200
  };
182
- const _encodeSignature = async (webAuthnSignature, validUntil) => {
201
+ /**
202
+ * Optional drift guard. Recomputes the counterfactual address off-chain (using
203
+ * the pinned {@link BEACON_PROXY_CREATION_CODE}) and compares it against the
204
+ * on-chain `DeviceWalletFactory.getCounterFactualAddress` view - which derives
205
+ * the address from the BeaconProxy the factory ACTUALLY deploys. A mismatch
206
+ * means the pinned proxy bytecode (or init encoding) has drifted from the
207
+ * deployed contract, so this throws early instead of letting a UserOp deploy to,
208
+ * or fund, the wrong address.
209
+ *
210
+ * Not wired into the default account-creation path (it costs one extra RPC);
211
+ * call it explicitly in environments where you want the extra safety, e.g.
212
+ * after a contract redeploy or on first use against a new chain.
213
+ */
214
+ export const _assertCounterfactualMatchesOnChain = async (client, deviceUniqueIdentifier, deviceWalletOwnerKey, salt) => {
215
+ const chainID = await client.getChainId();
216
+ const rpcURL = client.transport.url;
217
+ const values = _getChainSpecificConstants(chainID, rpcURL);
218
+ const offChain = await getCounterFactualAddress(client, deviceUniqueIdentifier, deviceWalletOwnerKey, salt);
219
+ const deviceWalletFactory = getContract({
220
+ abi: DeviceWalletFactory,
221
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
222
+ client,
223
+ });
224
+ // on-chain view arg order is (ownerKey, uid, salt) - differs from createAccount
225
+ const onChain = await deviceWalletFactory.read.getCounterFactualAddress([
226
+ deviceWalletOwnerKey,
227
+ deviceUniqueIdentifier,
228
+ salt,
229
+ ]);
230
+ if (getAddress(offChain) !== getAddress(onChain)) {
231
+ throw new CounterfactualMismatchError(getAddress(offChain), getAddress(onChain));
232
+ }
233
+ return offChain;
234
+ };
235
+ export const _encodeSignature = async (webAuthnSignature, validUntil) => {
183
236
  const encodedWebAuthnSignatureBytes = encodeAbiParameters([
184
237
  {
185
238
  type: "tuple",
@@ -200,30 +253,30 @@ const _encodeSignature = async (webAuthnSignature, validUntil) => {
200
253
  return signature;
201
254
  };
202
255
  // message here is the original message data (string or Uint8Array) directly from the app
203
- const _signMessage = async (message, credentialId, rpId) => {
256
+ export const _signMessage = async (message, credentialId, rpId) => {
204
257
  const validUntil = Math.floor(Date.now() / 1000) + SIGNATURE_VALIDITY_SECONDS;
205
- const payload = hashMessage({ raw: stringToBytes(message) });
258
+ // viem's SignableMessage is `string | { raw: Hex | ByteArray }`. A plain
259
+ // string is a UTF-8 message; the `{ raw }` form is already-serialized bytes
260
+ // (possibly a pre-computed digest). hashMessage handles both natively, so
261
+ // forward the message as-is rather than force-casting it to a string.
262
+ const payload = hashMessage(message);
206
263
  // The original message is passed to the stamp and sign function.
207
264
  // The stamp and sign function creates the EIP-191 digest hash using its hashMessage function
208
265
  // The result of the hashMessage(message) will be the `payload` used as a challenge
209
266
  const webAuthnSignature = await _stamp(credentialId, rpId, payload);
210
267
  return _encodeSignature(webAuthnSignature, validUntil);
211
268
  };
212
- const _signTypedData = async (typedData, organiationId, signWith) => {
269
+ export const _signTypedData = async (typedData, credentialId, rpId) => {
213
270
  // signature valid until, UNIX timestamp in seconds
214
271
  const validUntil = Math.floor(Date.now() / 1000) + SIGNATURE_VALIDITY_SECONDS;
215
- // TODO: Implement stamping for Typed Data
216
- let webAuthnSignature = {
217
- authenticatorData: `0x`,
218
- clientDataJSON: "",
219
- challengeIndex: BigInt(0),
220
- typeIndex: BigInt(0),
221
- r: BigInt(0),
222
- s: BigInt(0)
223
- };
272
+ // EIP-712 digest is the WebAuthn challenge, mirroring _signMessage's use of
273
+ // the EIP-191 digest. The contract's isValidSignature receives this same
274
+ // hashTypedData result and verifies the passkey signature against it.
275
+ const payload = hashTypedData(typedData);
276
+ const webAuthnSignature = await _stamp(credentialId, rpId, payload);
224
277
  return _encodeSignature(webAuthnSignature, validUntil);
225
278
  };
226
- const _signUserOperationHash = async (credentialId, rpId, userOpHash) => {
279
+ export const _signUserOperationHash = async (credentialId, rpId, userOpHash) => {
227
280
  const validUntil = Math.floor(Date.now() / 1000) + SIGNATURE_VALIDITY_SECONDS;
228
281
  const messagePrecursor = encodePacked(["uint8", "uint48", "bytes32"], [
229
282
  1,
@@ -240,7 +293,6 @@ export const _getSmartWallet = async (client, credentialId, rpId, organiationId,
240
293
  const values = _getChainSpecificConstants(chainID, rpcURL);
241
294
  if (!client.account)
242
295
  throw new Error('Error: No signer account found with WalletClient');
243
- const signWith = client.account.address;
244
296
  return toSmartContractAccount({
245
297
  /// REQUIRED PARAMS ///
246
298
  source: "MyAccount",
@@ -255,7 +307,7 @@ export const _getSmartWallet = async (client, credentialId, rpId, organiationId,
255
307
  // given a UO in the form of {target, data, value} should output the calldata for calling your contract's execution method
256
308
  encodeExecute: async (uo) => _encodeExecute(uo),
257
309
  signMessage: async ({ message }) => _signMessage(message, credentialId, rpId),
258
- signTypedData: async (typedData) => _signTypedData(typedData, organiationId, signWith),
310
+ signTypedData: async (typedData) => _signTypedData(typedData, credentialId, rpId),
259
311
  /// OPTIONAL PARAMS ///
260
312
  // if you already know your account's address, pass that in here to avoid generating a new counterfactual
261
313
  accountAddress: await getCounterFactualAddress(client, deviceUniqueIdentifier, deviceWalletOwnerKey, salt),
@@ -0,0 +1,42 @@
1
+ import { _getChainSpecificConstants } from "../constants.js";
2
+ import { MissingEOAWalletError } from "../errors.js";
3
+ import { DeviceWallet } from "../../abis/index.js";
4
+ /**
5
+ * Admin-EOA logic targeting a specific `DeviceWallet` instance (its address is
6
+ * passed in - there is no single factory address). Both functions are admin
7
+ * gated on chain (`deployESIMWallet` is `onlyESIMWalletAdmin`,
8
+ * `setESIMUniqueIdentifierForAnESIMWallet` is `onlyESIMWalletAdminOrRegistry`),
9
+ * so they cannot be driven from a device-wallet userOp and live on the EOA surface.
10
+ */
11
+ /** Deploy a new eSIM wallet under a device wallet. `onlyESIMWalletAdmin`. */
12
+ export const _deployESIMWallet = async (client, deviceWalletAddress, hasAccessToETH, salt) => {
13
+ const chainID = await client.getChainId();
14
+ const rpcURL = client.transport.url;
15
+ const values = _getChainSpecificConstants(chainID, rpcURL);
16
+ if (!client.account)
17
+ throw new MissingEOAWalletError();
18
+ return client.writeContract({
19
+ address: deviceWalletAddress,
20
+ chain: values.chain,
21
+ account: client.account.address,
22
+ abi: DeviceWallet,
23
+ functionName: 'deployESIMWallet',
24
+ args: [hasAccessToETH, salt]
25
+ });
26
+ };
27
+ /** Bind an eSIM's unique identifier to its wallet. `onlyESIMWalletAdminOrRegistry`. */
28
+ export const _setESIMUniqueIdentifierForAnESIMWallet = async (client, deviceWalletAddress, eSIMWalletAddress, eSIMUniqueIdentifier) => {
29
+ const chainID = await client.getChainId();
30
+ const rpcURL = client.transport.url;
31
+ const values = _getChainSpecificConstants(chainID, rpcURL);
32
+ if (!client.account)
33
+ throw new MissingEOAWalletError();
34
+ return client.writeContract({
35
+ address: deviceWalletAddress,
36
+ chain: values.chain,
37
+ account: client.account.address,
38
+ abi: DeviceWallet,
39
+ functionName: 'setESIMUniqueIdentifierForAnESIMWallet',
40
+ args: [eSIMWalletAddress, eSIMUniqueIdentifier]
41
+ });
42
+ };
@@ -0,0 +1,133 @@
1
+ import { _getChainSpecificConstants } from "../constants.js";
2
+ import { MissingEOAWalletError } from "../errors.js";
3
+ import { DeviceWalletFactory } from "../../abis/index.js";
4
+ /**
5
+ * Admin-EOA logic for `DeviceWalletFactory`.
6
+ *
7
+ * Every function here is `onlyAdmin` / `onlyAdminOrRegistry` / `onlyOwner` on
8
+ * chain, i.e. the caller must be the `eSIMWalletAdmin` (or `upgradeManager`)
9
+ * EOA - never a device-wallet userOp. They therefore live on the EOA surface
10
+ * (`KokioAdmin`) and use `writeContract`, mirroring `_createAccountWithEOA`
11
+ * (which is reused as-is from `../deviceWalletFactory.js`).
12
+ */
13
+ /**
14
+ * Batch-deploy device wallets for lazy/fiat users. `onlyAdminOrRegistry`,
15
+ * `payable`: `value` is the total ETH pot from which each `depositAmounts[i]`
16
+ * is drawn; any surplus is refunded to the caller on chain.
17
+ */
18
+ export const _deployDeviceWalletForUsers = async (client, deviceUniqueIdentifiers, deviceWalletOwnersKey, salts, depositAmounts, value) => {
19
+ const chainID = await client.getChainId();
20
+ const rpcURL = client.transport.url;
21
+ const values = _getChainSpecificConstants(chainID, rpcURL);
22
+ if (!client.account)
23
+ throw new MissingEOAWalletError();
24
+ return client.writeContract({
25
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
26
+ chain: values.chain,
27
+ account: client.account.address,
28
+ abi: DeviceWalletFactory,
29
+ functionName: 'deployDeviceWalletForUsers',
30
+ args: [deviceUniqueIdentifiers, deviceWalletOwnersKey, salts, depositAmounts],
31
+ value
32
+ });
33
+ };
34
+ /** Register a freshly created device wallet with the factory. `onlyAdminOrRegistry`. */
35
+ export const _postCreateAccount = async (client, deviceWallet, deviceUniqueIdentifier, deviceWalletOwnerKey) => {
36
+ const chainID = await client.getChainId();
37
+ const rpcURL = client.transport.url;
38
+ const values = _getChainSpecificConstants(chainID, rpcURL);
39
+ if (!client.account)
40
+ throw new MissingEOAWalletError();
41
+ return client.writeContract({
42
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
43
+ chain: values.chain,
44
+ account: client.account.address,
45
+ abi: DeviceWalletFactory,
46
+ functionName: 'postCreateAccount',
47
+ args: [deviceWallet, deviceUniqueIdentifier, deviceWalletOwnerKey]
48
+ });
49
+ };
50
+ /** One-time wiring of the registry into the factory. `onlyAdmin`. */
51
+ export const _addRegistryAddress = async (client, registryContractAddress) => {
52
+ const chainID = await client.getChainId();
53
+ const rpcURL = client.transport.url;
54
+ const values = _getChainSpecificConstants(chainID, rpcURL);
55
+ if (!client.account)
56
+ throw new MissingEOAWalletError();
57
+ return client.writeContract({
58
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
59
+ chain: values.chain,
60
+ account: client.account.address,
61
+ abi: DeviceWalletFactory,
62
+ functionName: 'addRegistryAddress',
63
+ args: [registryContractAddress]
64
+ });
65
+ };
66
+ /** Update the vault that receives eSIM payments. `onlyAdmin`. */
67
+ export const _updateVaultAddress = async (client, newVaultAddress) => {
68
+ const chainID = await client.getChainId();
69
+ const rpcURL = client.transport.url;
70
+ const values = _getChainSpecificConstants(chainID, rpcURL);
71
+ if (!client.account)
72
+ throw new MissingEOAWalletError();
73
+ return client.writeContract({
74
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
75
+ chain: values.chain,
76
+ account: client.account.address,
77
+ abi: DeviceWalletFactory,
78
+ functionName: 'updateVaultAddress',
79
+ args: [newVaultAddress]
80
+ });
81
+ };
82
+ /** Step 1 of the 2-step admin handover: propose a new admin. `onlyAdmin`. */
83
+ export const _requestAdminUpdate = async (client, newAdmin) => {
84
+ const chainID = await client.getChainId();
85
+ const rpcURL = client.transport.url;
86
+ const values = _getChainSpecificConstants(chainID, rpcURL);
87
+ if (!client.account)
88
+ throw new MissingEOAWalletError();
89
+ return client.writeContract({
90
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
91
+ chain: values.chain,
92
+ account: client.account.address,
93
+ abi: DeviceWalletFactory,
94
+ functionName: 'requestAdminUpdate',
95
+ args: [newAdmin]
96
+ });
97
+ };
98
+ /**
99
+ * Step 2 of the 2-step admin handover: the proposed admin accepts. The chain
100
+ * requires `msg.sender` to equal the pending admin, so the `client` here must
101
+ * be the newly proposed admin EOA.
102
+ */
103
+ export const _acceptAdminUpdate = async (client) => {
104
+ const chainID = await client.getChainId();
105
+ const rpcURL = client.transport.url;
106
+ const values = _getChainSpecificConstants(chainID, rpcURL);
107
+ if (!client.account)
108
+ throw new MissingEOAWalletError();
109
+ return client.writeContract({
110
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
111
+ chain: values.chain,
112
+ account: client.account.address,
113
+ abi: DeviceWalletFactory,
114
+ functionName: 'acceptAdminUpdate',
115
+ args: []
116
+ });
117
+ };
118
+ /** Point the device-wallet beacon at a new implementation. `onlyOwner` (upgradeManager). */
119
+ export const _updateDeviceWalletImplementation = async (client, newDeviceImpl) => {
120
+ const chainID = await client.getChainId();
121
+ const rpcURL = client.transport.url;
122
+ const values = _getChainSpecificConstants(chainID, rpcURL);
123
+ if (!client.account)
124
+ throw new MissingEOAWalletError();
125
+ return client.writeContract({
126
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
127
+ chain: values.chain,
128
+ account: client.account.address,
129
+ abi: DeviceWalletFactory,
130
+ functionName: 'updateDeviceWalletImplementation',
131
+ args: [newDeviceImpl]
132
+ });
133
+ };
@@ -0,0 +1,30 @@
1
+ import { _getChainSpecificConstants } from "../constants.js";
2
+ import { MissingEOAWalletError } from "../errors.js";
3
+ import { ESIMWallet } from "../../abis/index.js";
4
+ /**
5
+ * Admin-EOA logic targeting a specific `ESIMWallet` instance (address passed in).
6
+ * `buyDataBundle` is `onlyDeviceWalletOrESIMWalletAdmin`, so the admin EOA can
7
+ * call it directly.
8
+ */
9
+ /**
10
+ * Buy a data bundle for an eSIM wallet. `onlyDeviceWalletOrESIMWalletAdmin`,
11
+ * `payable`. `value` is optional: the contract pulls any shortfall from the
12
+ * device wallet's balance, so an admin can pass `0n` when the wallet is funded,
13
+ * or forward `dataBundlePrice` to pay directly.
14
+ */
15
+ export const _buyDataBundle = async (client, eSIMWalletAddress, dataBundleDetails, value = 0n) => {
16
+ const chainID = await client.getChainId();
17
+ const rpcURL = client.transport.url;
18
+ const values = _getChainSpecificConstants(chainID, rpcURL);
19
+ if (!client.account)
20
+ throw new MissingEOAWalletError();
21
+ return client.writeContract({
22
+ address: eSIMWalletAddress,
23
+ chain: values.chain,
24
+ account: client.account.address,
25
+ abi: ESIMWallet,
26
+ functionName: 'buyDataBundle',
27
+ args: [dataBundleDetails],
28
+ value
29
+ });
30
+ };
@@ -0,0 +1,43 @@
1
+ import { _getChainSpecificConstants } from "../constants.js";
2
+ import { MissingEOAWalletError } from "../errors.js";
3
+ import { ESIMWalletFactory } from "../../abis/index.js";
4
+ /**
5
+ * Admin-EOA logic for `ESIMWalletFactory`. Both functions are owner-gated
6
+ * (`addRegistryAddress` requires `msg.sender == owner()`, `updateESIMWalletImplementation`
7
+ * is `onlyOwner`), so the `client` must carry the `upgradeManager` EOA.
8
+ *
9
+ * Note: `ESIMWalletFactory.deployESIMWallet` is intentionally NOT exposed - it is
10
+ * `onlyRegistryOrDeviceWalletFactoryOrDeviceWallet`, so a bare EOA always reverts.
11
+ */
12
+ /** One-time wiring of the registry into the eSIM factory. Owner only. */
13
+ export const _addRegistryAddress = async (client, registryContractAddress) => {
14
+ const chainID = await client.getChainId();
15
+ const rpcURL = client.transport.url;
16
+ const values = _getChainSpecificConstants(chainID, rpcURL);
17
+ if (!client.account)
18
+ throw new MissingEOAWalletError();
19
+ return client.writeContract({
20
+ address: values.factoryAddresses.ESIM_WALLET_FACTORY,
21
+ chain: values.chain,
22
+ account: client.account.address,
23
+ abi: ESIMWalletFactory,
24
+ functionName: 'addRegistryAddress',
25
+ args: [registryContractAddress]
26
+ });
27
+ };
28
+ /** Point the eSIM-wallet beacon at a new implementation. `onlyOwner`. */
29
+ export const _updateESIMWalletImplementation = async (client, eSIMWalletImpl) => {
30
+ const chainID = await client.getChainId();
31
+ const rpcURL = client.transport.url;
32
+ const values = _getChainSpecificConstants(chainID, rpcURL);
33
+ if (!client.account)
34
+ throw new MissingEOAWalletError();
35
+ return client.writeContract({
36
+ address: values.factoryAddresses.ESIM_WALLET_FACTORY,
37
+ chain: values.chain,
38
+ account: client.account.address,
39
+ abi: ESIMWalletFactory,
40
+ functionName: 'updateESIMWalletImplementation',
41
+ args: [eSIMWalletImpl]
42
+ });
43
+ };
@@ -0,0 +1,62 @@
1
+ import { _getChainSpecificConstants } from "../constants.js";
2
+ import { MissingEOAWalletError } from "../errors.js";
3
+ import { LazyWalletRegistry } from "../../abis/index.js";
4
+ /**
5
+ * Admin-EOA logic for `LazyWalletRegistry`. All three functions are
6
+ * `onlyESIMWalletAdmin` on chain, so they can only succeed from the admin EOA -
7
+ * a device-wallet userOp (whose sender is the smart account) always reverts.
8
+ * This is why they belong on the EOA surface rather than the mobile userOp one.
9
+ */
10
+ /** Record fiat/lazy purchase history for a batch of devices. `onlyESIMWalletAdmin`. */
11
+ export const _batchPopulateHistory = async (client, deviceUniqueIdentifiers, eSIMUniqueIdentifiers, dataBundleDetails) => {
12
+ const chainID = await client.getChainId();
13
+ const rpcURL = client.transport.url;
14
+ const values = _getChainSpecificConstants(chainID, rpcURL);
15
+ if (!client.account)
16
+ throw new MissingEOAWalletError();
17
+ return client.writeContract({
18
+ address: values.factoryAddresses.LAZY_WALLET_REGISTRY,
19
+ chain: values.chain,
20
+ account: client.account.address,
21
+ abi: LazyWalletRegistry,
22
+ functionName: 'batchPopulateHistory',
23
+ args: [deviceUniqueIdentifiers, eSIMUniqueIdentifiers, dataBundleDetails]
24
+ });
25
+ };
26
+ /**
27
+ * Materialise a lazily-provisioned device wallet and its eSIMs on chain.
28
+ * `onlyESIMWalletAdmin`, `payable`: the contract requires `depositAmount == msg.value`,
29
+ * so `value` is set to `depositAmount` here.
30
+ */
31
+ export const _deployLazyWalletAndSetESIMIdentifier = async (client, deviceOwnerPublicKey, deviceUniqueIdentifier, salt, depositAmount) => {
32
+ const chainID = await client.getChainId();
33
+ const rpcURL = client.transport.url;
34
+ const values = _getChainSpecificConstants(chainID, rpcURL);
35
+ if (!client.account)
36
+ throw new MissingEOAWalletError();
37
+ return client.writeContract({
38
+ address: values.factoryAddresses.LAZY_WALLET_REGISTRY,
39
+ chain: values.chain,
40
+ account: client.account.address,
41
+ abi: LazyWalletRegistry,
42
+ functionName: 'deployLazyWalletAndSetESIMIdentifier',
43
+ args: [deviceOwnerPublicKey, deviceUniqueIdentifier, salt, depositAmount],
44
+ value: depositAmount
45
+ });
46
+ };
47
+ /** Re-point an eSIM identifier from an old device to a new one. `onlyESIMWalletAdmin`. */
48
+ export const _switchESIMIdentifierToNewDeviceIdentifier = async (client, eSIMIdentifier, oldDeviceIdentifier, newDeviceIdentifier) => {
49
+ const chainID = await client.getChainId();
50
+ const rpcURL = client.transport.url;
51
+ const values = _getChainSpecificConstants(chainID, rpcURL);
52
+ if (!client.account)
53
+ throw new MissingEOAWalletError();
54
+ return client.writeContract({
55
+ address: values.factoryAddresses.LAZY_WALLET_REGISTRY,
56
+ chain: values.chain,
57
+ account: client.account.address,
58
+ abi: LazyWalletRegistry,
59
+ functionName: 'switchESIMIdentifierToNewDeviceIdentifier',
60
+ args: [eSIMIdentifier, oldDeviceIdentifier, newDeviceIdentifier]
61
+ });
62
+ };
@@ -0,0 +1,45 @@
1
+ import { publicActions } from "viem";
2
+ import { DeviceWallet } from "../../../abis/index.js";
3
+ /**
4
+ * Read-only admin logic targeting a specific `DeviceWallet` instance (its address
5
+ * is passed in). Surfaces the instance's public storage getters + `getVaultAddress`
6
+ * view for the backend. Each read extends the `WalletClient` with `publicActions`;
7
+ * no EOA account is required, and the target is the instance address (not a
8
+ * factory address, so no chain-constants lookup is needed).
9
+ */
10
+ /** The device's unique identifier string. */
11
+ export const _deviceUniqueIdentifier = async (client, deviceWalletAddress) => {
12
+ return client.extend(publicActions).readContract({
13
+ address: deviceWalletAddress,
14
+ abi: DeviceWallet,
15
+ functionName: "deviceUniqueIdentifier",
16
+ args: []
17
+ });
18
+ };
19
+ /** Whether an eSIM wallet is a valid child of this device wallet. */
20
+ export const _isValidESIMWallet = async (client, deviceWalletAddress, eSIMWallet) => {
21
+ return client.extend(publicActions).readContract({
22
+ address: deviceWalletAddress,
23
+ abi: DeviceWallet,
24
+ functionName: "isValidESIMWallet",
25
+ args: [eSIMWallet]
26
+ });
27
+ };
28
+ /** Whether an eSIM wallet is allowed to pull ETH from this device wallet. */
29
+ export const _canPullETH = async (client, deviceWalletAddress, eSIMWallet) => {
30
+ return client.extend(publicActions).readContract({
31
+ address: deviceWalletAddress,
32
+ abi: DeviceWallet,
33
+ functionName: "canPullETH",
34
+ args: [eSIMWallet]
35
+ });
36
+ };
37
+ /** The vault address this device wallet pays eSIM purchases to. */
38
+ export const _getVaultAddress = async (client, deviceWalletAddress) => {
39
+ return client.extend(publicActions).readContract({
40
+ address: deviceWalletAddress,
41
+ abi: DeviceWallet,
42
+ functionName: "getVaultAddress",
43
+ args: []
44
+ });
45
+ };
@@ -0,0 +1,87 @@
1
+ import { publicActions } from "viem";
2
+ import { _getChainSpecificConstants } from "../../constants.js";
3
+ import { DeviceWalletFactory } from "../../../abis/index.js";
4
+ /**
5
+ * Read-only admin logic for `DeviceWalletFactory` - the contract's public
6
+ * storage getters and `view` functions, surfaced for the backend.
7
+ *
8
+ * A viem `WalletClient` carries no public actions, so each read extends it with
9
+ * `publicActions` (reusing the same transport, so it also works under an anvil
10
+ * fork) before calling `readContract`. Reads need no EOA account, so there is no
11
+ * `MissingEOAWalletError` guard.
12
+ */
13
+ /** The admin EOA (`eSIMWalletAdmin`) currently set on the factory. */
14
+ export const _eSIMWalletAdmin = async (client) => {
15
+ const chainID = await client.getChainId();
16
+ const rpcURL = client.transport.url;
17
+ const values = _getChainSpecificConstants(chainID, rpcURL);
18
+ return client.extend(publicActions).readContract({
19
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
20
+ abi: DeviceWalletFactory,
21
+ functionName: "eSIMWalletAdmin",
22
+ args: []
23
+ });
24
+ };
25
+ /** The vault EOA that receives eSIM payments. */
26
+ export const _vault = async (client) => {
27
+ const chainID = await client.getChainId();
28
+ const rpcURL = client.transport.url;
29
+ const values = _getChainSpecificConstants(chainID, rpcURL);
30
+ return client.extend(publicActions).readContract({
31
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
32
+ abi: DeviceWalletFactory,
33
+ functionName: "vault",
34
+ args: []
35
+ });
36
+ };
37
+ /** The pending admin proposed via `requestAdminUpdate` (zero address if none). */
38
+ export const _newRequestedAdmin = async (client) => {
39
+ const chainID = await client.getChainId();
40
+ const rpcURL = client.transport.url;
41
+ const values = _getChainSpecificConstants(chainID, rpcURL);
42
+ return client.extend(publicActions).readContract({
43
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
44
+ abi: DeviceWalletFactory,
45
+ functionName: "newRequestedAdmin",
46
+ args: []
47
+ });
48
+ };
49
+ /** Whether a device wallet has been registered with the factory. */
50
+ export const _deviceWalletInfoAdded = async (client, deviceWallet) => {
51
+ const chainID = await client.getChainId();
52
+ const rpcURL = client.transport.url;
53
+ const values = _getChainSpecificConstants(chainID, rpcURL);
54
+ return client.extend(publicActions).readContract({
55
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
56
+ abi: DeviceWalletFactory,
57
+ functionName: "deviceWalletInfoAdded",
58
+ args: [deviceWallet]
59
+ });
60
+ };
61
+ /** The current device-wallet beacon implementation. */
62
+ export const _getCurrentDeviceWalletImplementation = async (client) => {
63
+ const chainID = await client.getChainId();
64
+ const rpcURL = client.transport.url;
65
+ const values = _getChainSpecificConstants(chainID, rpcURL);
66
+ return client.extend(publicActions).readContract({
67
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
68
+ abi: DeviceWalletFactory,
69
+ functionName: "getCurrentDeviceWalletImplementation",
70
+ args: []
71
+ });
72
+ };
73
+ /**
74
+ * The counterfactual (CREATE2) device-wallet address for an owner key. On-chain
75
+ * arg order is `(ownerKey, uid, salt)` - note this differs from `createAccount`.
76
+ */
77
+ export const _getCounterFactualAddress = async (client, deviceWalletOwnerKey, deviceUniqueIdentifier, salt) => {
78
+ const chainID = await client.getChainId();
79
+ const rpcURL = client.transport.url;
80
+ const values = _getChainSpecificConstants(chainID, rpcURL);
81
+ return client.extend(publicActions).readContract({
82
+ address: values.factoryAddresses.DEVICE_WALLET_FACTORY,
83
+ abi: DeviceWalletFactory,
84
+ functionName: "getCounterFactualAddress",
85
+ args: [deviceWalletOwnerKey, deviceUniqueIdentifier, salt]
86
+ });
87
+ };