@frak-labs/core-sdk 1.0.2 → 1.1.0-beta.53162c2d
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 +0 -1
- package/cdn/bundle.js +1 -14
- package/dist/actions-BVbAHcBk.cjs +1 -0
- package/dist/actions-dH5NFCj1.js +1 -0
- package/dist/actions.cjs +1 -1
- package/dist/actions.d.cts +2 -2
- package/dist/actions.d.ts +2 -2
- package/dist/actions.js +1 -1
- package/dist/bundle.cjs +1 -1
- package/dist/bundle.d.cts +4 -4
- package/dist/bundle.d.ts +4 -4
- package/dist/bundle.js +1 -1
- package/dist/frakContext-CTKalP6g.js +1 -0
- package/dist/frakContext-_b-_uwgd.cjs +1 -0
- package/dist/{index-DzVPSUQq.d.ts → index-5JycXTk0.d.cts} +248 -352
- package/dist/{index-quaxtKRh.d.ts → index-BD1gOEIo.d.ts} +1 -1
- package/dist/{index-BsBbSMxk.d.cts → index-C2xfJCUg.d.cts} +1 -1
- package/dist/{index-s1vE3jLz.d.cts → index-RNzVfwrP.d.ts} +248 -352
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/{openSso-DyUQew2K.d.ts → openSso-CtDyvXLM.d.ts} +19 -20
- package/dist/{openSso-rQhLhPbq.d.cts → openSso-DIBN_iiz.d.cts} +18 -19
- package/dist/src-CDfF2FOa.cjs +1 -0
- package/dist/src-DFiac-zk.js +1 -0
- package/package.json +4 -4
- package/src/actions/ensureIdentity.ts +3 -3
- package/src/actions/openSso.ts +13 -6
- package/src/actions/referral/processReferral.test.ts +38 -22
- package/src/actions/referral/processReferral.ts +6 -4
- package/src/actions/referral/referralInteraction.test.ts +7 -7
- package/src/actions/referral/referralInteraction.ts +1 -1
- package/src/actions/sendInteraction.ts +1 -1
- package/src/actions/trackPurchaseStatus.test.ts +4 -4
- package/src/actions/trackPurchaseStatus.ts +3 -3
- package/src/actions/wrapper/siweAuthenticate.test.ts +1 -5
- package/src/actions/wrapper/siweAuthenticate.ts +25 -1
- package/src/clients/createIFrameFrakClient.ts +5 -21
- package/src/clients/index.ts +0 -1
- package/src/clients/transports/iframeLifecycleManager.test.ts +10 -10
- package/src/clients/transports/iframeLifecycleManager.ts +3 -3
- package/src/config/index.ts +3 -0
- package/src/{utils → config}/sdkConfigStore.ts +1 -1
- package/src/{utils/constants.test.ts → constants.test.ts} +1 -6
- package/src/constants.ts +15 -0
- package/src/context/address.ts +76 -0
- package/src/{utils/FrakContext.test.ts → context/frakContext.test.ts} +4 -2
- package/src/{utils/FrakContext.ts → context/frakContext.ts} +4 -4
- package/src/{utils → context}/frakContextV2Codec.test.ts +1 -1
- package/src/{utils → context}/frakContextV2Codec.ts +6 -6
- package/src/context/index.ts +6 -0
- package/src/index.ts +17 -22
- package/src/stubs/rrweb.ts +8 -4
- package/src/types/client.ts +0 -3
- package/src/types/config.ts +11 -0
- package/src/types/index.ts +1 -0
- package/src/types/rpc/sso.ts +6 -2
- package/src/utils/{deepLinkWithFallback.ts → browser/deepLinkWithFallback.ts} +10 -5
- package/src/utils/{inAppBrowser.ts → browser/inAppBrowser.ts} +13 -0
- package/src/utils/browser/index.ts +13 -0
- package/src/utils/{formatAmount.test.ts → format/formatAmount.test.ts} +1 -1
- package/src/utils/{formatAmount.ts → format/formatAmount.ts} +1 -1
- package/src/utils/{getCurrencyAmountKey.test.ts → format/getCurrencyAmountKey.test.ts} +2 -2
- package/src/utils/{getCurrencyAmountKey.ts → format/getCurrencyAmountKey.ts} +1 -1
- package/src/utils/{getSupportedCurrency.test.ts → format/getSupportedCurrency.test.ts} +2 -2
- package/src/utils/{getSupportedCurrency.ts → format/getSupportedCurrency.ts} +2 -2
- package/src/utils/{getSupportedLocale.test.ts → format/getSupportedLocale.test.ts} +3 -3
- package/src/utils/{getSupportedLocale.ts → format/getSupportedLocale.ts} +2 -2
- package/src/utils/format/index.ts +4 -0
- package/src/utils/{iframeHelper.test.ts → iframe/iframeHelper.test.ts} +3 -3
- package/src/utils/{iframeHelper.ts → iframe/iframeHelper.ts} +3 -3
- package/src/utils/iframe/index.ts +6 -0
- package/src/utils/index.ts +31 -24
- package/src/utils/sso/index.ts +6 -0
- package/src/utils/{sso.ts → sso/sso.ts} +2 -2
- package/dist/actions-DihYM-OG.js +0 -1
- package/dist/actions-cYbmqewX.cjs +0 -1
- package/dist/sdkConfigStore-BXzz5PlK.js +0 -1
- package/dist/sdkConfigStore-DDL_fjYX.cjs +0 -1
- package/dist/src-CfxklqLh.cjs +0 -13
- package/dist/src-VDUSvqqt.js +0 -13
- package/src/clients/DebugInfo.test.ts +0 -418
- package/src/clients/DebugInfo.ts +0 -182
- package/src/utils/computeLegacyProductId.ts +0 -11
- package/src/utils/constants.ts +0 -9
- /package/src/{utils → clients}/ssoUrlListener.test.ts +0 -0
- /package/src/{utils → clients}/ssoUrlListener.ts +0 -0
- /package/src/{utils → config}/backendUrl.test.ts +0 -0
- /package/src/{utils → config}/backendUrl.ts +0 -0
- /package/src/{utils → config}/clientId.test.ts +0 -0
- /package/src/{utils → config}/clientId.ts +0 -0
- /package/src/{utils → config}/sdkConfigStore.test.ts +0 -0
- /package/src/{utils → context}/mergeAttribution.test.ts +0 -0
- /package/src/{utils → context}/mergeAttribution.ts +0 -0
- /package/src/utils/{deepLinkWithFallback.test.ts → browser/deepLinkWithFallback.test.ts} +0 -0
|
@@ -2,7 +2,7 @@ import type { Hex } from "viem";
|
|
|
2
2
|
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
3
3
|
import { referralInteraction } from "./referralInteraction";
|
|
4
4
|
|
|
5
|
-
vi.mock("../../
|
|
5
|
+
vi.mock("../../context", () => ({
|
|
6
6
|
FrakContextManager: {
|
|
7
7
|
parse: vi.fn(),
|
|
8
8
|
},
|
|
@@ -30,7 +30,7 @@ describe("referralInteraction", () => {
|
|
|
30
30
|
});
|
|
31
31
|
|
|
32
32
|
test("should parse context from window location", async () => {
|
|
33
|
-
const { FrakContextManager } = await import("../../
|
|
33
|
+
const { FrakContextManager } = await import("../../context");
|
|
34
34
|
const { watchWalletStatus } = await import("../index");
|
|
35
35
|
const { processReferral } = await import("./processReferral");
|
|
36
36
|
|
|
@@ -46,7 +46,7 @@ describe("referralInteraction", () => {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
test("should get current wallet status", async () => {
|
|
49
|
-
const { FrakContextManager } = await import("../../
|
|
49
|
+
const { FrakContextManager } = await import("../../context");
|
|
50
50
|
const { watchWalletStatus } = await import("../index");
|
|
51
51
|
const { processReferral } = await import("./processReferral");
|
|
52
52
|
|
|
@@ -63,7 +63,7 @@ describe("referralInteraction", () => {
|
|
|
63
63
|
});
|
|
64
64
|
|
|
65
65
|
test("should call processReferral with all parameters", async () => {
|
|
66
|
-
const { FrakContextManager } = await import("../../
|
|
66
|
+
const { FrakContextManager } = await import("../../context");
|
|
67
67
|
const { watchWalletStatus } = await import("../index");
|
|
68
68
|
const { processReferral } = await import("./processReferral");
|
|
69
69
|
|
|
@@ -87,7 +87,7 @@ describe("referralInteraction", () => {
|
|
|
87
87
|
});
|
|
88
88
|
|
|
89
89
|
test("should return result from processReferral", async () => {
|
|
90
|
-
const { FrakContextManager } = await import("../../
|
|
90
|
+
const { FrakContextManager } = await import("../../context");
|
|
91
91
|
const { watchWalletStatus } = await import("../index");
|
|
92
92
|
const { processReferral } = await import("./processReferral");
|
|
93
93
|
|
|
@@ -101,7 +101,7 @@ describe("referralInteraction", () => {
|
|
|
101
101
|
});
|
|
102
102
|
|
|
103
103
|
test("should return undefined on error", async () => {
|
|
104
|
-
const { FrakContextManager } = await import("../../
|
|
104
|
+
const { FrakContextManager } = await import("../../context");
|
|
105
105
|
const { watchWalletStatus } = await import("../index");
|
|
106
106
|
const { processReferral } = await import("./processReferral");
|
|
107
107
|
|
|
@@ -124,7 +124,7 @@ describe("referralInteraction", () => {
|
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
test("should work with empty options", async () => {
|
|
127
|
-
const { FrakContextManager } = await import("../../
|
|
127
|
+
const { FrakContextManager } = await import("../../context");
|
|
128
128
|
const { watchWalletStatus } = await import("../index");
|
|
129
129
|
const { processReferral } = await import("./processReferral");
|
|
130
130
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { getClientId } from "../config/clientId";
|
|
1
2
|
import type { FrakClient } from "../types";
|
|
2
3
|
import type { SendInteractionParamsType } from "../types/rpc/interaction";
|
|
3
|
-
import { getClientId } from "../utils/clientId";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Send an interaction to the backend via the listener RPC.
|
|
@@ -7,18 +7,18 @@ import {
|
|
|
7
7
|
test,
|
|
8
8
|
} from "../../tests/vitest-fixtures";
|
|
9
9
|
|
|
10
|
-
vi.mock("../
|
|
10
|
+
vi.mock("../config/clientId", () => ({
|
|
11
11
|
getClientId: vi.fn().mockReturnValue("test-client-id"),
|
|
12
12
|
}));
|
|
13
13
|
|
|
14
|
-
vi.mock("../
|
|
14
|
+
vi.mock("../config/sdkConfigStore", () => ({
|
|
15
15
|
sdkConfigStore: {
|
|
16
16
|
resolveMerchantId: vi.fn().mockResolvedValue(undefined),
|
|
17
17
|
},
|
|
18
18
|
}));
|
|
19
19
|
|
|
20
|
-
import { getClientId } from "../
|
|
21
|
-
import { sdkConfigStore } from "../
|
|
20
|
+
import { getClientId } from "../config/clientId";
|
|
21
|
+
import { sdkConfigStore } from "../config/sdkConfigStore";
|
|
22
22
|
import { trackPurchaseStatus } from "./trackPurchaseStatus";
|
|
23
23
|
|
|
24
24
|
describe.sequential("trackPurchaseStatus", () => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { getBackendUrl } from "../
|
|
2
|
-
import { getClientId } from "../
|
|
3
|
-
import { sdkConfigStore } from "../
|
|
1
|
+
import { getBackendUrl } from "../config/backendUrl";
|
|
2
|
+
import { getClientId } from "../config/clientId";
|
|
3
|
+
import { sdkConfigStore } from "../config/sdkConfigStore";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Function used to track the status of a purchase
|
|
@@ -4,10 +4,6 @@ vi.mock("../displayModal", () => ({
|
|
|
4
4
|
displayModal: vi.fn(),
|
|
5
5
|
}));
|
|
6
6
|
|
|
7
|
-
vi.mock("viem/siwe", () => ({
|
|
8
|
-
generateSiweNonce: vi.fn(() => "mock-nonce-123456"),
|
|
9
|
-
}));
|
|
10
|
-
|
|
11
7
|
import type { Address, Hex } from "viem";
|
|
12
8
|
import { describe, expect, it } from "../../../tests/vitest-fixtures";
|
|
13
9
|
import type { FrakClient } from "../../types";
|
|
@@ -65,7 +61,7 @@ describe("siweAuthenticate", () => {
|
|
|
65
61
|
siweAuthenticate: {
|
|
66
62
|
siwe: expect.objectContaining({
|
|
67
63
|
domain: "example.com",
|
|
68
|
-
nonce:
|
|
64
|
+
nonce: expect.stringMatching(/^[0-9a-f]{96}$/),
|
|
69
65
|
uri: "https://example.com",
|
|
70
66
|
version: "1",
|
|
71
67
|
}),
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { generateSiweNonce } from "viem/siwe";
|
|
2
1
|
import type {
|
|
3
2
|
FrakClient,
|
|
4
3
|
ModalRpcMetadata,
|
|
@@ -7,6 +6,31 @@ import type {
|
|
|
7
6
|
} from "../../types";
|
|
8
7
|
import { displayModal } from "../displayModal";
|
|
9
8
|
|
|
9
|
+
/**
|
|
10
|
+
* Generate a random EIP-4361-compatible nonce.
|
|
11
|
+
*
|
|
12
|
+
* Matches viem/siwe's `generateSiweNonce` shape (96 alphanumeric chars) but
|
|
13
|
+
* inlined here to avoid pulling viem's runtime utilities into the bundle.
|
|
14
|
+
* Uses `crypto.getRandomValues` when available (browsers, modern Node) and
|
|
15
|
+
* falls back to `Math.random` for the rare environment without WebCrypto.
|
|
16
|
+
*/
|
|
17
|
+
function generateSiweNonce(length = 96): string {
|
|
18
|
+
const byteCount = Math.ceil(length / 2);
|
|
19
|
+
let hex = "";
|
|
20
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
21
|
+
const bytes = new Uint8Array(byteCount);
|
|
22
|
+
crypto.getRandomValues(bytes);
|
|
23
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
24
|
+
hex += bytes[i].toString(16).padStart(2, "0");
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
for (let i = 0; i < byteCount; i++) {
|
|
28
|
+
hex += ((Math.random() * 256) | 0).toString(16).padStart(2, "0");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return hex.substring(0, length);
|
|
32
|
+
}
|
|
33
|
+
|
|
10
34
|
/**
|
|
11
35
|
* Parameter used to directly show a modal used to authenticate with SIWE
|
|
12
36
|
* @inline
|
|
@@ -6,17 +6,16 @@ import {
|
|
|
6
6
|
RpcErrorCodes,
|
|
7
7
|
} from "@frak-labs/frame-connector";
|
|
8
8
|
import { OpenPanel } from "@openpanel/web";
|
|
9
|
+
import { getClientId } from "../config/clientId";
|
|
10
|
+
import { sdkConfigStore } from "../config/sdkConfigStore";
|
|
11
|
+
import { BACKUP_KEY } from "../constants";
|
|
9
12
|
import type { FrakLifecycleEvent } from "../types";
|
|
10
13
|
import type { FrakClient } from "../types/client";
|
|
11
14
|
import type { FrakWalletSdkConfig } from "../types/config";
|
|
12
15
|
import type { SdkResolvedConfig } from "../types/resolvedConfig";
|
|
13
16
|
import type { IFrameRpcSchema } from "../types/rpc";
|
|
14
|
-
import { getClientId } from "../utils";
|
|
15
17
|
import { clearAllCache } from "../utils/cache";
|
|
16
|
-
import {
|
|
17
|
-
import { sdkConfigStore } from "../utils/sdkConfigStore";
|
|
18
|
-
import { setupSsoUrlListener } from "../utils/ssoUrlListener";
|
|
19
|
-
import { DebugInfoGatherer } from "./DebugInfo";
|
|
18
|
+
import { setupSsoUrlListener } from "./ssoUrlListener";
|
|
20
19
|
import {
|
|
21
20
|
createIFrameLifecycleManager,
|
|
22
21
|
type IframeLifecycleManager,
|
|
@@ -84,9 +83,6 @@ export function createIFrameFrakClient({
|
|
|
84
83
|
// lifecycle manager resolves the `isConnected` promise.
|
|
85
84
|
const handshakeStartedAt = Date.now();
|
|
86
85
|
|
|
87
|
-
// Create our debug info gatherer
|
|
88
|
-
const debugInfo = new DebugInfoGatherer(config, iframe);
|
|
89
|
-
|
|
90
86
|
// Validate iframe
|
|
91
87
|
if (!iframe.contentWindow) {
|
|
92
88
|
throw new FrakRpcError(
|
|
@@ -115,17 +111,6 @@ export function createIFrameFrakClient({
|
|
|
115
111
|
return ctx;
|
|
116
112
|
},
|
|
117
113
|
},
|
|
118
|
-
// Save debug info
|
|
119
|
-
{
|
|
120
|
-
onRequest(message, ctx) {
|
|
121
|
-
debugInfo.setLastRequest(message);
|
|
122
|
-
return ctx;
|
|
123
|
-
},
|
|
124
|
-
onResponse(message, response) {
|
|
125
|
-
debugInfo.setLastResponse(message, response);
|
|
126
|
-
return response;
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
114
|
],
|
|
130
115
|
// Add lifecycle handlers to process iframe lifecycle events
|
|
131
116
|
lifecycleHandlers: {
|
|
@@ -227,7 +212,7 @@ export function createIFrameFrakClient({
|
|
|
227
212
|
contextSent,
|
|
228
213
|
openPanel,
|
|
229
214
|
})
|
|
230
|
-
.then(() =>
|
|
215
|
+
.then(() => {})
|
|
231
216
|
.catch((err) => {
|
|
232
217
|
contextSent.reject(err);
|
|
233
218
|
throw err;
|
|
@@ -235,7 +220,6 @@ export function createIFrameFrakClient({
|
|
|
235
220
|
|
|
236
221
|
return {
|
|
237
222
|
config,
|
|
238
|
-
debugInfo,
|
|
239
223
|
waitForConnection: lifecycleManager.isConnected,
|
|
240
224
|
waitForSetup,
|
|
241
225
|
request: rpcClient.request,
|
package/src/clients/index.ts
CHANGED
|
@@ -16,19 +16,19 @@ vi.mock("@frak-labs/frame-connector", () => ({
|
|
|
16
16
|
},
|
|
17
17
|
}));
|
|
18
18
|
|
|
19
|
-
vi.mock("../../
|
|
19
|
+
vi.mock("../../constants", () => ({
|
|
20
20
|
BACKUP_KEY: "frak-backup-key",
|
|
21
21
|
}));
|
|
22
22
|
|
|
23
|
-
vi.mock("../../utils/iframeHelper", () => ({
|
|
23
|
+
vi.mock("../../utils/iframe/iframeHelper", () => ({
|
|
24
24
|
changeIframeVisibility: vi.fn(),
|
|
25
25
|
}));
|
|
26
26
|
|
|
27
|
-
vi.mock("../../
|
|
27
|
+
vi.mock("../../config/clientId", () => ({
|
|
28
28
|
getClientId: vi.fn(() => "mock-client-id"),
|
|
29
29
|
}));
|
|
30
30
|
|
|
31
|
-
vi.mock("../../utils/deepLinkWithFallback", () => ({
|
|
31
|
+
vi.mock("../../utils/browser/deepLinkWithFallback", () => ({
|
|
32
32
|
isFrakDeepLink: vi.fn((url: string) => url.startsWith("frakwallet://")),
|
|
33
33
|
triggerDeepLinkWithFallback: vi.fn(),
|
|
34
34
|
}));
|
|
@@ -185,7 +185,7 @@ describe("createIFrameLifecycleManager", () => {
|
|
|
185
185
|
"./iframeLifecycleManager"
|
|
186
186
|
);
|
|
187
187
|
const { changeIframeVisibility } = await import(
|
|
188
|
-
"../../utils/iframeHelper"
|
|
188
|
+
"../../utils/iframe/iframeHelper"
|
|
189
189
|
);
|
|
190
190
|
|
|
191
191
|
const mockIframe = document.createElement("iframe");
|
|
@@ -211,7 +211,7 @@ describe("createIFrameLifecycleManager", () => {
|
|
|
211
211
|
"./iframeLifecycleManager"
|
|
212
212
|
);
|
|
213
213
|
const { changeIframeVisibility } = await import(
|
|
214
|
-
"../../utils/iframeHelper"
|
|
214
|
+
"../../utils/iframe/iframeHelper"
|
|
215
215
|
);
|
|
216
216
|
|
|
217
217
|
const mockIframe = document.createElement("iframe");
|
|
@@ -301,7 +301,7 @@ describe("createIFrameLifecycleManager", () => {
|
|
|
301
301
|
"./iframeLifecycleManager"
|
|
302
302
|
);
|
|
303
303
|
const { triggerDeepLinkWithFallback } = await import(
|
|
304
|
-
"../../utils/deepLinkWithFallback"
|
|
304
|
+
"../../utils/browser/deepLinkWithFallback"
|
|
305
305
|
);
|
|
306
306
|
|
|
307
307
|
Object.defineProperty(window, "location", {
|
|
@@ -340,7 +340,7 @@ describe("createIFrameLifecycleManager", () => {
|
|
|
340
340
|
"./iframeLifecycleManager"
|
|
341
341
|
);
|
|
342
342
|
const { triggerDeepLinkWithFallback } = await import(
|
|
343
|
-
"../../utils/deepLinkWithFallback"
|
|
343
|
+
"../../utils/browser/deepLinkWithFallback"
|
|
344
344
|
);
|
|
345
345
|
|
|
346
346
|
Object.defineProperty(window, "location", {
|
|
@@ -396,7 +396,7 @@ describe("createIFrameLifecycleManager", () => {
|
|
|
396
396
|
"./iframeLifecycleManager"
|
|
397
397
|
);
|
|
398
398
|
const { triggerDeepLinkWithFallback } = await import(
|
|
399
|
-
"../../utils/deepLinkWithFallback"
|
|
399
|
+
"../../utils/browser/deepLinkWithFallback"
|
|
400
400
|
);
|
|
401
401
|
|
|
402
402
|
Object.defineProperty(window, "location", {
|
|
@@ -453,7 +453,7 @@ describe("createIFrameLifecycleManager", () => {
|
|
|
453
453
|
"./iframeLifecycleManager"
|
|
454
454
|
);
|
|
455
455
|
const { changeIframeVisibility } = await import(
|
|
456
|
-
"../../utils/iframeHelper"
|
|
456
|
+
"../../utils/iframe/iframeHelper"
|
|
457
457
|
);
|
|
458
458
|
|
|
459
459
|
const mockIframe = document.createElement("iframe");
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Deferred } from "@frak-labs/frame-connector";
|
|
2
|
+
import { BACKUP_KEY } from "../../constants";
|
|
2
3
|
import type { FrakLifecycleEvent } from "../../types";
|
|
3
|
-
import { BACKUP_KEY } from "../../utils/constants";
|
|
4
4
|
import {
|
|
5
5
|
isFrakDeepLink,
|
|
6
6
|
triggerDeepLinkWithFallback,
|
|
7
|
-
} from "../../utils/deepLinkWithFallback";
|
|
8
|
-
import { changeIframeVisibility } from "../../utils/iframeHelper";
|
|
7
|
+
} from "../../utils/browser/deepLinkWithFallback";
|
|
8
|
+
import { changeIframeVisibility } from "../../utils/iframe/iframeHelper";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Detect iOS in-app browsers (Instagram, Facebook) where server-side
|
|
@@ -14,8 +14,8 @@ import type {
|
|
|
14
14
|
MerchantConfigResponse,
|
|
15
15
|
SdkResolvedConfig,
|
|
16
16
|
} from "../types/resolvedConfig";
|
|
17
|
+
import { clearAllCache, withCache } from "../utils/cache";
|
|
17
18
|
import { getBackendUrl } from "./backendUrl";
|
|
18
|
-
import { clearAllCache, withCache } from "./cache";
|
|
19
19
|
|
|
20
20
|
const GLOBAL_KEY = "__frakSdkConfig";
|
|
21
21
|
const CACHE_TTL = 30_000; // 30 seconds
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Tests for constants
|
|
3
|
-
* Tests backup key constant value
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, expect, it } from "../../tests/vitest-fixtures";
|
|
1
|
+
import { describe, expect, it } from "../tests/vitest-fixtures";
|
|
7
2
|
import { BACKUP_KEY } from "./constants";
|
|
8
3
|
|
|
9
4
|
describe("constants", () => {
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The backup key for client side backup if needed
|
|
3
|
+
*/
|
|
4
|
+
export const BACKUP_KEY = "nexus-wallet-backup";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Deep link scheme for Frak Wallet mobile app.
|
|
8
|
+
*
|
|
9
|
+
* Replaced at build time via tsdown/Vite `define`. Defaults to the prod scheme;
|
|
10
|
+
* in-monorepo dev builds (listener at wallet-dev.frak.id) override this with
|
|
11
|
+
* `frakwallet-dev://` so deep links open the dev wallet variant (id.frak.wallet.dev).
|
|
12
|
+
* External integrators consuming the published NPM/CDN bundle always see the prod scheme.
|
|
13
|
+
*/
|
|
14
|
+
export const DEEP_LINK_SCHEME: string =
|
|
15
|
+
process.env.DEEP_LINK_SCHEME ?? "frakwallet://";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Address } from "viem";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Address utilities — minimal, dependency-free replacements for the subset of
|
|
5
|
+
* `viem` helpers we used to import. Keeping these in-house lets the SDK ship
|
|
6
|
+
* without pulling viem's checksum/keccak/error chain into the bundle.
|
|
7
|
+
*
|
|
8
|
+
* Scope is intentionally narrow:
|
|
9
|
+
* - `isAddress`: shape-only validation (no EIP-55 checksum)
|
|
10
|
+
* - `areAddressesEqual`: case-insensitive equality
|
|
11
|
+
* - `addressToBytes` / `bytesToAddress`: fixed 20-byte conversion
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Matches a 0x-prefixed 40-char hex string regardless of case. */
|
|
15
|
+
const ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check whether a value is a syntactically valid Ethereum address.
|
|
19
|
+
*
|
|
20
|
+
* This intentionally skips EIP-55 checksum validation: the SDK never produces
|
|
21
|
+
* checksum-cased payloads, and downstream consumers (wallet, indexer) treat
|
|
22
|
+
* addresses case-insensitively. Avoiding the checksum path drops keccak256 +
|
|
23
|
+
* @noble/hashes from the bundle.
|
|
24
|
+
*/
|
|
25
|
+
export function isAddress(value: unknown): value is Address {
|
|
26
|
+
return typeof value === "string" && ADDRESS_REGEX.test(value);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Case-insensitive equality check for two Ethereum addresses.
|
|
31
|
+
*
|
|
32
|
+
* Both inputs are assumed to be syntactically valid addresses; callers that
|
|
33
|
+
* receive untrusted input should validate via {@link isAddress} first.
|
|
34
|
+
*/
|
|
35
|
+
export function areAddressesEqual(a: Address, b: Address): boolean {
|
|
36
|
+
return a.toLowerCase() === b.toLowerCase();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Decode a 20-byte Ethereum address into a fixed-size Uint8Array(20).
|
|
41
|
+
*
|
|
42
|
+
* Throws when the input is not exactly `0x` + 40 hex chars — callers wrap
|
|
43
|
+
* the call in try/catch (see {@link FrakContextManager.compress}) so any
|
|
44
|
+
* malformed input degrades to a graceful undefined return.
|
|
45
|
+
*/
|
|
46
|
+
export function addressToBytes(address: Address): Uint8Array {
|
|
47
|
+
const bytes = new Uint8Array(20);
|
|
48
|
+
for (let i = 0; i < 20; i++) {
|
|
49
|
+
const byte = Number.parseInt(
|
|
50
|
+
address.substring(2 + i * 2, 4 + i * 2),
|
|
51
|
+
16
|
|
52
|
+
);
|
|
53
|
+
if (Number.isNaN(byte)) {
|
|
54
|
+
throw new Error(`Invalid address: ${address}`);
|
|
55
|
+
}
|
|
56
|
+
bytes[i] = byte;
|
|
57
|
+
}
|
|
58
|
+
return bytes;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Lookup table avoids `padStart` overhead in the hot encode loop. */
|
|
62
|
+
const HEX_BYTE = /*#__PURE__*/ Array.from({ length: 256 }, (_, i) =>
|
|
63
|
+
i.toString(16).padStart(2, "0")
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Encode a 20-byte Uint8Array (or a 20-byte subarray view) into a lowercase
|
|
68
|
+
* hex Ethereum address. The caller MUST guarantee `bytes.length === 20`.
|
|
69
|
+
*/
|
|
70
|
+
export function bytesToAddress(bytes: Uint8Array): Address {
|
|
71
|
+
let out = "0x";
|
|
72
|
+
for (let i = 0; i < 20; i++) {
|
|
73
|
+
out += HEX_BYTE[bytes[i]];
|
|
74
|
+
}
|
|
75
|
+
return out as Address;
|
|
76
|
+
}
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
vi,
|
|
10
10
|
} from "../../tests/vitest-fixtures";
|
|
11
11
|
import type { FrakContextV1, FrakContextV2 } from "../types";
|
|
12
|
-
import { FrakContextManager } from "./
|
|
12
|
+
import { FrakContextManager } from "./frakContext";
|
|
13
13
|
|
|
14
14
|
describe("FrakContextManager", () => {
|
|
15
15
|
let consoleErrorSpy: any;
|
|
@@ -144,7 +144,9 @@ describe("FrakContextManager", () => {
|
|
|
144
144
|
const { encodeFrakContextV2 } = await import(
|
|
145
145
|
"./frakContextV2Codec"
|
|
146
146
|
);
|
|
147
|
-
const { base64urlEncode } = await import(
|
|
147
|
+
const { base64urlEncode } = await import(
|
|
148
|
+
"../utils/compression/b64"
|
|
149
|
+
);
|
|
148
150
|
const encoded = encodeFrakContextV2(v2Context);
|
|
149
151
|
expect(encoded).toBeDefined();
|
|
150
152
|
const tampered = new Uint8Array(encoded as Uint8Array);
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { type Address, bytesToHex, hexToBytes, isAddress } from "viem";
|
|
2
1
|
import type {
|
|
3
2
|
AttributionParams,
|
|
4
3
|
FrakContext,
|
|
@@ -6,7 +5,8 @@ import type {
|
|
|
6
5
|
FrakContextV2,
|
|
7
6
|
} from "../types";
|
|
8
7
|
import { isV2Context } from "../types";
|
|
9
|
-
import { base64urlDecode, base64urlEncode } from "
|
|
8
|
+
import { base64urlDecode, base64urlEncode } from "../utils/compression/b64";
|
|
9
|
+
import { addressToBytes, bytesToAddress, isAddress } from "./address";
|
|
10
10
|
import { decodeFrakContextV2, encodeFrakContextV2 } from "./frakContextV2Codec";
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -34,7 +34,7 @@ function compress(context?: FrakContextV1 | FrakContextV2): string | undefined {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
// V1 legacy: compress wallet address as raw bytes
|
|
37
|
-
const bytes =
|
|
37
|
+
const bytes = addressToBytes(context.r);
|
|
38
38
|
return base64urlEncode(bytes);
|
|
39
39
|
} catch (e) {
|
|
40
40
|
console.error("Error compressing Frak context", { e, context });
|
|
@@ -64,7 +64,7 @@ function decompress(context?: string): FrakContext | undefined {
|
|
|
64
64
|
return undefined;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
const hex =
|
|
67
|
+
const hex = bytesToAddress(bytes);
|
|
68
68
|
if (isAddress(hex)) {
|
|
69
69
|
return { r: hex };
|
|
70
70
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Address } from "viem";
|
|
2
2
|
import { describe, expect, it } from "../../tests/vitest-fixtures";
|
|
3
3
|
import type { FrakContextV2 } from "../types";
|
|
4
|
-
import { base64urlEncode } from "
|
|
4
|
+
import { base64urlEncode } from "../utils/compression/b64";
|
|
5
5
|
import {
|
|
6
6
|
decodeFrakContextV2,
|
|
7
7
|
encodeFrakContextV2,
|
|
@@ -28,8 +28,9 @@
|
|
|
28
28
|
*
|
|
29
29
|
* @ignore
|
|
30
30
|
*/
|
|
31
|
-
import {
|
|
31
|
+
import type { Address } from "viem";
|
|
32
32
|
import type { FrakContextV2 } from "../types";
|
|
33
|
+
import { addressToBytes, bytesToAddress, isAddress } from "./address";
|
|
33
34
|
|
|
34
35
|
const VERSION_V2 = 0x02;
|
|
35
36
|
const VERSION_MASK = 0x0f;
|
|
@@ -111,7 +112,7 @@ export function encodeFrakContextV2(ctx: FrakContextV2): Uint8Array | null {
|
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
if (hasW) {
|
|
114
|
-
buf.set(
|
|
115
|
+
buf.set(addressToBytes(ctx.w as Address), offset);
|
|
115
116
|
offset += ADDRESS_BYTES;
|
|
116
117
|
}
|
|
117
118
|
|
|
@@ -165,10 +166,9 @@ export function decodeFrakContextV2(buf: Uint8Array): FrakContextV2 | null {
|
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
if (hasW) {
|
|
168
|
-
const walletHex =
|
|
169
|
-
buf.subarray(offset, offset + ADDRESS_BYTES)
|
|
170
|
-
|
|
171
|
-
) as Address;
|
|
169
|
+
const walletHex = bytesToAddress(
|
|
170
|
+
buf.subarray(offset, offset + ADDRESS_BYTES)
|
|
171
|
+
);
|
|
172
172
|
if (!isAddress(walletHex)) return null;
|
|
173
173
|
out.w = walletHex;
|
|
174
174
|
offset += ADDRESS_BYTES;
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
// Clients
|
|
2
2
|
|
|
3
3
|
export { ssoPopupFeatures, ssoPopupName } from "./actions/openSso";
|
|
4
|
+
export { createIFrameFrakClient, setupClient } from "./clients";
|
|
5
|
+
// Config (reactive merchant config + identity)
|
|
4
6
|
export {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from "./
|
|
9
|
-
|
|
7
|
+
getBackendUrl,
|
|
8
|
+
getClientId,
|
|
9
|
+
sdkConfigStore,
|
|
10
|
+
} from "./config";
|
|
11
|
+
// Constants
|
|
12
|
+
export { DEEP_LINK_SCHEME } from "./constants";
|
|
10
13
|
export type { InteractionTypeKey } from "./constants/interactionTypes";
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
// Context (FrakContext URL codec + attribution merge)
|
|
15
|
+
export {
|
|
16
|
+
FrakContextManager,
|
|
17
|
+
type MergeAttributionInput,
|
|
18
|
+
mergeAttribution,
|
|
19
|
+
} from "./context";
|
|
13
20
|
// Types
|
|
14
21
|
export type {
|
|
15
22
|
AttributionDefaults,
|
|
@@ -48,6 +55,7 @@ export type {
|
|
|
48
55
|
// Compression
|
|
49
56
|
KeyProvider,
|
|
50
57
|
Language,
|
|
58
|
+
ListenerPreloadOption,
|
|
51
59
|
LocalizedI18nConfig,
|
|
52
60
|
LoggedInEmbeddedView,
|
|
53
61
|
LoggedOutEmbeddedView,
|
|
@@ -87,7 +95,7 @@ export type {
|
|
|
87
95
|
UtmParams,
|
|
88
96
|
WalletStatusReturnType,
|
|
89
97
|
} from "./types";
|
|
90
|
-
|
|
98
|
+
|
|
91
99
|
// Utils
|
|
92
100
|
export {
|
|
93
101
|
type AppSpecificSsoMetadata,
|
|
@@ -97,30 +105,18 @@ export {
|
|
|
97
105
|
type CompressedSsoData,
|
|
98
106
|
clearAllCache,
|
|
99
107
|
compressJsonToB64,
|
|
100
|
-
createIframe,
|
|
101
|
-
DEEP_LINK_SCHEME,
|
|
102
108
|
type DeepLinkFallbackOptions,
|
|
103
109
|
decompressJsonFromB64,
|
|
104
|
-
FrakContextManager,
|
|
105
110
|
type FullSsoParams,
|
|
106
111
|
findIframeInOpener,
|
|
107
112
|
formatAmount,
|
|
108
113
|
generateSsoUrl,
|
|
109
|
-
getBackendUrl,
|
|
110
|
-
getCache,
|
|
111
|
-
getClientId,
|
|
112
114
|
getCurrencyAmountKey,
|
|
113
115
|
getSupportedCurrency,
|
|
114
|
-
getSupportedLocale,
|
|
115
|
-
isChromiumAndroid,
|
|
116
|
-
isFrakDeepLink,
|
|
117
116
|
isInAppBrowser,
|
|
118
117
|
isIOS,
|
|
119
|
-
|
|
120
|
-
mergeAttribution,
|
|
118
|
+
isMobile,
|
|
121
119
|
redirectToExternalBrowser,
|
|
122
|
-
sdkConfigStore,
|
|
123
|
-
toAndroidIntentUrl,
|
|
124
120
|
trackEvent,
|
|
125
121
|
triggerDeepLinkWithFallback,
|
|
126
122
|
withCache,
|
|
@@ -129,4 +125,3 @@ export type {
|
|
|
129
125
|
SdkEventMap,
|
|
130
126
|
SdkHandshakeFailureReason,
|
|
131
127
|
} from "./utils/analytics";
|
|
132
|
-
export { computeLegacyProductId } from "./utils/computeLegacyProductId";
|
package/src/stubs/rrweb.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Stub for rrweb. The
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Stub for rrweb. The IIFE/CDN bundles inline every dependency
|
|
3
|
+
* (alwaysBundle catch-all), which would also pull in rrweb via the dynamic
|
|
4
|
+
* `replay` chunk loaded by @openpanel/web. Session replay is disabled in our
|
|
5
|
+
* SDK, so we alias rrweb to a noop record() to keep the CDN bundle small.
|
|
6
|
+
*
|
|
7
|
+
* The NPM ESM/CJS builds don't need this alias: @openpanel/web 1.4.1+ loads
|
|
8
|
+
* rrweb through a dynamic `import("./replay-…")`, so consumers' bundlers can
|
|
9
|
+
* tree-shake / code-split it on their own.
|
|
6
10
|
*/
|
|
7
11
|
export function record() {
|
|
8
12
|
return () => {};
|