passkey-kit 0.3.0 → 0.4.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.
package/README.md CHANGED
@@ -13,13 +13,14 @@ On the client:
13
13
  ```ts
14
14
  const account = new PasskeyKit({
15
15
  rpcUrl: env.RPC_URL,
16
- networkPassphrase: env.NETWORK_PASSPHRASE
16
+ networkPassphrase: env.NETWORK_PASSPHRASE,
17
+ factoryContractId: env.FACTORY_CONTRACT_ID,
17
18
  });
18
19
  ```
19
20
 
20
21
  On the server:
21
22
  ```ts
22
- const account = new PasskeyKit({
23
+ const account = new PasskeyBase({
23
24
  launchtubeUrl: env.LAUNCHTUBE_URL,
24
25
  launchtubeJwt: env.LAUNCHTUBE_JWT,
25
26
  });
@@ -37,6 +38,7 @@ For any questions or to showcase your progress please join the `#passkeys` chann
37
38
 
38
39
  Passkey kit consists of three primary directories:
39
40
  - `./contracts` - Contains the Rust Soroban smart contracts of the smart wallet implementation.
41
+ - `./zephyr` - Contains the [Zephyr](https://www.mercurydata.app/products/zephyr-vm) program for processing smart wallet events.
40
42
  - `./src` - Contains the TypeScript files for the actual TS SDK library.
41
43
  - `./demo` - Contains a basic demo of the SDK in action.
42
44
 
package/cheatsheet CHANGED
@@ -9,7 +9,8 @@ pnpm publish --no-git-checks
9
9
 
10
10
  export MERCURY_JWT=???
11
11
  mercury-cli --jwt $MERCURY_JWT --local false --mainnet false deploy
12
- mercury-cli --jwt $MERCURY_JWT --local false --mainnet false catchup --contracts "???" # don't forget to subscribe to the contract first
13
- curl -X POST https://api.mercurydata.app/zephyr/execute -H "Authorization: Bearer $MERCURY_JWT" -H 'Content-Type: application/json' -d '{"mode":{"Function": {"fname": "get_signers_by_address", "arguments": "{\"address\": \"CBLWEW63G2KU7ZGVQTTFTJLGQSXAU5PU5ZXERQDDLL4LBA72D7ER6C75\"}"}}}'
12
+ mercury-cli --jwt $MERCURY_JWT --local false --mainnet false catchup --contracts "CCXIUFR243N7QWKXY44LZO3DVUXHAVBEEJWFGNGNVVB44VJXIYWVGNTR" # don't forget to subscribe to the contract first
13
+ curl -X GET https://api.mercurydata.app/catchups/4
14
+ curl -X POST https://api.mercurydata.app/zephyr/execute -H "Authorization: Bearer $MERCURY_JWT" -H 'Content-Type: application/json' -d '{"mode":{"Function": {"fname": "get_signers_by_address", "arguments": "{\"address\": \"CCXIUFR243N7QWKXY44LZO3DVUXHAVBEEJWFGNGNVVB44VJXIYWVGNTR\"}"}}}'
14
15
  curl -X POST https://api.mercurydata.app/zephyr/execute -H "Authorization: Bearer $MERCURY_JWT" -H 'Content-Type: application/json' -d '{"mode":{"Function": {"fname": "get_address_by_signer", "arguments": "{\"id\": [78,127,30,69,248,174,95,168,47,59,161,168,51,120,117,135,112,133,232,92]}"}}}'
15
- curl -X POST https://api.mercurydata.app/zephyr/execute -H "Authorization: Bearer $MERCURY_JWT" -H 'Content-Type: application/json' -d '{"mode":{"Function": {"fname": "get_events_by_address", "arguments": "{\"address\": \"CBLWEW63G2KU7ZGVQTTFTJLGQSXAU5PU5ZXERQDDLL4LBA72D7ER6C75\"}"}}}'
16
+ curl -X POST https://api.mercurydata.app/zephyr/execute -H "Authorization: Bearer $MERCURY_JWT" -H 'Content-Type: application/json' -d '{"mode":{"Function": {"fname": "get_events_by_address", "arguments": "{\"address\": \"CCXIUFR243N7QWKXY44LZO3DVUXHAVBEEJWFGNGNVVB44VJXIYWVGNTR\"}"}}}'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "passkey-kit",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "A helper library for creating and using passkey accounts on the Stellar blockchain.",
5
5
  "author": "Tyler van der Hoeven",
6
6
  "license": "MIT",
@@ -11,13 +11,13 @@
11
11
  "@simplewebauthn/browser": "^10.0.0",
12
12
  "@stellar/stellar-sdk": "12.1.0",
13
13
  "base64url": "^3.0.1",
14
- "bigint-conversion": "^2.4.3",
15
14
  "buffer": "^6.0.3",
16
15
  "cbor-x": "^1.5.9",
17
- "passkey-factory-sdk": "0.1.0",
18
- "passkey-kit-sdk": "0.1.0"
16
+ "passkey-factory-sdk": "0.2.0",
17
+ "passkey-kit-sdk": "0.2.0"
19
18
  },
20
19
  "devDependencies": {
20
+ "@simplewebauthn/types": "^10.0.0",
21
21
  "typescript": "^5.5.3"
22
22
  },
23
23
  "workspaces": [
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.0",
2
+ "version": "0.2.0",
3
3
  "name": "passkey-factory-sdk",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
@@ -10,9 +10,9 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "buffer": "6.0.3",
13
- "@stellar/stellar-sdk": "12.0.1"
13
+ "@stellar/stellar-sdk": "12.1.0"
14
14
  },
15
15
  "devDependencies": {
16
- "typescript": "5.4.5"
16
+ "typescript": "5.5.3"
17
17
  }
18
18
  }
@@ -15,13 +15,13 @@ if (typeof window !== 'undefined') {
15
15
  export const networks = {
16
16
  testnet: {
17
17
  networkPassphrase: "Test SDF Network ; September 2015",
18
- contractId: "CA6DVSXLLOSI32JAPGP4MLOG64FGERLYCP2SGRX2A6MPWGA5KDUKL66P",
18
+ contractId: "CBCPNJNIR7I6ZI5AIXTMYI3MCDCSTNUZ57XL75HQWI6Y4ESWUP24HRBG",
19
19
  }
20
20
  } as const
21
21
 
22
22
  export const Errors = {
23
- 1: { message: "" },
24
- 2: { message: "" }
23
+ 1: { message: "NotInitialized" },
24
+ 2: { message: "AlreadyInitialized" }
25
25
  }
26
26
 
27
27
  export interface Client {
@@ -3,7 +3,7 @@ import { AssembledTransaction, Client as ContractClient, ClientOptions as Contra
3
3
  export declare const networks: {
4
4
  readonly testnet: {
5
5
  readonly networkPassphrase: "Test SDF Network ; September 2015";
6
- readonly contractId: "CA6DVSXLLOSI32JAPGP4MLOG64FGERLYCP2SGRX2A6MPWGA5KDUKL66P";
6
+ readonly contractId: "CBCPNJNIR7I6ZI5AIXTMYI3MCDCSTNUZ57XL75HQWI6Y4ESWUP24HRBG";
7
7
  };
8
8
  };
9
9
  export declare const Errors: {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.1.0",
2
+ "version": "0.2.0",
3
3
  "name": "passkey-kit-sdk",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
@@ -10,9 +10,9 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "buffer": "6.0.3",
13
- "@stellar/stellar-sdk": "12.0.1"
13
+ "@stellar/stellar-sdk": "12.1.0"
14
14
  },
15
15
  "devDependencies": {
16
- "typescript": "5.4.5"
16
+ "typescript": "5.5.3"
17
17
  }
18
18
  }
@@ -15,21 +15,19 @@ if (typeof window !== 'undefined') {
15
15
  export const networks = {
16
16
  testnet: {
17
17
  networkPassphrase: "Test SDF Network ; September 2015",
18
- contractId: "CA6DVSXLLOSI32JAPGP4MLOG64FGERLYCP2SGRX2A6MPWGA5KDUKL66P",
18
+ contractId: "CBCPNJNIR7I6ZI5AIXTMYI3MCDCSTNUZ57XL75HQWI6Y4ESWUP24HRBG",
19
19
  }
20
20
  } as const
21
21
 
22
22
  export const Errors = {
23
- 1: { message: "" },
24
- 2: { message: "" },
25
- 3: { message: "" },
26
- 4: { message: "" },
27
- 5: { message: "" },
28
- 6: { message: "" },
29
- 7: { message: "" },
30
- 8: { message: "" },
31
- 9: { message: "" },
32
- 10: { message: "" }
23
+ 1: { message: "NotFound" },
24
+ 2: { message: "NotPermitted" },
25
+ 3: { message: "AlreadyInitialized" },
26
+ 4: { message: "ClientDataJsonChallengeIncorrect" },
27
+ 5: { message: "Secp256r1PublicKeyParse" },
28
+ 6: { message: "Secp256r1SignatureParse" },
29
+ 7: { message: "Secp256r1VerifyFailed" },
30
+ 8: { message: "JsonParseError" }
33
31
  }
34
32
 
35
33
  export interface Signature {
@@ -61,29 +59,9 @@ export interface Client {
61
59
  }) => Promise<AssembledTransaction<Result<void>>>
62
60
 
63
61
  /**
64
- * Construct and simulate a upgrade transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
65
- */
66
- upgrade: ({ hash }: { hash: Buffer }, options?: {
67
- /**
68
- * The fee to pay for the transaction. Default: BASE_FEE
69
- */
70
- fee?: number;
71
-
72
- /**
73
- * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT
74
- */
75
- timeoutInSeconds?: number;
76
-
77
- /**
78
- * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true
79
- */
80
- simulate?: boolean;
81
- }) => Promise<AssembledTransaction<Result<void>>>
82
-
83
- /**
84
- * Construct and simulate a add_sig transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
62
+ * Construct and simulate a add transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
85
63
  */
86
- add_sig: ({ id, pk }: { id: Buffer, pk: Buffer }, options?: {
64
+ add: ({ id, pk, admin }: { id: Buffer, pk: Buffer, admin: boolean }, options?: {
87
65
  /**
88
66
  * The fee to pay for the transaction. Default: BASE_FEE
89
67
  */
@@ -101,9 +79,9 @@ export interface Client {
101
79
  }) => Promise<AssembledTransaction<Result<void>>>
102
80
 
103
81
  /**
104
- * Construct and simulate a rm_sig transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
82
+ * Construct and simulate a remove transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
105
83
  */
106
- rm_sig: ({ id }: { id: Buffer }, options?: {
84
+ remove: ({ id }: { id: Buffer }, options?: {
107
85
  /**
108
86
  * The fee to pay for the transaction. Default: BASE_FEE
109
87
  */
@@ -121,9 +99,9 @@ export interface Client {
121
99
  }) => Promise<AssembledTransaction<Result<void>>>
122
100
 
123
101
  /**
124
- * Construct and simulate a re_super transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
102
+ * Construct and simulate a upgrade transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
125
103
  */
126
- re_super: ({ id }: { id: Buffer }, options?: {
104
+ upgrade: ({ hash }: { hash: Buffer }, options?: {
127
105
  /**
128
106
  * The fee to pay for the transaction. Default: BASE_FEE
129
107
  */
@@ -143,12 +121,11 @@ export interface Client {
143
121
  export class Client extends ContractClient {
144
122
  constructor(public readonly options: ContractClientOptions) {
145
123
  super(
146
- new ContractSpec(["AAAABAAAAAAAAAAAAAAABUVycm9yAAAAAAAACgAAAAAAAAAOTm90SW5pdGlhbGl6ZWQAAAAAAAEAAAAAAAAACE5vdEZvdW5kAAAAAgAAAAAAAAAMTm90UGVybWl0dGVkAAAAAwAAAAAAAAASQWxyZWFkeUluaXRpYWxpemVkAAAAAAAEAAAAAAAAAA5Kc29uUGFyc2VFcnJvcgAAAAAABQAAAAAAAAAOSW52YWxpZENvbnRleHQAAAAAAAYAAAAAAAAAIENsaWVudERhdGFKc29uQ2hhbGxlbmdlSW5jb3JyZWN0AAAABwAAAAAAAAAXU2VjcDI1NnIxUHVibGljS2V5UGFyc2UAAAAACAAAAAAAAAAXU2VjcDI1NnIxU2lnbmF0dXJlUGFyc2UAAAAACQAAAAAAAAAVU2VjcDI1NnIxVmVyaWZ5RmFpbGVkAAAAAAAACg==",
124
+ new ContractSpec(["AAAABAAAAAAAAAAAAAAABUVycm9yAAAAAAAACAAAAAAAAAAITm90Rm91bmQAAAABAAAAAAAAAAxOb3RQZXJtaXR0ZWQAAAACAAAAAAAAABJBbHJlYWR5SW5pdGlhbGl6ZWQAAAAAAAMAAAAAAAAAIENsaWVudERhdGFKc29uQ2hhbGxlbmdlSW5jb3JyZWN0AAAABAAAAAAAAAAXU2VjcDI1NnIxUHVibGljS2V5UGFyc2UAAAAABQAAAAAAAAAXU2VjcDI1NnIxU2lnbmF0dXJlUGFyc2UAAAAABgAAAAAAAAAVU2VjcDI1NnIxVmVyaWZ5RmFpbGVkAAAAAAAABwAAAAAAAAAOSnNvblBhcnNlRXJyb3IAAAAAAAg=",
147
125
  "AAAAAAAAAAAAAAAEaW5pdAAAAAIAAAAAAAAAAmlkAAAAAAAOAAAAAAAAAAJwawAAAAAD7gAAAEEAAAABAAAD6QAAA+0AAAAAAAAAAw==",
126
+ "AAAAAAAAAAAAAAADYWRkAAAAAAMAAAAAAAAAAmlkAAAAAAAOAAAAAAAAAAJwawAAAAAD7gAAAEEAAAAAAAAABWFkbWluAAAAAAAAAQAAAAEAAAPpAAAD7QAAAAAAAAAD",
127
+ "AAAAAAAAAAAAAAAGcmVtb3ZlAAAAAAABAAAAAAAAAAJpZAAAAAAADgAAAAEAAAPpAAAD7QAAAAAAAAAD",
148
128
  "AAAAAAAAAAAAAAAHdXBncmFkZQAAAAABAAAAAAAAAARoYXNoAAAD7gAAACAAAAABAAAD6QAAA+0AAAAAAAAAAw==",
149
- "AAAAAAAAAAAAAAAHYWRkX3NpZwAAAAACAAAAAAAAAAJpZAAAAAAADgAAAAAAAAACcGsAAAAAA+4AAABBAAAAAQAAA+kAAAPtAAAAAAAAAAM=",
150
- "AAAAAAAAAAAAAAAGcm1fc2lnAAAAAAABAAAAAAAAAAJpZAAAAAAADgAAAAEAAAPpAAAD7QAAAAAAAAAD",
151
- "AAAAAAAAAAAAAAAIcmVfc3VwZXIAAAABAAAAAAAAAAJpZAAAAAAADgAAAAEAAAPpAAAD7QAAAAAAAAAD",
152
129
  "AAAAAQAAAAAAAAAAAAAACVNpZ25hdHVyZQAAAAAAAAQAAAAAAAAAEmF1dGhlbnRpY2F0b3JfZGF0YQAAAAAADgAAAAAAAAAQY2xpZW50X2RhdGFfanNvbgAAAA4AAAAAAAAAAmlkAAAAAAAOAAAAAAAAAAlzaWduYXR1cmUAAAAAAAPuAAAAQA==",
153
130
  "AAAAAAAAAAAAAAAMX19jaGVja19hdXRoAAAAAwAAAAAAAAARc2lnbmF0dXJlX3BheWxvYWQAAAAAAAPuAAAAIAAAAAAAAAAJc2lnbmF0dXJlAAAAAAAH0AAAAAlTaWduYXR1cmUAAAAAAAAAAAAADWF1dGhfY29udGV4dHMAAAAAAAPqAAAH0AAAAAdDb250ZXh0AAAAAAEAAAPpAAAD7QAAAAAAAAAD"]),
154
131
  options
@@ -156,9 +133,8 @@ export class Client extends ContractClient {
156
133
  }
157
134
  public readonly fromJSON = {
158
135
  init: this.txFromJSON<Result<void>>,
159
- upgrade: this.txFromJSON<Result<void>>,
160
- add_sig: this.txFromJSON<Result<void>>,
161
- rm_sig: this.txFromJSON<Result<void>>,
162
- re_super: this.txFromJSON<Result<void>>
136
+ add: this.txFromJSON<Result<void>>,
137
+ remove: this.txFromJSON<Result<void>>,
138
+ upgrade: this.txFromJSON<Result<void>>
163
139
  }
164
140
  }
@@ -3,7 +3,7 @@ import { AssembledTransaction, Client as ContractClient, ClientOptions as Contra
3
3
  export declare const networks: {
4
4
  readonly testnet: {
5
5
  readonly networkPassphrase: "Test SDF Network ; September 2015";
6
- readonly contractId: "CA6DVSXLLOSI32JAPGP4MLOG64FGERLYCP2SGRX2A6MPWGA5KDUKL66P";
6
+ readonly contractId: "CBCPNJNIR7I6ZI5AIXTMYI3MCDCSTNUZ57XL75HQWI6Y4ESWUP24HRBG";
7
7
  };
8
8
  };
9
9
  export declare const Errors: {
@@ -31,12 +31,6 @@ export declare const Errors: {
31
31
  8: {
32
32
  message: string;
33
33
  };
34
- 9: {
35
- message: string;
36
- };
37
- 10: {
38
- message: string;
39
- };
40
34
  };
41
35
  export interface Signature {
42
36
  authenticator_data: Buffer;
@@ -66,30 +60,12 @@ export interface Client {
66
60
  simulate?: boolean;
67
61
  }) => Promise<AssembledTransaction<Result<void>>>;
68
62
  /**
69
- * Construct and simulate a upgrade transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
63
+ * Construct and simulate a add transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
70
64
  */
71
- upgrade: ({ hash }: {
72
- hash: Buffer;
73
- }, options?: {
74
- /**
75
- * The fee to pay for the transaction. Default: BASE_FEE
76
- */
77
- fee?: number;
78
- /**
79
- * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT
80
- */
81
- timeoutInSeconds?: number;
82
- /**
83
- * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true
84
- */
85
- simulate?: boolean;
86
- }) => Promise<AssembledTransaction<Result<void>>>;
87
- /**
88
- * Construct and simulate a add_sig transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
89
- */
90
- add_sig: ({ id, pk }: {
65
+ add: ({ id, pk, admin }: {
91
66
  id: Buffer;
92
67
  pk: Buffer;
68
+ admin: boolean;
93
69
  }, options?: {
94
70
  /**
95
71
  * The fee to pay for the transaction. Default: BASE_FEE
@@ -105,9 +81,9 @@ export interface Client {
105
81
  simulate?: boolean;
106
82
  }) => Promise<AssembledTransaction<Result<void>>>;
107
83
  /**
108
- * Construct and simulate a rm_sig transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
84
+ * Construct and simulate a remove transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
109
85
  */
110
- rm_sig: ({ id }: {
86
+ remove: ({ id }: {
111
87
  id: Buffer;
112
88
  }, options?: {
113
89
  /**
@@ -124,10 +100,10 @@ export interface Client {
124
100
  simulate?: boolean;
125
101
  }) => Promise<AssembledTransaction<Result<void>>>;
126
102
  /**
127
- * Construct and simulate a re_super transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
103
+ * Construct and simulate a upgrade transaction. Returns an `AssembledTransaction` object which will have a `result` field containing the result of the simulation. If this transaction changes contract state, you will need to call `signAndSend()` on the returned object.
128
104
  */
129
- re_super: ({ id }: {
130
- id: Buffer;
105
+ upgrade: ({ hash }: {
106
+ hash: Buffer;
131
107
  }, options?: {
132
108
  /**
133
109
  * The fee to pay for the transaction. Default: BASE_FEE
@@ -148,9 +124,8 @@ export declare class Client extends ContractClient {
148
124
  constructor(options: ContractClientOptions);
149
125
  readonly fromJSON: {
150
126
  init: (json: string) => AssembledTransaction<Result<void, import("@stellar/stellar-sdk/contract").ErrorMessage>>;
127
+ add: (json: string) => AssembledTransaction<Result<void, import("@stellar/stellar-sdk/contract").ErrorMessage>>;
128
+ remove: (json: string) => AssembledTransaction<Result<void, import("@stellar/stellar-sdk/contract").ErrorMessage>>;
151
129
  upgrade: (json: string) => AssembledTransaction<Result<void, import("@stellar/stellar-sdk/contract").ErrorMessage>>;
152
- add_sig: (json: string) => AssembledTransaction<Result<void, import("@stellar/stellar-sdk/contract").ErrorMessage>>;
153
- rm_sig: (json: string) => AssembledTransaction<Result<void, import("@stellar/stellar-sdk/contract").ErrorMessage>>;
154
- re_super: (json: string) => AssembledTransaction<Result<void, import("@stellar/stellar-sdk/contract").ErrorMessage>>;
155
130
  };
156
131
  }
package/src/kit.ts CHANGED
@@ -1,9 +1,9 @@
1
1
  import { Client as PasskeyClient } from 'passkey-kit-sdk'
2
2
  import { Client as FactoryClient } from 'passkey-factory-sdk'
3
- import { Address, Networks, StrKey, hash, xdr, Transaction, SorobanRpc, Operation, scValToNative, TransactionBuilder } from '@stellar/stellar-sdk'
4
- import { bufToBigint, bigintToBuf } from 'bigint-conversion'
3
+ import { Address, Networks, StrKey, hash, xdr, Transaction, SorobanRpc, Operation, TransactionBuilder } from '@stellar/stellar-sdk'
5
4
  import base64url from 'base64url'
6
5
  import { startRegistration, startAuthentication } from "@simplewebauthn/browser"
6
+ import type { AuthenticatorAttestationResponseJSON } from "@simplewebauthn/types"
7
7
  import { decode } from 'cbor-x/decode'
8
8
  import { Buffer } from 'buffer'
9
9
  import { PasskeyBase } from './base'
@@ -49,8 +49,8 @@ export class PasskeyKit extends PasskeyBase {
49
49
  })
50
50
  }
51
51
 
52
- public async createWallet(name: string, user: string) {
53
- const { keyId, publicKey } = await this.createKey(name, user)
52
+ public async createWallet(app: string, user: string) {
53
+ const { keyId, publicKey } = await this.createKey(app, user)
54
54
 
55
55
  const { result, built } = await this.factory.deploy({
56
56
  id: keyId,
@@ -72,17 +72,19 @@ export class PasskeyKit extends PasskeyBase {
72
72
  }
73
73
  }
74
74
 
75
- public async createKey(name: string, user: string) {
76
- const startRegistrationResponse = await startRegistration({
75
+ public async createKey(app: string, user: string) {
76
+ const now = new Date()
77
+ const displayName = `${user} — ${now.toLocaleString()}`
78
+ const { id, response} = await startRegistration({
77
79
  challenge: base64url("stellaristhebetterblockchain"),
78
80
  rp: {
79
81
  // id: undefined,
80
- name,
82
+ name: app,
81
83
  },
82
- user: {
83
- id: base64url(user),
84
- name: user,
85
- displayName: user,
84
+ user: { // TODO there's a real danger here of overwriting a user's key if they use the same `user` name
85
+ id: base64url(`${user}:${now.getTime()}:${Math.random()}`),
86
+ name: displayName,
87
+ displayName
86
88
  },
87
89
  authenticatorSelection: {
88
90
  requireResidentKey: false,
@@ -94,18 +96,12 @@ export class PasskeyKit extends PasskeyBase {
94
96
  });
95
97
 
96
98
  if (!this.keyId)
97
- this.keyId = startRegistrationResponse.id
99
+ this.keyId = id;
98
100
 
99
- const { publicKeyObject } = this.getPublicKeyObject(startRegistrationResponse.response.attestationObject);
100
-
101
- const publicKey = Buffer.from([
102
- 4, // (0x04 prefix) https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm
103
- ...publicKeyObject.get('-2')!,
104
- ...publicKeyObject.get('-3')!
105
- ])
101
+ const publicKey = this.getPublicKey(response);
106
102
 
107
103
  return {
108
- keyId: base64url.toBuffer(startRegistrationResponse.id),
104
+ keyId: base64url.toBuffer(id),
109
105
  publicKey
110
106
  }
111
107
  }
@@ -115,21 +111,23 @@ export class PasskeyKit extends PasskeyBase {
115
111
  getContractId?: GetContractIdFunction
116
112
  }) {
117
113
  let { keyId, getContractId } = opts
118
- let keyIdBuffer: Uint8Array
114
+ let keyIdBuffer: Buffer
119
115
 
120
116
  if (!keyId) {
121
- const { id } = await startAuthentication({
117
+ const response = await startAuthentication({
122
118
  challenge: base64url("stellaristhebetterblockchain"),
123
119
  // rpId: undefined,
124
120
  userVerification: "discouraged",
125
121
  });
126
122
 
127
- keyId = id
123
+ console.log(response);
124
+
125
+ keyId = response.id
128
126
  }
129
127
 
130
128
  if (keyId instanceof Uint8Array) {
131
- keyIdBuffer = keyId
132
- keyId = base64url(keyId)
129
+ keyIdBuffer = Buffer.from(keyId)
130
+ keyId = base64url(keyIdBuffer)
133
131
  } else {
134
132
  keyIdBuffer = base64url.toBuffer(keyId)
135
133
  }
@@ -222,7 +220,7 @@ export class PasskeyKit extends PasskeyBase {
222
220
  allowCredentials: [
223
221
  {
224
222
  id: keyId instanceof Uint8Array
225
- ? base64url(keyId)
223
+ ? base64url(Buffer.from(keyId))
226
224
  : keyId || this.keyId!,
227
225
  type: "public-key",
228
226
  },
@@ -231,8 +229,9 @@ export class PasskeyKit extends PasskeyBase {
231
229
  }
232
230
  );
233
231
 
234
- const signatureRaw = base64url.toBuffer(authenticationResponse.response.signature);
235
- const signature = this.convertEcdsaSignatureAsnToCompact(signatureRaw);
232
+ const signature = this.compactSignature(
233
+ base64url.toBuffer(authenticationResponse.response.signature)
234
+ );
236
235
 
237
236
  credentials.signatureExpirationLedger(lastLedger + ledgersToLive)
238
237
  credentials.signature(xdr.ScVal.scvMap([
@@ -323,27 +322,6 @@ export class PasskeyKit extends PasskeyBase {
323
322
 
324
323
  return SorobanRpc.assembleTransaction(txn, sim).build().toXDR()
325
324
  }
326
- public async getSuperKeyId() {
327
- const data: Map<string, any> = new Map()
328
-
329
- const { val } = await this.rpc.getContractData(
330
- this.wallet!.options.contractId,
331
- xdr.ScVal.scvLedgerKeyContractInstance(),
332
- );
333
-
334
- val.contractData()
335
- .val()
336
- .instance()
337
- .storage()
338
- ?.forEach((entry) => {
339
- data.set(
340
- scValToNative(entry.key()),
341
- scValToNative(entry.val()),
342
- );
343
- });
344
-
345
- return base64url(data.get('super'))
346
- }
347
325
 
348
326
  /* TODO
349
327
  - Add a getKeyInfo action to get info about a specific passkey
@@ -351,133 +329,66 @@ export class PasskeyKit extends PasskeyBase {
351
329
  @Later
352
330
  */
353
331
 
354
- private getPublicKeyObject(attestationObject: string) {
355
- const { authData } = decode(base64url.toBuffer(attestationObject));
356
- const authDataUint8Array = new Uint8Array(authData);
357
- const authDataView = new DataView(authDataUint8Array.buffer, 0, authDataUint8Array.length);
358
-
359
- let offset = 0;
360
-
361
- // RP ID Hash (32 bytes)
362
- const rpIdHash = authData.slice(offset, offset + 32);
363
- offset += 32;
364
-
365
- // Flags (1 byte)
366
- const flags = authDataView.getUint8(offset);
367
- offset += 1;
368
-
369
- // Sign Count (4 bytes, big endian)
370
- const signCount = authDataView.getUint32(offset, false);
371
- offset += 4;
372
-
373
- // Attested Credential Data, if present
374
- if (flags & 0x40) { // Checking the AT flag
375
- // AAGUID (16 bytes)
376
- const aaguid = authData.slice(offset, offset + 16);
377
- offset += 16;
378
-
379
- // Credential ID Length (2 bytes, big endian)
380
- const credIdLength = authDataView.getUint16(offset, false);
381
- offset += 2;
382
-
383
- // Credential ID (variable length)
384
- const credentialId = authData.slice(offset, offset + credIdLength);
385
- offset += credIdLength;
386
-
387
- // Credential Public Key - (77 bytes...I hope)
388
- const credentialPublicKey = authData.slice(offset, offset + 77);
389
- offset += 77;
390
-
391
- // Any leftover bytes. I found some when using a YubiKey
392
- const theRest = authData.slice(offset);
393
-
394
- // Decode the credential public key to COSE
395
- const publicKeyObject = new Map<string, any>(Object.entries(decode(credentialPublicKey)));
396
-
397
- return {
398
- rpIdHash,
399
- flags,
400
- signCount,
401
- aaguid,
402
- credIdLength,
403
- credentialId,
404
- credentialPublicKey,
405
- theRest,
406
- publicKeyObject
407
- };
408
- }
409
-
410
- throw new Error("Attested credential data not present in the flags.");
411
- }
412
-
413
- private convertEcdsaSignatureAsnToCompact(sig: Buffer) {
414
- // Define the order of the curve secp256k1
415
- // https://github.com/RustCrypto/elliptic-curves/blob/master/p256/src/lib.rs#L72
416
- const q = Buffer.from('ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551', 'hex')
417
-
418
- // ASN Sequence
419
- let offset = 0;
420
- if (sig[offset] != 0x30) {
421
- throw "signature is not a sequence";
422
- }
423
- offset += 1;
332
+ private getPublicKey(response: AuthenticatorAttestationResponseJSON) {
333
+ let publicKey: Buffer | undefined
424
334
 
425
- // ASN Sequence Byte Length
426
- offset += 1;
335
+ if (response.publicKey)
336
+ publicKey = base64url.toBuffer(response.publicKey).slice(response.publicKey.length - 65)
427
337
 
428
- // ASN Integer (R)
429
- if (sig[offset] != 0x02) {
430
- throw "first element in sequence is not an integer";
338
+ if (
339
+ !publicKey
340
+ || publicKey[0] !== 0x04
341
+ || publicKey.length !== 65
342
+ ) {
343
+ // Extract and decode attestation object (CBOR), slice authenticator data to get COSE-encoded public key, and decode COSE to obtain key components
344
+ const { authData } = response.authenticatorData ? { authData: base64url.toBuffer(response.authenticatorData) } : decode(base64url.toBuffer(response.attestationObject));
345
+ const credentialIdLength = (authData[53] << 8) + authData[54];
346
+ const publicKeyBytes = authData.slice(55 + credentialIdLength);
347
+ const publicKeyCose = decode(publicKeyBytes);
348
+
349
+ publicKey = Buffer.from([
350
+ 0x04, // (0x04 prefix) https://en.bitcoin.it/wiki/Elliptic_Curve_Digital_Signature_Algorithm
351
+ ...publicKeyCose['-2'],
352
+ ...publicKeyCose['-3']
353
+ ])
431
354
  }
432
- offset += 1;
433
355
 
434
- // ASN Integer (R) Byte Length
435
- const rLen = sig[offset];
436
- offset += 1;
356
+ return publicKey
357
+ }
358
+ private compactSignature(signature: Buffer) {
359
+ // Decode the DER signature
360
+ let offset = 2;
437
361
 
438
- // ASN Integer (R) Byte Value
439
- if (rLen >= 33) {
440
- if (rLen != 33 || sig[offset] != 0x00) {
441
- throw "can only handle larger than 32 byte R's that are len 33 and lead with zero";
442
- }
443
- offset += 1;
444
- }
445
- const r = sig.slice(offset, offset + 32);
446
- offset += 32;
362
+ const rLength = signature[offset + 1];
363
+ const r = signature.slice(offset + 2, offset + 2 + rLength);
447
364
 
448
- // ASN Integer (S)
449
- if (sig[offset] != 0x02) {
450
- throw "second element in sequence is not an integer";
451
- }
452
- offset += 1;
365
+ offset += 2 + rLength;
453
366
 
454
- // ASN Integer (S) Byte Length
455
- const sLen = sig[offset];
456
- offset += 1;
367
+ const sLength = signature[offset + 1];
368
+ const s = signature.slice(offset + 2, offset + 2 + sLength);
457
369
 
458
- // ASN Integer (S) Byte Value
459
- if (sLen >= 33) {
460
- if (sLen != 33 || sig[offset] != 0x00) {
461
- throw "can only handle larger than 32 byte R's that are len 33 and lead with zero";
462
- }
463
- offset += 1;
464
- }
370
+ // Convert r and s to BigInt
371
+ const rBigInt = BigInt('0x' + r.toString('hex'));
372
+ let sBigInt = BigInt('0x' + s.toString('hex'));
465
373
 
466
- const s = sig.slice(offset, offset + 32);
374
+ // Ensure s is in the low-S form
375
+ // https://github.com/stellar/stellar-protocol/discussions/1435#discussioncomment-8809175
376
+ // https://discord.com/channels/897514728459468821/1233048618571927693
377
+ // Define the order of the curve secp256r1
378
+ // https://github.com/RustCrypto/elliptic-curves/blob/master/p256/src/lib.rs#L72
379
+ const n = BigInt('0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551');
380
+ const halfN = n / 2n;
467
381
 
468
- offset += 32;
382
+ if (sBigInt > halfN)
383
+ sBigInt = n - sBigInt;
469
384
 
470
- let signature64: Buffer
385
+ // Convert back to buffers and ensure they are 32 bytes
386
+ const rPadded = Buffer.from(rBigInt.toString(16).padStart(64, '0'), 'hex');
387
+ const sLowS = Buffer.from(sBigInt.toString(16).padStart(64, '0'), 'hex');
471
388
 
472
- // Force low S range
473
- // https://github.com/stellar/stellar-protocol/discussions/1435#discussioncomment-8809175
474
- // https://discord.com/channels/897514728459468821/1233048618571927693
475
- if (bufToBigint(s) > ((bufToBigint(q) - BigInt(1)) / BigInt(2))) {
476
- signature64 = Buffer.from([...r, ...Buffer.from(bigintToBuf(bufToBigint(q) - bufToBigint(s), true) as ArrayBuffer)]);
477
- } else {
478
- signature64 = Buffer.from([...r, ...s]);
479
- }
389
+ // Concatenate r and low-s
390
+ const concatSignature = Buffer.concat([rPadded, sLowS]);
480
391
 
481
- return signature64;
392
+ return concatSignature;
482
393
  }
483
394
  }
package/tsconfig.json CHANGED
@@ -6,7 +6,6 @@
6
6
  "target": "ESNext",
7
7
  "module": "ESNext",
8
8
  "moduleDetection": "force",
9
- // "allowJs": true,
10
9
 
11
10
  // Bundler mode
12
11
  "moduleResolution": "node",
@@ -26,5 +25,5 @@
26
25
  "noPropertyAccessFromIndexSignature": false,
27
26
  "strictNullChecks": true,
28
27
  },
29
- "exclude": ["./demo", "./packages", "./types", "./contracts", "./zephyr"]
28
+ "exclude": ["./demo", "./packages", "./types", "./contracts", "./zephyr", "./bun_tests"]
30
29
  }
package/types/kit.d.ts CHANGED
@@ -19,12 +19,12 @@ export declare class PasskeyKit extends PasskeyBase {
19
19
  networkPassphrase: string;
20
20
  factoryContractId: string;
21
21
  });
22
- createWallet(name: string, user: string): Promise<{
22
+ createWallet(app: string, user: string): Promise<{
23
23
  keyId: Buffer;
24
24
  contractId: string;
25
25
  xdr: string;
26
26
  }>;
27
- createKey(name: string, user: string): Promise<{
27
+ createKey(app: string, user: string): Promise<{
28
28
  keyId: Buffer;
29
29
  publicKey: Buffer;
30
30
  }>;
@@ -32,7 +32,7 @@ export declare class PasskeyKit extends PasskeyBase {
32
32
  keyId?: string | Uint8Array;
33
33
  getContractId?: GetContractIdFunction;
34
34
  }): Promise<{
35
- keyId: Uint8Array;
35
+ keyId: Buffer;
36
36
  contractId: string;
37
37
  }>;
38
38
  signAuthEntry(entry: xdr.SorobanAuthorizationEntry, options?: {
@@ -47,8 +47,7 @@ export declare class PasskeyKit extends PasskeyBase {
47
47
  keyId?: 'any' | string | Uint8Array;
48
48
  ledgersToLive?: number;
49
49
  }): Promise<string>;
50
- getSuperKeyId(): Promise<string>;
51
- private getPublicKeyObject;
52
- private convertEcdsaSignatureAsnToCompact;
50
+ private getPublicKey;
51
+ private compactSignature;
53
52
  }
54
53
  export {};
@@ -0,0 +1,9 @@
1
+ export declare class PasskeyBase {
2
+ launchtubeUrl: string | undefined;
3
+ launchtubeJwt: string | undefined;
4
+ constructor(options: {
5
+ launchtubeUrl?: string;
6
+ launchtubeJwt?: string;
7
+ });
8
+ send(xdr: string, fee?: number): Promise<any>;
9
+ }
@@ -0,0 +1,3 @@
1
+ import { PasskeyBase } from "./base";
2
+ import { PasskeyKit } from "./kit";
3
+ export { PasskeyBase, PasskeyKit };
@@ -0,0 +1,53 @@
1
+ import { Client as PasskeyClient } from 'passkey-kit-sdk';
2
+ import { Client as FactoryClient } from 'passkey-factory-sdk';
3
+ import { Networks, xdr, Transaction, SorobanRpc } from '@stellar/stellar-sdk';
4
+ import { Buffer } from 'buffer';
5
+ import { PasskeyBase } from './base';
6
+ type GetContractIdFunction = (keyId: string) => Promise<string>;
7
+ export declare class PasskeyKit extends PasskeyBase {
8
+ keyId: string | undefined;
9
+ keyExpired: boolean | undefined;
10
+ wallet: PasskeyClient | undefined;
11
+ factory: FactoryClient;
12
+ networkPassphrase: Networks;
13
+ rpcUrl: string;
14
+ rpc: SorobanRpc.Server;
15
+ constructor(options: {
16
+ rpcUrl: string;
17
+ launchtubeUrl?: string;
18
+ launchtubeJwt?: string;
19
+ networkPassphrase: string;
20
+ factoryContractId: string;
21
+ });
22
+ createWallet(name: string, user: string): Promise<{
23
+ keyId: Buffer;
24
+ contractId: string;
25
+ xdr: string;
26
+ }>;
27
+ createKey(name: string, user: string): Promise<{
28
+ keyId: Buffer;
29
+ publicKey: Buffer;
30
+ }>;
31
+ connectWallet(opts: {
32
+ keyId?: string | Uint8Array;
33
+ getContractId?: GetContractIdFunction;
34
+ }): Promise<{
35
+ keyId: Buffer;
36
+ contractId: string;
37
+ }>;
38
+ signAuthEntry(entry: xdr.SorobanAuthorizationEntry, options?: {
39
+ keyId?: 'any' | string | Uint8Array;
40
+ ledgersToLive?: number;
41
+ }): Promise<xdr.SorobanAuthorizationEntry>;
42
+ signAuthEntries(entries: xdr.SorobanAuthorizationEntry[], options?: {
43
+ keyId?: 'any' | string | Uint8Array;
44
+ ledgersToLive?: number;
45
+ }): Promise<xdr.SorobanAuthorizationEntry[]>;
46
+ sign(txn: Transaction | string, options?: {
47
+ keyId?: 'any' | string | Uint8Array;
48
+ ledgersToLive?: number;
49
+ }): Promise<string>;
50
+ private getPublicKeyObject;
51
+ private convertEcdsaSignatureAsnToCompact;
52
+ }
53
+ export {};
@@ -0,0 +1 @@
1
+ export {};