@frak-labs/core-sdk 0.1.0-beta.afa252b0 → 0.1.0-beta.dd44738a
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/cdn/bundle.js +3 -3
- package/dist/bundle.cjs +1 -1
- package/dist/bundle.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +4 -2
- package/src/actions/displayEmbeddedWallet.ts +20 -0
- package/src/actions/displayModal.ts +131 -0
- package/src/actions/getProductInformation.ts +14 -0
- package/src/actions/index.ts +29 -0
- package/src/actions/openSso.ts +116 -0
- package/src/actions/prepareSso.ts +48 -0
- package/src/actions/referral/processReferral.ts +230 -0
- package/src/actions/referral/referralInteraction.ts +57 -0
- package/src/actions/sendInteraction.ts +32 -0
- package/src/actions/trackPurchaseStatus.ts +53 -0
- package/src/actions/watchWalletStatus.ts +94 -0
- package/src/actions/wrapper/modalBuilder.ts +212 -0
- package/src/actions/wrapper/sendTransaction.ts +62 -0
- package/src/actions/wrapper/siweAuthenticate.ts +94 -0
- package/src/bundle.ts +3 -0
- package/src/clients/DebugInfo.ts +182 -0
- package/src/clients/createIFrameFrakClient.ts +287 -0
- package/src/clients/index.ts +3 -0
- package/src/clients/setupClient.ts +71 -0
- package/src/clients/transports/iframeLifecycleManager.ts +88 -0
- package/src/constants/interactionTypes.ts +44 -0
- package/src/constants/locales.ts +14 -0
- package/src/constants/productTypes.ts +33 -0
- package/src/index.ts +103 -0
- package/src/interactions/index.ts +5 -0
- package/src/interactions/pressEncoder.ts +53 -0
- package/src/interactions/purchaseEncoder.ts +94 -0
- package/src/interactions/referralEncoder.ts +47 -0
- package/src/interactions/retailEncoder.ts +37 -0
- package/src/interactions/webshopEncoder.ts +30 -0
- package/src/types/client.ts +14 -0
- package/src/types/compression.ts +22 -0
- package/src/types/config.ts +111 -0
- package/src/types/context.ts +13 -0
- package/src/types/index.ts +70 -0
- package/src/types/lifecycle/client.ts +46 -0
- package/src/types/lifecycle/iframe.ts +35 -0
- package/src/types/lifecycle/index.ts +2 -0
- package/src/types/rpc/displayModal.ts +84 -0
- package/src/types/rpc/embedded/index.ts +68 -0
- package/src/types/rpc/embedded/loggedIn.ts +55 -0
- package/src/types/rpc/embedded/loggedOut.ts +28 -0
- package/src/types/rpc/interaction.ts +43 -0
- package/src/types/rpc/modal/final.ts +46 -0
- package/src/types/rpc/modal/generic.ts +46 -0
- package/src/types/rpc/modal/index.ts +20 -0
- package/src/types/rpc/modal/login.ts +32 -0
- package/src/types/rpc/modal/openSession.ts +25 -0
- package/src/types/rpc/modal/siweAuthenticate.ts +37 -0
- package/src/types/rpc/modal/transaction.ts +33 -0
- package/src/types/rpc/productInformation.ts +59 -0
- package/src/types/rpc/sso.ts +80 -0
- package/src/types/rpc/walletStatus.ts +35 -0
- package/src/types/rpc.ts +158 -0
- package/src/types/transport.ts +34 -0
- package/src/utils/FrakContext.ts +152 -0
- package/src/utils/compression/b64.ts +29 -0
- package/src/utils/compression/compress.ts +11 -0
- package/src/utils/compression/decompress.ts +11 -0
- package/src/utils/compression/index.ts +3 -0
- package/src/utils/computeProductId.ts +11 -0
- package/src/utils/constants.ts +4 -0
- package/src/utils/formatAmount.ts +18 -0
- package/src/utils/getCurrencyAmountKey.ts +15 -0
- package/src/utils/getSupportedCurrency.ts +14 -0
- package/src/utils/getSupportedLocale.ts +16 -0
- package/src/utils/iframeHelper.ts +142 -0
- package/src/utils/index.ts +21 -0
- package/src/utils/sso.ts +119 -0
- package/src/utils/ssoUrlListener.ts +60 -0
- package/src/utils/trackEvent.ts +26 -0
package/src/types/rpc.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { Hex } from "viem";
|
|
2
|
+
import type { FrakWalletSdkConfig } from "./config";
|
|
3
|
+
import type {
|
|
4
|
+
ModalRpcMetadata,
|
|
5
|
+
ModalRpcStepsInput,
|
|
6
|
+
ModalRpcStepsResultType,
|
|
7
|
+
} from "./rpc/displayModal";
|
|
8
|
+
import type {
|
|
9
|
+
DisplayEmbeddedWalletParamsType,
|
|
10
|
+
DisplayEmbeddedWalletResultType,
|
|
11
|
+
} from "./rpc/embedded";
|
|
12
|
+
import type {
|
|
13
|
+
PreparedInteraction,
|
|
14
|
+
SendInteractionReturnType,
|
|
15
|
+
} from "./rpc/interaction";
|
|
16
|
+
import type { GetProductInformationReturnType } from "./rpc/productInformation";
|
|
17
|
+
import type {
|
|
18
|
+
OpenSsoParamsType,
|
|
19
|
+
OpenSsoReturnType,
|
|
20
|
+
PrepareSsoParamsType,
|
|
21
|
+
PrepareSsoReturnType,
|
|
22
|
+
} from "./rpc/sso";
|
|
23
|
+
import type { WalletStatusReturnType } from "./rpc/walletStatus";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* RPC interface that's used for the iframe communication
|
|
27
|
+
*
|
|
28
|
+
* Define all the methods available within the iFrame RPC client with response type annotations
|
|
29
|
+
*
|
|
30
|
+
* @group RPC Schema
|
|
31
|
+
*
|
|
32
|
+
* @remarks
|
|
33
|
+
* Each method in the schema now includes a ResponseType field that indicates:
|
|
34
|
+
* - "promise": One-shot request that resolves once
|
|
35
|
+
* - "stream": Streaming request that can emit multiple values
|
|
36
|
+
*
|
|
37
|
+
* ### Methods:
|
|
38
|
+
*
|
|
39
|
+
* #### frak_listenToWalletStatus
|
|
40
|
+
* - Params: None
|
|
41
|
+
* - Returns: {@link WalletStatusReturnType}
|
|
42
|
+
* - Response Type: stream (emits updates when wallet status changes)
|
|
43
|
+
*
|
|
44
|
+
* #### frak_displayModal
|
|
45
|
+
* - Params: [requests: {@link ModalRpcStepsInput}, metadata?: {@link ModalRpcMetadata}, configMetadata: {@link FrakWalletSdkConfig}["metadata"]]
|
|
46
|
+
* - Returns: {@link ModalRpcStepsResultType}
|
|
47
|
+
* - Response Type: promise (one-shot)
|
|
48
|
+
*
|
|
49
|
+
* #### frak_sendInteraction
|
|
50
|
+
* - Params: [productId: Hex, interaction: {@link PreparedInteraction}, signature?: Hex]
|
|
51
|
+
* - Returns: {@link SendInteractionReturnType}
|
|
52
|
+
* - Response Type: promise (one-shot)
|
|
53
|
+
*
|
|
54
|
+
* #### frak_sso
|
|
55
|
+
* - Params: [params: {@link OpenSsoParamsType}, name: string, customCss?: string]
|
|
56
|
+
* - Returns: {@link OpenSsoReturnType}
|
|
57
|
+
* - Response Type: promise (one-shot)
|
|
58
|
+
*
|
|
59
|
+
* #### frak_getProductInformation
|
|
60
|
+
* - Params: None
|
|
61
|
+
* - Returns: {@link GetProductInformationReturnType}
|
|
62
|
+
* - Response Type: promise (one-shot)
|
|
63
|
+
*
|
|
64
|
+
* #### frak_displayEmbeddedWallet
|
|
65
|
+
* - Params: [request: {@link DisplayEmbeddedWalletParamsType}, metadata: {@link FrakWalletSdkConfig}["metadata"]]
|
|
66
|
+
* - Returns: {@link DisplayEmbeddedWalletResultType}
|
|
67
|
+
* - Response Type: promise (one-shot)
|
|
68
|
+
*/
|
|
69
|
+
export type IFrameRpcSchema = [
|
|
70
|
+
/**
|
|
71
|
+
* Method used to listen to the wallet status
|
|
72
|
+
* This is a streaming method that emits updates when wallet status changes
|
|
73
|
+
*/
|
|
74
|
+
{
|
|
75
|
+
Method: "frak_listenToWalletStatus";
|
|
76
|
+
Parameters?: undefined;
|
|
77
|
+
ReturnType: WalletStatusReturnType;
|
|
78
|
+
},
|
|
79
|
+
/**
|
|
80
|
+
* Method to display a modal with the provided steps
|
|
81
|
+
* This is a one-shot request
|
|
82
|
+
*/
|
|
83
|
+
{
|
|
84
|
+
Method: "frak_displayModal";
|
|
85
|
+
Parameters: [
|
|
86
|
+
requests: ModalRpcStepsInput,
|
|
87
|
+
metadata: ModalRpcMetadata | undefined,
|
|
88
|
+
configMetadata: FrakWalletSdkConfig["metadata"],
|
|
89
|
+
];
|
|
90
|
+
ReturnType: ModalRpcStepsResultType;
|
|
91
|
+
},
|
|
92
|
+
/**
|
|
93
|
+
* Method to transmit a user interaction
|
|
94
|
+
* This is a one-shot request
|
|
95
|
+
*/
|
|
96
|
+
{
|
|
97
|
+
Method: "frak_sendInteraction";
|
|
98
|
+
Parameters: [
|
|
99
|
+
productId: Hex,
|
|
100
|
+
interaction: PreparedInteraction,
|
|
101
|
+
signature?: Hex,
|
|
102
|
+
];
|
|
103
|
+
ReturnType: SendInteractionReturnType;
|
|
104
|
+
},
|
|
105
|
+
/**
|
|
106
|
+
* Method to prepare SSO (generate URL for popup)
|
|
107
|
+
* Returns the SSO URL that should be opened in a popup
|
|
108
|
+
* Only used for popup flows (not redirect flows)
|
|
109
|
+
*/
|
|
110
|
+
{
|
|
111
|
+
Method: "frak_prepareSso";
|
|
112
|
+
Parameters: [
|
|
113
|
+
params: PrepareSsoParamsType,
|
|
114
|
+
name: string,
|
|
115
|
+
customCss?: string,
|
|
116
|
+
];
|
|
117
|
+
ReturnType: PrepareSsoReturnType;
|
|
118
|
+
},
|
|
119
|
+
/**
|
|
120
|
+
* Method to open/trigger SSO
|
|
121
|
+
* Either triggers redirect (if openInSameWindow/redirectUrl)
|
|
122
|
+
* Or waits for popup completion (if popup mode)
|
|
123
|
+
* This method handles BOTH redirect and popup flows
|
|
124
|
+
*/
|
|
125
|
+
{
|
|
126
|
+
Method: "frak_openSso";
|
|
127
|
+
Parameters: [
|
|
128
|
+
params: OpenSsoParamsType,
|
|
129
|
+
name: string,
|
|
130
|
+
customCss?: string,
|
|
131
|
+
];
|
|
132
|
+
ReturnType: OpenSsoReturnType;
|
|
133
|
+
},
|
|
134
|
+
/**
|
|
135
|
+
* Method to get current product information's
|
|
136
|
+
* - Is product minted?
|
|
137
|
+
* - Does it have running campaign?
|
|
138
|
+
* - Estimated reward on actions
|
|
139
|
+
* This is a one-shot request
|
|
140
|
+
*/
|
|
141
|
+
{
|
|
142
|
+
Method: "frak_getProductInformation";
|
|
143
|
+
Parameters?: undefined;
|
|
144
|
+
ReturnType: GetProductInformationReturnType;
|
|
145
|
+
},
|
|
146
|
+
/**
|
|
147
|
+
* Method to show the embedded wallet, with potential customization
|
|
148
|
+
* This is a one-shot request
|
|
149
|
+
*/
|
|
150
|
+
{
|
|
151
|
+
Method: "frak_displayEmbeddedWallet";
|
|
152
|
+
Parameters: [
|
|
153
|
+
request: DisplayEmbeddedWalletParamsType,
|
|
154
|
+
metadata: FrakWalletSdkConfig["metadata"],
|
|
155
|
+
];
|
|
156
|
+
ReturnType: DisplayEmbeddedWalletResultType;
|
|
157
|
+
},
|
|
158
|
+
];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { LifecycleMessage, RpcClient } from "@frak-labs/frame-connector";
|
|
2
|
+
import type { ClientLifecycleEvent, IFrameLifecycleEvent } from "./lifecycle";
|
|
3
|
+
import type { IFrameRpcSchema } from "./rpc";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* IFrame transport interface
|
|
7
|
+
*/
|
|
8
|
+
export type IFrameTransport = {
|
|
9
|
+
/**
|
|
10
|
+
* Wait for the connection to be established
|
|
11
|
+
*/
|
|
12
|
+
waitForConnection: Promise<boolean>;
|
|
13
|
+
/**
|
|
14
|
+
* Wait for the setup to be done
|
|
15
|
+
*/
|
|
16
|
+
waitForSetup: Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Function used to perform a single request via the iframe transport
|
|
19
|
+
*/
|
|
20
|
+
request: RpcClient<IFrameRpcSchema, LifecycleMessage>["request"];
|
|
21
|
+
/**
|
|
22
|
+
* Function used to listen to a request response via the iframe transport
|
|
23
|
+
*/
|
|
24
|
+
listenerRequest: RpcClient<IFrameRpcSchema, LifecycleMessage>["listen"];
|
|
25
|
+
/**
|
|
26
|
+
* Function used to destroy the iframe transport
|
|
27
|
+
*/
|
|
28
|
+
destroy: () => Promise<void>;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Represent an iframe event
|
|
33
|
+
*/
|
|
34
|
+
export type FrakLifecycleEvent = IFrameLifecycleEvent | ClientLifecycleEvent;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { type Address, bytesToHex, hexToBytes } from "viem";
|
|
2
|
+
import type { FrakContext } from "../types";
|
|
3
|
+
import { base64urlDecode, base64urlEncode } from "./compression/b64";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The context key
|
|
7
|
+
*/
|
|
8
|
+
const contextKey = "fCtx";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Compress the current Frak context
|
|
12
|
+
* @param context - The context to be compressed
|
|
13
|
+
* @returns A compressed string containing the Frak context
|
|
14
|
+
*/
|
|
15
|
+
function compress(context?: Partial<FrakContext>): string | undefined {
|
|
16
|
+
if (!context?.r) return;
|
|
17
|
+
try {
|
|
18
|
+
const bytes = hexToBytes(context.r);
|
|
19
|
+
return base64urlEncode(bytes);
|
|
20
|
+
} catch (e) {
|
|
21
|
+
console.error("Error compressing Frak context", { e, context });
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Decompress the given Frak context
|
|
28
|
+
* @param context - The raw context to be decompressed into a `FrakContext`
|
|
29
|
+
* @returns The decompressed Frak context, or undefined if it fails
|
|
30
|
+
*/
|
|
31
|
+
function decompress(context?: string): FrakContext | undefined {
|
|
32
|
+
if (!context || context.length === 0) return;
|
|
33
|
+
try {
|
|
34
|
+
const bytes = base64urlDecode(context);
|
|
35
|
+
return { r: bytesToHex(bytes, { size: 20 }) as Address };
|
|
36
|
+
} catch (e) {
|
|
37
|
+
console.error("Error decompressing Frak context", { e, context });
|
|
38
|
+
}
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Parse the current URL into a Frak Context
|
|
44
|
+
* @param args
|
|
45
|
+
* @param args.url - The url to parse
|
|
46
|
+
* @returns The parsed Frak context
|
|
47
|
+
*/
|
|
48
|
+
function parse({ url }: { url: string }) {
|
|
49
|
+
if (!url) return null;
|
|
50
|
+
|
|
51
|
+
// Check if the url contain the frak context key
|
|
52
|
+
const urlObj = new URL(url);
|
|
53
|
+
const frakContext = urlObj.searchParams.get(contextKey);
|
|
54
|
+
if (!frakContext) return null;
|
|
55
|
+
|
|
56
|
+
// Decompress and return it
|
|
57
|
+
return decompress(frakContext);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Populate the current url with the given Frak context
|
|
62
|
+
* @param args
|
|
63
|
+
* @param args.url - The url to update
|
|
64
|
+
* @param args.context - The context to update
|
|
65
|
+
* @returns The new url with the Frak context
|
|
66
|
+
*/
|
|
67
|
+
function update({
|
|
68
|
+
url,
|
|
69
|
+
context,
|
|
70
|
+
}: { url?: string; context: Partial<FrakContext> }) {
|
|
71
|
+
if (!url) return null;
|
|
72
|
+
|
|
73
|
+
// Parse the current context
|
|
74
|
+
const currentContext = parse({ url });
|
|
75
|
+
|
|
76
|
+
// Merge the current context with the new context
|
|
77
|
+
const mergedContext = currentContext
|
|
78
|
+
? { ...currentContext, ...context }
|
|
79
|
+
: context;
|
|
80
|
+
|
|
81
|
+
// If we don't have a referrer, early exit
|
|
82
|
+
if (!mergedContext.r) return null;
|
|
83
|
+
|
|
84
|
+
// Compress it
|
|
85
|
+
const compressedContext = compress(mergedContext);
|
|
86
|
+
if (!compressedContext) return null;
|
|
87
|
+
|
|
88
|
+
// Build the new url and return it
|
|
89
|
+
const urlObj = new URL(url);
|
|
90
|
+
urlObj.searchParams.set(contextKey, compressedContext);
|
|
91
|
+
return urlObj.toString();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Remove Frak context from current url
|
|
96
|
+
* @param url - The url to update
|
|
97
|
+
* @returns The new url without the Frak context
|
|
98
|
+
*/
|
|
99
|
+
function remove(url: string) {
|
|
100
|
+
const urlObj = new URL(url);
|
|
101
|
+
urlObj.searchParams.delete(contextKey);
|
|
102
|
+
return urlObj.toString();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Replace the current url with the given Frak context
|
|
107
|
+
* @param args
|
|
108
|
+
* @param args.url - The url to update
|
|
109
|
+
* @param args.context - The context to update
|
|
110
|
+
*/
|
|
111
|
+
function replaceUrl({
|
|
112
|
+
url: baseUrl,
|
|
113
|
+
context,
|
|
114
|
+
}: { url?: string; context: Partial<FrakContext> | null }) {
|
|
115
|
+
// If no window here early exit
|
|
116
|
+
if (!window.location?.href || typeof window === "undefined") {
|
|
117
|
+
console.error("No window found, can't update context");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// If no url, try to use the current one
|
|
122
|
+
const url = baseUrl ?? window.location.href;
|
|
123
|
+
|
|
124
|
+
// Get our new url with the frak context
|
|
125
|
+
let newUrl: string | null;
|
|
126
|
+
if (context !== null) {
|
|
127
|
+
newUrl = update({
|
|
128
|
+
url,
|
|
129
|
+
context,
|
|
130
|
+
});
|
|
131
|
+
} else {
|
|
132
|
+
newUrl = remove(url);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// If no new url, early exit
|
|
136
|
+
if (!newUrl) return;
|
|
137
|
+
|
|
138
|
+
// Update the url
|
|
139
|
+
window.history.replaceState(null, "", newUrl.toString());
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Export our frak context
|
|
144
|
+
*/
|
|
145
|
+
export const FrakContextManager = {
|
|
146
|
+
compress,
|
|
147
|
+
decompress,
|
|
148
|
+
parse,
|
|
149
|
+
update,
|
|
150
|
+
remove,
|
|
151
|
+
replaceUrl,
|
|
152
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encode a buffer to a base64url encoded string
|
|
3
|
+
* @param buffer The buffer to encode
|
|
4
|
+
* @returns The encoded string
|
|
5
|
+
*/
|
|
6
|
+
export function base64urlEncode(buffer: Uint8Array): string {
|
|
7
|
+
return btoa(Array.from(buffer, (b) => String.fromCharCode(b)).join(""))
|
|
8
|
+
.replace(/\+/g, "-")
|
|
9
|
+
.replace(/\//g, "_")
|
|
10
|
+
.replace(/=+$/, "");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Decode a base64url encoded string
|
|
15
|
+
* @param value The value to decode
|
|
16
|
+
* @returns The decoded value
|
|
17
|
+
*/
|
|
18
|
+
export function base64urlDecode(value: string): Uint8Array {
|
|
19
|
+
const m = value.length % 4;
|
|
20
|
+
return Uint8Array.from(
|
|
21
|
+
atob(
|
|
22
|
+
value
|
|
23
|
+
.replace(/-/g, "+")
|
|
24
|
+
.replace(/_/g, "/")
|
|
25
|
+
.padEnd(value.length + (m === 0 ? 0 : 4 - m), "=")
|
|
26
|
+
),
|
|
27
|
+
(c) => c.charCodeAt(0)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { compressJson } from "@frak-labs/frame-connector";
|
|
2
|
+
import { base64urlEncode } from "./b64";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Compress json data
|
|
6
|
+
* @param data
|
|
7
|
+
* @ignore
|
|
8
|
+
*/
|
|
9
|
+
export function compressJsonToB64(data: unknown): string {
|
|
10
|
+
return base64urlEncode(compressJson(data));
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { decompressJson } from "@frak-labs/frame-connector";
|
|
2
|
+
import { base64urlDecode } from "./b64";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Decompress json data
|
|
6
|
+
* @param data
|
|
7
|
+
* @ignore
|
|
8
|
+
*/
|
|
9
|
+
export function decompressJsonFromB64<T>(data: string): T | null {
|
|
10
|
+
return decompressJson(base64urlDecode(data));
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { keccak256, toHex } from "viem";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Compute the product id from a domain
|
|
5
|
+
* @ignore
|
|
6
|
+
*/
|
|
7
|
+
export function computeProductId({ domain }: { domain?: string } = {}) {
|
|
8
|
+
const effectiveDomain = domain ?? window.location.host;
|
|
9
|
+
const normalizedDomain = effectiveDomain.replace("www.", "");
|
|
10
|
+
return keccak256(toHex(normalizedDomain));
|
|
11
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Currency } from "../types";
|
|
2
|
+
import { getSupportedCurrency } from "./getSupportedCurrency";
|
|
3
|
+
import { getSupportedLocale } from "./getSupportedLocale";
|
|
4
|
+
|
|
5
|
+
export function formatAmount(amount: number, currency?: Currency) {
|
|
6
|
+
// Get the supported locale (e.g. "fr-FR")
|
|
7
|
+
const supportedLocale = getSupportedLocale(currency);
|
|
8
|
+
|
|
9
|
+
// Get the supported currency (e.g. "eur")
|
|
10
|
+
const supportedCurrency = getSupportedCurrency(currency);
|
|
11
|
+
|
|
12
|
+
return amount.toLocaleString(supportedLocale, {
|
|
13
|
+
style: "currency",
|
|
14
|
+
currency: supportedCurrency,
|
|
15
|
+
minimumFractionDigits: 0,
|
|
16
|
+
maximumFractionDigits: 2,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Currency, TokenAmountType } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Get the currency amount key for a given currency
|
|
5
|
+
* @param currency - The currency to use
|
|
6
|
+
* @returns The currency amount key
|
|
7
|
+
*/
|
|
8
|
+
export function getCurrencyAmountKey(
|
|
9
|
+
currency?: Currency
|
|
10
|
+
): keyof TokenAmountType {
|
|
11
|
+
if (!currency) {
|
|
12
|
+
return "eurAmount";
|
|
13
|
+
}
|
|
14
|
+
return `${currency}Amount` as keyof TokenAmountType;
|
|
15
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { locales } from "../constants/locales";
|
|
2
|
+
import type { Currency } from "../types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get the supported currency for a given currency
|
|
6
|
+
* @param currency - The currency to use
|
|
7
|
+
* @returns The supported currency
|
|
8
|
+
*/
|
|
9
|
+
export function getSupportedCurrency(currency?: Currency): Currency {
|
|
10
|
+
if (!currency) {
|
|
11
|
+
return "eur";
|
|
12
|
+
}
|
|
13
|
+
return currency in locales ? currency : "eur";
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type LocalesKey, locales } from "../constants/locales";
|
|
2
|
+
import type { Currency } from "../types";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Get the supported locale for a given currency
|
|
6
|
+
* @param currency - The currency to use
|
|
7
|
+
* @returns The supported locale
|
|
8
|
+
*/
|
|
9
|
+
export function getSupportedLocale(
|
|
10
|
+
currency?: Currency
|
|
11
|
+
): (typeof locales)[LocalesKey] {
|
|
12
|
+
if (!currency) {
|
|
13
|
+
return locales.eur;
|
|
14
|
+
}
|
|
15
|
+
return locales[currency] ?? locales.eur;
|
|
16
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type { FrakWalletSdkConfig } from "../types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Base props for the iframe
|
|
5
|
+
* @ignore
|
|
6
|
+
*/
|
|
7
|
+
export const baseIframeProps = {
|
|
8
|
+
id: "frak-wallet",
|
|
9
|
+
name: "frak-wallet",
|
|
10
|
+
title: "Frak Wallet",
|
|
11
|
+
allow: "publickey-credentials-get *; clipboard-write; web-share *",
|
|
12
|
+
style: {
|
|
13
|
+
width: "0",
|
|
14
|
+
height: "0",
|
|
15
|
+
border: "0",
|
|
16
|
+
position: "absolute",
|
|
17
|
+
zIndex: 2000001,
|
|
18
|
+
top: "-1000px",
|
|
19
|
+
left: "-1000px",
|
|
20
|
+
colorScheme: "auto",
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create the Frak iframe
|
|
26
|
+
* @param args
|
|
27
|
+
* @param args.walletBaseUrl - Use `config.walletUrl` instead. Will be removed in future versions.
|
|
28
|
+
* @param args.config - The configuration object containing iframe options, including the replacement for `walletBaseUrl`.
|
|
29
|
+
*/
|
|
30
|
+
export function createIframe({
|
|
31
|
+
walletBaseUrl,
|
|
32
|
+
config,
|
|
33
|
+
}: { walletBaseUrl?: string; config?: FrakWalletSdkConfig }): Promise<
|
|
34
|
+
HTMLIFrameElement | undefined
|
|
35
|
+
> {
|
|
36
|
+
// Check if the iframe is already created
|
|
37
|
+
const alreadyCreatedIFrame = document.querySelector("#frak-wallet");
|
|
38
|
+
|
|
39
|
+
// If the iframe is already created, remove it
|
|
40
|
+
if (alreadyCreatedIFrame) {
|
|
41
|
+
alreadyCreatedIFrame.remove();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const iframe = document.createElement("iframe");
|
|
45
|
+
|
|
46
|
+
// Set the base iframe props
|
|
47
|
+
iframe.id = baseIframeProps.id;
|
|
48
|
+
iframe.name = baseIframeProps.name;
|
|
49
|
+
iframe.allow = baseIframeProps.allow;
|
|
50
|
+
iframe.style.zIndex = baseIframeProps.style.zIndex.toString();
|
|
51
|
+
|
|
52
|
+
changeIframeVisibility({ iframe, isVisible: false });
|
|
53
|
+
document.body.appendChild(iframe);
|
|
54
|
+
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
iframe?.addEventListener("load", () => resolve(iframe));
|
|
57
|
+
iframe.src = `${config?.walletUrl ?? walletBaseUrl ?? "https://wallet.frak.id"}/listener`;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Change the visibility of the given iframe
|
|
62
|
+
* @ignore
|
|
63
|
+
*/
|
|
64
|
+
export function changeIframeVisibility({
|
|
65
|
+
iframe,
|
|
66
|
+
isVisible,
|
|
67
|
+
}: {
|
|
68
|
+
iframe: HTMLIFrameElement;
|
|
69
|
+
isVisible: boolean;
|
|
70
|
+
}) {
|
|
71
|
+
if (!isVisible) {
|
|
72
|
+
iframe.style.width = "0";
|
|
73
|
+
iframe.style.height = "0";
|
|
74
|
+
iframe.style.border = "0";
|
|
75
|
+
iframe.style.position = "fixed";
|
|
76
|
+
iframe.style.top = "-1000px";
|
|
77
|
+
iframe.style.left = "-1000px";
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
iframe.style.position = "fixed";
|
|
82
|
+
iframe.style.top = "0";
|
|
83
|
+
iframe.style.left = "0";
|
|
84
|
+
iframe.style.width = "100%";
|
|
85
|
+
iframe.style.height = "100%";
|
|
86
|
+
iframe.style.pointerEvents = "auto";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Find an iframe within window.opener by pathname
|
|
91
|
+
*
|
|
92
|
+
* When a popup is opened via window.open from an iframe, window.opener points to
|
|
93
|
+
* the parent window, not the iframe itself. This utility searches through all frames
|
|
94
|
+
* in window.opener to find an iframe matching the specified pathname.
|
|
95
|
+
*
|
|
96
|
+
* @param pathname - The pathname to search for (default: "/listener")
|
|
97
|
+
* @returns The matching iframe window, or null if not found
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* // Find the default /listener iframe
|
|
102
|
+
* const listenerIframe = findIframeInOpener();
|
|
103
|
+
*
|
|
104
|
+
* // Find a custom iframe
|
|
105
|
+
* const customIframe = findIframeInOpener("/my-custom-iframe");
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export function findIframeInOpener(pathname = "/listener"): Window | null {
|
|
109
|
+
if (!window.opener) return null;
|
|
110
|
+
|
|
111
|
+
const frameCheck = (frame: Window) => {
|
|
112
|
+
try {
|
|
113
|
+
return (
|
|
114
|
+
frame.location.origin === window.location.origin &&
|
|
115
|
+
frame.location.pathname === pathname
|
|
116
|
+
);
|
|
117
|
+
} catch {
|
|
118
|
+
// Cross-origin frame, skip
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Check if the openner window is the right one
|
|
124
|
+
if (frameCheck(window.opener)) return window.opener;
|
|
125
|
+
|
|
126
|
+
// Search through frames in window.opener
|
|
127
|
+
try {
|
|
128
|
+
const frames = window.opener.frames;
|
|
129
|
+
for (let i = 0; i < frames.length; i++) {
|
|
130
|
+
if (frameCheck(frames[i])) {
|
|
131
|
+
return frames[i];
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
} catch (error) {
|
|
136
|
+
console.error(
|
|
137
|
+
`[findIframeInOpener] Error finding iframe with pathname ${pathname}:`,
|
|
138
|
+
error
|
|
139
|
+
);
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createIframe,
|
|
3
|
+
baseIframeProps,
|
|
4
|
+
findIframeInOpener,
|
|
5
|
+
} from "./iframeHelper";
|
|
6
|
+
export { compressJsonToB64 } from "./compression/compress";
|
|
7
|
+
export { decompressJsonFromB64 } from "./compression/decompress";
|
|
8
|
+
export { base64urlDecode, base64urlEncode } from "./compression/b64";
|
|
9
|
+
export { FrakContextManager } from "./FrakContext";
|
|
10
|
+
export { getSupportedCurrency } from "./getSupportedCurrency";
|
|
11
|
+
export { getSupportedLocale } from "./getSupportedLocale";
|
|
12
|
+
export { getCurrencyAmountKey } from "./getCurrencyAmountKey";
|
|
13
|
+
export { formatAmount } from "./formatAmount";
|
|
14
|
+
export { trackEvent } from "./trackEvent";
|
|
15
|
+
export { Deferred } from "@frak-labs/frame-connector";
|
|
16
|
+
export {
|
|
17
|
+
generateSsoUrl,
|
|
18
|
+
type CompressedSsoData,
|
|
19
|
+
type FullSsoParams,
|
|
20
|
+
type AppSpecificSsoMetadata,
|
|
21
|
+
} from "./sso";
|