passkey-kit 0.4.5 → 0.5.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 +39 -5
- package/package.json +1 -1
- package/pnpm-workspace.yaml +2 -1
- package/src/base.ts +3 -182
- package/src/index.ts +2 -2
- package/src/kit.ts +8 -20
- package/src/server.ts +191 -0
- package/types/base.d.ts +1 -24
- package/types/index.d.ts +2 -2
- package/types/kit.d.ts +3 -7
- package/types/server.d.ts +27 -0
package/README.md
CHANGED
|
@@ -12,28 +12,62 @@ pnpm i passkey-kit
|
|
|
12
12
|
On the client:
|
|
13
13
|
```ts
|
|
14
14
|
const account = new PasskeyKit({
|
|
15
|
-
rpcUrl: env.
|
|
16
|
-
networkPassphrase: env.
|
|
17
|
-
factoryContractId: env.
|
|
15
|
+
rpcUrl: env.PUBLIC_rpcUrl,
|
|
16
|
+
networkPassphrase: env.PUBLIC_networkPassphrase,
|
|
17
|
+
factoryContractId: env.PUBLIC_factoryContractId,
|
|
18
18
|
});
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
On the server:
|
|
22
22
|
```ts
|
|
23
23
|
const account = new PasskeyBase({
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
rpcUrl: env.PUBLIC_rpcUrl,
|
|
25
|
+
launchtubeUrl: env.PUBLIC_launchtubeUrl,
|
|
26
|
+
launchtubeJwt: env.PRIVATE_launchtubeJwt,
|
|
27
|
+
mercuryUrl: env.PUBLIC_mercuryUrl,
|
|
28
|
+
mercuryEmail: env.PRIVATE_mercuryEmail,
|
|
29
|
+
mercuryPassword: env.PRIVATE_mercuryPassword,
|
|
26
30
|
});
|
|
27
31
|
```
|
|
28
32
|
|
|
29
33
|
Note that while I don't recommend it you can use the server-intended `send` method on the client side by passing in the `launchtubeUrl` and `launchtubeJwt` values to the `PasskeyKit` constructor. We do this in the `./demo` site to ease demonstration, development and experimentation, however in a production environment you'll want to keep your secrets safe on the server.
|
|
30
34
|
|
|
35
|
+
In order to utilize the zephyr indexing service to track available signers and reverse lookup smart wallet contract addresses from passkey ids you'll need to deploy the Zephyr program from inside the `./zephyr` directory.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
cd ./zephyr
|
|
39
|
+
cargo install mercury-cli
|
|
40
|
+
# Get a JWT from Mercury https://test.mercurydata.app
|
|
41
|
+
export MERCURY_JWT="<YOUR.MERCURY.JWT>"
|
|
42
|
+
# Make sure you're on Rust version 1.79.0 or newer
|
|
43
|
+
mercury-cli --jwt $MERCURY_JWT --local false --mainnet false deploy
|
|
44
|
+
```
|
|
45
|
+
|
|
31
46
|
This is a fully typed library so docs aren't provided, however there's a full example showcasing all the core public methods in the `./demo` directory. I also recommend reviewing the [Super Peach](https://github.com/kalepail/superpeach) repo for an example of how you could implement both the client and server side in a more real-world scenario.
|
|
32
47
|
|
|
33
48
|
Good luck, have fun, and change the world!
|
|
34
49
|
|
|
35
50
|
For any questions or to showcase your progress please join the `#passkeys` channel on our [Discord](https://discord.gg/stellardev).
|
|
36
51
|
|
|
52
|
+
## Some Notes
|
|
53
|
+
|
|
54
|
+
This is a TypeScript library and the npm package doesn't export a JavaScript version. The `@stellar/stellar-sdk` library is enormous and I really don't wan't folks bundling it up twice. Therefore you'll need to ensure you're transpiling this library into your project and that goes for either a TS project or a JS one. For many of you this will "just work" but for others you'll need to do some fiddling.
|
|
55
|
+
|
|
56
|
+
For example if you're using NextJS this will mean modifying your `next.config.mjs` file to include the following packages in the `transpilePackages` key:
|
|
57
|
+
```mjs
|
|
58
|
+
/** @type {import('next').NextConfig} */
|
|
59
|
+
const nextConfig = {
|
|
60
|
+
transpilePackages: [
|
|
61
|
+
'passkey-kit',
|
|
62
|
+
'passkey-factory-sdk',
|
|
63
|
+
'passkey-kit-sdk'
|
|
64
|
+
]
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export default nextConfig;
|
|
68
|
+
```
|
|
69
|
+
If someone smarter than me knows how to include an optional JS build from a TS library please submit a PR. I just don't want to deploy a compiled version of this and wind up having folks doubling up on an already gargantuan dependency.
|
|
70
|
+
|
|
37
71
|
## Contributing
|
|
38
72
|
|
|
39
73
|
Passkey kit consists of three primary directories:
|
package/package.json
CHANGED
package/pnpm-workspace.yaml
CHANGED
package/src/base.ts
CHANGED
|
@@ -1,192 +1,13 @@
|
|
|
1
|
-
import { SorobanRpc
|
|
2
|
-
import base64url from "base64url"
|
|
1
|
+
import { SorobanRpc } from "@stellar/stellar-sdk"
|
|
3
2
|
|
|
4
3
|
export class PasskeyBase {
|
|
5
4
|
public rpc: SorobanRpc.Server | undefined
|
|
6
5
|
public rpcUrl: string | undefined
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
public mercuryUrl: string | undefined
|
|
10
|
-
public mercuryJwt: string | undefined
|
|
11
|
-
public mercuryEmail: string | undefined
|
|
12
|
-
public mercuryPassword: string | undefined
|
|
13
|
-
|
|
14
|
-
constructor(options: {
|
|
15
|
-
rpcUrl?: string,
|
|
16
|
-
launchtubeUrl?: string,
|
|
17
|
-
launchtubeJwt?: string,
|
|
18
|
-
mercuryUrl?: string,
|
|
19
|
-
mercuryJwt?: string,
|
|
20
|
-
mercuryEmail?: string,
|
|
21
|
-
mercuryPassword?: string
|
|
22
|
-
}) {
|
|
23
|
-
const {
|
|
24
|
-
rpcUrl,
|
|
25
|
-
launchtubeUrl,
|
|
26
|
-
launchtubeJwt,
|
|
27
|
-
mercuryUrl,
|
|
28
|
-
mercuryJwt,
|
|
29
|
-
mercuryEmail,
|
|
30
|
-
mercuryPassword
|
|
31
|
-
} = options
|
|
32
|
-
|
|
33
|
-
if (launchtubeUrl)
|
|
34
|
-
this.launchtubeUrl = launchtubeUrl
|
|
35
|
-
|
|
36
|
-
if (launchtubeJwt)
|
|
37
|
-
this.launchtubeJwt = launchtubeJwt
|
|
38
|
-
|
|
39
|
-
if (mercuryUrl)
|
|
40
|
-
this.mercuryUrl = mercuryUrl
|
|
41
|
-
|
|
42
|
-
if (mercuryJwt)
|
|
43
|
-
this.mercuryJwt = mercuryJwt
|
|
44
|
-
|
|
45
|
-
if (mercuryEmail)
|
|
46
|
-
this.mercuryEmail = mercuryEmail
|
|
47
|
-
|
|
48
|
-
if (mercuryPassword)
|
|
49
|
-
this.mercuryPassword = mercuryPassword
|
|
50
|
-
|
|
51
|
-
if (!mercuryJwt && mercuryUrl && mercuryEmail && mercuryPassword)
|
|
52
|
-
this.setMercuryJwt()
|
|
53
|
-
|
|
6
|
+
|
|
7
|
+
constructor(rpcUrl?: string) {
|
|
54
8
|
if (rpcUrl) {
|
|
55
9
|
this.rpcUrl = rpcUrl
|
|
56
10
|
this.rpc = new SorobanRpc.Server(rpcUrl)
|
|
57
11
|
}
|
|
58
12
|
}
|
|
59
|
-
|
|
60
|
-
public async setMercuryJwt() {
|
|
61
|
-
if (!this.mercuryEmail || !this.mercuryPassword)
|
|
62
|
-
throw new Error('Mercury service not configured')
|
|
63
|
-
|
|
64
|
-
const { data: { authenticate: { jwtToken } } } = await fetch(`${this.mercuryUrl}/graphql`, {
|
|
65
|
-
method: 'POST',
|
|
66
|
-
headers: {
|
|
67
|
-
'Content-Type': 'application/json',
|
|
68
|
-
},
|
|
69
|
-
body: JSON.stringify({
|
|
70
|
-
query: `mutation {
|
|
71
|
-
authenticate(input: {
|
|
72
|
-
email: "${this.mercuryEmail}"
|
|
73
|
-
password: "${this.mercuryPassword}"
|
|
74
|
-
}) {
|
|
75
|
-
jwtToken
|
|
76
|
-
}
|
|
77
|
-
}`
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
.then(async (res) => {
|
|
81
|
-
if (res.ok)
|
|
82
|
-
return res.json()
|
|
83
|
-
|
|
84
|
-
throw await res.json()
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
this.mercuryJwt = jwtToken
|
|
88
|
-
return jwtToken
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
public async getSigners(contractId: string) {
|
|
92
|
-
if (!this.rpc || !this.mercuryUrl || !this.mercuryJwt)
|
|
93
|
-
throw new Error('Mercury service not configured')
|
|
94
|
-
|
|
95
|
-
const signers = await fetch(`${this.mercuryUrl}/zephyr/execute`, {
|
|
96
|
-
method: 'POST',
|
|
97
|
-
headers: {
|
|
98
|
-
'Content-Type': 'application/json',
|
|
99
|
-
Authorization: `Bearer ${this.mercuryJwt}`
|
|
100
|
-
},
|
|
101
|
-
body: JSON.stringify({
|
|
102
|
-
mode: {
|
|
103
|
-
Function: {
|
|
104
|
-
fname: "get_signers_by_address",
|
|
105
|
-
arguments: JSON.stringify({
|
|
106
|
-
address: contractId
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
.then(async (res) => {
|
|
113
|
-
if (res.ok)
|
|
114
|
-
return res.json()
|
|
115
|
-
|
|
116
|
-
throw await res.json()
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
for (const signer of signers) {
|
|
120
|
-
if (!signer.admin) {
|
|
121
|
-
try {
|
|
122
|
-
await this.rpc.getContractData(contractId, xdr.ScVal.scvBytes(signer.id), SorobanRpc.Durability.Temporary)
|
|
123
|
-
} catch {
|
|
124
|
-
signer.expired = true
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
signer.id = base64url(signer.id)
|
|
129
|
-
signer.pk = base64url(signer.pk)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return signers as { id: string, pk: string, admin: boolean, expired?: boolean }[]
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
public async getContractId(keyId: string) {
|
|
136
|
-
if (!this.mercuryUrl || !this.mercuryJwt)
|
|
137
|
-
return
|
|
138
|
-
|
|
139
|
-
const res = await fetch(`${this.mercuryUrl}/zephyr/execute`, {
|
|
140
|
-
method: 'POST',
|
|
141
|
-
headers: {
|
|
142
|
-
'Content-Type': 'application/json',
|
|
143
|
-
Authorization: `Bearer ${this.mercuryJwt}`
|
|
144
|
-
},
|
|
145
|
-
body: JSON.stringify({
|
|
146
|
-
mode: {
|
|
147
|
-
Function: {
|
|
148
|
-
fname: "get_address_by_signer",
|
|
149
|
-
arguments: JSON.stringify({
|
|
150
|
-
id: [...base64url.toBuffer(keyId)]
|
|
151
|
-
})
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
})
|
|
155
|
-
})
|
|
156
|
-
.then(async (res) => {
|
|
157
|
-
if (res.ok)
|
|
158
|
-
return res.json()
|
|
159
|
-
|
|
160
|
-
throw await res.json()
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
return res[0]?.address as string | undefined
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/* TODO
|
|
167
|
-
- Add a method for getting a paginated or filtered list of all a wallet's events
|
|
168
|
-
@Later
|
|
169
|
-
*/
|
|
170
|
-
|
|
171
|
-
public async send(xdr: string, fee: number = 10_000) {
|
|
172
|
-
if (!this.launchtubeUrl || !this.launchtubeJwt)
|
|
173
|
-
throw new Error('Launchtube service not configured')
|
|
174
|
-
|
|
175
|
-
const data = new FormData();
|
|
176
|
-
|
|
177
|
-
data.set('xdr', xdr);
|
|
178
|
-
data.set('fee', fee.toString());
|
|
179
|
-
|
|
180
|
-
return fetch(this.launchtubeUrl, {
|
|
181
|
-
method: 'POST',
|
|
182
|
-
headers: {
|
|
183
|
-
authorization: `Bearer ${this.launchtubeJwt}`,
|
|
184
|
-
},
|
|
185
|
-
body: data
|
|
186
|
-
}).then(async (res) => {
|
|
187
|
-
if (res.ok)
|
|
188
|
-
return res.json()
|
|
189
|
-
else throw await res.json()
|
|
190
|
-
})
|
|
191
|
-
}
|
|
192
13
|
}
|
package/src/index.ts
CHANGED
package/src/kit.ts
CHANGED
|
@@ -8,6 +8,8 @@ import { Buffer } from 'buffer'
|
|
|
8
8
|
import { PasskeyBase } from './base'
|
|
9
9
|
|
|
10
10
|
export class PasskeyKit extends PasskeyBase {
|
|
11
|
+
declare public rpc: SorobanRpc.Server
|
|
12
|
+
declare public rpcUrl: string
|
|
11
13
|
public keyId: string | undefined
|
|
12
14
|
public networkPassphrase: string
|
|
13
15
|
public factory: FactoryClient
|
|
@@ -17,22 +19,10 @@ export class PasskeyKit extends PasskeyBase {
|
|
|
17
19
|
rpcUrl: string,
|
|
18
20
|
networkPassphrase: string,
|
|
19
21
|
factoryContractId: string,
|
|
20
|
-
launchtubeUrl?: string,
|
|
21
|
-
launchtubeJwt?: string,
|
|
22
|
-
mercuryUrl?: string,
|
|
23
|
-
mercuryJwt?: string,
|
|
24
|
-
mercuryEmail?: string,
|
|
25
|
-
mercuryPassword?: string
|
|
26
22
|
}) {
|
|
27
23
|
const { rpcUrl, networkPassphrase, factoryContractId } = options
|
|
28
24
|
|
|
29
|
-
super(
|
|
30
|
-
|
|
31
|
-
if (!this.rpcUrl)
|
|
32
|
-
this.rpcUrl = rpcUrl
|
|
33
|
-
|
|
34
|
-
if (!this.rpc)
|
|
35
|
-
this.rpc = new SorobanRpc.Server(rpcUrl)
|
|
25
|
+
super(rpcUrl)
|
|
36
26
|
|
|
37
27
|
this.networkPassphrase = networkPassphrase
|
|
38
28
|
this.factory = new FactoryClient({
|
|
@@ -103,7 +93,7 @@ export class PasskeyKit extends PasskeyBase {
|
|
|
103
93
|
keyId?: string | Uint8Array,
|
|
104
94
|
getContractId?: (keyId: string) => Promise<string | undefined>
|
|
105
95
|
}) {
|
|
106
|
-
let { keyId, getContractId
|
|
96
|
+
let { keyId, getContractId } = opts || {}
|
|
107
97
|
let keyIdBuffer: Buffer
|
|
108
98
|
|
|
109
99
|
if (!keyId) {
|
|
@@ -114,8 +104,6 @@ export class PasskeyKit extends PasskeyBase {
|
|
|
114
104
|
timeout: 120_000
|
|
115
105
|
});
|
|
116
106
|
|
|
117
|
-
// console.log(response);
|
|
118
|
-
|
|
119
107
|
keyId = response.id
|
|
120
108
|
}
|
|
121
109
|
|
|
@@ -145,11 +133,11 @@ export class PasskeyKit extends PasskeyBase {
|
|
|
145
133
|
// attempt passkey id derivation
|
|
146
134
|
try {
|
|
147
135
|
// TODO what is the error if the entry exists but is archived?
|
|
148
|
-
await this.rpc
|
|
136
|
+
await this.rpc.getContractData(contractId, xdr.ScVal.scvLedgerKeyContractInstance())
|
|
149
137
|
}
|
|
150
138
|
// if that fails look up from the `getContractId` function
|
|
151
139
|
catch {
|
|
152
|
-
contractId = await getContractId(keyId)
|
|
140
|
+
contractId = getContractId ? await getContractId(keyId) : undefined
|
|
153
141
|
}
|
|
154
142
|
|
|
155
143
|
if (!contractId)
|
|
@@ -177,7 +165,7 @@ export class PasskeyKit extends PasskeyBase {
|
|
|
177
165
|
// Default mirrors DEFAULT_TIMEOUT (currently 5 minutes) https://github.com/stellar/js-stellar-sdk/blob/master/src/contract/utils.ts#L7
|
|
178
166
|
let { keyId, ledgersToLive = 60 } = options || {}
|
|
179
167
|
|
|
180
|
-
const lastLedger = await this.rpc
|
|
168
|
+
const lastLedger = await this.rpc.getLatestLedger().then(({ sequence }) => sequence)
|
|
181
169
|
const credentials = entry.credentials().address();
|
|
182
170
|
const preimage = xdr.HashIdPreimage.envelopeTypeSorobanAuthorization(
|
|
183
171
|
new xdr.HashIdPreimageSorobanAuthorization({
|
|
@@ -300,7 +288,7 @@ export class PasskeyKit extends PasskeyBase {
|
|
|
300
288
|
await this.signAuthEntries(entries, options)
|
|
301
289
|
}
|
|
302
290
|
|
|
303
|
-
const sim = await this.rpc
|
|
291
|
+
const sim = await this.rpc.simulateTransaction(txn)
|
|
304
292
|
|
|
305
293
|
if (
|
|
306
294
|
SorobanRpc.Api.isSimulationError(sim)
|
package/src/server.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { SorobanRpc, xdr } from "@stellar/stellar-sdk"
|
|
2
|
+
import base64url from "base64url"
|
|
3
|
+
import { PasskeyBase } from "./base"
|
|
4
|
+
|
|
5
|
+
export class PasskeyServer extends PasskeyBase {
|
|
6
|
+
public launchtubeUrl: string | undefined
|
|
7
|
+
public launchtubeJwt: string | undefined
|
|
8
|
+
public mercuryUrl: string | undefined
|
|
9
|
+
public mercuryJwt: string | undefined
|
|
10
|
+
public mercuryEmail: string | undefined
|
|
11
|
+
public mercuryPassword: string | undefined
|
|
12
|
+
|
|
13
|
+
constructor(options: {
|
|
14
|
+
rpcUrl?: string,
|
|
15
|
+
launchtubeUrl?: string,
|
|
16
|
+
launchtubeJwt?: string,
|
|
17
|
+
mercuryUrl?: string,
|
|
18
|
+
mercuryJwt?: string,
|
|
19
|
+
mercuryEmail?: string,
|
|
20
|
+
mercuryPassword?: string
|
|
21
|
+
}) {
|
|
22
|
+
const {
|
|
23
|
+
rpcUrl,
|
|
24
|
+
launchtubeUrl,
|
|
25
|
+
launchtubeJwt,
|
|
26
|
+
mercuryUrl,
|
|
27
|
+
mercuryJwt,
|
|
28
|
+
mercuryEmail,
|
|
29
|
+
mercuryPassword
|
|
30
|
+
} = options
|
|
31
|
+
|
|
32
|
+
super(rpcUrl)
|
|
33
|
+
|
|
34
|
+
if (launchtubeUrl)
|
|
35
|
+
this.launchtubeUrl = launchtubeUrl
|
|
36
|
+
|
|
37
|
+
if (launchtubeJwt)
|
|
38
|
+
this.launchtubeJwt = launchtubeJwt
|
|
39
|
+
|
|
40
|
+
if (mercuryUrl)
|
|
41
|
+
this.mercuryUrl = mercuryUrl
|
|
42
|
+
|
|
43
|
+
if (mercuryJwt)
|
|
44
|
+
this.mercuryJwt = mercuryJwt
|
|
45
|
+
|
|
46
|
+
if (mercuryEmail)
|
|
47
|
+
this.mercuryEmail = mercuryEmail
|
|
48
|
+
|
|
49
|
+
if (mercuryPassword)
|
|
50
|
+
this.mercuryPassword = mercuryPassword
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public async setMercuryJwt() {
|
|
54
|
+
if (!this.mercuryUrl || !this.mercuryEmail || !this.mercuryPassword)
|
|
55
|
+
throw new Error('Mercury service not configured')
|
|
56
|
+
|
|
57
|
+
const { data: { authenticate: { jwtToken } } } = await fetch(`${this.mercuryUrl}/graphql`, {
|
|
58
|
+
method: 'POST',
|
|
59
|
+
headers: {
|
|
60
|
+
'Content-Type': 'application/json',
|
|
61
|
+
},
|
|
62
|
+
body: JSON.stringify({
|
|
63
|
+
query: `mutation {
|
|
64
|
+
authenticate(input: {
|
|
65
|
+
email: "${this.mercuryEmail}"
|
|
66
|
+
password: "${this.mercuryPassword}"
|
|
67
|
+
}) {
|
|
68
|
+
jwtToken
|
|
69
|
+
}
|
|
70
|
+
}`
|
|
71
|
+
})
|
|
72
|
+
})
|
|
73
|
+
.then(async (res) => {
|
|
74
|
+
if (res.ok)
|
|
75
|
+
return res.json()
|
|
76
|
+
|
|
77
|
+
throw await res.json()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
this.mercuryJwt = jwtToken
|
|
81
|
+
return jwtToken
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public async getSigners(contractId: string) {
|
|
85
|
+
if (this.rpc && this.mercuryUrl && !this.mercuryJwt)
|
|
86
|
+
await this.setMercuryJwt()
|
|
87
|
+
|
|
88
|
+
if (!this.rpc || !this.mercuryUrl || !this.mercuryJwt)
|
|
89
|
+
throw new Error('Mercury service not configured')
|
|
90
|
+
|
|
91
|
+
const signers = await fetch(`${this.mercuryUrl}/zephyr/execute`, {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
headers: {
|
|
94
|
+
'Content-Type': 'application/json',
|
|
95
|
+
Authorization: `Bearer ${this.mercuryJwt}`
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
mode: {
|
|
99
|
+
Function: {
|
|
100
|
+
fname: "get_signers_by_address",
|
|
101
|
+
arguments: JSON.stringify({
|
|
102
|
+
address: contractId
|
|
103
|
+
})
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
.then(async (res) => {
|
|
109
|
+
if (res.ok)
|
|
110
|
+
return res.json()
|
|
111
|
+
|
|
112
|
+
throw await res.json()
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
for (const signer of signers) {
|
|
116
|
+
if (!signer.admin) {
|
|
117
|
+
try {
|
|
118
|
+
await this.rpc.getContractData(contractId, xdr.ScVal.scvBytes(signer.id), SorobanRpc.Durability.Temporary)
|
|
119
|
+
} catch {
|
|
120
|
+
signer.expired = true
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
signer.id = base64url(signer.id)
|
|
125
|
+
signer.pk = base64url(signer.pk)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return signers as { id: string, pk: string, admin: boolean, expired?: boolean }[]
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public async getContractId(keyId: string) {
|
|
132
|
+
if (this.mercuryUrl && !this.mercuryJwt)
|
|
133
|
+
await this.setMercuryJwt()
|
|
134
|
+
|
|
135
|
+
if (!this.mercuryUrl || !this.mercuryJwt)
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
const res = await fetch(`${this.mercuryUrl}/zephyr/execute`, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: {
|
|
141
|
+
'Content-Type': 'application/json',
|
|
142
|
+
Authorization: `Bearer ${this.mercuryJwt}`
|
|
143
|
+
},
|
|
144
|
+
body: JSON.stringify({
|
|
145
|
+
mode: {
|
|
146
|
+
Function: {
|
|
147
|
+
fname: "get_address_by_signer",
|
|
148
|
+
arguments: JSON.stringify({
|
|
149
|
+
id: [...base64url.toBuffer(keyId)]
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
.then(async (res) => {
|
|
156
|
+
if (res.ok)
|
|
157
|
+
return res.json()
|
|
158
|
+
|
|
159
|
+
throw await res.json()
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
return res[0]?.address as string | undefined
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* TODO
|
|
166
|
+
- Add a method for getting a paginated or filtered list of all a wallet's events
|
|
167
|
+
@Later
|
|
168
|
+
*/
|
|
169
|
+
|
|
170
|
+
public async send(xdr: string, fee: number = 10_000) {
|
|
171
|
+
if (!this.launchtubeUrl || !this.launchtubeJwt)
|
|
172
|
+
throw new Error('Launchtube service not configured')
|
|
173
|
+
|
|
174
|
+
const data = new FormData();
|
|
175
|
+
|
|
176
|
+
data.set('xdr', xdr);
|
|
177
|
+
data.set('fee', fee.toString());
|
|
178
|
+
|
|
179
|
+
return fetch(this.launchtubeUrl, {
|
|
180
|
+
method: 'POST',
|
|
181
|
+
headers: {
|
|
182
|
+
authorization: `Bearer ${this.launchtubeJwt}`,
|
|
183
|
+
},
|
|
184
|
+
body: data
|
|
185
|
+
}).then(async (res) => {
|
|
186
|
+
if (res.ok)
|
|
187
|
+
return res.json()
|
|
188
|
+
else throw await res.json()
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
}
|
package/types/base.d.ts
CHANGED
|
@@ -2,28 +2,5 @@ import { SorobanRpc } from "@stellar/stellar-sdk";
|
|
|
2
2
|
export declare class PasskeyBase {
|
|
3
3
|
rpc: SorobanRpc.Server | undefined;
|
|
4
4
|
rpcUrl: string | undefined;
|
|
5
|
-
|
|
6
|
-
launchtubeJwt: string | undefined;
|
|
7
|
-
mercuryUrl: string | undefined;
|
|
8
|
-
mercuryJwt: string | undefined;
|
|
9
|
-
mercuryEmail: string | undefined;
|
|
10
|
-
mercuryPassword: string | undefined;
|
|
11
|
-
constructor(options: {
|
|
12
|
-
rpcUrl?: string;
|
|
13
|
-
launchtubeUrl?: string;
|
|
14
|
-
launchtubeJwt?: string;
|
|
15
|
-
mercuryUrl?: string;
|
|
16
|
-
mercuryJwt?: string;
|
|
17
|
-
mercuryEmail?: string;
|
|
18
|
-
mercuryPassword?: string;
|
|
19
|
-
});
|
|
20
|
-
setMercuryJwt(): Promise<any>;
|
|
21
|
-
getSigners(contractId: string): Promise<{
|
|
22
|
-
id: string;
|
|
23
|
-
pk: string;
|
|
24
|
-
admin: boolean;
|
|
25
|
-
expired?: boolean;
|
|
26
|
-
}[]>;
|
|
27
|
-
getContractId(keyId: string): Promise<string | undefined>;
|
|
28
|
-
send(xdr: string, fee?: number): Promise<any>;
|
|
5
|
+
constructor(rpcUrl?: string);
|
|
29
6
|
}
|
package/types/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PasskeyServer } from "./server";
|
|
2
2
|
import { PasskeyKit } from "./kit";
|
|
3
|
-
export {
|
|
3
|
+
export { PasskeyServer, PasskeyKit };
|
package/types/kit.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Client as PasskeyClient } from 'passkey-kit-sdk';
|
|
2
2
|
import { Client as FactoryClient } from 'passkey-factory-sdk';
|
|
3
|
-
import { xdr, Transaction } from '@stellar/stellar-sdk';
|
|
3
|
+
import { 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 {
|
|
7
|
+
rpc: SorobanRpc.Server;
|
|
8
|
+
rpcUrl: string;
|
|
7
9
|
keyId: string | undefined;
|
|
8
10
|
networkPassphrase: string;
|
|
9
11
|
factory: FactoryClient;
|
|
@@ -12,12 +14,6 @@ export declare class PasskeyKit extends PasskeyBase {
|
|
|
12
14
|
rpcUrl: string;
|
|
13
15
|
networkPassphrase: string;
|
|
14
16
|
factoryContractId: string;
|
|
15
|
-
launchtubeUrl?: string;
|
|
16
|
-
launchtubeJwt?: string;
|
|
17
|
-
mercuryUrl?: string;
|
|
18
|
-
mercuryJwt?: string;
|
|
19
|
-
mercuryEmail?: string;
|
|
20
|
-
mercuryPassword?: string;
|
|
21
17
|
});
|
|
22
18
|
createWallet(app: string, user: string): Promise<{
|
|
23
19
|
keyId: Buffer;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { PasskeyBase } from "./base";
|
|
2
|
+
export declare class PasskeyServer extends PasskeyBase {
|
|
3
|
+
launchtubeUrl: string | undefined;
|
|
4
|
+
launchtubeJwt: string | undefined;
|
|
5
|
+
mercuryUrl: string | undefined;
|
|
6
|
+
mercuryJwt: string | undefined;
|
|
7
|
+
mercuryEmail: string | undefined;
|
|
8
|
+
mercuryPassword: string | undefined;
|
|
9
|
+
constructor(options: {
|
|
10
|
+
rpcUrl?: string;
|
|
11
|
+
launchtubeUrl?: string;
|
|
12
|
+
launchtubeJwt?: string;
|
|
13
|
+
mercuryUrl?: string;
|
|
14
|
+
mercuryJwt?: string;
|
|
15
|
+
mercuryEmail?: string;
|
|
16
|
+
mercuryPassword?: string;
|
|
17
|
+
});
|
|
18
|
+
setMercuryJwt(): Promise<any>;
|
|
19
|
+
getSigners(contractId: string): Promise<{
|
|
20
|
+
id: string;
|
|
21
|
+
pk: string;
|
|
22
|
+
admin: boolean;
|
|
23
|
+
expired?: boolean;
|
|
24
|
+
}[]>;
|
|
25
|
+
getContractId(keyId: string): Promise<string | undefined>;
|
|
26
|
+
send(xdr: string, fee?: number): Promise<any>;
|
|
27
|
+
}
|