passkey-kit 0.2.0 → 0.2.3

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/cheatsheet ADDED
@@ -0,0 +1,5 @@
1
+ # Install contract sdks
2
+ npm publish --workspaces
3
+
4
+ # Install passkey-kit
5
+ pnpm publish --no-git-checks
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "passkey-kit",
3
- "version": "0.2.0",
3
+ "version": "0.2.3",
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",
@@ -14,8 +14,8 @@
14
14
  "bigint-conversion": "^2.4.3",
15
15
  "buffer": "^6.0.3",
16
16
  "cbor-x": "^1.5.9",
17
- "passkey-factory-sdk": "0.0.7",
18
- "passkey-kit-sdk": "0.0.7"
17
+ "passkey-factory-sdk": "0.0.8",
18
+ "passkey-kit-sdk": "0.0.8"
19
19
  },
20
20
  "devDependencies": {
21
21
  "typescript": "^5.4.5"
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.7",
2
+ "version": "0.0.8",
3
3
  "name": "passkey-factory-sdk",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
@@ -1,12 +1,10 @@
1
1
  import { Buffer } from "buffer";
2
2
  import {
3
- Client as ContractClient,
4
- Spec as ContractSpec,
5
- } from '@stellar/stellar-sdk/contract';
6
- import type {
7
3
  AssembledTransaction,
4
+ Client as ContractClient,
8
5
  ClientOptions as ContractClientOptions,
9
6
  Result,
7
+ Spec as ContractSpec,
10
8
  } from '@stellar/stellar-sdk/contract';
11
9
 
12
10
  if (typeof window !== 'undefined') {
@@ -17,7 +15,7 @@ if (typeof window !== 'undefined') {
17
15
  export const networks = {
18
16
  testnet: {
19
17
  networkPassphrase: "Test SDF Network ; September 2015",
20
- contractId: "CDSGG7BSWYWQMTY5KTZVDTC34ZESMZ75ZGSTTVHS4NIKCF4PM3GDJ4G3",
18
+ contractId: "CBXQAM53YVVPKAPS2NCSOCXTOK6HOXA3JLURVLD6VFQUHJPKFVBJIBHQ",
21
19
  }
22
20
  } as const
23
21
 
@@ -1,10 +1,9 @@
1
1
  import { Buffer } from "buffer";
2
- import { Client as ContractClient } from '@stellar/stellar-sdk/contract';
3
- import type { AssembledTransaction, ClientOptions as ContractClientOptions, Result } from '@stellar/stellar-sdk/contract';
2
+ import { AssembledTransaction, Client as ContractClient, ClientOptions as ContractClientOptions, Result } from '@stellar/stellar-sdk/contract';
4
3
  export declare const networks: {
5
4
  readonly testnet: {
6
5
  readonly networkPassphrase: "Test SDF Network ; September 2015";
7
- readonly contractId: "CDSGG7BSWYWQMTY5KTZVDTC34ZESMZ75ZGSTTVHS4NIKCF4PM3GDJ4G3";
6
+ readonly contractId: "CBXQAM53YVVPKAPS2NCSOCXTOK6HOXA3JLURVLD6VFQUHJPKFVBJIBHQ";
8
7
  };
9
8
  };
10
9
  export declare const Errors: {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.7",
2
+ "version": "0.0.8",
3
3
  "name": "passkey-kit-sdk",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
@@ -1,12 +1,10 @@
1
1
  import { Buffer } from "buffer";
2
2
  import {
3
- Client as ContractClient,
4
- Spec as ContractSpec,
5
- } from '@stellar/stellar-sdk/contract';
6
- import type {
7
3
  AssembledTransaction,
4
+ Client as ContractClient,
8
5
  ClientOptions as ContractClientOptions,
9
6
  Result,
7
+ Spec as ContractSpec,
10
8
  } from '@stellar/stellar-sdk/contract';
11
9
 
12
10
  if (typeof window !== 'undefined') {
@@ -17,7 +15,7 @@ if (typeof window !== 'undefined') {
17
15
  export const networks = {
18
16
  testnet: {
19
17
  networkPassphrase: "Test SDF Network ; September 2015",
20
- contractId: "CDSGG7BSWYWQMTY5KTZVDTC34ZESMZ75ZGSTTVHS4NIKCF4PM3GDJ4G3",
18
+ contractId: "CBXQAM53YVVPKAPS2NCSOCXTOK6HOXA3JLURVLD6VFQUHJPKFVBJIBHQ",
21
19
  }
22
20
  } as const
23
21
 
@@ -82,6 +80,26 @@ export interface Client {
82
80
  simulate?: boolean;
83
81
  }) => Promise<AssembledTransaction<Result<void>>>
84
82
 
83
+ /**
84
+ * 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.
85
+ */
86
+ upgrade: ({ hash }: { hash: Buffer }, options?: {
87
+ /**
88
+ * The fee to pay for the transaction. Default: BASE_FEE
89
+ */
90
+ fee?: number;
91
+
92
+ /**
93
+ * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT
94
+ */
95
+ timeoutInSeconds?: number;
96
+
97
+ /**
98
+ * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true
99
+ */
100
+ simulate?: boolean;
101
+ }) => Promise<AssembledTransaction<Result<void>>>
102
+
85
103
  /**
86
104
  * Construct and simulate a resudo 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.
87
105
  */
@@ -148,6 +166,7 @@ export class Client extends ContractClient {
148
166
  new ContractSpec(["AAAABAAAAAAAAAAAAAAABUVycm9yAAAAAAAACgAAAAAAAAAJTm90SW5pdGVkAAAAAAAAAQAAAAAAAAAITm90Rm91bmQAAAACAAAAAAAAAAxOb3RQZXJtaXR0ZWQAAAADAAAAAAAAAA1BbHJlYWR5SW5pdGVkAAAAAAAABAAAAAAAAAAOSnNvblBhcnNlRXJyb3IAAAAAAAUAAAAAAAAADkludmFsaWRDb250ZXh0AAAAAAAGAAAAAAAAACBDbGllbnREYXRhSnNvbkNoYWxsZW5nZUluY29ycmVjdAAAAAcAAAAAAAAAF1NlY3AyNTZyMVB1YmxpY0tleVBhcnNlAAAAAAgAAAAAAAAAF1NlY3AyNTZyMVNpZ25hdHVyZVBhcnNlAAAAAAkAAAAAAAAAFVNlY3AyNTZyMVZlcmlmeUZhaWxlZAAAAAAAAAo=",
149
167
  "AAAAAAAAAAAAAAAKZXh0ZW5kX3R0bAAAAAAAAAAAAAA=",
150
168
  "AAAAAAAAAAAAAAAEaW5pdAAAAAMAAAAAAAAAAmlkAAAAAAAOAAAAAAAAAAJwawAAAAAD7gAAAEEAAAAAAAAAB2ZhY3RvcnkAAAAAEwAAAAEAAAPpAAAD7QAAAAAAAAAD",
169
+ "AAAAAAAAAAAAAAAHdXBncmFkZQAAAAABAAAAAAAAAARoYXNoAAAD7gAAACAAAAABAAAD6QAAA+0AAAAAAAAAAw==",
151
170
  "AAAAAAAAAAAAAAAGcmVzdWRvAAAAAAABAAAAAAAAAAJpZAAAAAAADgAAAAEAAAPpAAAD7QAAAAAAAAAD",
152
171
  "AAAAAAAAAAAAAAAGcm1fc2lnAAAAAAABAAAAAAAAAAJpZAAAAAAADgAAAAEAAAPpAAAD7QAAAAAAAAAD",
153
172
  "AAAAAAAAAAAAAAAHYWRkX3NpZwAAAAACAAAAAAAAAAJpZAAAAAAADgAAAAAAAAACcGsAAAAAA+4AAABBAAAAAQAAA+kAAAPtAAAAAAAAAAM=",
@@ -159,6 +178,7 @@ export class Client extends ContractClient {
159
178
  public readonly fromJSON = {
160
179
  extend_ttl: this.txFromJSON<null>,
161
180
  init: this.txFromJSON<Result<void>>,
181
+ upgrade: this.txFromJSON<Result<void>>,
162
182
  resudo: this.txFromJSON<Result<void>>,
163
183
  rm_sig: this.txFromJSON<Result<void>>,
164
184
  add_sig: this.txFromJSON<Result<void>>,
@@ -1,10 +1,9 @@
1
1
  import { Buffer } from "buffer";
2
- import { Client as ContractClient } from '@stellar/stellar-sdk/contract';
3
- import type { AssembledTransaction, ClientOptions as ContractClientOptions, Result } from '@stellar/stellar-sdk/contract';
2
+ import { AssembledTransaction, Client as ContractClient, ClientOptions as ContractClientOptions, Result } from '@stellar/stellar-sdk/contract';
4
3
  export declare const networks: {
5
4
  readonly testnet: {
6
5
  readonly networkPassphrase: "Test SDF Network ; September 2015";
7
- readonly contractId: "CDSGG7BSWYWQMTY5KTZVDTC34ZESMZ75ZGSTTVHS4NIKCF4PM3GDJ4G3";
6
+ readonly contractId: "CBXQAM53YVVPKAPS2NCSOCXTOK6HOXA3JLURVLD6VFQUHJPKFVBJIBHQ";
8
7
  };
9
8
  };
10
9
  export declare const Errors: {
@@ -84,6 +83,25 @@ export interface Client {
84
83
  */
85
84
  simulate?: boolean;
86
85
  }) => Promise<AssembledTransaction<Result<void>>>;
86
+ /**
87
+ * 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.
88
+ */
89
+ upgrade: ({ hash }: {
90
+ hash: Buffer;
91
+ }, options?: {
92
+ /**
93
+ * The fee to pay for the transaction. Default: BASE_FEE
94
+ */
95
+ fee?: number;
96
+ /**
97
+ * The maximum amount of time to wait for the transaction to complete. Default: DEFAULT_TIMEOUT
98
+ */
99
+ timeoutInSeconds?: number;
100
+ /**
101
+ * Whether to automatically simulate the transaction when constructing the AssembledTransaction. Default: true
102
+ */
103
+ simulate?: boolean;
104
+ }) => Promise<AssembledTransaction<Result<void>>>;
87
105
  /**
88
106
  * Construct and simulate a resudo 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
107
  */
@@ -149,6 +167,7 @@ export declare class Client extends ContractClient {
149
167
  readonly fromJSON: {
150
168
  extend_ttl: (json: string) => AssembledTransaction<null>;
151
169
  init: (json: string) => AssembledTransaction<Result<void, import("@stellar/stellar-sdk/contract").ErrorMessage>>;
170
+ upgrade: (json: string) => AssembledTransaction<Result<void, import("@stellar/stellar-sdk/contract").ErrorMessage>>;
152
171
  resudo: (json: string) => AssembledTransaction<Result<void, import("@stellar/stellar-sdk/contract").ErrorMessage>>;
153
172
  rm_sig: (json: string) => AssembledTransaction<Result<void, import("@stellar/stellar-sdk/contract").ErrorMessage>>;
154
173
  add_sig: (json: string) => AssembledTransaction<Result<void, import("@stellar/stellar-sdk/contract").ErrorMessage>>;
package/src/base.ts CHANGED
@@ -1,54 +1,42 @@
1
- import { Networks, Transaction, Horizon, FeeBumpTransaction } from '@stellar/stellar-sdk'
2
-
3
1
  export class PasskeyBase {
4
- public networkPassphrase: Networks
5
- public horizonUrl: string
6
- public horizon: Horizon.Server
7
- public feeBumpUrl: string | undefined
8
- public feeBumpJwt: string | undefined
2
+ public launchtubeUrl: string | undefined
3
+ public launchtubeJwt: string | undefined
9
4
 
10
5
  constructor(options: {
11
- networkPassphrase: Networks,
12
- horizonUrl: string,
13
- feeBumpUrl?: string,
14
- feeBumpJwt?: string,
6
+ launchtubeUrl?: string,
7
+ launchtubeJwt?: string,
15
8
  }) {
16
9
  const {
17
- networkPassphrase,
18
- horizonUrl,
19
- feeBumpUrl,
20
- feeBumpJwt,
10
+ launchtubeUrl,
11
+ launchtubeJwt,
21
12
  } = options
22
13
 
23
- this.networkPassphrase = networkPassphrase
24
- this.horizonUrl = horizonUrl
25
- this.horizon = new Horizon.Server(horizonUrl)
26
-
27
- if (feeBumpUrl)
28
- this.feeBumpUrl = feeBumpUrl
14
+ if (launchtubeUrl)
15
+ this.launchtubeUrl = launchtubeUrl
29
16
 
30
- if (feeBumpJwt)
31
- this.feeBumpJwt = feeBumpJwt
17
+ if (launchtubeJwt)
18
+ this.launchtubeJwt = launchtubeJwt
32
19
  }
33
20
 
34
- public async send(txn: Transaction, fee: number = 10_000) {
21
+ public async send(xdr: string, fee: number = 10_000) {
22
+ if (!this.launchtubeUrl || !this.launchtubeJwt)
23
+ throw new Error('Launchtube service not configured')
24
+
35
25
  const data = new FormData();
36
26
 
37
- data.set('xdr', txn.toXDR());
27
+ data.set('xdr', xdr);
38
28
  data.set('fee', fee.toString());
39
29
 
40
- const bumptxn = await fetch(this.feeBumpUrl!, {
30
+ return fetch(this.launchtubeUrl, {
41
31
  method: 'POST',
42
32
  headers: {
43
- authorization: `Bearer ${this.feeBumpJwt}`,
33
+ authorization: `Bearer ${this.launchtubeJwt}`,
44
34
  },
45
35
  body: data
46
36
  }).then(async (res) => {
47
37
  if (res.ok)
48
- return res.text()
38
+ return res.json()
49
39
  else throw await res.json()
50
40
  })
51
-
52
- return this.horizon.submitTransaction(new FeeBumpTransaction(bumptxn, this.networkPassphrase))
53
41
  }
54
42
  }
package/src/kit.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Client as PasskeyClient } from 'passkey-kit-sdk'
2
2
  import { Client as FactoryClient, networks } from 'passkey-factory-sdk'
3
- import { Address, Networks, StrKey, hash, xdr, Transaction, Horizon, SorobanRpc, Operation, scValToNative, TransactionBuilder } from '@stellar/stellar-sdk'
3
+ import { Address, Networks, StrKey, hash, xdr, Transaction, SorobanRpc, Operation, scValToNative, TransactionBuilder, authorizeEntry } from '@stellar/stellar-sdk'
4
4
  import { bufToBigint, bigintToBuf } from 'bigint-conversion'
5
5
  import base64url from 'base64url'
6
6
  import { startRegistration, startAuthentication } from "@simplewebauthn/browser"
@@ -12,12 +12,7 @@ import { PasskeyBase } from './base'
12
12
  - Clean up these params and the interface as a whole
13
13
  Might put wallet activities and maybe factory as well into the root of the class vs buried inside this.wallet and this.factory
14
14
  @Later
15
-
16
15
  - Right now publicKey can mean a Stellar public key or a passkey public key, there should be a noted difference
17
-
18
- - It's finally time to make a server and a client version of this package
19
- Maybe. I want to support passing "bad" secret values on the client.
20
- It's maybe only the server that could be smaller and cleaner by opting out out WebAuthN stuff
21
16
  */
22
17
 
23
18
  export class PasskeyKit extends PasskeyBase {
@@ -25,65 +20,46 @@ export class PasskeyKit extends PasskeyBase {
25
20
  public sudoKeyId: string | undefined
26
21
  public wallet: PasskeyClient | undefined
27
22
  public factory: FactoryClient
28
- public sequencePublicKey: string
29
- // public networkPassphrase: Networks
30
- // public horizonUrl: string
31
- public horizon: Horizon.Server
23
+ public networkPassphrase: Networks
32
24
  public rpcUrl: string
33
25
  public rpc: SorobanRpc.Server
34
- // public feeBumpUrl: string | undefined
35
- // public feeBumpJwt: string | undefined
36
26
  public factoryContractId: string = networks.testnet.contractId
37
27
 
38
- /* TODO
28
+ /* NOTE
39
29
  - Consider adding the ability to pass in a keyId and maybe even a contractId in order to preconnect to a wallet
40
30
  If just a keyId call `connectWallet` in order to get the contractId
41
31
  If both keyId and contractId are passed in then we can skip the connectWallet call (though we won't get the sudoKeyId in that case)
42
- We don't stictly _need_ this as a dev can just call `connectWallet` after class instantiation but it might be a nice convenience
43
- @Later
32
+ We don't strictly _need_ this as a dev can just call `connectWallet` after class instantiation but it might be a nice convenience
33
+ `connectWallet` is async making it tricky to know when the wallet is "ready". Thus I think it's better for `connectWallet` to be called externally
34
+ @No
44
35
  */
45
36
  constructor(options: {
46
- sequencePublicKey: string,
47
- networkPassphrase: Networks,
48
- horizonUrl: string,
49
37
  rpcUrl: string,
50
- feeBumpUrl?: string,
51
- feeBumpJwt?: string,
52
- /* TODO
53
- - Maybe remove this? The factory should likely be baked in a bit more tightly
54
- On the other hand once we have a standard interface the factory interface only uses the `deploy` method right now inside the interface
55
- @Later
56
- */
57
- factoryContractId?: string,
38
+ launchtubeUrl?: string,
39
+ launchtubeJwt?: string,
40
+ factoryContractId?: string,
41
+ networkPassphrase: Networks,
58
42
  }) {
59
43
  const {
60
- sequencePublicKey,
61
- networkPassphrase,
62
- horizonUrl,
63
44
  rpcUrl,
64
- feeBumpUrl,
65
- feeBumpJwt,
66
- factoryContractId
45
+ launchtubeUrl,
46
+ launchtubeJwt,
47
+ factoryContractId,
48
+ networkPassphrase,
67
49
  } = options
68
50
 
69
51
  super({
70
- networkPassphrase,
71
- horizonUrl,
72
- feeBumpUrl,
73
- feeBumpJwt,
52
+ launchtubeUrl,
53
+ launchtubeJwt,
74
54
  })
75
55
 
76
- this.sequencePublicKey = sequencePublicKey
77
-
78
- this.horizon = new Horizon.Server(horizonUrl)
79
- this.rpcUrl = rpcUrl
80
- this.rpc = new SorobanRpc.Server(rpcUrl)
81
-
82
56
  if (factoryContractId)
83
57
  this.factoryContractId = factoryContractId
84
58
 
59
+ this.rpcUrl = rpcUrl
60
+ this.rpc = new SorobanRpc.Server(rpcUrl)
61
+ this.networkPassphrase = networkPassphrase
85
62
  this.factory = new FactoryClient({
86
- publicKey: sequencePublicKey,
87
63
  contractId: this.factoryContractId,
88
64
  networkPassphrase,
89
65
  rpcUrl
@@ -101,7 +77,6 @@ export class PasskeyKit extends PasskeyBase {
101
77
  const contractId = result.unwrap() as string
102
78
 
103
79
  this.wallet = new PasskeyClient({
104
- publicKey: this.sequencePublicKey,
105
80
  contractId,
106
81
  networkPassphrase: this.networkPassphrase,
107
82
  rpcUrl: this.rpcUrl
@@ -157,34 +132,21 @@ export class PasskeyKit extends PasskeyBase {
157
132
  }
158
133
  }
159
134
 
160
- public async connectWallet(id?: string) {
161
- /* TODO
162
- - Support passing in a contractId as well as a keyId
163
- Maybe not as we wouldn't have a keyId which could have interesting consequences
164
- Also not sure what the use case would be for this where keyId wouldn't also be possible and better
165
- @No
166
- */
167
-
168
- // @ts-ignore
169
- // https://github.com/stellar/js-stellar-base/issues/750
170
- // if (id && StrKey.isValidContract(id)) {
171
-
172
- // } else {
173
-
174
- // }
175
-
176
- const startAuthenticationResponse = id
177
- ? { id }
178
- : await startAuthentication({
135
+ public async connectWallet(keyId?: string) {
136
+ if (!keyId) {
137
+ const { id } = await startAuthentication({
179
138
  challenge: base64url("stellaristhebetterblockchain"),
180
139
  // rpId: undefined,
181
140
  userVerification: "discouraged",
182
141
  });
183
142
 
143
+ keyId = id
144
+ }
145
+
184
146
  if (!this.keyId)
185
- this.keyId = startAuthenticationResponse.id
147
+ this.keyId = keyId
186
148
 
187
- const keyIdBuffer = base64url.toBuffer(startAuthenticationResponse.id)
149
+ const keyIdBuffer = base64url.toBuffer(keyId)
188
150
 
189
151
  // NOTE might not need this for derivation as all signers are stored in the factory and we can use that lookup as both primary and secondary
190
152
  let contractId = StrKey.encodeContract(hash(xdr.HashIdPreimage.envelopeTypeContractId(
@@ -199,25 +161,26 @@ export class PasskeyKit extends PasskeyBase {
199
161
  })
200
162
  ).toXDR()));
201
163
 
164
+ let contractData: SorobanRpc.Api.LedgerEntryResult | undefined
165
+
202
166
  // attempt passkey id derivation
203
167
  try {
204
- await this.rpc.getContractData(contractId, xdr.ScVal.scvLedgerKeyContractInstance())
168
+ contractData = await this.rpc.getContractData(contractId, xdr.ScVal.scvLedgerKeyContractInstance())
205
169
  }
206
170
  // if that fails look up from the factory mapper
207
171
  catch {
208
- const { val } = await this.rpc.getContractData(this.factoryContractId, xdr.ScVal.scvBytes(keyIdBuffer))
209
- contractId = scValToNative(val.contractData().val())
172
+ contractData = await this.rpc.getContractData(this.factoryContractId, xdr.ScVal.scvBytes(keyIdBuffer))
173
+ contractId = scValToNative(contractData.val.contractData().val())
210
174
  }
211
175
 
212
176
  this.wallet = new PasskeyClient({
213
- publicKey: this.sequencePublicKey,
214
177
  contractId,
215
178
  networkPassphrase: this.networkPassphrase,
216
179
  rpcUrl: this.rpcUrl
217
180
  })
218
181
 
219
- // get and set the sudo signer
220
- await this.getData()
182
+ // get and set the sudo signer (passing in `contractData` from the `getContractData` call above to avoid dupe requests)
183
+ await this.getData(contractData)
221
184
 
222
185
  return {
223
186
  keyId: keyIdBuffer,
@@ -225,9 +188,8 @@ export class PasskeyKit extends PasskeyBase {
225
188
  }
226
189
  }
227
190
 
228
- // TODO is there anything to be done with using the TS bindings `signAuthEntries` logic? Likely not but worth exploring
229
- public async sign(
230
- txn: Transaction | string,
191
+ public async signAuthEntry(
192
+ entry: xdr.SorobanAuthorizationEntry,
231
193
  options?: {
232
194
  keyId?: 'any' | 'sudo' | string | Uint8Array
233
195
  ledgersToLive?: number
@@ -236,39 +198,29 @@ export class PasskeyKit extends PasskeyBase {
236
198
  // Default mirrors DEFAULT_TIMEOUT (currently 5 minutes) https://github.com/stellar/js-stellar-sdk/blob/master/src/contract/utils.ts#L7
237
199
  let { keyId, ledgersToLive = 60 } = options || {}
238
200
 
239
- // hack to ensure we don't stack fees when simulating and assembling multiple times
240
- txn = TransactionBuilder.cloneFrom(new Transaction(
241
- typeof txn === 'string'
242
- ? txn
243
- : txn.toXDR(),
244
- this.networkPassphrase
245
- ), { fee: '0' }).build()
246
-
247
- // NOTE hard coded to sign only Soroban transactions and only and always the first auth
248
- // TODO switch to support signing multiple and specific auth entries. Possibly pass in auth entry vs the entire txn
249
- const op = txn.operations[0] as Operation.InvokeHostFunction
250
- const auth = op.auth![0]
251
201
  const lastLedger = await this.rpc.getLatestLedger().then(({ sequence }) => sequence)
252
- const authHash = hash(
253
- xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(
254
- new xdr.HashIdPreimageSorobanAuthorization({
255
- networkId: hash(Buffer.from(this.networkPassphrase, 'utf-8')),
256
- nonce: auth.credentials().address().nonce(),
257
- signatureExpirationLedger: lastLedger + ledgersToLive,
258
- invocation: auth.rootInvocation()
259
- })
260
- ).toXDR()
202
+ const credentials = entry.credentials().address();
203
+ const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(
204
+ new xdr.HashIdPreimageSorobanAuthorization({
205
+ networkId: hash(Buffer.from(this.networkPassphrase)),
206
+ nonce: credentials.nonce(),
207
+ signatureExpirationLedger: lastLedger + ledgersToLive,
208
+ invocation: entry.rootInvocation()
209
+ })
261
210
  )
211
+ const payload = hash(preimage.toXDR())
262
212
 
263
213
  const authenticationResponse = await startAuthentication(
264
- keyId === 'any' || (keyId === 'sudo' && !this.sudoKeyId) || (!keyId && !this.keyId)
214
+ keyId === 'any'
215
+ || (keyId === 'sudo' && !this.sudoKeyId)
216
+ || (!keyId && !this.keyId)
265
217
  ? {
266
- challenge: base64url(authHash),
218
+ challenge: base64url(payload),
267
219
  // rpId: undefined,
268
220
  userVerification: "discouraged",
269
221
  }
270
222
  : {
271
- challenge: base64url(authHash),
223
+ challenge: base64url(payload),
272
224
  // rpId: undefined,
273
225
  allowCredentials: [
274
226
  {
@@ -284,19 +236,11 @@ export class PasskeyKit extends PasskeyBase {
284
236
  }
285
237
  );
286
238
 
287
- // set sudo if this is a sudo request
288
- if (keyId === 'sudo')
289
- this.sudoKeyId = authenticationResponse.id
290
-
291
- // reset this.keyId to be the most recently used passkey
292
- this.keyId = authenticationResponse.id
293
-
294
239
  const signatureRaw = base64url.toBuffer(authenticationResponse.response.signature);
295
240
  const signature = this.convertEcdsaSignatureAsnToCompact(signatureRaw);
296
- const creds = auth.credentials().address();
297
-
298
- creds.signatureExpirationLedger(lastLedger + ledgersToLive)
299
- creds.signature(xdr.ScVal.scvMap([
241
+
242
+ credentials.signatureExpirationLedger(lastLedger + ledgersToLive)
243
+ credentials.signature(xdr.ScVal.scvMap([
300
244
  new xdr.ScMapEntry({
301
245
  key: xdr.ScVal.scvSymbol('authenticator_data'),
302
246
  val: xdr.ScVal.scvBytes(base64url.toBuffer(authenticationResponse.response.authenticatorData)),
@@ -314,21 +258,84 @@ export class PasskeyKit extends PasskeyBase {
314
258
  val: xdr.ScVal.scvBytes(signature),
315
259
  }),
316
260
  ]))
261
+
262
+ return entry
263
+ }
264
+ public async signAuthEntries(
265
+ entries: xdr.SorobanAuthorizationEntry[],
266
+ options?: {
267
+ keyId?: 'any' | 'sudo' | string | Uint8Array
268
+ ledgersToLive?: number
269
+ }
270
+ ) {
271
+ for (const auth of entries) {
272
+ if (
273
+ auth.credentials().switch().name === 'sorobanCredentialsAddress'
274
+ && auth.credentials().address().address().switch().name === 'scAddressTypeContract'
275
+ ) {
276
+ // If auth entry matches our Smart Wallet move forward with the signature request
277
+ if (Address.contract(auth.credentials().address().address().contractId()).toString() === this.wallet!.options.contractId)
278
+ await this.signAuthEntry(auth, options)
279
+ }
280
+ }
281
+
282
+ return entries
283
+ }
284
+ /* NOTE
285
+ - Is there anything to be done with using the TS bindings `signAuthEntries` logic? Likely not but worth exploring
286
+ Perhaps not but maybe the js-sdk's `authorizeEntry` or `authorizeInvocation`
287
+ https://stellar.github.io/js-stellar-sdk/global.html#authorizeInvocation
288
+ These methods all only support ed25519 Keypairs
289
+ @No
290
+ */
291
+ public async sign(
292
+ txn: Transaction | string,
293
+ options?: {
294
+ keyId?: 'any' | 'sudo' | string | Uint8Array
295
+ ledgersToLive?: number
296
+ }
297
+ ) {
298
+ // Hack to ensure we don't stack fees when simulating and assembling multiple times
299
+ txn = TransactionBuilder.cloneFrom(new Transaction(
300
+ typeof txn === 'string'
301
+ ? txn
302
+ : txn.toXDR(),
303
+ this.networkPassphrase
304
+ ), { fee: '0' }).build()
305
+
306
+ if (txn.operations.length !== 1)
307
+ throw new Error('Must include only one Soroban operation')
308
+
309
+ for (const op of txn.operations) {
310
+ if (
311
+ op.type !== 'invokeHostFunction'
312
+ && op.type !== 'extendFootprintTtl'
313
+ && op.type !== 'restoreFootprint'
314
+ ) throw new Error('Must include only one operation of type `invokeHostFunction` or `extendFootprintTtl` or `restoreFootprint`')
315
+ }
316
+
317
+ // Only need to sign auth for `invokeHostFunction` operations
318
+ if (txn.operations[0].type === 'invokeHostFunction') {
319
+ const entries = (txn.operations[0] as Operation.InvokeHostFunction).auth
320
+
321
+ if (entries)
322
+ await this.signAuthEntries(entries, options)
323
+ }
317
324
 
318
325
  const sim = await this.rpc.simulateTransaction(txn)
319
326
 
320
327
  if (
321
328
  SorobanRpc.Api.isSimulationError(sim)
322
- || SorobanRpc.Api.isSimulationRestore(sim) // TODO handle restore flow
329
+ || SorobanRpc.Api.isSimulationRestore(sim) // TODO handle state archival
323
330
  ) throw sim
324
331
 
325
332
  return SorobanRpc.assembleTransaction(txn, sim).build().toXDR()
326
333
  }
327
334
 
328
- public async getData() {
335
+ public async getData(contractData?: SorobanRpc.Api.LedgerEntryResult) {
329
336
  const data: Map<string, any> = new Map()
330
337
 
331
- const { val } = await this.rpc.getContractData(
338
+ const { val } = contractData || await this.rpc.getContractData(
332
339
  this.wallet!.options.contractId,
333
340
  xdr.ScVal.scvLedgerKeyContractInstance(),
334
341
  );
package/types/base.d.ts CHANGED
@@ -1,15 +1,9 @@
1
- import { Networks, Transaction, Horizon } from '@stellar/stellar-sdk';
2
1
  export declare class PasskeyBase {
3
- networkPassphrase: Networks;
4
- horizonUrl: string;
5
- horizon: Horizon.Server;
6
- feeBumpUrl: string | undefined;
7
- feeBumpJwt: string | undefined;
2
+ launchtubeUrl: string | undefined;
3
+ launchtubeJwt: string | undefined;
8
4
  constructor(options: {
9
- networkPassphrase: Networks;
10
- horizonUrl: string;
11
- feeBumpUrl?: string;
12
- feeBumpJwt?: string;
5
+ launchtubeUrl?: string;
6
+ launchtubeJwt?: string;
13
7
  });
14
- send(txn: Transaction, fee?: number): Promise<Horizon.HorizonApi.SubmitTransactionResponse>;
8
+ send(xdr: string, fee?: number): Promise<any>;
15
9
  }
package/types/kit.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Client as PasskeyClient } from 'passkey-kit-sdk';
2
2
  import { Client as FactoryClient } from 'passkey-factory-sdk';
3
- import { Networks, Transaction, Horizon, SorobanRpc } from '@stellar/stellar-sdk';
3
+ import { Networks, xdr, Transaction, SorobanRpc } from '@stellar/stellar-sdk';
4
4
  import { Buffer } from 'buffer';
5
5
  import { PasskeyBase } from './base';
6
6
  export declare class PasskeyKit extends PasskeyBase {
@@ -8,19 +8,16 @@ export declare class PasskeyKit extends PasskeyBase {
8
8
  sudoKeyId: string | undefined;
9
9
  wallet: PasskeyClient | undefined;
10
10
  factory: FactoryClient;
11
- sequencePublicKey: string;
12
- horizon: Horizon.Server;
11
+ networkPassphrase: Networks;
13
12
  rpcUrl: string;
14
13
  rpc: SorobanRpc.Server;
15
14
  factoryContractId: string;
16
15
  constructor(options: {
17
- sequencePublicKey: string;
18
- networkPassphrase: Networks;
19
- horizonUrl: string;
20
16
  rpcUrl: string;
21
- feeBumpUrl?: string;
22
- feeBumpJwt?: string;
17
+ launchtubeUrl?: string;
18
+ launchtubeJwt?: string;
23
19
  factoryContractId?: string;
20
+ networkPassphrase: Networks;
24
21
  });
25
22
  createWallet(name: string, user: string): Promise<{
26
23
  keyId: Buffer;
@@ -31,15 +28,23 @@ export declare class PasskeyKit extends PasskeyBase {
31
28
  keyId: Buffer;
32
29
  publicKey: Buffer;
33
30
  }>;
34
- connectWallet(id?: string): Promise<{
31
+ connectWallet(keyId?: string): Promise<{
35
32
  keyId: Buffer;
36
33
  contractId: string;
37
34
  }>;
35
+ signAuthEntry(entry: xdr.SorobanAuthorizationEntry, options?: {
36
+ keyId?: 'any' | 'sudo' | string | Uint8Array;
37
+ ledgersToLive?: number;
38
+ }): Promise<xdr.SorobanAuthorizationEntry>;
39
+ signAuthEntries(entries: xdr.SorobanAuthorizationEntry[], options?: {
40
+ keyId?: 'any' | 'sudo' | string | Uint8Array;
41
+ ledgersToLive?: number;
42
+ }): Promise<xdr.SorobanAuthorizationEntry[]>;
38
43
  sign(txn: Transaction | string, options?: {
39
44
  keyId?: 'any' | 'sudo' | string | Uint8Array;
40
45
  ledgersToLive?: number;
41
46
  }): Promise<string>;
42
- getData(): Promise<Map<string, any>>;
47
+ getData(contractData?: SorobanRpc.Api.LedgerEntryResult): Promise<Map<string, any>>;
43
48
  private getPublicKeyObject;
44
49
  private convertEcdsaSignatureAsnToCompact;
45
50
  }