@frak-labs/core-sdk 0.1.0-beta.afa252b0 → 0.1.0-beta.d9302e66
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/package.json +22 -17
- package/src/actions/displayEmbeddedWallet.test.ts +194 -0
- package/src/actions/displayEmbeddedWallet.ts +20 -0
- package/src/actions/displayModal.test.ts +387 -0
- package/src/actions/displayModal.ts +131 -0
- package/src/actions/getProductInformation.test.ts +133 -0
- package/src/actions/getProductInformation.ts +14 -0
- package/src/actions/index.ts +29 -0
- package/src/actions/openSso.test.ts +407 -0
- package/src/actions/openSso.ts +116 -0
- package/src/actions/prepareSso.test.ts +223 -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.test.ts +219 -0
- package/src/actions/sendInteraction.ts +32 -0
- package/src/actions/trackPurchaseStatus.test.ts +287 -0
- package/src/actions/trackPurchaseStatus.ts +53 -0
- package/src/actions/watchWalletStatus.test.ts +372 -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 +73 -0
- package/src/clients/transports/iframeLifecycleManager.ts +90 -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 +101 -0
- package/src/interactions/index.ts +5 -0
- package/src/interactions/pressEncoder.test.ts +215 -0
- package/src/interactions/pressEncoder.ts +53 -0
- package/src/interactions/purchaseEncoder.test.ts +291 -0
- package/src/interactions/purchaseEncoder.ts +99 -0
- package/src/interactions/referralEncoder.test.ts +170 -0
- package/src/interactions/referralEncoder.ts +47 -0
- package/src/interactions/retailEncoder.test.ts +107 -0
- package/src/interactions/retailEncoder.ts +37 -0
- package/src/interactions/webshopEncoder.test.ts +56 -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 +71 -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.test.ts +338 -0
- package/src/utils/FrakContext.ts +158 -0
- package/src/utils/compression/b64.test.ts +181 -0
- package/src/utils/compression/b64.ts +29 -0
- package/src/utils/compression/compress.test.ts +123 -0
- package/src/utils/compression/compress.ts +11 -0
- package/src/utils/compression/decompress.test.ts +145 -0
- package/src/utils/compression/decompress.ts +11 -0
- package/src/utils/compression/index.ts +3 -0
- package/src/utils/computeProductId.test.ts +80 -0
- package/src/utils/computeProductId.ts +11 -0
- package/src/utils/constants.test.ts +23 -0
- package/src/utils/constants.ts +4 -0
- package/src/utils/formatAmount.test.ts +113 -0
- package/src/utils/formatAmount.ts +18 -0
- package/src/utils/getCurrencyAmountKey.test.ts +44 -0
- package/src/utils/getCurrencyAmountKey.ts +15 -0
- package/src/utils/getSupportedCurrency.test.ts +51 -0
- package/src/utils/getSupportedCurrency.ts +14 -0
- package/src/utils/getSupportedLocale.test.ts +64 -0
- package/src/utils/getSupportedLocale.ts +16 -0
- package/src/utils/iframeHelper.test.ts +450 -0
- package/src/utils/iframeHelper.ts +143 -0
- package/src/utils/index.ts +21 -0
- package/src/utils/sso.test.ts +361 -0
- package/src/utils/sso.ts +119 -0
- package/src/utils/ssoUrlListener.ts +60 -0
- package/src/utils/trackEvent.test.ts +162 -0
- package/src/utils/trackEvent.ts +26 -0
- package/cdn/bundle.js +0 -19
- package/cdn/bundle.js.LICENSE.txt +0 -10
- package/dist/actions.cjs +0 -1
- package/dist/actions.d.cts +0 -1481
- package/dist/actions.d.ts +0 -1481
- package/dist/actions.js +0 -1
- package/dist/bundle.cjs +0 -13
- package/dist/bundle.d.cts +0 -2087
- package/dist/bundle.d.ts +0 -2087
- package/dist/bundle.js +0 -13
- package/dist/index.cjs +0 -13
- package/dist/index.d.cts +0 -1387
- package/dist/index.d.ts +0 -1387
- package/dist/index.js +0 -13
- package/dist/interactions.cjs +0 -1
- package/dist/interactions.d.cts +0 -182
- package/dist/interactions.d.ts +0 -182
- package/dist/interactions.js +0 -1
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Deferred } from "@frak-labs/frame-connector";
|
|
2
|
+
import type { FrakLifecycleEvent } from "../../types";
|
|
3
|
+
import { BACKUP_KEY } from "../../utils/constants";
|
|
4
|
+
import { changeIframeVisibility } from "../../utils/iframeHelper";
|
|
5
|
+
|
|
6
|
+
/** @ignore */
|
|
7
|
+
export type IframeLifecycleManager = {
|
|
8
|
+
isConnected: Promise<boolean>;
|
|
9
|
+
handleEvent: (messageEvent: FrakLifecycleEvent) => Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create a new iframe lifecycle handler
|
|
14
|
+
* @ignore
|
|
15
|
+
*/
|
|
16
|
+
export function createIFrameLifecycleManager({
|
|
17
|
+
iframe,
|
|
18
|
+
}: {
|
|
19
|
+
iframe: HTMLIFrameElement;
|
|
20
|
+
}): IframeLifecycleManager {
|
|
21
|
+
// Create the isConnected listener
|
|
22
|
+
const isConnectedDeferred = new Deferred<boolean>();
|
|
23
|
+
|
|
24
|
+
// Build the handler itself
|
|
25
|
+
const handler = async (messageEvent: FrakLifecycleEvent) => {
|
|
26
|
+
if (!("iframeLifecycle" in messageEvent)) return;
|
|
27
|
+
|
|
28
|
+
const { iframeLifecycle: event, data } = messageEvent;
|
|
29
|
+
|
|
30
|
+
switch (event) {
|
|
31
|
+
// Resolve the isConnected promise
|
|
32
|
+
case "connected":
|
|
33
|
+
isConnectedDeferred.resolve(true);
|
|
34
|
+
break;
|
|
35
|
+
// Perform a frak backup
|
|
36
|
+
case "do-backup":
|
|
37
|
+
if (data.backup) {
|
|
38
|
+
localStorage.setItem(BACKUP_KEY, data.backup);
|
|
39
|
+
} else {
|
|
40
|
+
localStorage.removeItem(BACKUP_KEY);
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
// Remove frak backup
|
|
44
|
+
case "remove-backup":
|
|
45
|
+
localStorage.removeItem(BACKUP_KEY);
|
|
46
|
+
break;
|
|
47
|
+
// Change iframe visibility
|
|
48
|
+
case "show":
|
|
49
|
+
case "hide":
|
|
50
|
+
changeIframeVisibility({
|
|
51
|
+
iframe,
|
|
52
|
+
isVisible: event === "show",
|
|
53
|
+
});
|
|
54
|
+
break;
|
|
55
|
+
// Handshake handling
|
|
56
|
+
case "handshake": {
|
|
57
|
+
iframe.contentWindow?.postMessage(
|
|
58
|
+
{
|
|
59
|
+
clientLifecycle: "handshake-response",
|
|
60
|
+
data: {
|
|
61
|
+
token: data.token,
|
|
62
|
+
currentUrl: window.location.href,
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
"*"
|
|
66
|
+
);
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
// Redirect handling
|
|
70
|
+
case "redirect": {
|
|
71
|
+
const redirectUrl = new URL(data.baseRedirectUrl);
|
|
72
|
+
|
|
73
|
+
// If we got a u append the current location dynamicly
|
|
74
|
+
if (redirectUrl.searchParams.has("u")) {
|
|
75
|
+
redirectUrl.searchParams.delete("u");
|
|
76
|
+
redirectUrl.searchParams.append("u", window.location.href);
|
|
77
|
+
window.location.href = redirectUrl.toString();
|
|
78
|
+
} else {
|
|
79
|
+
window.location.href = data.baseRedirectUrl;
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
handleEvent: handler,
|
|
88
|
+
isConnected: isConnectedDeferred.promise,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The final keys for each interaction types (e.g. `openArticle`) -> interaction type
|
|
3
|
+
* @inline
|
|
4
|
+
*/
|
|
5
|
+
export type InteractionTypesKey = {
|
|
6
|
+
[K in keyof typeof interactionTypes]: keyof (typeof interactionTypes)[K];
|
|
7
|
+
}[keyof typeof interactionTypes];
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The keys for each interaction types (e.g. `press.openArticle`) -> category_type.interaction_type
|
|
11
|
+
* @inline
|
|
12
|
+
*/
|
|
13
|
+
export type FullInteractionTypesKey = {
|
|
14
|
+
[Category in keyof typeof interactionTypes]: `${Category & string}.${keyof (typeof interactionTypes)[Category] & string}`;
|
|
15
|
+
}[keyof typeof interactionTypes];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Each interactions types according to the product types
|
|
19
|
+
*/
|
|
20
|
+
export const interactionTypes = {
|
|
21
|
+
press: {
|
|
22
|
+
openArticle: "0xc0a24ffb",
|
|
23
|
+
readArticle: "0xd5bd0fbe",
|
|
24
|
+
},
|
|
25
|
+
dapp: {
|
|
26
|
+
proofVerifiableStorageUpdate: "0x2ab2aeef",
|
|
27
|
+
callableVerifiableStorageUpdate: "0xa07da986",
|
|
28
|
+
},
|
|
29
|
+
webshop: {
|
|
30
|
+
open: "0xb311798f",
|
|
31
|
+
},
|
|
32
|
+
referral: {
|
|
33
|
+
referred: "0x010cc3b9",
|
|
34
|
+
createLink: "0xb2c0f17c",
|
|
35
|
+
},
|
|
36
|
+
purchase: {
|
|
37
|
+
started: "0xd87e90c3",
|
|
38
|
+
completed: "0x8403aeb4",
|
|
39
|
+
unsafeCompleted: "0x4d5b14e0",
|
|
40
|
+
},
|
|
41
|
+
retail: {
|
|
42
|
+
customerMeeting: "0x74489004",
|
|
43
|
+
},
|
|
44
|
+
} as const;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The keys for each product types
|
|
3
|
+
* @inline
|
|
4
|
+
*/
|
|
5
|
+
export type ProductTypesKey = keyof typeof productTypes;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* List of the product types per denominator
|
|
9
|
+
*/
|
|
10
|
+
export const productTypes = {
|
|
11
|
+
// content type
|
|
12
|
+
dapp: 1,
|
|
13
|
+
press: 2,
|
|
14
|
+
webshop: 3,
|
|
15
|
+
retail: 4,
|
|
16
|
+
|
|
17
|
+
// feature type
|
|
18
|
+
referral: 30,
|
|
19
|
+
purchase: 31,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Bitmask for each product types
|
|
24
|
+
*/
|
|
25
|
+
export const productTypesMask: Record<ProductTypesKey, bigint> = Object.entries(
|
|
26
|
+
productTypes
|
|
27
|
+
).reduce(
|
|
28
|
+
(acc, [key, value]) => {
|
|
29
|
+
acc[key as ProductTypesKey] = BigInt(1) << BigInt(value);
|
|
30
|
+
return acc;
|
|
31
|
+
},
|
|
32
|
+
{} as Record<ProductTypesKey, bigint>
|
|
33
|
+
);
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// Clients
|
|
2
|
+
|
|
3
|
+
export { ssoPopupFeatures, ssoPopupName } from "./actions/openSso";
|
|
4
|
+
export {
|
|
5
|
+
createIFrameFrakClient,
|
|
6
|
+
DebugInfoGatherer,
|
|
7
|
+
setupClient,
|
|
8
|
+
} from "./clients";
|
|
9
|
+
export {
|
|
10
|
+
type FullInteractionTypesKey,
|
|
11
|
+
type InteractionTypesKey,
|
|
12
|
+
interactionTypes,
|
|
13
|
+
} from "./constants/interactionTypes";
|
|
14
|
+
export { type LocalesKey, locales } from "./constants/locales";
|
|
15
|
+
// Constants
|
|
16
|
+
export {
|
|
17
|
+
type ProductTypesKey,
|
|
18
|
+
productTypes,
|
|
19
|
+
productTypesMask,
|
|
20
|
+
} from "./constants/productTypes";
|
|
21
|
+
// Types
|
|
22
|
+
export type {
|
|
23
|
+
ClientLifecycleEvent,
|
|
24
|
+
CompressedData,
|
|
25
|
+
Currency,
|
|
26
|
+
// RPC Embedded wallet
|
|
27
|
+
DisplayEmbeddedWalletParamsType,
|
|
28
|
+
DisplayEmbeddedWalletResultType,
|
|
29
|
+
DisplayModalParamsType,
|
|
30
|
+
EmbeddedViewActionReferred,
|
|
31
|
+
EmbeddedViewActionSharing,
|
|
32
|
+
FinalActionType,
|
|
33
|
+
FinalModalStepType,
|
|
34
|
+
// Client
|
|
35
|
+
FrakClient,
|
|
36
|
+
// Utils
|
|
37
|
+
FrakContext,
|
|
38
|
+
FrakLifecycleEvent,
|
|
39
|
+
// Config
|
|
40
|
+
FrakWalletSdkConfig,
|
|
41
|
+
GetProductInformationReturnType,
|
|
42
|
+
HashProtectedData,
|
|
43
|
+
I18nConfig,
|
|
44
|
+
IFrameLifecycleEvent,
|
|
45
|
+
IFrameRpcSchema,
|
|
46
|
+
// Transport
|
|
47
|
+
IFrameTransport,
|
|
48
|
+
// Compression
|
|
49
|
+
KeyProvider,
|
|
50
|
+
Language,
|
|
51
|
+
LocalizedI18nConfig,
|
|
52
|
+
LoggedInEmbeddedView,
|
|
53
|
+
LoggedOutEmbeddedView,
|
|
54
|
+
LoginModalStepType,
|
|
55
|
+
ModalRpcMetadata,
|
|
56
|
+
ModalRpcStepsInput,
|
|
57
|
+
ModalRpcStepsResultType,
|
|
58
|
+
// RPC Modal types
|
|
59
|
+
ModalStepMetadata,
|
|
60
|
+
// RPC Modal generics
|
|
61
|
+
ModalStepTypes,
|
|
62
|
+
OpenInteractionSessionModalStepType,
|
|
63
|
+
OpenInteractionSessionReturnType,
|
|
64
|
+
OpenSsoParamsType,
|
|
65
|
+
OpenSsoReturnType,
|
|
66
|
+
PreparedInteraction,
|
|
67
|
+
PrepareSsoParamsType,
|
|
68
|
+
PrepareSsoReturnType,
|
|
69
|
+
SendInteractionParamsType,
|
|
70
|
+
SendInteractionReturnType,
|
|
71
|
+
SendTransactionModalStepType,
|
|
72
|
+
SendTransactionReturnType,
|
|
73
|
+
SendTransactionTxType,
|
|
74
|
+
SiweAuthenticateModalStepType,
|
|
75
|
+
SiweAuthenticateReturnType,
|
|
76
|
+
SiweAuthenticationParams,
|
|
77
|
+
SsoMetadata,
|
|
78
|
+
TokenAmountType,
|
|
79
|
+
// Rpc
|
|
80
|
+
WalletStatusReturnType,
|
|
81
|
+
} from "./types";
|
|
82
|
+
// Utils
|
|
83
|
+
export {
|
|
84
|
+
type AppSpecificSsoMetadata,
|
|
85
|
+
base64urlDecode,
|
|
86
|
+
base64urlEncode,
|
|
87
|
+
baseIframeProps,
|
|
88
|
+
type CompressedSsoData,
|
|
89
|
+
compressJsonToB64,
|
|
90
|
+
createIframe,
|
|
91
|
+
decompressJsonFromB64,
|
|
92
|
+
FrakContextManager,
|
|
93
|
+
type FullSsoParams,
|
|
94
|
+
findIframeInOpener,
|
|
95
|
+
formatAmount,
|
|
96
|
+
generateSsoUrl,
|
|
97
|
+
getCurrencyAmountKey,
|
|
98
|
+
getSupportedCurrency,
|
|
99
|
+
getSupportedLocale,
|
|
100
|
+
trackEvent,
|
|
101
|
+
} from "./utils";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { PressInteractionEncoder } from "./pressEncoder";
|
|
2
|
+
export { PurchaseInteractionEncoder } from "./purchaseEncoder";
|
|
3
|
+
export { ReferralInteractionEncoder } from "./referralEncoder";
|
|
4
|
+
export { RetailInteractionEncoder } from "./retailEncoder";
|
|
5
|
+
export { WebShopInteractionEncoder } from "./webshopEncoder";
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for PressInteractionEncoder
|
|
3
|
+
* Tests encoding of press-related user interactions
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { pad, toHex } from "viem";
|
|
7
|
+
import { describe, expect, it, test } from "../../tests/vitest-fixtures";
|
|
8
|
+
import { interactionTypes } from "../constants/interactionTypes";
|
|
9
|
+
import { productTypes } from "../constants/productTypes";
|
|
10
|
+
import { PressInteractionEncoder } from "./pressEncoder";
|
|
11
|
+
|
|
12
|
+
describe("PressInteractionEncoder", () => {
|
|
13
|
+
describe("openArticle", () => {
|
|
14
|
+
test("should encode open article interaction with correct structure", ({
|
|
15
|
+
mockArticleId,
|
|
16
|
+
}) => {
|
|
17
|
+
const interaction = PressInteractionEncoder.openArticle({
|
|
18
|
+
articleId: mockArticleId,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Should return PreparedInteraction structure
|
|
22
|
+
expect(interaction).toHaveProperty("handlerTypeDenominator");
|
|
23
|
+
expect(interaction).toHaveProperty("interactionData");
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("should use press product type in handlerTypeDenominator", ({
|
|
27
|
+
mockArticleId,
|
|
28
|
+
}) => {
|
|
29
|
+
const interaction = PressInteractionEncoder.openArticle({
|
|
30
|
+
articleId: mockArticleId,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
// Should use press product type (2)
|
|
34
|
+
const expectedDenominator = toHex(productTypes.press);
|
|
35
|
+
expect(interaction.handlerTypeDenominator).toBe(
|
|
36
|
+
expectedDenominator
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("should include openArticle interaction type in data", ({
|
|
41
|
+
mockArticleId,
|
|
42
|
+
}) => {
|
|
43
|
+
const interaction = PressInteractionEncoder.openArticle({
|
|
44
|
+
articleId: mockArticleId,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Should start with openArticle interaction type
|
|
48
|
+
expect(interaction.interactionData).toContain(
|
|
49
|
+
interactionTypes.press.openArticle
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
test("should pad article ID to 32 bytes", ({ mockArticleId }) => {
|
|
54
|
+
const interaction = PressInteractionEncoder.openArticle({
|
|
55
|
+
articleId: mockArticleId,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Article ID should be padded to 32 bytes (64 hex chars + 0x prefix = 66 chars)
|
|
59
|
+
const paddedArticleId = pad(mockArticleId, { size: 32 });
|
|
60
|
+
expect(interaction.interactionData).toContain(
|
|
61
|
+
paddedArticleId.slice(2)
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("should handle short article IDs by padding", () => {
|
|
66
|
+
const shortArticleId = "0x01" as const;
|
|
67
|
+
const interaction = PressInteractionEncoder.openArticle({
|
|
68
|
+
articleId: shortArticleId,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Should pad short IDs to 32 bytes
|
|
72
|
+
expect(interaction.interactionData).toBeDefined();
|
|
73
|
+
// Total length: openArticle (10 chars) + padded ID (64 chars) + 0x (2 chars) = 76 chars
|
|
74
|
+
expect(interaction.interactionData.length).toBeGreaterThan(64);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("should produce consistent output for same article ID", ({
|
|
78
|
+
mockArticleId,
|
|
79
|
+
}) => {
|
|
80
|
+
const interaction1 = PressInteractionEncoder.openArticle({
|
|
81
|
+
articleId: mockArticleId,
|
|
82
|
+
});
|
|
83
|
+
const interaction2 = PressInteractionEncoder.openArticle({
|
|
84
|
+
articleId: mockArticleId,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Same input should produce same output
|
|
88
|
+
expect(interaction1).toEqual(interaction2);
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("readArticle", () => {
|
|
93
|
+
test("should encode read article interaction with correct structure", ({
|
|
94
|
+
mockArticleId,
|
|
95
|
+
}) => {
|
|
96
|
+
const interaction = PressInteractionEncoder.readArticle({
|
|
97
|
+
articleId: mockArticleId,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Should return PreparedInteraction structure
|
|
101
|
+
expect(interaction).toHaveProperty("handlerTypeDenominator");
|
|
102
|
+
expect(interaction).toHaveProperty("interactionData");
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("should use press product type in handlerTypeDenominator", ({
|
|
106
|
+
mockArticleId,
|
|
107
|
+
}) => {
|
|
108
|
+
const interaction = PressInteractionEncoder.readArticle({
|
|
109
|
+
articleId: mockArticleId,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Should use press product type (2)
|
|
113
|
+
const expectedDenominator = toHex(productTypes.press);
|
|
114
|
+
expect(interaction.handlerTypeDenominator).toBe(
|
|
115
|
+
expectedDenominator
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("should include readArticle interaction type in data", ({
|
|
120
|
+
mockArticleId,
|
|
121
|
+
}) => {
|
|
122
|
+
const interaction = PressInteractionEncoder.readArticle({
|
|
123
|
+
articleId: mockArticleId,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Should start with readArticle interaction type
|
|
127
|
+
expect(interaction.interactionData).toContain(
|
|
128
|
+
interactionTypes.press.readArticle
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("should pad article ID to 32 bytes", ({ mockArticleId }) => {
|
|
133
|
+
const interaction = PressInteractionEncoder.readArticle({
|
|
134
|
+
articleId: mockArticleId,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Article ID should be padded to 32 bytes
|
|
138
|
+
const paddedArticleId = pad(mockArticleId, { size: 32 });
|
|
139
|
+
expect(interaction.interactionData).toContain(
|
|
140
|
+
paddedArticleId.slice(2)
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("should produce different output than openArticle for same ID", ({
|
|
145
|
+
mockArticleId,
|
|
146
|
+
}) => {
|
|
147
|
+
const openInteraction = PressInteractionEncoder.openArticle({
|
|
148
|
+
articleId: mockArticleId,
|
|
149
|
+
});
|
|
150
|
+
const readInteraction = PressInteractionEncoder.readArticle({
|
|
151
|
+
articleId: mockArticleId,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Different interaction types should produce different data
|
|
155
|
+
expect(openInteraction.interactionData).not.toBe(
|
|
156
|
+
readInteraction.interactionData
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
// But should have same handlerTypeDenominator (both press)
|
|
160
|
+
expect(openInteraction.handlerTypeDenominator).toBe(
|
|
161
|
+
readInteraction.handlerTypeDenominator
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe("interaction data format", () => {
|
|
167
|
+
test("should concatenate interaction type and padded article ID", ({
|
|
168
|
+
mockArticleId,
|
|
169
|
+
}) => {
|
|
170
|
+
const interaction = PressInteractionEncoder.openArticle({
|
|
171
|
+
articleId: mockArticleId,
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Should start with interaction type (10 chars including 0x)
|
|
175
|
+
expect(interaction.interactionData.slice(0, 10)).toBe(
|
|
176
|
+
interactionTypes.press.openArticle
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Should be followed by padded article ID (64 hex chars)
|
|
180
|
+
expect(interaction.interactionData.length).toBe(74); // 0x + 8 (type) + 64 (padded ID)
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("should produce valid hex strings", ({ mockArticleId }) => {
|
|
184
|
+
const openInteraction = PressInteractionEncoder.openArticle({
|
|
185
|
+
articleId: mockArticleId,
|
|
186
|
+
});
|
|
187
|
+
const readInteraction = PressInteractionEncoder.readArticle({
|
|
188
|
+
articleId: mockArticleId,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
// Both should be valid hex strings starting with 0x
|
|
192
|
+
expect(openInteraction.interactionData).toMatch(/^0x[0-9a-f]+$/);
|
|
193
|
+
expect(readInteraction.interactionData).toMatch(/^0x[0-9a-f]+$/);
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it("should handle different article IDs correctly", () => {
|
|
197
|
+
const articleId1 =
|
|
198
|
+
"0x0000000000000000000000000000000000000000000000000000000000000001" as const;
|
|
199
|
+
const articleId2 =
|
|
200
|
+
"0x0000000000000000000000000000000000000000000000000000000000000002" as const;
|
|
201
|
+
|
|
202
|
+
const interaction1 = PressInteractionEncoder.openArticle({
|
|
203
|
+
articleId: articleId1,
|
|
204
|
+
});
|
|
205
|
+
const interaction2 = PressInteractionEncoder.openArticle({
|
|
206
|
+
articleId: articleId2,
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Different article IDs should produce different interaction data
|
|
210
|
+
expect(interaction1.interactionData).not.toBe(
|
|
211
|
+
interaction2.interactionData
|
|
212
|
+
);
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { concatHex, type Hex, pad, toHex } from "viem";
|
|
2
|
+
import { interactionTypes } from "../constants/interactionTypes";
|
|
3
|
+
import { productTypes } from "../constants/productTypes";
|
|
4
|
+
import type { PreparedInteraction } from "../types";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Press interactions allow you to track user engagement with articles or other press content on your platform.
|
|
8
|
+
* After setting up these interactions, you can create acquisition campaign based on the user engagement with your press content.
|
|
9
|
+
*
|
|
10
|
+
* :::info
|
|
11
|
+
* To properly handle press interactions, ensure that the "Press" product type is enabled in your Business dashboard.
|
|
12
|
+
* :::
|
|
13
|
+
*
|
|
14
|
+
* @description Encode press related user interactions
|
|
15
|
+
*
|
|
16
|
+
* @group Interactions Encoder
|
|
17
|
+
*
|
|
18
|
+
* @see {@link PreparedInteraction} The prepared interaction object that can be sent
|
|
19
|
+
* @see {@link !actions.sendInteraction | `sendInteraction()`} Action used to send the prepared interaction to the Frak Wallet
|
|
20
|
+
*/
|
|
21
|
+
export const PressInteractionEncoder = {
|
|
22
|
+
/**
|
|
23
|
+
* Encode an open article interaction
|
|
24
|
+
* @param args
|
|
25
|
+
* @param args.articleId - The id of the article the user opened (32 bytes), could be a `keccak256` hash of the article slug, or your internal id
|
|
26
|
+
*/
|
|
27
|
+
openArticle({ articleId }: { articleId: Hex }): PreparedInteraction {
|
|
28
|
+
const interactionData = concatHex([
|
|
29
|
+
interactionTypes.press.openArticle,
|
|
30
|
+
pad(articleId, { size: 32 }),
|
|
31
|
+
]);
|
|
32
|
+
return {
|
|
33
|
+
handlerTypeDenominator: toHex(productTypes.press),
|
|
34
|
+
interactionData,
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Encode a read article interaction
|
|
40
|
+
* @param args
|
|
41
|
+
* @param args.articleId - The id of the article the user opened (32 bytes), could be a `keccak256` hash of the article slug, or your internal id
|
|
42
|
+
*/
|
|
43
|
+
readArticle({ articleId }: { articleId: Hex }): PreparedInteraction {
|
|
44
|
+
const interactionData = concatHex([
|
|
45
|
+
interactionTypes.press.readArticle,
|
|
46
|
+
pad(articleId, { size: 32 }),
|
|
47
|
+
]);
|
|
48
|
+
return {
|
|
49
|
+
handlerTypeDenominator: toHex(productTypes.press),
|
|
50
|
+
interactionData,
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|