@ckb-ccc/joy-id 0.0.5-alpha.0 → 0.0.5-alpha.2

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 (51) hide show
  1. package/dist/advancedBarrel.d.ts +1 -0
  2. package/dist/advancedBarrel.d.ts.map +1 -1
  3. package/dist/advancedBarrel.js +1 -0
  4. package/dist/barrel.d.ts +1 -0
  5. package/dist/barrel.d.ts.map +1 -1
  6. package/dist/barrel.js +1 -0
  7. package/dist/btc/index.d.ts +6 -3
  8. package/dist/btc/index.d.ts.map +1 -1
  9. package/dist/btc/index.js +28 -17
  10. package/dist/ckb/index.d.ts +9 -3
  11. package/dist/ckb/index.d.ts.map +1 -1
  12. package/dist/ckb/index.js +58 -30
  13. package/dist/common/index.d.ts +24 -0
  14. package/dist/common/index.d.ts.map +1 -0
  15. package/dist/common/index.js +50 -0
  16. package/dist/evm/index.d.ts +20 -0
  17. package/dist/evm/index.d.ts.map +1 -0
  18. package/dist/evm/index.js +78 -0
  19. package/dist/signerFactory/index.d.ts +1 -1
  20. package/dist/signerFactory/index.d.ts.map +1 -1
  21. package/dist/signerFactory/index.js +10 -10
  22. package/dist.commonjs/advancedBarrel.d.ts +1 -0
  23. package/dist.commonjs/advancedBarrel.d.ts.map +1 -1
  24. package/dist.commonjs/advancedBarrel.js +1 -0
  25. package/dist.commonjs/barrel.d.ts +1 -0
  26. package/dist.commonjs/barrel.d.ts.map +1 -1
  27. package/dist.commonjs/barrel.js +1 -0
  28. package/dist.commonjs/btc/index.d.ts +6 -3
  29. package/dist.commonjs/btc/index.d.ts.map +1 -1
  30. package/dist.commonjs/btc/index.js +27 -16
  31. package/dist.commonjs/ckb/index.d.ts +9 -3
  32. package/dist.commonjs/ckb/index.d.ts.map +1 -1
  33. package/dist.commonjs/ckb/index.js +57 -29
  34. package/dist.commonjs/common/index.d.ts +24 -0
  35. package/dist.commonjs/common/index.d.ts.map +1 -0
  36. package/dist.commonjs/common/index.js +54 -0
  37. package/dist.commonjs/evm/index.d.ts +20 -0
  38. package/dist.commonjs/evm/index.d.ts.map +1 -0
  39. package/dist.commonjs/evm/index.js +82 -0
  40. package/dist.commonjs/signerFactory/index.d.ts +1 -1
  41. package/dist.commonjs/signerFactory/index.d.ts.map +1 -1
  42. package/dist.commonjs/signerFactory/index.js +10 -10
  43. package/package.json +3 -3
  44. package/src/advancedBarrel.ts +1 -0
  45. package/src/barrel.ts +1 -0
  46. package/src/btc/index.ts +45 -19
  47. package/src/ckb/index.ts +89 -44
  48. package/src/common/index.ts +100 -0
  49. package/src/evm/index.ts +118 -0
  50. package/src/signerFactory/index.ts +14 -9
  51. package/dist.commonjs/tsconfig.commonjs.tsbuildinfo +0 -1
package/src/btc/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ccc } from "@ckb-ccc/core";
2
- import { authWithPopup, signMessageWithPopup } from "@joyid/common";
2
+ import { DappRequestType, buildJoyIDURL } from "@joyid/common";
3
+ import { createPopup } from "../common";
3
4
  import {
4
5
  Connection,
5
6
  ConnectionsRepo,
@@ -19,15 +20,34 @@ export class BitcoinSigner extends ccc.SignerBtc {
19
20
 
20
21
  constructor(
21
22
  client: ccc.Client,
23
+ private readonly name: string,
24
+ private readonly icon: string,
22
25
  private readonly addressType: "auto" | "p2wpkh" | "p2tr" = "auto",
23
- private readonly uri = "https://app.joy.id",
26
+ private readonly appUri = "https://app.joy.id",
24
27
  private readonly connectionsRepo: ConnectionsRepo = new ConnectionsRepoLocalStorage(),
25
28
  ) {
26
29
  super(client);
27
30
  }
28
31
 
29
32
  async replaceClient(client: ccc.Client): Promise<BitcoinSigner> {
30
- return new BitcoinSigner(client, this.addressType, this.uri);
33
+ return new BitcoinSigner(
34
+ client,
35
+ this.name,
36
+ this.icon,
37
+ this.addressType,
38
+ this.appUri,
39
+ this.connectionsRepo,
40
+ );
41
+ }
42
+
43
+ private getConfig() {
44
+ return {
45
+ redirectURL: location.href,
46
+ joyidAppURL: this.appUri,
47
+ requestNetwork: `btc-${this.addressType}`,
48
+ name: this.name,
49
+ logo: this.icon,
50
+ };
31
51
  }
32
52
 
33
53
  async getBtcAccount(): Promise<string> {
@@ -41,10 +61,10 @@ export class BitcoinSigner extends ccc.SignerBtc {
41
61
  }
42
62
 
43
63
  async connect(): Promise<void> {
44
- const res = await authWithPopup({
45
- joyidAppURL: this.uri,
46
- redirectURL: location.href,
47
- requestNetwork: `btc-${this.addressType}`,
64
+ const config = this.getConfig();
65
+ const res = await createPopup(buildJoyIDURL(config, "popup", "/auth"), {
66
+ ...config,
67
+ type: DappRequestType.Auth,
48
68
  });
49
69
 
50
70
  const { address, pubkey } = (() => {
@@ -61,11 +81,11 @@ export class BitcoinSigner extends ccc.SignerBtc {
61
81
  };
62
82
  await Promise.all([
63
83
  this.connectionsRepo.set(
64
- { uri: this.uri, addressType: `btc-${res.btcAddressType}` },
84
+ { uri: this.appUri, addressType: `btc-${res.btcAddressType}` },
65
85
  this.connection,
66
86
  ),
67
87
  this.connectionsRepo.set(
68
- { uri: this.uri, addressType: "btc-auto" },
88
+ { uri: this.appUri, addressType: "btc-auto" },
69
89
  this.connection,
70
90
  ),
71
91
  ]);
@@ -77,26 +97,32 @@ export class BitcoinSigner extends ccc.SignerBtc {
77
97
  }
78
98
 
79
99
  this.connection = await this.connectionsRepo.get({
80
- uri: this.uri,
100
+ uri: this.appUri,
81
101
  addressType: `btc-${this.addressType}`,
82
102
  });
83
103
  return this.connection !== undefined;
84
104
  }
85
105
 
86
- async signMessage(message: string | ccc.BytesLike): Promise<string> {
106
+ async signMessageRaw(message: string | ccc.BytesLike): Promise<string> {
87
107
  const { address } = this.assertConnection();
88
108
 
89
109
  const challenge =
90
110
  typeof message === "string" ? message : ccc.hexFrom(message).slice(2);
91
111
 
92
- const { signature } = await signMessageWithPopup({
93
- joyidAppURL: this.uri,
94
- requestNetwork: `btc-${this.addressType}`,
95
- challenge,
96
- address,
97
- signMessageType: "ecdsa",
98
- redirectURL: location.href,
99
- });
112
+ const config = this.getConfig();
113
+ const { signature } = await createPopup(
114
+ buildJoyIDURL(
115
+ {
116
+ ...config,
117
+ challenge,
118
+ address,
119
+ signMessageType: "ecdsa",
120
+ },
121
+ "popup",
122
+ "/sign-message",
123
+ ),
124
+ { ...config, type: DappRequestType.SignMessage },
125
+ );
100
126
  return signature;
101
127
  }
102
128
  }
package/src/ckb/index.ts CHANGED
@@ -1,13 +1,7 @@
1
1
  import { ccc } from "@ckb-ccc/core";
2
2
  import { Aggregator } from "@joyid/ckb";
3
- import {
4
- DappRequestType,
5
- authWithPopup,
6
- buildJoyIDURL,
7
- createBlockDialog,
8
- openPopup,
9
- runPopup,
10
- } from "@joyid/common";
3
+ import { DappRequestType, buildJoyIDURL } from "@joyid/common";
4
+ import { createPopup } from "../common";
11
5
  import {
12
6
  Connection,
13
7
  ConnectionsRepo,
@@ -15,6 +9,14 @@ import {
15
9
  } from "../connectionsStorage";
16
10
 
17
11
  export class CkbSigner extends ccc.Signer {
12
+ get type(): ccc.SignerType {
13
+ return ccc.SignerType.CKB;
14
+ }
15
+
16
+ get signType(): ccc.SignerSignType {
17
+ return ccc.SignerSignType.JoyId;
18
+ }
19
+
18
20
  private connection?: Connection;
19
21
 
20
22
  private async assertConnection(): Promise<Connection> {
@@ -27,7 +29,9 @@ export class CkbSigner extends ccc.Signer {
27
29
 
28
30
  constructor(
29
31
  client: ccc.Client,
30
- private readonly _uri?: string,
32
+ private readonly name: string,
33
+ private readonly icon: string,
34
+ private readonly _appUri?: string,
31
35
  private readonly _aggregatorUri?: string,
32
36
  private readonly connectionsRepo: ConnectionsRepo = new ConnectionsRepoLocalStorage(),
33
37
  ) {
@@ -35,34 +39,44 @@ export class CkbSigner extends ccc.Signer {
35
39
  }
36
40
 
37
41
  async replaceClient(client: ccc.Client): Promise<CkbSigner> {
38
- return new CkbSigner(client, this._uri);
42
+ return new CkbSigner(
43
+ client,
44
+ this.name,
45
+ this.icon,
46
+ this._appUri,
47
+ this._aggregatorUri,
48
+ this.connectionsRepo,
49
+ );
39
50
  }
40
51
 
41
- private async getUri(): Promise<string> {
42
- if (this._uri) {
43
- return this._uri;
44
- }
45
-
46
- return (await this.client.getAddressPrefix()) === "ckb"
47
- ? "https://app.joy.id"
48
- : "https://testnet.joyid.dev";
52
+ private getConfig() {
53
+ return {
54
+ redirectURL: location.href,
55
+ joyidAppURL:
56
+ this._appUri ?? this.client.addressPrefix === "ckb"
57
+ ? "https://app.joy.id"
58
+ : "https://testnet.joyid.dev",
59
+ name: this.name,
60
+ logo: this.icon,
61
+ };
49
62
  }
50
63
 
51
- private async getAggregatorUri(): Promise<string> {
64
+ private getAggregatorUri(): string {
52
65
  if (this._aggregatorUri) {
53
66
  return this._aggregatorUri;
54
67
  }
55
68
 
56
- return (await this.client.getAddressPrefix()) === "ckb"
69
+ return this.client.addressPrefix === "ckb"
57
70
  ? "https://cota.nervina.dev/mainnet-aggregator"
58
71
  : "https://cota.nervina.dev/aggregator";
59
72
  }
60
73
 
61
74
  async connect(): Promise<void> {
62
- const uri = await this.getUri();
63
- const res = await authWithPopup({
64
- joyidAppURL: uri,
65
- redirectURL: location.href,
75
+ const config = this.getConfig();
76
+
77
+ const res = await createPopup(buildJoyIDURL(config, "popup", "/auth"), {
78
+ ...config,
79
+ type: DappRequestType.Auth,
66
80
  });
67
81
 
68
82
  this.connection = {
@@ -90,6 +104,14 @@ export class CkbSigner extends ccc.Signer {
90
104
  return (await this.assertConnection()).address;
91
105
  }
92
106
 
107
+ async getIdentity(): Promise<string> {
108
+ const connection = await this.assertConnection();
109
+ return JSON.stringify({
110
+ keyType: connection.keyType,
111
+ publicKey: connection.publicKey.slice(2),
112
+ });
113
+ }
114
+
93
115
  async getAddressObj(): Promise<ccc.Address> {
94
116
  return await ccc.Address.fromString(
95
117
  await this.getInternalAddress(),
@@ -161,38 +183,61 @@ export class CkbSigner extends ccc.Signer {
161
183
  async signOnlyTransaction(
162
184
  txLike: ccc.TransactionLike,
163
185
  ): Promise<ccc.Transaction> {
164
- const popup = openPopup("");
165
- if (!popup) {
166
- return createBlockDialog(async () => this.signOnlyTransaction(txLike));
167
- }
168
186
  const tx = ccc.Transaction.from(txLike);
169
187
  const { script } = await this.getAddressObj();
170
188
 
171
- popup.location.href = buildJoyIDURL(
189
+ const config = this.getConfig();
190
+ const res = await createPopup(
191
+ buildJoyIDURL(
192
+ {
193
+ ...config,
194
+ tx: JSON.parse(tx.stringify()),
195
+ signerAddress: (await this.assertConnection()).address,
196
+ witnessIndex: await tx.findInputIndexByLock(script, this.client),
197
+ },
198
+ "popup",
199
+ "/sign-ckb-raw-tx",
200
+ ),
172
201
  {
173
- joyidAppURL: await this.getUri(),
174
- tx: JSON.parse(tx.stringify()),
175
- signerAddress: (await this.assertConnection()).address,
176
- redirectURL: location.href,
177
- witnessIndex: await tx.findInputIndexByLock(script, this.client),
202
+ ...config,
203
+ type: DappRequestType.SignCkbRawTx,
178
204
  },
179
- "popup",
180
- "/sign-ckb-raw-tx",
181
205
  );
182
206
 
183
- const res = await runPopup({
184
- timeoutInSeconds: 3600,
185
- popup,
186
- type: DappRequestType.SignCkbRawTx,
187
- });
188
-
189
207
  return ccc.Transaction.from(res.tx);
190
208
  }
191
209
 
210
+ async signMessageRaw(message: string | ccc.BytesLike): Promise<string> {
211
+ const { address } = await this.assertConnection();
212
+
213
+ const challenge =
214
+ typeof message === "string" ? message : ccc.hexFrom(message).slice(2);
215
+
216
+ const config = this.getConfig();
217
+ const res = await createPopup(
218
+ buildJoyIDURL(
219
+ {
220
+ ...config,
221
+ challenge,
222
+ isData: typeof message !== "string",
223
+ address,
224
+ },
225
+ "popup",
226
+ "/sign-message",
227
+ ),
228
+ { ...config, type: DappRequestType.SignMessage },
229
+ );
230
+ return JSON.stringify({
231
+ signature: res.signature,
232
+ alg: res.alg,
233
+ message: res.message,
234
+ });
235
+ }
236
+
192
237
  private async saveConnection() {
193
238
  return this.connectionsRepo.set(
194
239
  {
195
- uri: await this.getUri(),
240
+ uri: this.getConfig().joyidAppURL,
196
241
  addressType: "ckb",
197
242
  },
198
243
  this.connection,
@@ -201,7 +246,7 @@ export class CkbSigner extends ccc.Signer {
201
246
 
202
247
  private async restoreConnection() {
203
248
  this.connection = await this.connectionsRepo.get({
204
- uri: await this.getUri(),
249
+ uri: this.getConfig().joyidAppURL,
205
250
  addressType: "ckb",
206
251
  });
207
252
  }
@@ -0,0 +1,100 @@
1
+ import {
2
+ AuthResponseData,
3
+ DappRequestType,
4
+ EvmWeb2LoginResponse,
5
+ PopupCancelledError,
6
+ PopupConfigOptions,
7
+ PopupNotSupportedError,
8
+ PopupTimeoutError,
9
+ SignCkbTxResponseData,
10
+ SignCotaNFTResponseData,
11
+ SignEvmTxResponseData,
12
+ SignMessageResponseData,
13
+ SignNostrEventData,
14
+ createBlockDialog,
15
+ isStandaloneBrowser,
16
+ openPopup,
17
+ } from "@joyid/common";
18
+
19
+ export interface PopupReturnType {
20
+ [DappRequestType.Auth]: AuthResponseData;
21
+ [DappRequestType.SignMessage]: SignMessageResponseData;
22
+ [DappRequestType.SignEvm]: SignEvmTxResponseData;
23
+ [DappRequestType.SignPsbt]: SignEvmTxResponseData;
24
+ [DappRequestType.BatchSignPsbt]: {
25
+ psbts: string[];
26
+ };
27
+ [DappRequestType.SignCkbTx]: SignCkbTxResponseData;
28
+ [DappRequestType.SignCotaNFT]: SignCotaNFTResponseData;
29
+ [DappRequestType.SignCkbRawTx]: SignCkbTxResponseData;
30
+ [DappRequestType.SignNostrEvent]: SignNostrEventData;
31
+ [DappRequestType.EncryptNostrMessage]: any;
32
+ [DappRequestType.DecryptNostrMessage]: any;
33
+ [DappRequestType.AuthMiniApp]: any;
34
+ [DappRequestType.SignMiniAppEvm]: any;
35
+ [DappRequestType.SignMiniAppMessage]: any;
36
+ [DappRequestType.EvmWeb2Login]: EvmWeb2LoginResponse;
37
+ }
38
+
39
+ export async function createPopup<T extends DappRequestType>(
40
+ url: string,
41
+ config: PopupConfigOptions<T> & { joyidAppURL: string },
42
+ ): Promise<PopupReturnType[T]> {
43
+ if (config.popup == null) {
44
+ config.popup = openPopup("");
45
+
46
+ if (config.popup == null) {
47
+ return createBlockDialog(async () => createPopup(url, config));
48
+ }
49
+ }
50
+
51
+ config.popup.location.href = url;
52
+
53
+ return new Promise((resolve, reject) => {
54
+ if (isStandaloneBrowser()) {
55
+ reject(new PopupNotSupportedError(config.popup));
56
+ }
57
+ let popupEventListener: (e: MessageEvent) => void;
58
+ let timeoutId: undefined | ReturnType<typeof setTimeout>;
59
+ // Check each second if the popup is closed triggering a PopupCancelledError
60
+ const popupTimer = setInterval(() => {
61
+ if (config.popup?.closed) {
62
+ clearInterval(popupTimer);
63
+ clearTimeout(timeoutId);
64
+ window.removeEventListener("message", popupEventListener, false);
65
+ reject(new PopupCancelledError(config.popup));
66
+ }
67
+ }, 1000);
68
+
69
+ timeoutId = setTimeout(
70
+ () => {
71
+ clearInterval(popupTimer);
72
+ reject(new PopupTimeoutError(config.popup));
73
+ window.removeEventListener("message", popupEventListener, false);
74
+ },
75
+ (config.timeoutInSeconds ?? 3000) * 1000,
76
+ );
77
+
78
+ popupEventListener = (e: MessageEvent) => {
79
+ const { joyidAppURL } = config;
80
+ const appURL = new URL(joyidAppURL);
81
+ if (e.origin !== appURL.origin) {
82
+ return;
83
+ }
84
+ if (!e.data || e.data?.type !== config.type) {
85
+ return;
86
+ }
87
+
88
+ clearTimeout(timeoutId);
89
+ clearInterval(popupTimer);
90
+ window.removeEventListener("message", popupEventListener, false);
91
+ config.popup.close();
92
+ if (e.data.error) {
93
+ reject(new Error(e.data.error));
94
+ }
95
+ resolve(e.data.data);
96
+ };
97
+
98
+ window.addEventListener("message", popupEventListener);
99
+ });
100
+ }
@@ -0,0 +1,118 @@
1
+ import { ccc } from "@ckb-ccc/core";
2
+ import { DappRequestType, buildJoyIDURL } from "@joyid/common";
3
+ import { createPopup } from "../common";
4
+ import {
5
+ Connection,
6
+ ConnectionsRepo,
7
+ ConnectionsRepoLocalStorage,
8
+ } from "../connectionsStorage";
9
+
10
+ export class EvmSigner extends ccc.SignerEvm {
11
+ private connection?: Connection;
12
+
13
+ private assertConnection() {
14
+ if (!this.isConnected() || !this.connection) {
15
+ throw new Error("Not connected");
16
+ }
17
+
18
+ return this.connection;
19
+ }
20
+
21
+ constructor(
22
+ client: ccc.Client,
23
+ private readonly name: string,
24
+ private readonly icon: string,
25
+ private readonly appUri = "https://app.joy.id",
26
+ private readonly connectionsRepo: ConnectionsRepo = new ConnectionsRepoLocalStorage(),
27
+ ) {
28
+ super(client);
29
+ }
30
+
31
+ async replaceClient(client: ccc.Client): Promise<EvmSigner> {
32
+ return new EvmSigner(
33
+ client,
34
+ this.name,
35
+ this.icon,
36
+ this.appUri,
37
+ this.connectionsRepo,
38
+ );
39
+ }
40
+
41
+ private getConfig() {
42
+ return {
43
+ redirectURL: location.href,
44
+ joyidAppURL: this.appUri,
45
+ requestNetwork: `ethereum`,
46
+ name: this.name,
47
+ logo: this.icon,
48
+ };
49
+ }
50
+
51
+ async getEvmAccount() {
52
+ return this.assertConnection().address;
53
+ }
54
+
55
+ async connect(): Promise<void> {
56
+ const config = await this.getConfig();
57
+
58
+ const res = await createPopup(buildJoyIDURL(config, "popup", "/auth"), {
59
+ ...config,
60
+ type: DappRequestType.Auth,
61
+ });
62
+
63
+ this.connection = {
64
+ address: res.ethAddress,
65
+ publicKey: ccc.hexFrom(res.pubkey),
66
+ keyType: res.keyType,
67
+ };
68
+ await this.saveConnection();
69
+ }
70
+
71
+ async isConnected(): Promise<boolean> {
72
+ if (this.connection) {
73
+ return true;
74
+ }
75
+ await this.restoreConnection();
76
+ return this.connection !== undefined;
77
+ }
78
+
79
+ async signMessageRaw(message: string | ccc.BytesLike): Promise<ccc.Hex> {
80
+ const { address } = this.assertConnection();
81
+
82
+ const challenge =
83
+ typeof message === "string" ? message : ccc.hexFrom(message).slice(2);
84
+
85
+ const config = this.getConfig();
86
+ const { signature } = await createPopup(
87
+ buildJoyIDURL(
88
+ {
89
+ ...config,
90
+ challenge,
91
+ isData: typeof message !== "string",
92
+ address,
93
+ },
94
+ "popup",
95
+ "/sign-message",
96
+ ),
97
+ { ...config, type: DappRequestType.SignMessage },
98
+ );
99
+ return ccc.hexFrom(signature);
100
+ }
101
+
102
+ private async saveConnection() {
103
+ return this.connectionsRepo.set(
104
+ {
105
+ uri: this.getConfig().joyidAppURL,
106
+ addressType: "ethereum",
107
+ },
108
+ this.connection,
109
+ );
110
+ }
111
+
112
+ private async restoreConnection() {
113
+ this.connection = await this.connectionsRepo.get({
114
+ uri: this.getConfig().joyidAppURL,
115
+ addressType: "ethereum",
116
+ });
117
+ }
118
+ }
@@ -1,28 +1,33 @@
1
1
  import { ccc } from "@ckb-ccc/core";
2
2
  import { BitcoinSigner } from "../btc";
3
3
  import { CkbSigner } from "../ckb";
4
+ import { EvmSigner } from "../evm";
4
5
 
5
- export function getJoyIdSigners(client: ccc.Client): ccc.SignerInfo[] {
6
+ export function getJoyIdSigners(
7
+ client: ccc.Client,
8
+ name: string,
9
+ icon: string,
10
+ ): ccc.SignerInfo[] {
6
11
  return [
7
12
  {
8
- type: ccc.SignerType.CKB,
9
13
  name: "CKB",
10
- signer: new CkbSigner(client),
14
+ signer: new CkbSigner(client, name, icon),
11
15
  },
12
16
  {
13
- type: ccc.SignerType.BTC,
14
17
  name: "BTC",
15
- signer: new BitcoinSigner(client),
18
+ signer: new BitcoinSigner(client, name, icon),
19
+ },
20
+ {
21
+ name: "EVM",
22
+ signer: new EvmSigner(client, name, icon),
16
23
  },
17
24
  {
18
- type: ccc.SignerType.BTC,
19
25
  name: "BTC (P2WPKH)",
20
- signer: new BitcoinSigner(client, "p2wpkh"),
26
+ signer: new BitcoinSigner(client, name, icon, "p2wpkh"),
21
27
  },
22
28
  {
23
- type: ccc.SignerType.BTC,
24
29
  name: "BTC (P2TR)",
25
- signer: new BitcoinSigner(client, "p2tr"),
30
+ signer: new BitcoinSigner(client, name, icon, "p2tr"),
26
31
  },
27
32
  ];
28
33
  }