@getpara/solana-wallet-connectors 2.0.0-alpha.19 → 2.0.0-alpha.21

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/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { SolanaExternalWalletContext } from './providers/SolanaExternalWalletContext.js';
2
- export type { SolanaExternalWalletContextType } from './providers/SolanaExternalWalletContext.js';
2
+ export { type SolanaExternalWalletContextType, defaultSolanaExternalWallet, } from './providers/SolanaExternalWalletContext.js';
3
3
  export { ParaSolanaProvider } from './providers/ParaSolanaProvider.js';
4
4
  export type { ParaSolanaProviderConfig, ParaSolanaProviderProps } from './providers/ParaSolanaProvider.js';
5
5
  export * from './wallets/connectors/index.js';
package/dist/index.js CHANGED
@@ -1,8 +1,12 @@
1
1
  "use client";
2
2
  import { SolanaExternalWalletContext } from "./providers/SolanaExternalWalletContext.js";
3
+ import {
4
+ defaultSolanaExternalWallet
5
+ } from "./providers/SolanaExternalWalletContext.js";
3
6
  import { ParaSolanaProvider } from "./providers/ParaSolanaProvider.js";
4
7
  export * from "./wallets/connectors/index.js";
5
8
  export {
6
9
  ParaSolanaProvider,
7
- SolanaExternalWalletContext
10
+ SolanaExternalWalletContext,
11
+ defaultSolanaExternalWallet
8
12
  };
@@ -1,33 +1,18 @@
1
1
  import { PropsWithChildren } from 'react';
2
- import ParaWeb from '@getpara/web-sdk';
3
- import { WalletList } from '../types/Wallet.js';
4
- import { TExternalWallet, type CommonWallet } from '@getpara/react-common';
5
- export type SolanaExternalWalletContextType = {
6
- wallets: CommonWallet[];
2
+ import { ExternalWalletInfo } from '@getpara/web-sdk';
3
+ import { CreateWalletFn } from '../types/Wallet.js';
4
+ import { ExternalWalletContextType, ExternalWalletProviderConfig, ExternalWalletProviderConfigBase } from '@getpara/react-common';
5
+ export declare const defaultSolanaExternalWallet: {
6
+ wallets: any[];
7
7
  disconnect: () => Promise<void>;
8
- signMessage: (message: string) => Promise<{
9
- signature?: string;
10
- error?: string;
11
- }>;
12
- signVerificationMessage: () => Promise<{
13
- address?: string;
14
- signature?: string;
15
- error?: string;
16
- }>;
8
+ signMessage: () => Promise<{}>;
9
+ signVerificationMessage: () => Promise<{}>;
10
+ requestInfo: () => Promise<ExternalWalletInfo>;
11
+ disconnectBase: () => Promise<void>;
17
12
  };
13
+ export type SolanaExternalWalletContextType = ExternalWalletContextType;
18
14
  export declare const SolanaExternalWalletContext: import("react").Context<SolanaExternalWalletContextType>;
19
- export type SolanaExternalWalletProviderConfig = {
20
- onSwitchWallet?: (args: {
21
- address?: string;
22
- error?: string;
23
- }) => void;
24
- para: ParaWeb;
25
- walletsWithFullAuth: TExternalWallet[];
26
- includeWalletVerification?: boolean;
27
- connectionOnly?: boolean;
28
- };
29
- type SolanaExternalWalletProviderConfigFull = {
30
- wallets: WalletList;
31
- } & SolanaExternalWalletProviderConfig;
15
+ export type SolanaExternalWalletProviderConfig = ExternalWalletProviderConfigBase;
16
+ type SolanaExternalWalletProviderConfigFull = ExternalWalletProviderConfig<CreateWalletFn>;
32
17
  export declare function SolanaExternalWalletProvider({ children, wallets: walletFns, onSwitchWallet, para, walletsWithFullAuth, includeWalletVerification, connectionOnly, }: SolanaExternalWalletProviderConfigFull & PropsWithChildren): import("react/jsx-runtime").JSX.Element;
33
18
  export {};
@@ -38,13 +38,15 @@ var __async = (__this, __arguments, generator) => {
38
38
  import { jsx } from "react/jsx-runtime";
39
39
  import { createContext, useEffect, useMemo, useRef } from "react";
40
40
  import { useWallet } from "@solana/wallet-adapter-react";
41
- import { WalletReadyState } from "@solana/wallet-adapter-base";
41
+ import { isIosAndRedirectable, WalletReadyState } from "@solana/wallet-adapter-base";
42
42
  import bs58 from "bs58";
43
43
  const defaultSolanaExternalWallet = {
44
44
  wallets: [],
45
45
  disconnect: () => Promise.resolve(),
46
46
  signMessage: () => Promise.resolve({}),
47
- signVerificationMessage: () => Promise.resolve({})
47
+ signVerificationMessage: () => Promise.resolve({}),
48
+ requestInfo: () => Promise.resolve({}),
49
+ disconnectBase: () => Promise.resolve()
48
50
  };
49
51
  const SolanaExternalWalletContext = createContext(defaultSolanaExternalWallet);
50
52
  function SolanaExternalWalletProvider({
@@ -59,19 +61,31 @@ function SolanaExternalWalletProvider({
59
61
  const {
60
62
  wallets: adapters,
61
63
  select: selectWallet,
62
- disconnect: _disconnect,
64
+ disconnect,
63
65
  publicKey: solanaAddress,
64
66
  wallet,
65
67
  connecting,
66
- connected,
67
68
  signMessage: solanaSignMessage
68
69
  } = useWallet();
70
+ const isLinkingAccount = useRef(false);
71
+ const solanaSignMessageRef = useRef(solanaSignMessage);
72
+ const solanaAddressRef = useRef(solanaAddress);
69
73
  const verificationMessage = useRef();
70
74
  const reset = () => __async(this, null, function* () {
71
- yield _disconnect();
75
+ yield disconnect();
72
76
  yield para.logout();
73
77
  });
74
- const login = (_0) => __async(this, [_0], function* ({ address, providerName }) {
78
+ const _reset = (..._0) => __async(this, [..._0], function* ({ logout = false } = {}) {
79
+ yield disconnect();
80
+ if (logout) {
81
+ yield para.logout();
82
+ }
83
+ });
84
+ const login = (_0) => __async(this, [_0], function* ({
85
+ address,
86
+ providerId,
87
+ providerName
88
+ }) {
75
89
  var _a, _b;
76
90
  try {
77
91
  return yield para.loginExternalWallet({
@@ -79,6 +93,7 @@ function SolanaExternalWalletProvider({
79
93
  address,
80
94
  type: "SOLANA",
81
95
  provider: providerName,
96
+ providerId,
82
97
  withFullParaAuth: walletsWithFullAuth == null ? void 0 : walletsWithFullAuth.includes(
83
98
  (_b = (_a = getWallet(providerName != null ? providerName : "")) == null ? void 0 : _a.id.toUpperCase()) != null ? _b : ""
84
99
  ),
@@ -92,7 +107,7 @@ function SolanaExternalWalletProvider({
92
107
  }
93
108
  });
94
109
  const switchWallet = (address) => __async(this, null, function* () {
95
- var _a;
110
+ var _a, _b, _c, _d;
96
111
  let error;
97
112
  if (!address) {
98
113
  yield para.logout();
@@ -103,7 +118,8 @@ function SolanaExternalWalletProvider({
103
118
  try {
104
119
  yield login({
105
120
  address,
106
- providerName: (_a = wallet == null ? void 0 : wallet.adapter) == null ? void 0 : _a.name
121
+ providerId: (_c = getWallet((_b = (_a = wallet == null ? void 0 : wallet.adapter) == null ? void 0 : _a.name) != null ? _b : "")) == null ? void 0 : _c.internalId,
122
+ providerName: (_d = wallet == null ? void 0 : wallet.adapter) == null ? void 0 : _d.name
107
123
  });
108
124
  } catch (err) {
109
125
  error = err;
@@ -115,25 +131,41 @@ function SolanaExternalWalletProvider({
115
131
  useEffect(() => {
116
132
  var _a;
117
133
  const storedExternalWallet = para.externalWallets[(_a = solanaAddress == null ? void 0 : solanaAddress.toString()) != null ? _a : ""];
118
- if (!!solanaAddress && !storedExternalWallet) {
134
+ if (!!solanaAddress && !storedExternalWallet && !isLinkingAccount.current) {
119
135
  reset();
120
136
  }
121
137
  }, []);
138
+ useEffect(() => {
139
+ solanaSignMessageRef.current = solanaSignMessage;
140
+ }, [solanaSignMessage]);
141
+ useEffect(() => {
142
+ solanaAddressRef.current = solanaAddress;
143
+ }, [solanaAddress]);
122
144
  useEffect(() => {
123
145
  const storedExternalWallet = Object.values(para.externalWallets || {})[0];
124
- if (!connecting && (!wallet || (wallet == null ? void 0 : wallet.adapter.connected)) && (storedExternalWallet == null ? void 0 : storedExternalWallet.type) === "SOLANA" && (storedExternalWallet == null ? void 0 : storedExternalWallet.address) !== (solanaAddress == null ? void 0 : solanaAddress.toString())) {
146
+ if (!connecting && (!wallet || (wallet == null ? void 0 : wallet.adapter.connected)) && (storedExternalWallet == null ? void 0 : storedExternalWallet.type) === "SOLANA" && (storedExternalWallet == null ? void 0 : storedExternalWallet.address) !== (solanaAddress == null ? void 0 : solanaAddress.toString()) && !isLinkingAccount.current) {
125
147
  switchWallet(solanaAddress == null ? void 0 : solanaAddress.toString());
126
148
  }
127
149
  }, [solanaAddress, connecting, wallet]);
128
- const signMessage = (message) => __async(this, null, function* () {
150
+ const signMessage = (_0) => __async(this, [_0], function* ({ message }) {
151
+ var _a, _b, _c, _d;
129
152
  try {
153
+ let solanaAddressNow = (_a = solanaAddressRef.current) != null ? _a : solanaAddress, solanaSignMessageNow = (_b = solanaSignMessageRef.current) != null ? _b : solanaSignMessage;
154
+ while (!solanaAddressNow || !solanaSignMessageNow) {
155
+ yield new Promise((resolve) => setTimeout(resolve, 100));
156
+ solanaAddressNow = (_c = solanaAddressRef.current) != null ? _c : solanaAddress;
157
+ solanaSignMessageNow = (_d = solanaSignMessageRef.current) != null ? _d : solanaSignMessage;
158
+ }
130
159
  const encodedMessage = new TextEncoder().encode(message);
131
- const signature = yield solanaSignMessage(encodedMessage);
160
+ const signature = yield solanaSignMessageNow(encodedMessage);
161
+ solanaAddressRef.current = void 0;
162
+ solanaSignMessageRef.current = void 0;
132
163
  return {
133
- address: solanaAddress.toString(),
164
+ address: solanaAddressNow.toString(),
134
165
  signature: bs58.encode(signature)
135
166
  };
136
167
  } catch (e) {
168
+ console.error(e);
137
169
  if (e.message.includes("User rejected the request")) {
138
170
  return { error: "Signature request rejected" };
139
171
  }
@@ -141,28 +173,46 @@ function SolanaExternalWalletProvider({
141
173
  }
142
174
  });
143
175
  const signVerificationMessage = () => __async(this, null, function* () {
144
- const signature = yield signMessage(verificationMessage.current);
176
+ const signature = yield signMessage({ message: verificationMessage.current });
145
177
  return signature;
146
178
  });
147
- const connect = (adapter) => __async(this, null, function* () {
148
- yield _disconnect();
179
+ const connectBase = (adapter, _switchWallet = false) => __async(this, null, function* () {
149
180
  if (!adapter) {
150
- return { error: "Adapter not found." };
181
+ throw new Error("Adapter not found.");
151
182
  }
152
183
  selectWallet(adapter.name);
153
184
  yield new Promise((resolve) => setTimeout(resolve, 100));
185
+ try {
186
+ yield adapter.connect();
187
+ yield new Promise((resolve) => setTimeout(resolve, 100));
188
+ while (!adapter.publicKey) {
189
+ yield new Promise((resolve) => setTimeout(resolve, 100));
190
+ }
191
+ const address = adapter.publicKey.toString();
192
+ return address;
193
+ } catch (e) {
194
+ console.error(e);
195
+ yield adapter.disconnect();
196
+ throw e;
197
+ }
198
+ });
199
+ const connect = (adapter) => __async(this, null, function* () {
200
+ var _a;
201
+ if (isIosAndRedirectable()) {
202
+ return;
203
+ }
204
+ yield disconnect();
154
205
  let address;
155
206
  let error;
156
207
  let authState;
157
208
  try {
158
- yield adapter.connect();
159
- address = adapter.publicKey.toString();
209
+ address = yield connectBase(adapter, true);
160
210
  if (address) {
161
211
  try {
162
- authState = yield login({ address, providerName: adapter.name });
212
+ authState = yield login({ address, providerId: (_a = getWallet(adapter.name)) == null ? void 0 : _a.internalId, providerName: adapter.name });
163
213
  verificationMessage.current = authState.stage === "verify" ? authState.signatureVerificationMessage : void 0;
164
214
  } catch (err) {
165
- yield _disconnect();
215
+ yield disconnect();
166
216
  address = void 0;
167
217
  error = err;
168
218
  }
@@ -183,6 +233,40 @@ function SolanaExternalWalletProvider({
183
233
  }
184
234
  return { address, error, authState };
185
235
  });
236
+ const requestInfo = (providerId) => __async(this, null, function* () {
237
+ var _a, _b;
238
+ const wallet2 = wallets.find((w) => w.internalId === providerId);
239
+ const adapter = getAdapter((_a = wallet2.name) != null ? _a : "");
240
+ isLinkingAccount.current = true;
241
+ try {
242
+ const address = yield connectBase(adapter);
243
+ const externalWallet = {
244
+ address,
245
+ type: "SOLANA",
246
+ providerId: wallet2.internalId,
247
+ provider: wallet2.name
248
+ };
249
+ return externalWallet;
250
+ } catch (e) {
251
+ console.error("Error linking account:", e);
252
+ throw new Error((_b = e == null ? void 0 : e.message) != null ? _b : e);
253
+ }
254
+ });
255
+ const disconnectBase = (providerId) => __async(this, null, function* () {
256
+ var _a, _b;
257
+ const wallet2 = wallets.find((w) => w.internalId === providerId);
258
+ const adapter = getAdapter((_a = wallet2.name) != null ? _a : "");
259
+ if (!(adapter == null ? void 0 : adapter.connected)) {
260
+ return;
261
+ }
262
+ isLinkingAccount.current = true;
263
+ try {
264
+ yield adapter.disconnect();
265
+ } catch (e) {
266
+ console.error("Error disconnecting wallet:", e);
267
+ throw new Error((_b = e == null ? void 0 : e.message) != null ? _b : e);
268
+ }
269
+ });
186
270
  const getAdapter = (name) => {
187
271
  var _a;
188
272
  return (_a = adapters.find((a) => a.adapter.name === "Mobile Wallet Adapter" ? a : a.adapter.name === name ? a : false)) == null ? void 0 : _a.adapter;
@@ -194,23 +278,16 @@ function SolanaExternalWalletProvider({
194
278
  return __spreadValues({
195
279
  connect: () => connect(adapter),
196
280
  connectMobile: () => connect(adapter),
197
- getQrUri: () => "",
198
281
  type: "SOLANA",
199
282
  installed: adapter && ((adapter == null ? void 0 : adapter.readyState) === WalletReadyState.Installed || (adapter == null ? void 0 : adapter.readyState) === WalletReadyState.Loadable)
200
283
  }, metaData);
201
284
  });
202
- const disconnect = () => __async(this, null, function* () {
203
- yield _disconnect();
204
- if (connected) {
205
- typeof window !== void 0 && (window == null ? void 0 : window.location.reload());
206
- }
207
- });
208
285
  return /* @__PURE__ */ jsx(
209
286
  SolanaExternalWalletContext.Provider,
210
287
  {
211
288
  value: useMemo(
212
- () => ({ wallets, disconnect, signMessage, signVerificationMessage }),
213
- [wallets, disconnect, signMessage, signVerificationMessage]
289
+ () => ({ wallets, disconnect, signMessage, signVerificationMessage, requestInfo, disconnectBase }),
290
+ [wallets, disconnect, signMessage, signVerificationMessage, requestInfo, disconnectBase]
214
291
  ),
215
292
  children
216
293
  }
@@ -218,5 +295,6 @@ function SolanaExternalWalletProvider({
218
295
  }
219
296
  export {
220
297
  SolanaExternalWalletContext,
221
- SolanaExternalWalletProvider
298
+ SolanaExternalWalletProvider,
299
+ defaultSolanaExternalWallet
222
300
  };
@@ -1,6 +1,4 @@
1
1
  import { type WalletMetadata } from '@getpara/react-common';
2
- export type Wallet = {
3
- getUri?: (uri: string) => string;
4
- } & WalletMetadata;
2
+ export type Wallet = WalletMetadata;
5
3
  export type CreateWalletFn = () => Wallet;
6
4
  export type WalletList = CreateWalletFn[];
@@ -1,13 +1,43 @@
1
1
  "use client";
2
+ var __async = (__this, __arguments, generator) => {
3
+ return new Promise((resolve, reject) => {
4
+ var fulfilled = (value) => {
5
+ try {
6
+ step(generator.next(value));
7
+ } catch (e) {
8
+ reject(e);
9
+ }
10
+ };
11
+ var rejected = (value) => {
12
+ try {
13
+ step(generator.throw(value));
14
+ } catch (e) {
15
+ reject(e);
16
+ }
17
+ };
18
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
19
+ step((generator = generator.apply(__this, __arguments)).next());
20
+ });
21
+ };
22
+ import { isIosAndRedirectable } from "@solana/wallet-adapter-base";
2
23
  import { icon } from "./backpackIcon.js";
3
24
  const backpackWallet = () => {
4
25
  return {
5
26
  id: "backpack",
27
+ internalId: "BACKPACK",
6
28
  name: "Backpack",
7
29
  iconUrl: icon,
8
30
  isExtension: true,
9
31
  isMobile: true,
10
- getUri: () => "",
32
+ hasIosSafariExtension: false,
33
+ getQrUri: () => __async(void 0, null, function* () {
34
+ if (typeof window !== "undefined" && isIosAndRedirectable()) {
35
+ const url = encodeURIComponent(window.location.href);
36
+ const ref = encodeURIComponent(window.location.origin);
37
+ return `https://backpack.app/ul/v1/browse/${url}?ref=${ref}`;
38
+ }
39
+ return "";
40
+ }),
11
41
  downloadUrl: "https://backpack.app/download"
12
42
  };
13
43
  };
@@ -1,13 +1,15 @@
1
1
  "use client";
2
2
  import { icon } from "./glowIcon.js";
3
+ import { isIosAndRedirectable } from "@solana/wallet-adapter-base";
3
4
  const glowWallet = () => {
4
5
  return {
5
6
  id: "glow",
7
+ internalId: "GLOW",
6
8
  name: "Glow",
7
9
  iconUrl: icon,
8
10
  isExtension: true,
9
11
  isMobile: true,
10
- getUri: () => "",
12
+ hasIosSafariExtension: isIosAndRedirectable(),
11
13
  downloadUrl: "https://glow.app"
12
14
  };
13
15
  };
@@ -1,13 +1,43 @@
1
1
  "use client";
2
+ var __async = (__this, __arguments, generator) => {
3
+ return new Promise((resolve, reject) => {
4
+ var fulfilled = (value) => {
5
+ try {
6
+ step(generator.next(value));
7
+ } catch (e) {
8
+ reject(e);
9
+ }
10
+ };
11
+ var rejected = (value) => {
12
+ try {
13
+ step(generator.throw(value));
14
+ } catch (e) {
15
+ reject(e);
16
+ }
17
+ };
18
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
19
+ step((generator = generator.apply(__this, __arguments)).next());
20
+ });
21
+ };
22
+ import { isIosAndRedirectable } from "@solana/wallet-adapter-base";
2
23
  import { icon } from "./phantomIcon.js";
3
24
  const phantomWallet = () => {
4
25
  return {
5
26
  id: "phantom",
27
+ internalId: "PHANTOM",
6
28
  name: "Phantom",
7
29
  iconUrl: icon,
8
30
  isExtension: true,
9
31
  isMobile: true,
10
- getUri: () => "",
32
+ hasIosSafariExtension: false,
33
+ getQrUri: () => __async(void 0, null, function* () {
34
+ if (typeof window !== "undefined" && isIosAndRedirectable()) {
35
+ const url = encodeURIComponent(window.location.href);
36
+ const ref = encodeURIComponent(window.location.origin);
37
+ return `https://phantom.app/ul/browse/${url}?ref=${ref}`;
38
+ }
39
+ return "";
40
+ }),
11
41
  downloadUrl: "https://phantom.app/download"
12
42
  };
13
43
  };
@@ -1,13 +1,43 @@
1
1
  "use client";
2
+ var __async = (__this, __arguments, generator) => {
3
+ return new Promise((resolve, reject) => {
4
+ var fulfilled = (value) => {
5
+ try {
6
+ step(generator.next(value));
7
+ } catch (e) {
8
+ reject(e);
9
+ }
10
+ };
11
+ var rejected = (value) => {
12
+ try {
13
+ step(generator.throw(value));
14
+ } catch (e) {
15
+ reject(e);
16
+ }
17
+ };
18
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
19
+ step((generator = generator.apply(__this, __arguments)).next());
20
+ });
21
+ };
22
+ import { isIosAndRedirectable } from "@solana/wallet-adapter-base";
2
23
  import { icon } from "./solflareIcon.js";
3
24
  const solflareWallet = () => {
4
25
  return {
5
26
  id: "solflare",
27
+ internalId: "SOLFLARE",
6
28
  name: "Solflare",
7
29
  iconUrl: icon,
8
30
  isExtension: true,
9
31
  isMobile: true,
10
- getUri: () => "",
32
+ hasIosSafariExtension: false,
33
+ getQrUri: () => __async(void 0, null, function* () {
34
+ if (typeof window !== "undefined" && isIosAndRedirectable()) {
35
+ const url = encodeURIComponent(window.location.href);
36
+ const ref = encodeURIComponent(window.location.origin);
37
+ return `https://solflare.com/ul/v1/browse/${url}?ref=${ref}`;
38
+ }
39
+ return "";
40
+ }),
11
41
  downloadUrl: "https://www.solflare.com/download/"
12
42
  };
13
43
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getpara/solana-wallet-connectors",
3
- "version": "2.0.0-alpha.19",
3
+ "version": "2.0.0-alpha.21",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,15 +14,15 @@
14
14
  "test": "vitest run --coverage"
15
15
  },
16
16
  "dependencies": {
17
- "@getpara/web-sdk": "2.0.0-alpha.19",
18
- "@solana-mobile/wallet-adapter-mobile": "2.1.3",
19
- "@solana/wallet-adapter-base": "0.9.23",
20
- "@solana/wallet-adapter-react": "0.15.35",
21
- "@solana/wallet-adapter-walletconnect": "0.1.16",
17
+ "@getpara/web-sdk": "2.0.0-alpha.21",
18
+ "@solana-mobile/wallet-adapter-mobile": "2.2.0",
19
+ "@solana/wallet-adapter-base": "0.9.27",
20
+ "@solana/wallet-adapter-react": "0.15.39",
21
+ "@solana/wallet-adapter-walletconnect": "0.1.21",
22
22
  "bs58": "6.0.0"
23
23
  },
24
24
  "devDependencies": {
25
- "@getpara/react-common": "2.0.0-alpha.19",
25
+ "@getpara/react-common": "2.0.0-alpha.21",
26
26
  "@types/react": "^18.0.31",
27
27
  "@types/react-dom": "^18.2.7",
28
28
  "typescript": "^5.4.3"
@@ -35,5 +35,5 @@
35
35
  "dist",
36
36
  "package.json"
37
37
  ],
38
- "gitHead": "80d2c5d7e1203fb12378aa29bfd4d7003c7d7075"
38
+ "gitHead": "03daa3537312bc52e0640225c8abdbf8047c15da"
39
39
  }