Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 1.24 #1178

Merged
merged 7 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
TW-1496: [Mises] Ads impressions rework (#1169)
* TW-1474: Support Mises browser. + IS_MISES_BROWSER + Minor UI fixes

* TW-1474: Support Mises browser. Fix Accounts dropdown

* TW-1474: Support Mises browser. -- Unnecessary scroll in overlays

* TW-1496: [Mises] Ads impressions rework

* TW-1496: [Mises] Ads impressions rework. Enable pipeline

* TW-1496: [Mises] Ads impressions rework. Tuned logic to requirements

* TW-1496: [Mises] Ads impressions rework. + useAdsImpressionsLinking()

* TW-1496: [Mises] Ads impressions rework. + ECDSA

* TW-1496: [Mises] Ads impressions rework. ECDSA. jwk -> pkcs8 + spki
  • Loading branch information
alex-tsx authored Jul 26, 2024
commit a4cfad4a7a740b4db1522c8c46fe31d814a1bf96
1 change: 1 addition & 0 deletions .env.dist
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ TEMPLE_WALLET_UTORG_SID=

TEMPLE_WALLET_API_URL=https://temple-api-mainnet.prod.templewallet.com
TEMPLE_WALLET_DEXES_API_URL=wss://dexes-api-mainnet.prod.templewallet.com
TEMPLE_ADS_API_URL=

TEMPLE_WALLET_ROUTE3_AUTH_TOKEN=
TEMPLE_WALLET_MOONPAY_API_KEY=
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches:
- master
- development
- 'TW-[0-9]+-epic-**'

jobs:
pull-request-check:
Expand All @@ -31,6 +32,7 @@ jobs:
TEMPLE_WALLET_UTORG_SID: ${{ secrets.TEMPLE_WALLET_UTORG_SID }}
TEMPLE_WALLET_API_URL: ${{ vars.TEMPLE_WALLET_API_URL }}
TEMPLE_WALLET_DEXES_API_URL: ${{ vars.TEMPLE_WALLET_DEXES_API_URL }}
TEMPLE_ADS_API_URL: ${{ vars.TEMPLE_ADS_API_URL }}
TEMPLE_WALLET_ROUTE3_AUTH_TOKEN: ${{ vars.TEMPLE_WALLET_ROUTE3_AUTH_TOKEN }}
TEMPLE_WALLET_MOONPAY_API_KEY: ${{ secrets.TEMPLE_WALLET_MOONPAY_API_KEY }}
TEMPLE_FIREBASE_CONFIG: ${{ secrets.TEMPLE_FIREBASE_CONFIG }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/manual-builds.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
TEMPLE_WALLET_UTORG_SID: ${{ secrets.TEMPLE_WALLET_UTORG_SID }}
TEMPLE_WALLET_API_URL: ${{ vars.TEMPLE_WALLET_API_URL }}
TEMPLE_WALLET_DEXES_API_URL: ${{ vars.TEMPLE_WALLET_DEXES_API_URL }}
TEMPLE_ADS_API_URL: ${{ vars.TEMPLE_ADS_API_URL }}
TEMPLE_WALLET_ROUTE3_AUTH_TOKEN: ${{ vars.TEMPLE_WALLET_ROUTE3_AUTH_TOKEN }}
TEMPLE_WALLET_MOONPAY_API_KEY: ${{ secrets.TEMPLE_WALLET_MOONPAY_API_KEY }}
TEMPLE_FIREBASE_CONFIG: ${{ secrets.TEMPLE_FIREBASE_CONFIG }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ jobs:
TEMPLE_WALLET_UTORG_SID: ${{ secrets.TEMPLE_WALLET_UTORG_SID }}
TEMPLE_WALLET_API_URL: ${{ vars.TEMPLE_WALLET_API_URL }}
TEMPLE_WALLET_DEXES_API_URL: ${{ vars.TEMPLE_WALLET_DEXES_API_URL }}
TEMPLE_ADS_API_URL: ${{ vars.TEMPLE_ADS_API_URL }}
TEMPLE_WALLET_ROUTE3_AUTH_TOKEN: ${{ vars.TEMPLE_WALLET_ROUTE3_AUTH_TOKEN }}
TEMPLE_WALLET_MOONPAY_API_KEY: ${{ secrets.TEMPLE_WALLET_MOONPAY_API_KEY }}
TEMPLE_FIREBASE_CONFIG: ${{ secrets.TEMPLE_FIREBASE_CONFIG }}
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/secrets-setup/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ inputs:
required: true
TEMPLE_WALLET_DEXES_API_URL:
required: true
TEMPLE_ADS_API_URL:
required: true
TEMPLE_FIREBASE_CONFIG:
required: true
TEMPLE_FIREBASE_MESSAGING_VAPID_KEY:
Expand Down Expand Up @@ -123,6 +125,7 @@ runs:

TEMPLE_WALLET_API_URL=${{ inputs.TEMPLE_WALLET_API_URL }}
TEMPLE_WALLET_DEXES_API_URL=${{ inputs.TEMPLE_WALLET_DEXES_API_URL }}
TEMPLE_ADS_API_URL=${{ inputs.TEMPLE_ADS_API_URL }}
TEMPLE_WALLET_ROUTE3_AUTH_TOKEN=${{ inputs.TEMPLE_WALLET_ROUTE3_AUTH_TOKEN }}
TEMPLE_WALLET_MOONPAY_API_KEY=${{ inputs.TEMPLE_WALLET_MOONPAY_API_KEY }}
TEMPLE_FIREBASE_CONFIG=${{ inputs.TEMPLE_FIREBASE_CONFIG }}
Expand Down
2 changes: 2 additions & 0 deletions src/app/WithDataLoading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { dispatch } from 'app/store';
import { loadTokensScamlistActions } from 'app/store/assets/actions';
import { loadSwapDexesAction, loadSwapTokensAction } from 'app/store/swap/actions';

import { useAdsImpressionsLinking } from './hooks/use-ads-impressions-linking';
import { useAdvertisingLoading } from './hooks/use-advertising.hook';
import { useAssetsLoading } from './hooks/use-assets-loading';
import { useAssetsMigrations } from './hooks/use-assets-migrations';
Expand Down Expand Up @@ -42,6 +43,7 @@ export const WithDataLoading: FC<PropsWithChildren> = ({ children }) => {
useStorageAnalytics();
useConversionTracking();
useUserIdAccountPkhSync();
useAdsImpressionsLinking();

return <>{children}</>;
};
18 changes: 18 additions & 0 deletions src/app/hooks/use-ads-impressions-linking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { dispatch } from 'app/store';
import { setAdsImpressionsLinkedAction } from 'app/store/settings/actions';
import { useIsAdsImpressionsLinkedSelector } from 'app/store/settings/selectors';
import { performLinkingOfAdsImpressions } from 'lib/ads/link-ads-impressions';
import { useDidMount } from 'lib/ui/hooks';

import { useAdsViewerPkh } from './use-ads-viewer-pkh';

export const useAdsImpressionsLinking = () => {
const linked = useIsAdsImpressionsLinkedSelector();
const accountPkh = useAdsViewerPkh();

useDidMount(() => {
if (linked) return;

performLinkingOfAdsImpressions(accountPkh).then(() => void dispatch(setAdsImpressionsLinkedAction()));
});
};
16 changes: 16 additions & 0 deletions src/app/storage/app-install-id.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { fetchFromStorage, putToStorage } from 'lib/storage';

const storageKey = 'APP_INSTALL_IDENTITY';

interface AppInstallIdentity {
version: string;
privateKey: string;
publicKey: string;
publicKeyHash: string;
ts: string;
}

export const getStoredAppInstallIdentity = () => fetchFromStorage<AppInstallIdentity>(storageKey);

export const putStoredAppInstallIdentity = (value: AppInstallIdentity) =>
putToStorage<AppInstallIdentity>(storageKey, value);
2 changes: 2 additions & 0 deletions src/app/store/settings/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ export const setOnRampPossibilityAction = createAction<boolean>('settings/SET_ON
export const setConversionTrackedAction = createAction<void>('settings/SET_CONVERSION_TRACKED');

export const setPendingReactivateAdsAction = createAction<boolean>('settings/SET_PENDING_REACTIVATE_ADS');

export const setAdsImpressionsLinkedAction = createAction('settings/SET_ADS_IMPRESSIONS_LINKED');
5 changes: 5 additions & 0 deletions src/app/store/settings/reducers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createReducer } from '@reduxjs/toolkit';

import {
setAdsImpressionsLinkedAction,
setConversionTrackedAction,
setIsAnalyticsEnabledAction,
setOnRampPossibilityAction,
Expand Down Expand Up @@ -29,4 +30,8 @@ export const settingsReducer = createReducer<SettingsState>(settingsInitialState
builder.addCase(setPendingReactivateAdsAction, (state, { payload }) => {
state.pendingReactivateAds = payload;
});

builder.addCase(setAdsImpressionsLinkedAction, state => {
state.adsImpressionsLinked = true;
});
});
2 changes: 2 additions & 0 deletions src/app/store/settings/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,5 @@ export const useOnRampPossibilitySelector = () => useSelector(({ settings }) =>
export const useIsConversionTrackedSelector = () => useSelector(({ settings }) => settings.isConversionTracked);

export const useIsPendingReactivateAdsSelector = () => useSelector(({ settings }) => settings.pendingReactivateAds);

export const useIsAdsImpressionsLinkedSelector = () => useSelector(({ settings }) => settings.adsImpressionsLinked);
3 changes: 2 additions & 1 deletion src/app/store/settings/state.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ export const mockSettingsState: SettingsState = {
balanceMode: BalanceMode.Fiat,
isOnRampPossibility: false,
isConversionTracked: false,
pendingReactivateAds: false
pendingReactivateAds: false,
adsImpressionsLinked: false
};
4 changes: 3 additions & 1 deletion src/app/store/settings/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface SettingsState {
isOnRampPossibility: boolean;
isConversionTracked: boolean;
pendingReactivateAds: boolean;
adsImpressionsLinked: boolean;
}

export const settingsInitialState: SettingsState = {
Expand All @@ -20,5 +21,6 @@ export const settingsInitialState: SettingsState = {
balanceMode: BalanceMode.Fiat,
isOnRampPossibility: false,
isConversionTracked: false,
pendingReactivateAds: false
pendingReactivateAds: false,
adsImpressionsLinked: false
};
18 changes: 4 additions & 14 deletions src/app/templates/partners-promotion/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
usePromotionHidingTimestampSelector
} from 'app/store/partners-promotion/selectors';
import { AdsProviderName, AdsProviderTitle } from 'lib/ads';
import { AnalyticsEventCategory, useAnalytics } from 'lib/analytics';
import { postAdImpression } from 'lib/apis/ads-api';
import { AD_HIDING_TIMEOUT } from 'lib/constants';

import { HypelabPromotion } from './components/hypelab-promotion';
Expand Down Expand Up @@ -40,7 +40,6 @@ const shouldBeHiddenTemporarily = (hiddenAt: number) => {
export const PartnersPromotion = memo<PartnersPromotionProps>(({ variant, id, pageName, withPersonaProvider }) => {
const isImageAd = variant === PartnersPromotionVariant.Image;
const adsViewerAddress = useAdsViewerPkh();
const { trackEvent } = useAnalytics();
const { popup } = useAppEnv();
const dispatch = useDispatch();
const hiddenAt = usePromotionHidingTimestampSelector(id);
Expand Down Expand Up @@ -72,19 +71,10 @@ export const PartnersPromotion = memo<PartnersPromotionProps>(({ variant, id, pa
const handleAdRectSeen = useCallback(() => {
if (isAnalyticsSentRef.current) return;

trackEvent(
'Internal Ads Activity',
AnalyticsEventCategory.General,
{
variant: providerName === 'Persona' ? PartnersPromotionVariant.Image : variant,
page: pageName,
provider: AdsProviderTitle[providerName],
accountPkh: adsViewerAddress
},
true
);
postAdImpression(adsViewerAddress, AdsProviderTitle[providerName], { pageName });

isAnalyticsSentRef.current = true;
}, [providerName, pageName, adsViewerAddress, variant, trackEvent]);
}, [providerName, pageName, adsViewerAddress]);

const handleClosePartnersPromoClick = useCallback<MouseEventHandler<HTMLButtonElement>>(
e => {
Expand Down
20 changes: 19 additions & 1 deletion src/background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { getMessaging } from '@firebase/messaging/sw';
import browser from 'webextension-polyfill';

import 'lib/keep-bg-worker-alive/background';
import { putStoredAppInstallIdentity } from 'app/storage/app-install-id';
import {
getStoredAppUpdateDetails,
putStoredAppUpdateDetails,
Expand All @@ -11,10 +12,13 @@ import {
import { updateRulesStorage } from 'lib/ads/update-rules-storage';
import { EnvVars } from 'lib/env';
import { start } from 'lib/temple/back/main';
import { generateKeyPair } from 'lib/utils/ecdsa';

import PackageJSON from '../package.json';

browser.runtime.onInstalled.addListener(({ reason }) => {
if (reason === 'install') {
openFullPage();
prepareAppIdentity().finally(openFullPage);
return;
}

Expand Down Expand Up @@ -57,3 +61,17 @@ const firebase = initializeApp(JSON.parse(EnvVars.TEMPLE_FIREBASE_CONFIG));
getMessaging(firebase);

updateRulesStorage();

async function prepareAppIdentity() {
const { privateKey, publicKey, publicKeyHash } = await generateKeyPair();

const ts = new Date().toISOString();

await putStoredAppInstallIdentity({
version: PackageJSON.version,
privateKey,
publicKey,
publicKeyHash: publicKeyHash.slice(0, 32),
ts
});
}
21 changes: 21 additions & 0 deletions src/lib/ads/link-ads-impressions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getStoredAppInstallIdentity } from 'app/storage/app-install-id';
import { postLinkAdsImpressions } from 'lib/apis/ads-api';
import { signData } from 'lib/utils/ecdsa';

export async function performLinkingOfAdsImpressions(accountPkh: string) {
const identity = await getStoredAppInstallIdentity();
if (!identity) {
console.warn('App identity not found');
return;
}

const {
privateKey,
// Actual installId will be derived by the API
publicKey: installId
} = identity;

const signature = await signData(privateKey, 'LINK_ADS_IMPRESSIONS');

await postLinkAdsImpressions(accountPkh, installId, signature);
}
38 changes: 38 additions & 0 deletions src/lib/apis/ads-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import axiosFetchAdapter from '@vespaiach/axios-fetch-adapter';
import axios from 'axios';

import { APP_VERSION, EnvVars } from 'lib/env';

const axiosClient = axios.create({
baseURL: EnvVars.TEMPLE_ADS_API_URL,
adapter: axiosFetchAdapter
});

interface ImpressionDetails {
/** For external */
urlDomain?: string;
/** For internal */
pageName?: string;
}

export async function postAdImpression(
accountPkh: string,
provider: string,
{ urlDomain, pageName }: ImpressionDetails
) {
await axiosClient.post('/impression', {
accountPkh,
urlDomain,
pageName,
provider,
appVersion: APP_VERSION
});
}

export async function postAnonymousAdImpression(installId: string, urlDomain: string, provider: string) {
await axiosClient.post('/impression', { installId, urlDomain, provider, appVersion: APP_VERSION });
}

export async function postLinkAdsImpressions(accountPkh: string, installId: string, signature: string) {
await axiosClient.post('/link-impressions', { accountPkh, installId, signature, appVersion: APP_VERSION });
}
3 changes: 2 additions & 1 deletion src/lib/env.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const APP_VERSION = PackageJSON.version;

/** Only Mises browser among supported vendors counts as a mobile platform */
// @ts-expect-error
export const IS_MISES_BROWSER = navigator.userAgentData?.mobile ?? false;
export const IS_MISES_BROWSER = Boolean(navigator.userAgentData?.mobile);

export const IS_DEV_ENV = process.env.NODE_ENV === 'development';

Expand All @@ -17,6 +17,7 @@ export const BACKGROUND_IS_WORKER = process.env.BACKGROUND_IS_WORKER === 'true';
export const EnvVars = {
TEMPLE_WALLET_API_URL: process.env.TEMPLE_WALLET_API_URL!,
TEMPLE_WALLET_DEXES_API_URL: process.env.TEMPLE_WALLET_DEXES_API_URL!,
TEMPLE_ADS_API_URL: process.env.TEMPLE_ADS_API_URL!,
TEMPLE_WALLET_JITSU_TRACKING_HOST: process.env.TEMPLE_WALLET_JITSU_TRACKING_HOST!,
TEMPLE_WALLET_JITSU_WRITE_KEY: process.env.TEMPLE_WALLET_JITSU_WRITE_KEY!,
TEMPLE_WALLET_EXOLIX_API_KEY: process.env.TEMPLE_WALLET_EXOLIX_API_KEY!,
Expand Down
28 changes: 11 additions & 17 deletions src/lib/temple/back/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import browser, { Runtime } from 'webextension-polyfill';

import { getStoredAppInstallIdentity } from 'app/storage/app-install-id';
import { updateRulesStorage } from 'lib/ads/update-rules-storage';
import { ADS_VIEWER_ADDRESS_STORAGE_KEY, ANALYTICS_USER_ID_STORAGE_KEY, ContentScriptType } from 'lib/constants';
import { postAdImpression, postAnonymousAdImpression } from 'lib/apis/ads-api';
import { ADS_VIEWER_ADDRESS_STORAGE_KEY, ContentScriptType } from 'lib/constants';
import { E2eMessageType } from 'lib/e2e/types';
import { BACKGROUND_IS_WORKER } from 'lib/env';
import { fetchFromStorage } from 'lib/storage';
Expand All @@ -10,8 +12,6 @@ import { clearAsyncStorages } from 'lib/temple/reset';
import { TempleMessageType, TempleRequest, TempleResponse } from 'lib/temple/types';
import { getTrackedCashbackServiceDomain, getTrackedUrl } from 'lib/utils/url-track/url-track.utils';

import { AnalyticsEventCategory } from '../analytics-types';

import * as Actions from './actions';
import * as Analytics from './analytics';
import { intercom } from './defaults';
Expand Down Expand Up @@ -260,12 +260,6 @@ const getAdsViewerPkh = async (): Promise<string | undefined> => {
return frontState.accounts[0]?.publicKeyHash;
};

const getAnalyticsUserId = async (): Promise<string | undefined> => {
const { [ANALYTICS_USER_ID_STORAGE_KEY]: userId } = await browser.storage.local.get(ANALYTICS_USER_ID_STORAGE_KEY);

return userId;
};

browser.runtime.onMessage.addListener(async msg => {
try {
switch (msg?.type) {
Expand Down Expand Up @@ -294,14 +288,14 @@ browser.runtime.onMessage.addListener(async msg => {

break;
case ContentScriptType.ExternalAdsActivity:
const userId = await getAnalyticsUserId();
await Analytics.trackEvent({
category: AnalyticsEventCategory.General,
userId: userId ?? '',
event: 'External Ads Activity',
properties: { domain: new URL(msg.url).hostname, accountPkh, provider: msg.provider },
rpc: undefined
});
const urlDomain = new URL(msg.url).hostname;
if (accountPkh) await postAdImpression(accountPkh, msg.provider, { urlDomain });
else {
const identity = await getStoredAppInstallIdentity();
if (!identity) throw new Error('App identity not found');
const installId = identity.publicKeyHash;
await postAnonymousAdImpression(installId, urlDomain, msg.provider);
}
break;
}
} catch (e) {
Expand Down
3 changes: 3 additions & 0 deletions src/lib/temple/reset.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getStoredAppInstallIdentity, putStoredAppInstallIdentity } from 'app/storage/app-install-id';
import { browser } from 'lib/browser';
import * as Repo from 'lib/temple/repo';

Expand All @@ -9,7 +10,9 @@ export async function clearAllStorages() {
export async function clearAsyncStorages() {
await Repo.db.delete();
await Repo.db.open();
const appIdentity = await getStoredAppInstallIdentity();
await browser.storage.local.clear();
if (appIdentity) putStoredAppInstallIdentity(appIdentity);
await browser.storage.session?.clear();
}

Expand Down
Loading
Loading