kokio-sdk 0.1.3 → 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 (86) 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 +18 -2
  10. package/dist/esm/interface/constantsClass.js +33 -10
  11. package/dist/esm/interface/deviceWalletClass.js +1 -13
  12. package/dist/esm/interface/eSIMWalletClass.js +2 -8
  13. package/dist/esm/interface/eSIMWalletFactoryClass.js +2 -8
  14. package/dist/esm/interface/lazyWalletRegistryClass.js +2 -11
  15. package/dist/esm/logic/P256Verifier.js +8 -15
  16. package/dist/esm/logic/account-kit/createSmartAccount.js +78 -27
  17. package/dist/esm/logic/admin/deviceWallet.eoa.js +42 -0
  18. package/dist/esm/logic/admin/deviceWalletFactory.eoa.js +133 -0
  19. package/dist/esm/logic/admin/eSIMWallet.eoa.js +30 -0
  20. package/dist/esm/logic/admin/eSIMWalletFactory.eoa.js +43 -0
  21. package/dist/esm/logic/admin/lazyWalletRegistry.eoa.js +62 -0
  22. package/dist/esm/logic/admin/reads/deviceWallet.reads.js +45 -0
  23. package/dist/esm/logic/admin/reads/deviceWalletFactory.reads.js +87 -0
  24. package/dist/esm/logic/admin/reads/eSIMWallet.reads.js +44 -0
  25. package/dist/esm/logic/admin/reads/eSIMWalletFactory.reads.js +32 -0
  26. package/dist/esm/logic/admin/reads/lazyWalletRegistry.reads.js +62 -0
  27. package/dist/esm/logic/admin/reads/registry.reads.js +136 -0
  28. package/dist/esm/logic/admin/registry.eoa.js +23 -0
  29. package/dist/esm/logic/constants.js +31 -71
  30. package/dist/esm/logic/deviceWallet.js +28 -91
  31. package/dist/esm/logic/deviceWalletFactory.js +21 -30
  32. package/dist/esm/logic/eSIMWallet.js +25 -57
  33. package/dist/esm/logic/eSIMWalletFactory.js +17 -50
  34. package/dist/esm/logic/errors.js +118 -0
  35. package/dist/esm/logic/lazyWalletRegistry.js +12 -72
  36. package/dist/esm/logic/utils.js +3 -3
  37. package/dist/types/abis/BeaconProxy.d.ts +58 -2
  38. package/dist/types/abis/DeviceWallet.d.ts +702 -2
  39. package/dist/types/abis/DeviceWalletFactory.d.ts +634 -2
  40. package/dist/types/abis/ESIMWallet.d.ts +375 -2
  41. package/dist/types/abis/ESIMWalletFactory.d.ts +355 -2
  42. package/dist/types/abis/LazyWalletRegistry.d.ts +565 -2
  43. package/dist/types/abis/P256Verifier.d.ts +56 -2
  44. package/dist/types/abis/Registry.d.ts +623 -2
  45. package/dist/types/abis/RegistryHelper.d.ts +295 -2
  46. package/dist/types/admin/config-admin.d.ts +55 -0
  47. package/dist/types/admin/interface/deviceWalletClass.d.ts +17 -0
  48. package/dist/types/admin/interface/deviceWalletFactoryClass.d.ts +25 -0
  49. package/dist/types/admin/interface/eSIMWalletClass.d.ts +17 -0
  50. package/dist/types/admin/interface/eSIMWalletFactoryClass.d.ts +10 -0
  51. package/dist/types/admin/interface/lazyWalletRegistryClass.d.ts +14 -0
  52. package/dist/types/admin/interface/registryClass.d.ts +17 -0
  53. package/dist/types/config.d.ts +14 -2
  54. package/dist/types/interface/P256VerifierClass.d.ts +2 -1
  55. package/dist/types/interface/constantsClass.d.ts +23 -3
  56. package/dist/types/interface/deviceWalletClass.d.ts +24 -28
  57. package/dist/types/interface/deviceWalletFactoryClass.d.ts +27 -26
  58. package/dist/types/interface/eSIMWalletClass.d.ts +2 -4
  59. package/dist/types/interface/eSIMWalletFactoryClass.d.ts +24 -26
  60. package/dist/types/interface/lazyWalletRegistryClass.d.ts +1 -6
  61. package/dist/types/interface/smartAccountClass.d.ts +23 -23
  62. package/dist/types/logic/P256Verifier.d.ts +2 -1
  63. package/dist/types/logic/account-kit/createSmartAccount.d.ts +38 -1
  64. package/dist/types/logic/admin/deviceWallet.eoa.d.ts +12 -0
  65. package/dist/types/logic/admin/deviceWalletFactory.eoa.d.ts +33 -0
  66. package/dist/types/logic/admin/eSIMWallet.eoa.d.ts +14 -0
  67. package/dist/types/logic/admin/eSIMWalletFactory.eoa.d.ts +13 -0
  68. package/dist/types/logic/admin/lazyWalletRegistry.eoa.d.ts +18 -0
  69. package/dist/types/logic/admin/reads/deviceWallet.reads.d.ts +16 -0
  70. package/dist/types/logic/admin/reads/deviceWalletFactory.reads.d.ts +26 -0
  71. package/dist/types/logic/admin/reads/eSIMWallet.reads.d.ts +15 -0
  72. package/dist/types/logic/admin/reads/eSIMWalletFactory.reads.d.ts +10 -0
  73. package/dist/types/logic/admin/reads/lazyWalletRegistry.reads.d.ts +20 -0
  74. package/dist/types/logic/admin/reads/registry.reads.d.ts +34 -0
  75. package/dist/types/logic/admin/registry.eoa.d.ts +7 -0
  76. package/dist/types/logic/deviceWallet.d.ts +1 -5
  77. package/dist/types/logic/deviceWalletFactory.d.ts +5 -4
  78. package/dist/types/logic/eSIMWallet.d.ts +1 -3
  79. package/dist/types/logic/eSIMWalletFactory.d.ts +2 -4
  80. package/dist/types/logic/errors.d.ts +63 -0
  81. package/dist/types/logic/lazyWalletRegistry.d.ts +1 -6
  82. package/dist/types/logic/utils.d.ts +8 -1
  83. package/dist/types/types.d.ts +1 -1
  84. package/package.json +17 -4
  85. package/dist/esm/interface/utilsClass.js +0 -13
  86. package/dist/types/interface/utilsClass.d.ts +0 -2567
@@ -1,4 +1,4 @@
1
- import { _addRegistryAddress, _deployESIMWalletWithEOA, _deployESIMWalletWithUserOp, _getCurrentESIMWalletImplementation } from "../logic/eSIMWalletFactory.js";
1
+ import { _deployESIMWalletWithUserOp, _getCurrentESIMWalletImplementation } from "../logic/eSIMWalletFactory.js";
2
2
  export class ESIMWalletFactorySubPackage {
3
3
  smartAccountClient;
4
4
  walletClient;
@@ -6,16 +6,10 @@ export class ESIMWalletFactorySubPackage {
6
6
  this.smartAccountClient = smartAccountClient;
7
7
  this.walletClient = walletClient;
8
8
  }
9
- deployESIMWalletWithEOA(deviceWalletAddress, salt) {
10
- return _deployESIMWalletWithEOA(this.walletClient, deviceWalletAddress, salt);
11
- }
12
9
  deployESIMWalletWithUserOp(deviceWalletAddress, salt) {
13
10
  return _deployESIMWalletWithUserOp(this.smartAccountClient, deviceWalletAddress, salt);
14
11
  }
15
- addRegistryAddress(registryContractAddress) {
16
- return _addRegistryAddress(this.smartAccountClient, registryContractAddress);
17
- }
18
- _getCurrentESIMWalletImplementation() {
12
+ getCurrentESIMWalletImplementation() {
19
13
  return _getCurrentESIMWalletImplementation(this.smartAccountClient);
20
14
  }
21
15
  }
@@ -1,19 +1,10 @@
1
- import { _batchPopulateHistory, _deployLazyWalletAndSetESIMIdentifier, _isLazyWalletDeployed, _switchESIMIdentifierToNewDeviceIdentifier } from "../logic/lazyWalletRegistry.js";
1
+ import { _isLazyWalletDeployed } from "../logic/lazyWalletRegistry.js";
2
2
  export class LazyWalletRegistrySubPackage {
3
3
  client;
4
4
  constructor(client) {
5
5
  this.client = client;
6
6
  }
7
- _batchPopulateHistory(deviceUniqueIdentifiers, eSIMUniqueIdentifiers, dataBundleDetails) {
8
- return _batchPopulateHistory(this.client, deviceUniqueIdentifiers, eSIMUniqueIdentifiers, dataBundleDetails);
9
- }
10
- _deployLazyWalletAndSetESIMIdentifier(deviceOwnerPublicKey, deviceUniqueIdentifier, salt, depositAmount) {
11
- return _deployLazyWalletAndSetESIMIdentifier(this.client, deviceOwnerPublicKey, deviceUniqueIdentifier, salt, depositAmount);
12
- }
13
- _isLazyWalletDeployed(deviceUniqueIdentifier) {
7
+ isLazyWalletDeployed(deviceUniqueIdentifier) {
14
8
  return _isLazyWalletDeployed(this.client, deviceUniqueIdentifier);
15
9
  }
16
- _switchESIMIdentifierToNewDeviceIdentifier(eSIMIdentifier, oldDeviceIdentifier, newDeviceIdentifier) {
17
- return _switchESIMIdentifierToNewDeviceIdentifier(this.client, eSIMIdentifier, oldDeviceIdentifier, newDeviceIdentifier);
18
- }
19
10
  }
@@ -1,22 +1,15 @@
1
- import { encodeFunctionData } from "viem";
2
- import { _getChainSpecificConstants, customErrors } from "./constants.js";
1
+ import { _getChainSpecificConstants } from "./constants.js";
3
2
  import { P256Verifier } from "../abis/index.js";
3
+ // `verifySignature` is a `view` - read it directly instead of spending a userOp
4
+ // (the previous userOp form returned a userOp hash, never the boolean result).
4
5
  export const _verifySignature = async (client, message, requireMessageVerification, webAuthnSignature, x, y) => {
5
6
  const chainID = await client.getChainId();
6
7
  const rpcURL = client.transport.url;
7
8
  const values = _getChainSpecificConstants(chainID, rpcURL);
8
- if (!client.account)
9
- throw new Error(customErrors.MISSING_SMART_WALLET);
10
- // UserOp
11
- return client.sendUserOperation({
12
- account: client.account,
13
- uo: {
14
- target: values.factoryAddresses.P256VERIFIER,
15
- data: encodeFunctionData({
16
- abi: P256Verifier,
17
- functionName: "verifySignature",
18
- args: [message, requireMessageVerification, webAuthnSignature, x, y]
19
- })
20
- }
9
+ return client.readContract({
10
+ address: values.factoryAddresses.P256VERIFIER,
11
+ abi: P256Verifier,
12
+ functionName: "verifySignature",
13
+ args: [message, requireMessageVerification, webAuthnSignature, x, y]
21
14
  });
22
15
  };
@@ -1,9 +1,9 @@
1
- import { createSmartAccountClient, getEntryPoint, toSmartContractAccount, split } from "@aa-sdk/core";
2
- import { http, encodeFunctionData, encodePacked, encodeAbiParameters, parseAbiParameters, getContract, concat, keccak256, getContractAddress, getAddress, hashMessage, stringToBytes, toHex, hexToBytes, bytesToHex } from "viem";
1
+ import { createSmartAccountClient, getEntryPoint, toSmartContractAccount, split, erc7677Middleware } from "@aa-sdk/core";
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
- import { alchemyGasManagerMiddleware } from "@account-kit/infra";
7
7
  import { isoBase64URL } from "@simplewebauthn/server/helpers";
8
8
  import { Passkey } from "react-native-passkey";
9
9
  import { p256 } from "@noble/curves/nist.js";
@@ -16,6 +16,22 @@ var AuthenticatorTransport;
16
16
  AuthenticatorTransport["hybrid"] = "hybrid";
17
17
  AuthenticatorTransport["internal"] = "internal";
18
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";
19
35
  /*
20
36
  ** Stamp is client-side authentication. Since the passkeys are one the user's mobile device
21
37
  ** react-native-passkey helps fetch passkey for the user (provided credentialId, rpId).
@@ -23,7 +39,10 @@ var AuthenticatorTransport;
23
39
  */
24
40
  export const _stamp = async (credentialId, rpId, payload) => {
25
41
  const signingOptions = {
26
- 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))),
27
46
  allowCredentials: [{
28
47
  id: credentialId,
29
48
  type: "public-key",
@@ -120,7 +139,7 @@ const _encodeBatchExecute = async (txs) => {
120
139
  args: [new_txs]
121
140
  });
122
141
  };
123
- const _getAccountInitCode = async (client, deviceUniqueIdentifier, deviceWalletOwnerKey, salt) => {
142
+ export const _getAccountInitCode = async (client, deviceUniqueIdentifier, deviceWalletOwnerKey, salt) => {
124
143
  // To send with user operations
125
144
  const chainID = await client.getChainId();
126
145
  const rpcURL = client.transport.url;
@@ -132,7 +151,7 @@ const _getAccountInitCode = async (client, deviceUniqueIdentifier, deviceWalletO
132
151
  });
133
152
  return values.factoryAddresses.DEVICE_WALLET_FACTORY.concat(_remove0x(callData));
134
153
  };
135
- const getInitCodeHash = async (client, deviceUniqueIdentifier, deviceWalletOwnerKey) => {
154
+ export const getInitCodeHash = async (client, deviceUniqueIdentifier, deviceWalletOwnerKey) => {
136
155
  const chainID = await client.getChainId();
137
156
  const rpcURL = client.transport.url;
138
157
  const values = _getChainSpecificConstants(chainID, rpcURL);
@@ -145,7 +164,7 @@ const getInitCodeHash = async (client, deviceUniqueIdentifier, deviceWalletOwner
145
164
  address: deviceWalletFactoryAddress,
146
165
  client
147
166
  });
148
- const beacon = await deviceWalletFactory.read.beacon([]);
167
+ const beacon = await deviceWalletFactory.read.beacon();
149
168
  // Encode the DeviceWallet.init with the init params
150
169
  const deviceWalletInitData = encodeFunctionData({
151
170
  abi: DeviceWallet,
@@ -157,14 +176,13 @@ const getInitCodeHash = async (client, deviceUniqueIdentifier, deviceWalletOwner
157
176
  eSIMWalletFactoryAddress
158
177
  ]
159
178
  });
160
- const beaconProxyBytecode = "0x60a06040908082526104a8803803809161001982856102ae565b8339810182828203126101e95761002f826102e7565b60208084015191939091906001600160401b0382116101e9570182601f820112156101e957805190610060826102fb565b9361006d875195866102ae565b8285528383830101116101e957829060005b83811061029a57505060009184010152823b1561027a577fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5080546001600160a01b0319166001600160a01b038581169182179092558551635c60da1b60e01b8082529194928482600481895afa91821561026f57600092610238575b50813b1561021f5750508551847f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e600080a282511561020057508290600487518096819382525afa9283156101f5576000936101b3575b5091600080848461019096519101845af4903d156101aa573d610174816102fb565b90610181885192836102ae565b8152600081943d92013e610316565b505b6080525161012e908161037a82396080518160180152f35b60609250610316565b92508183813d83116101ee575b6101ca81836102ae565b810103126101e9576000806101e1610190956102e7565b945050610152565b600080fd5b503d6101c0565b85513d6000823e3d90fd5b9350505050346102105750610192565b63b398979f60e01b8152600490fd5b8751634c9c8ce360e01b81529116600482015260249150fd5b9091508481813d8311610268575b61025081836102ae565b810103126101e957610261906102e7565b90386100fb565b503d610246565b88513d6000823e3d90fd5b8351631933b43b60e21b81526001600160a01b0384166004820152602490fd5b81810183015186820184015284920161007f565b601f909101601f19168101906001600160401b038211908210176102d157604052565b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036101e957565b6001600160401b0381116102d157601f01601f191660200190565b9061033d575080511561032b57805190602001fd5b604051630a12f52160e11b8152600490fd5b81511580610370575b61034e575090565b604051639996b31560e01b81526001600160a01b039091166004820152602490fd5b50803b1561034656fe60806040819052635c60da1b60e01b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811560a9576000916054575b5060da565b905060203d60201160a3575b601f8101601f191682019167ffffffffffffffff831181841017608d576088926040520160b5565b38604f565b634e487b7160e01b600052604160045260246000fd5b503d6060565b6040513d6000823e3d90fd5b602090607f19011260d5576080516001600160a01b038116810360d55790565b600080fd5b6000808092368280378136915af43d82803e1560f4573d90f35b3d90fdfea264697066735822122099ba460fd62b3e22c737d15959887e6cae3498f3495d31e43e2bcf1283aec7d264736f6c63430008190033";
161
179
  // Encode BeaconProxy constructor args
162
180
  const beaconProxyConstructorArgs = encodeAbiParameters(parseAbiParameters("address, bytes"), [beacon, deviceWalletInitData]);
163
181
  // Compute initCode
164
- const initCode = concat([beaconProxyBytecode, beaconProxyConstructorArgs]);
182
+ const initCode = concat([BEACON_PROXY_CREATION_CODE, beaconProxyConstructorArgs]);
165
183
  return keccak256(initCode);
166
184
  };
167
- const getCounterFactualAddress = async (client, deviceUniqueIdentifier, deviceWalletOwnerKey, salt) => {
185
+ export const getCounterFactualAddress = async (client, deviceUniqueIdentifier, deviceWalletOwnerKey, salt) => {
168
186
  const chainID = await client.getChainId();
169
187
  const rpcURL = client.transport.url;
170
188
  const values = _getChainSpecificConstants(chainID, rpcURL);
@@ -180,7 +198,41 @@ const getCounterFactualAddress = async (client, deviceUniqueIdentifier, deviceWa
180
198
  });
181
199
  return getAddress(create2Address);
182
200
  };
183
- 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) => {
184
236
  const encodedWebAuthnSignatureBytes = encodeAbiParameters([
185
237
  {
186
238
  type: "tuple",
@@ -201,30 +253,30 @@ const _encodeSignature = async (webAuthnSignature, validUntil) => {
201
253
  return signature;
202
254
  };
203
255
  // message here is the original message data (string or Uint8Array) directly from the app
204
- const _signMessage = async (message, credentialId, rpId) => {
256
+ export const _signMessage = async (message, credentialId, rpId) => {
205
257
  const validUntil = Math.floor(Date.now() / 1000) + SIGNATURE_VALIDITY_SECONDS;
206
- 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);
207
263
  // The original message is passed to the stamp and sign function.
208
264
  // The stamp and sign function creates the EIP-191 digest hash using its hashMessage function
209
265
  // The result of the hashMessage(message) will be the `payload` used as a challenge
210
266
  const webAuthnSignature = await _stamp(credentialId, rpId, payload);
211
267
  return _encodeSignature(webAuthnSignature, validUntil);
212
268
  };
213
- const _signTypedData = async (typedData, organiationId, signWith) => {
269
+ export const _signTypedData = async (typedData, credentialId, rpId) => {
214
270
  // signature valid until, UNIX timestamp in seconds
215
271
  const validUntil = Math.floor(Date.now() / 1000) + SIGNATURE_VALIDITY_SECONDS;
216
- // TODO: Implement stamping for Typed Data
217
- let webAuthnSignature = {
218
- authenticatorData: `0x`,
219
- clientDataJSON: "",
220
- challengeIndex: BigInt(0),
221
- typeIndex: BigInt(0),
222
- r: BigInt(0),
223
- s: BigInt(0)
224
- };
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);
225
277
  return _encodeSignature(webAuthnSignature, validUntil);
226
278
  };
227
- const _signUserOperationHash = async (credentialId, rpId, userOpHash) => {
279
+ export const _signUserOperationHash = async (credentialId, rpId, userOpHash) => {
228
280
  const validUntil = Math.floor(Date.now() / 1000) + SIGNATURE_VALIDITY_SECONDS;
229
281
  const messagePrecursor = encodePacked(["uint8", "uint48", "bytes32"], [
230
282
  1,
@@ -241,7 +293,6 @@ export const _getSmartWallet = async (client, credentialId, rpId, organiationId,
241
293
  const values = _getChainSpecificConstants(chainID, rpcURL);
242
294
  if (!client.account)
243
295
  throw new Error('Error: No signer account found with WalletClient');
244
- const signWith = client.account.address;
245
296
  return toSmartContractAccount({
246
297
  /// REQUIRED PARAMS ///
247
298
  source: "MyAccount",
@@ -256,7 +307,7 @@ export const _getSmartWallet = async (client, credentialId, rpId, organiationId,
256
307
  // given a UO in the form of {target, data, value} should output the calldata for calling your contract's execution method
257
308
  encodeExecute: async (uo) => _encodeExecute(uo),
258
309
  signMessage: async ({ message }) => _signMessage(message, credentialId, rpId),
259
- signTypedData: async (typedData) => _signTypedData(typedData, organiationId, signWith),
310
+ signTypedData: async (typedData) => _signTypedData(typedData, credentialId, rpId),
260
311
  /// OPTIONAL PARAMS ///
261
312
  // if you already know your account's address, pass that in here to avoid generating a new counterfactual
262
313
  accountAddress: await getCounterFactualAddress(client, deviceUniqueIdentifier, deviceWalletOwnerKey, salt),
@@ -294,6 +345,6 @@ export const _getSmartWalletClient = async (client, pimlicoAPIKey, gasPolicyId,
294
345
  fallback: http(values.rpcURL),
295
346
  }),
296
347
  // transport: http(values.rpcURL),
297
- ...alchemyGasManagerMiddleware(gasPolicyId)
348
+ ...erc7677Middleware({ context: { policyId: gasPolicyId } })
298
349
  });
299
350
  };
@@ -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
+ };