diff --git a/.changeset/bumpy-ducks-travel.md b/.changeset/bumpy-ducks-travel.md
new file mode 100644
index 00000000000..133979d00ee
--- /dev/null
+++ b/.changeset/bumpy-ducks-travel.md
@@ -0,0 +1,5 @@
+---
+"@thirdweb-dev/service-utils": patch
+---
+
+expose project.services.iaw.smsEnabledCountryISOs from api response
diff --git a/apps/dashboard/src/@/api/sms.ts b/apps/dashboard/src/@/api/sms.ts
new file mode 100644
index 00000000000..af97360dbbd
--- /dev/null
+++ b/apps/dashboard/src/@/api/sms.ts
@@ -0,0 +1,53 @@
+import { API_SERVER_URL, THIRDWEB_API_SECRET } from "../constants/env";
+
+export type SMSCountryTiers = {
+ tier1: string[];
+ tier2: string[];
+ tier3: string[];
+ tier4: string[];
+ tier5: string[];
+};
+
+export async function getSMSCountryTiers() {
+ if (!THIRDWEB_API_SECRET) {
+ throw new Error("API_SERVER_SECRET is not set");
+ }
+ const res = await fetch(`${API_SERVER_URL}/v1/sms/list-country-tiers`, {
+ headers: {
+ "Content-Type": "application/json",
+ "x-service-api-key": THIRDWEB_API_SECRET,
+ },
+ next: {
+ revalidate: 15 * 60, //15 minutes
+ },
+ });
+
+ if (!res.ok) {
+ console.error(
+ "Failed to fetch sms country tiers",
+ res.status,
+ res.statusText,
+ );
+ res.body?.cancel();
+ return {
+ tier1: [],
+ tier2: [],
+ tier3: [],
+ tier4: [],
+ tier5: [],
+ };
+ }
+
+ try {
+ return (await res.json()).data as SMSCountryTiers;
+ } catch (e) {
+ console.error("Failed to parse sms country tiers", e);
+ return {
+ tier1: [],
+ tier2: [],
+ tier3: [],
+ tier4: [],
+ tier5: [],
+ };
+ }
+}
diff --git a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/settings/page.tsx b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/settings/page.tsx
index 11968dcc72e..3d75c14b4cd 100644
--- a/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/settings/page.tsx
+++ b/apps/dashboard/src/app/team/[team_slug]/[project_slug]/connect/in-app-wallets/settings/page.tsx
@@ -1,4 +1,5 @@
import { getProject } from "@/api/projects";
+import { getSMSCountryTiers } from "@/api/sms";
import { getTeamBySlug } from "@/api/team";
import { redirect } from "next/navigation";
import { InAppWalletSettingsPage } from "../../../../../../../components/embedded-wallets/Configure";
@@ -9,9 +10,10 @@ export default async function Page(props: {
}) {
const { team_slug, project_slug } = await props.params;
- const [team, project] = await Promise.all([
+ const [team, project, smsCountryTiers] = await Promise.all([
getTeamBySlug(team_slug),
getProject(team_slug, project_slug),
+ getSMSCountryTiers(),
]);
if (!team) {
@@ -29,6 +31,7 @@ export default async function Page(props: {
trackingCategory="in-app-wallet-project-settings"
teamSlug={team_slug}
validTeamPlan={getValidTeamPlan(team)}
+ smsCountryTiers={smsCountryTiers}
/>
);
}
diff --git a/apps/dashboard/src/components/embedded-wallets/Configure/InAppWalletSettingsUI.stories.tsx b/apps/dashboard/src/components/embedded-wallets/Configure/InAppWalletSettingsUI.stories.tsx
index a09a1842846..f8f2c934f38 100644
--- a/apps/dashboard/src/components/embedded-wallets/Configure/InAppWalletSettingsUI.stories.tsx
+++ b/apps/dashboard/src/components/embedded-wallets/Configure/InAppWalletSettingsUI.stories.tsx
@@ -64,6 +64,14 @@ function Variants(props: {
isUpdating={false}
trackingCategory="foo"
updateApiKey={() => {}}
+ smsCountryTiers={{
+ // scaffold some countries to play around with the UI
+ tier1: ["US", "CA"],
+ tier2: ["GB", "AU", "NZ"],
+ tier3: ["FR", "DE", "ES", "IT"],
+ tier4: ["JP", "KR", "MX", "RU"],
+ tier5: ["BR", "AR", "CO", "CL", "PE", "VE", "SA"],
+ }}
/>
diff --git a/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx b/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx
index aa2ca66a4d2..cd35d7f432f 100644
--- a/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx
+++ b/apps/dashboard/src/components/embedded-wallets/Configure/index.tsx
@@ -1,6 +1,7 @@
"use client";
import type { Project } from "@/api/projects";
+import type { SMSCountryTiers } from "@/api/sms";
import { DynamicHeight } from "@/components/ui/DynamicHeight";
import { Spinner } from "@/components/ui/Spinner/Spinner";
import { UnderlineLink } from "@/components/ui/UnderlineLink";
@@ -36,6 +37,7 @@ import { type UseFormReturn, useFieldArray, useForm } from "react-hook-form";
import { toast } from "sonner";
import { toArrFromList } from "utils/string";
import type { Team } from "../../../@/api/team";
+import CountrySelector from "./sms-country-select/country-selector";
type InAppWalletSettingsPageProps = {
trackingCategory: string;
@@ -43,6 +45,7 @@ type InAppWalletSettingsPageProps = {
teamId: string;
teamSlug: string;
validTeamPlan: Team["billingPlan"];
+ smsCountryTiers: SMSCountryTiers;
};
const TRACKING_CATEGORY = "embedded-wallet";
@@ -108,6 +111,7 @@ export function InAppWalletSettingsPage(props: InAppWalletSettingsPageProps) {
canEditAdvancedFeatures={props.validTeamPlan !== "free"}
updateApiKey={handleUpdateProject}
isUpdating={updateProject.isPending}
+ smsCountryTiers={props.smsCountryTiers}
/>
);
}
@@ -120,6 +124,7 @@ const InAppWalletSettingsPageUI: React.FC<
trackingData: UpdateAPIKeyTrackingData,
) => void;
isUpdating: boolean;
+ smsCountryTiers: SMSCountryTiers;
}
> = (props) => {
const embeddedWalletService = props.project.services.find(
@@ -185,6 +190,11 @@ export const InAppWalletSettingsUI: React.FC<
}
: undefined),
redirectUrls: (config.redirectUrls || []).join("\n"),
+ smsEnabledCountryISOs: config.smsEnabledCountryISOs
+ ? config.smsEnabledCountryISOs
+ : canEditAdvancedFeatures
+ ? ["US", "CA"]
+ : [],
},
});
@@ -228,6 +238,7 @@ export const InAppWalletSettingsUI: React.FC<
applicationImageUrl: branding?.applicationImageUrl,
applicationName: branding?.applicationName || props.project.name,
redirectUrls: toArrFromList(redirectUrls || "", true),
+ smsEnabledCountryISOs: values.smsEnabledCountryISOs,
};
});
@@ -256,6 +267,8 @@ export const InAppWalletSettingsUI: React.FC<
canEditAdvancedFeatures={canEditAdvancedFeatures}
/>
+
+
{/* Authentication */}
-
+
+
+
+