diff --git a/packages/desktop/App.svelte b/packages/desktop/App.svelte index 2e93160630..826cdd3801 100644 --- a/packages/desktop/App.svelte +++ b/packages/desktop/App.svelte @@ -32,6 +32,7 @@ import { _ } from '@core/i18n' import { getAndUpdateShimmerEvmTokensMetadata } from '@core/market/actions' import { initializeWalletConnect } from '@auxiliary/wallet-connect/actions' + import { getAndUpdateCountryCode } from '@auxiliary/country/actions' $: $activeProfile, saveActiveProfile() @@ -50,6 +51,7 @@ let splash = true void setupI18n({ fallbackLocale: 'en', initialLocale: $appSettings.language }) + void getAndUpdateCountryCode() onMount(async () => { if (features.analytics.appStart.enabled) { diff --git a/packages/desktop/features/onboarding.features.ts b/packages/desktop/features/onboarding.features.ts index 8ff9b0b705..e4611e3145 100644 --- a/packages/desktop/features/onboarding.features.ts +++ b/packages/desktop/features/onboarding.features.ts @@ -42,7 +42,7 @@ const onboardingFeaturesForIota: IOnboardingFeaturesForNetwork = { }, }, defaultIscChains: { - enabled: false, + enabled: true, }, defaultEvmChains: { enabled: false, diff --git a/packages/desktop/package.json b/packages/desktop/package.json index 78f93a0720..fb922eb060 100644 --- a/packages/desktop/package.json +++ b/packages/desktop/package.json @@ -1,7 +1,7 @@ { "name": "desktop", "productName": "Bloom", - "version": "1.0.3", + "version": "1.0.4", "description": "Simple and secure web3 wallet for the IOTA and Shimmer ecosystem", "main": "public/build/main.process.js", "repository": "git@github.com:bloomwalletio/bloom.git", diff --git a/packages/desktop/views/dashboard/components/DashboardSidebar.svelte b/packages/desktop/views/dashboard/components/DashboardSidebar.svelte index 5b039ffc1f..146e879e3b 100644 --- a/packages/desktop/views/dashboard/components/DashboardSidebar.svelte +++ b/packages/desktop/views/dashboard/components/DashboardSidebar.svelte @@ -16,12 +16,12 @@ } from '@core/router' import { isDashboardSideBarExpanded } from '@core/ui' import { IDashboardSidebarTab } from '@desktop/routers' - import features from '@features/features' import { Logo } from '@ui' import { campaignsRouter } from '../campaigns' import LedgerStatusTile from './LedgerStatusTile.svelte' import StrongholdStatusTile from './StrongholdStatusTile.svelte' import { BackupToast, VersionToast } from './toasts' + import { isFeatureEnabled, isFeatureNotGeoFenced } from '@lib/features/utils' let expanded = true function toggleExpand(): void { @@ -38,7 +38,7 @@ route: DashboardRoute.Wallet, onClick: openWallet, }, - ...(features?.collectibles?.enabled && profileFeatures?.collectibles + ...(isFeatureEnabled(DashboardRoute.Collectibles) && profileFeatures?.collectibles ? [ { icon: IconName.Image, @@ -48,7 +48,7 @@ }, ] : []), - ...(features?.governance?.enabled && profileFeatures?.governance + ...(isFeatureEnabled(DashboardRoute.Governance) && profileFeatures?.governance ? [ { icon: IconName.Bank, @@ -58,7 +58,7 @@ }, ] : []), - ...(features?.campaigns?.enabled && profileFeatures?.campaigns + ...(isFeatureEnabled(DashboardRoute.Campaigns) && profileFeatures?.campaigns ? [ { icon: IconName.Trophy, @@ -68,7 +68,9 @@ }, ] : []), - ...(features?.buySell?.enabled && profileFeatures?.buySell + ...(isFeatureEnabled(DashboardRoute.BuySell) && + isFeatureNotGeoFenced(DashboardRoute.BuySell) && + profileFeatures?.buySell ? [ { icon: IconName.ArrowDownUp, @@ -83,7 +85,7 @@ }, ] : []), - ...(features?.developerTools?.enabled && profileFeatures?.developer + ...(isFeatureEnabled('developerTools') && profileFeatures?.developer ? [ { icon: IconName.Developer, diff --git a/packages/desktop/views/settings/views/advanced/ToggleFeatures.svelte b/packages/desktop/views/settings/views/advanced/ToggleFeatures.svelte index c17f2dee8c..2e7a3415bf 100644 --- a/packages/desktop/views/settings/views/advanced/ToggleFeatures.svelte +++ b/packages/desktop/views/settings/views/advanced/ToggleFeatures.svelte @@ -4,6 +4,7 @@ import { activeProfile, updateActiveProfile } from '@core/profile/stores' import { DashboardRoute } from '@core/router' import SettingsSection from '../SettingsSection.svelte' + import { isFeatureNotGeoFenced, isFeatureNotGeoFenced } from '@lib/features/utils' const features = $activeProfile?.features ?? { [DashboardRoute.Wallet]: true, @@ -47,12 +48,14 @@ label={localize('tabs.campaigns')} bind:checked={features[DashboardRoute.Campaigns]} /> - + {#if isFeatureNotGeoFenced(DashboardRoute.BuySell)} + + {/if}
{ + const api = new CountryApi() + const countryCode = await api.getCountryCode() + updateCountryCode(countryCode) +} diff --git a/packages/shared/src/lib/auxiliary/country/actions/index.ts b/packages/shared/src/lib/auxiliary/country/actions/index.ts new file mode 100644 index 0000000000..a1b5ecf498 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/country/actions/index.ts @@ -0,0 +1 @@ +export * from './getAndUpdateCountryCode' diff --git a/packages/shared/src/lib/auxiliary/country/api/country.api.ts b/packages/shared/src/lib/auxiliary/country/api/country.api.ts new file mode 100644 index 0000000000..aa159cb0ca --- /dev/null +++ b/packages/shared/src/lib/auxiliary/country/api/country.api.ts @@ -0,0 +1,42 @@ +import { BaseApi } from '@core/utils' + +interface IIpApiResponse { + ip: string + network: string + version: string + city: string + region: string + region_code: string + country: string + country_name: string + country_code: string + country_code_iso3: string + country_capital: string + country_tld: string + continent_code: string + in_eu: boolean + postal: string + latitude: number + longitude: number + timezone: string + utc_offset: string + country_calling_code: string + currency: string + currency_name: string + languages: string + country_area: number + country_population: number + asn: string + org: string +} + +export class CountryApi extends BaseApi { + constructor() { + super('https://ipapi.co') + } + + async getCountryCode(): Promise { + const ipData = await this.get('json') + return ipData?.country_code + } +} diff --git a/packages/shared/src/lib/auxiliary/country/api/index.ts b/packages/shared/src/lib/auxiliary/country/api/index.ts new file mode 100644 index 0000000000..836019fd1c --- /dev/null +++ b/packages/shared/src/lib/auxiliary/country/api/index.ts @@ -0,0 +1 @@ +export * from './country.api' diff --git a/packages/shared/src/lib/auxiliary/country/index.ts b/packages/shared/src/lib/auxiliary/country/index.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/shared/src/lib/auxiliary/country/stores/country-code.store.ts b/packages/shared/src/lib/auxiliary/country/stores/country-code.store.ts new file mode 100644 index 0000000000..c40c032b26 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/country/stores/country-code.store.ts @@ -0,0 +1,9 @@ +import { writable } from 'svelte/store' + +export const countryCode = writable(undefined) + +export function updateCountryCode(_countryCode: string | undefined): void { + if (_countryCode) { + countryCode?.set(_countryCode) + } +} diff --git a/packages/shared/src/lib/auxiliary/country/stores/index.ts b/packages/shared/src/lib/auxiliary/country/stores/index.ts new file mode 100644 index 0000000000..2cdc6e5d88 --- /dev/null +++ b/packages/shared/src/lib/auxiliary/country/stores/index.ts @@ -0,0 +1 @@ +export * from './country-code.store' diff --git a/packages/shared/src/lib/core/app/interfaces/app-parameters.interface.ts b/packages/shared/src/lib/core/app/interfaces/app-parameters.interface.ts index 85177303a1..56d74cbab0 100644 --- a/packages/shared/src/lib/core/app/interfaces/app-parameters.interface.ts +++ b/packages/shared/src/lib/core/app/interfaces/app-parameters.interface.ts @@ -6,4 +6,7 @@ export interface IAppParameters { allowlists: { urls: string[] } + geoFence: { + buySell: string[] + } } diff --git a/packages/shared/src/lib/core/app/json/configurable-app-parameters.json b/packages/shared/src/lib/core/app/json/configurable-app-parameters.json index 6fc1f916d0..dd557bc87d 100644 --- a/packages/shared/src/lib/core/app/json/configurable-app-parameters.json +++ b/packages/shared/src/lib/core/app/json/configurable-app-parameters.json @@ -5,5 +5,8 @@ }, "allowlists": { "urls": ["https://tideprotocol.infura-ipfs.io"] + }, + "geoFence": { + "buySell": ["GB"] } } diff --git a/packages/shared/src/lib/core/profile/constants/profile-version.constant.ts b/packages/shared/src/lib/core/profile/constants/profile-version.constant.ts index daa1c6ad40..6276523770 100644 --- a/packages/shared/src/lib/core/profile/constants/profile-version.constant.ts +++ b/packages/shared/src/lib/core/profile/constants/profile-version.constant.ts @@ -1,7 +1,7 @@ import { AppStage } from '@core/app/enums' export const PROFILE_VERSION: Record = { - [AppStage.ALPHA]: 20, + [AppStage.ALPHA]: 21, [AppStage.BETA]: 1, [AppStage.PROD]: 11, } diff --git a/packages/shared/src/lib/core/profile/migrations/actions/addDefaultIscChainsToIotaProfiles.ts b/packages/shared/src/lib/core/profile/migrations/actions/addDefaultIscChainsToIotaProfiles.ts new file mode 100644 index 0000000000..1e071ed41d --- /dev/null +++ b/packages/shared/src/lib/core/profile/migrations/actions/addDefaultIscChainsToIotaProfiles.ts @@ -0,0 +1,15 @@ +import { DEFAULT_ISC_CHAINS_CONFIGURATIONS, SupportedStardustNetworkId } from '@core/network' +import { IPersistedProfile } from '@core/profile/interfaces' + +export function addDefaultIscChainsToIotaProfiles(profile: IPersistedProfile): void { + if (profile.network.id !== SupportedStardustNetworkId.Iota) { + return + } + + const chainConfiguration = DEFAULT_ISC_CHAINS_CONFIGURATIONS[SupportedStardustNetworkId.Iota] + + profile.network = { + ...profile.network, + chainConfigurations: chainConfiguration ? [chainConfiguration] : [], + } +} diff --git a/packages/shared/src/lib/core/profile/migrations/alpha/alpha-profile-migration-19-to-20.ts b/packages/shared/src/lib/core/profile/migrations/alpha/alpha-profile-migration-19-to-20.ts index eb60ffe498..f02cc5c5a8 100644 --- a/packages/shared/src/lib/core/profile/migrations/alpha/alpha-profile-migration-19-to-20.ts +++ b/packages/shared/src/lib/core/profile/migrations/alpha/alpha-profile-migration-19-to-20.ts @@ -1,10 +1,10 @@ -import { IPersistedProfile } from '../../interfaces' -import { migrateEvmContacts } from '../../migrations/actions/migrateEvmContacts' +import { IPersistedProfile } from '@core/profile/interfaces' +import { addDefaultIscChainsToIotaProfiles } from '../actions/addDefaultIscChainsToIotaProfiles' export function alphaProfileMigration19To20(existingProfile: unknown): Promise { const profile = existingProfile as IPersistedProfile - migrateEvmContacts(profile) + addDefaultIscChainsToIotaProfiles(profile) return Promise.resolve() } diff --git a/packages/shared/src/lib/core/profile/migrations/alpha/alpha-profile-migration-20-to-21.ts b/packages/shared/src/lib/core/profile/migrations/alpha/alpha-profile-migration-20-to-21.ts new file mode 100644 index 0000000000..973c9ed653 --- /dev/null +++ b/packages/shared/src/lib/core/profile/migrations/alpha/alpha-profile-migration-20-to-21.ts @@ -0,0 +1,10 @@ +import { IPersistedProfile } from '@core/profile/interfaces' +import { migrateEvmContacts } from '../../migrations/actions/migrateEvmContacts' + +export function alphaProfileMigration20To21(existingProfile: unknown): Promise { + const profile = existingProfile as IPersistedProfile + + migrateEvmContacts(profile) + + return Promise.resolve() +} diff --git a/packages/shared/src/lib/core/profile/migrations/alpha/alpha-profile-migration-map.ts b/packages/shared/src/lib/core/profile/migrations/alpha/alpha-profile-migration-map.ts index 97b22be1df..2ef5dc7dff 100644 --- a/packages/shared/src/lib/core/profile/migrations/alpha/alpha-profile-migration-map.ts +++ b/packages/shared/src/lib/core/profile/migrations/alpha/alpha-profile-migration-map.ts @@ -12,6 +12,7 @@ import { alphaProfileMigration17To18 } from './alpha-profile-migration-17-to-18' import { alphaProfileMigration18To19 } from './alpha-profile-migration-18-to-19' import { alphaProfileMigration19To20 } from './alpha-profile-migration-19-to-20' import { alphaProfileMigration2To3 } from './alpha-profile-migration-2-to-3' +import { alphaProfileMigration20To21 } from './alpha-profile-migration-20-to-21' import { alphaProfileMigration3To4 } from './alpha-profile-migration-3-to-4' import { alphaProfileMigration4To5 } from './alpha-profile-migration-4-to-5' import { alphaProfileMigration5To6 } from './alpha-profile-migration-5-to-6' @@ -43,4 +44,6 @@ export const ALPHA_PROFILE_MIGRATION_MAP: ProfileMigrationMap = { 18: alphaProfileMigration18To19, // ^^^ release 1.0.3 ^^^ 19: alphaProfileMigration19To20, + // ^^^ release 1.0.4 ^^^ + 20: alphaProfileMigration20To21, } diff --git a/packages/shared/src/lib/core/profile/migrations/prod/prod-profile-migration-9-to-10.ts b/packages/shared/src/lib/core/profile/migrations/prod/prod-profile-migration-9-to-10.ts index cd00960248..c57bc648b8 100644 --- a/packages/shared/src/lib/core/profile/migrations/prod/prod-profile-migration-9-to-10.ts +++ b/packages/shared/src/lib/core/profile/migrations/prod/prod-profile-migration-9-to-10.ts @@ -6,6 +6,7 @@ import { prodProfileMigration6To7 } from './prod-profile-migration-6-to-7' import { prodProfileMigration7To8 } from './prod-profile-migration-7-to-8' import { prodProfileMigration4To5 } from './prod-profile-migration-4-to-5' import { prodProfileMigration8To9 } from './prod-profile-migration-8-to-9' +import { addDefaultIscChainsToIotaProfiles } from '../actions/addDefaultIscChainsToIotaProfiles' export async function prodProfileMigration9To10(existingProfile: unknown): Promise { const profile = existingProfile as IPersistedProfile @@ -37,5 +38,7 @@ export async function prodProfileMigration9To10(existingProfile: unknown): Promi // prodProfileMigration8To9 recovery await prodProfileMigration8To9(profile) + addDefaultIscChainsToIotaProfiles(profile) + return Promise.resolve() } diff --git a/packages/shared/src/lib/core/profile/migrations/prod/prod-profile-migration-map.ts b/packages/shared/src/lib/core/profile/migrations/prod/prod-profile-migration-map.ts index 4313a0cdd1..ce1e78cff4 100644 --- a/packages/shared/src/lib/core/profile/migrations/prod/prod-profile-migration-map.ts +++ b/packages/shared/src/lib/core/profile/migrations/prod/prod-profile-migration-map.ts @@ -22,8 +22,9 @@ export const PROD_PROFILE_MIGRATION_MAP: ProfileMigrationMap = { 6: prodProfileMigration6To7, 7: prodProfileMigration7To8, // ^^^ release 1.0.2 ^^^ - 8: prodProfileMigration8To9, // Migration was removed and replaced with 10To11 after 9To10 + 8: prodProfileMigration8To9, // ^^^ release 1.0.3 ^^^ 9: prodProfileMigration9To10, // rechecking some previous migrations due to a bug during migrations 10: prodProfileMigration10To11, + // ^^^ release 1.0.4 ^^^ } diff --git a/packages/shared/src/lib/features/utils/index.ts b/packages/shared/src/lib/features/utils/index.ts index 29563dad5d..10cb189989 100644 --- a/packages/shared/src/lib/features/utils/index.ts +++ b/packages/shared/src/lib/features/utils/index.ts @@ -1 +1,2 @@ export * from './isFeatureEnabled' +export * from './isFeatureGeoFenced' diff --git a/packages/shared/src/lib/features/utils/isFeatureGeoFenced.ts b/packages/shared/src/lib/features/utils/isFeatureGeoFenced.ts new file mode 100644 index 0000000000..c00d640ad0 --- /dev/null +++ b/packages/shared/src/lib/features/utils/isFeatureGeoFenced.ts @@ -0,0 +1,21 @@ +import { countryCode } from '@auxiliary/country/stores' +import { appParameters } from '@core/app/stores' +import { get } from 'svelte/store' + +export function isFeatureNotGeoFenced(featureString: string): boolean { + const currentCountryCode = get(countryCode) + const featurePathToCheck = featureString.split('.') + const geoFence = get(appParameters)?.geoFence + let previousFeatures = geoFence + for (let i = 0; i < featurePathToCheck.length; i++) { + const currentFeature = previousFeatures?.[featurePathToCheck[i]] + if (!currentFeature) { + return true + } + if (i === featurePathToCheck.length - 1) { + return !currentFeature?.some((blockedCountry) => blockedCountry === currentCountryCode) + } + previousFeatures = currentFeature + } + return true +}