Skip to content

Commit 630f6cf

Browse files
save address in project services, set as default from server wallets table
1 parent b8b70c7 commit 630f6cf

File tree

10 files changed

+215
-136
lines changed

10 files changed

+215
-136
lines changed

apps/dashboard/src/@/components/project/create-project-modal/index.tsx

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,9 @@ import { Spinner } from "@/components/ui/Spinner";
3636
import { Textarea } from "@/components/ui/textarea";
3737
import { createProjectClient } from "@/hooks/useApi";
3838
import { useDashboardRouter } from "@/lib/DashboardRouter";
39-
import { getProjectWalletLabel } from "@/lib/project-wallet";
4039
import { projectDomainsSchema, projectNameSchema } from "@/schema/validations";
4140
import { toArrFromList } from "@/utils/string";
42-
import {
43-
createProjectServerWallet,
44-
createVaultAccountAndAccessToken,
45-
} from "../../../../app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/lib/vault.client";
41+
import { createVaultAccountAndAccessToken } from "../../../../app/(app)/team/[team_slug]/[project_slug]/(sidebar)/transactions/lib/vault.client";
4642

4743
const ALL_PROJECT_SERVICES = SERVICES.filter(
4844
(srv) => srv.name !== "relayer" && srv.name !== "chainsaw",
@@ -85,15 +81,6 @@ const CreateProjectDialog = (props: CreateProjectDialogProps) => {
8581
throw new Error("Missing management access token for project wallet");
8682
}
8783

88-
await createProjectServerWallet({
89-
label: getProjectWalletLabel(res.project.name),
90-
managementAccessToken,
91-
project: res.project,
92-
}).catch((error) => {
93-
console.error("Failed to create default project wallet", error);
94-
throw error;
95-
});
96-
9784
return {
9885
project: res.project,
9986
secret: res.secret,

apps/dashboard/src/@/lib/server/project-wallet.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,13 @@ export async function getProjectWallet(
3131

3232
const managementAccessToken =
3333
engineCloudService?.managementAccessToken || undefined;
34+
const projectWalletAddress = engineCloudService?.projectWalletAddress;
3435

35-
if (!managementAccessToken || !NEXT_PUBLIC_THIRDWEB_VAULT_URL) {
36+
if (
37+
!managementAccessToken ||
38+
!NEXT_PUBLIC_THIRDWEB_VAULT_URL ||
39+
!projectWalletAddress
40+
) {
3641
return undefined;
3742
}
3843

@@ -50,7 +55,7 @@ export async function getProjectWallet(
5055
options: {
5156
page: 0,
5257
// @ts-expect-error - SDK expects snake_case for pagination arguments
53-
page_size: 25,
58+
page_size: 100,
5459
},
5560
},
5661
});
@@ -71,10 +76,10 @@ export async function getProjectWallet(
7176
(item) => item.metadata?.projectId === project.id,
7277
);
7378

74-
const defaultWallet =
75-
serverWallets.find((item) => item.metadata?.label === expectedLabel) ??
76-
serverWallets.find((item) => item.metadata?.type === "server-wallet") ??
77-
serverWallets[0];
79+
const defaultWallet = serverWallets.find(
80+
(item) =>
81+
item.address.toLowerCase() === projectWalletAddress.toLowerCase(),
82+
);
7883

7984
if (!defaultWallet) {
8085
return undefined;

apps/dashboard/src/app/(app)/get-started/team/[team_slug]/create-project/_components/create-project-form.tsx

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,9 @@ import { Spinner } from "@/components/ui/Spinner";
3131
import { Textarea } from "@/components/ui/textarea";
3232
import { createProjectClient } from "@/hooks/useApi";
3333
import { useDashboardRouter } from "@/lib/DashboardRouter";
34-
import { getProjectWalletLabel } from "@/lib/project-wallet";
3534
import { projectDomainsSchema, projectNameSchema } from "@/schema/validations";
3635
import { toArrFromList } from "@/utils/string";
37-
import {
38-
createProjectServerWallet,
39-
createVaultAccountAndAccessToken,
40-
} from "../../../../../team/[team_slug]/[project_slug]/(sidebar)/transactions/lib/vault.client";
36+
import { createVaultAccountAndAccessToken } from "../../../../../team/[team_slug]/[project_slug]/(sidebar)/transactions/lib/vault.client";
4137

4238
const ALL_PROJECT_SERVICES = SERVICES.filter(
4339
(srv) => srv.name !== "relayer" && srv.name !== "chainsaw",
@@ -79,14 +75,6 @@ export function CreateProjectFormOnboarding(props: {
7975
);
8076
}
8177

82-
await createProjectServerWallet({
83-
label: getProjectWalletLabel(res.project.name),
84-
managementAccessToken,
85-
project: res.project,
86-
}).catch((error) => {
87-
console.error("Failed to create default project wallet", error);
88-
throw error;
89-
});
9078
return {
9179
project: res.project,
9280
secret: res.secret,

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectFTUX.tsx

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
import Link from "next/link";
88
import type { Project } from "@/api/project/projects";
99
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
10+
import { AlertDialogFooter } from "@/components/ui/alert-dialog";
1011
import { CodeServer } from "@/components/ui/code/code.server";
1112
import { UnderlineLink } from "@/components/ui/UnderlineLink";
1213
import { DotNetIcon } from "@/icons/brand-icons/DotNetIcon";
@@ -24,6 +25,7 @@ import {
2425
getProjectWallet,
2526
type ProjectWalletSummary,
2627
} from "@/lib/server/project-wallet";
28+
import CreateServerWallet from "../../transactions/server-wallets/components/create-server-wallet.client";
2729
import { ClientIDSection } from "./ClientIDSection";
2830
import { IntegrateAPIKeyCodeTabs } from "./IntegrateAPIKeyCodeTabs";
2931
import { ProjectWalletControls } from "./ProjectWalletControls.client";
@@ -33,6 +35,7 @@ export async function ProjectFTUX(props: {
3335
project: Project;
3436
teamSlug: string;
3537
wallet?: ProjectWalletSummary | undefined;
38+
managementAccessToken: string | undefined;
3639
}) {
3740
const projectWallet = props.wallet ?? (await getProjectWallet(props.project));
3841

@@ -42,6 +45,7 @@ export async function ProjectFTUX(props: {
4245
project={props.project}
4346
teamSlug={props.teamSlug}
4447
wallet={projectWallet}
48+
managementAccessToken={props.managementAccessToken}
4549
/>
4650
<IntegrateAPIKeySection
4751
project={props.project}
@@ -61,67 +65,71 @@ export function ProjectWalletSection(props: {
6165
project: Project;
6266
teamSlug: string;
6367
wallet: ProjectWalletSummary | undefined;
68+
managementAccessToken: string | undefined;
6469
}) {
6570
const defaultLabel = getProjectWalletLabel(props.project.name);
6671
const walletAddress = props.wallet?.address;
6772
const label = props.wallet?.label ?? defaultLabel;
6873

6974
return (
7075
<section>
71-
<h2 className="mb-3 font-semibold text-xl tracking-tight">
72-
Project Wallet
73-
</h2>
74-
7576
<div className="rounded-lg border border-border bg-card p-4">
7677
<div className="flex flex-col gap-4">
7778
<div className="flex items-center gap-3">
7879
<div className="rounded-full border border-border bg-background p-2">
7980
<WalletProductIcon className="size-5 text-muted-foreground" />
8081
</div>
8182
<div>
82-
<p className="font-semibold text-lg tracking-tight">{label}</p>
83+
<p className="font-semibold text-lg tracking-tight">
84+
Project Wallet
85+
</p>
8386
<p className="text-muted-foreground text-sm">
8487
Default managed server wallet for this project. Use it for
85-
deployments, payments, and other automated flows.
88+
deployments, payments, and API integrations.
8689
</p>
8790
</div>
8891
</div>
8992

9093
{walletAddress ? (
91-
<ProjectWalletControls
92-
label={label}
93-
project={{
94-
id: props.project.id,
95-
publishableKey: props.project.publishableKey,
96-
services: props.project.services,
97-
teamId: props.project.teamId,
98-
}}
99-
walletAddress={walletAddress}
100-
/>
94+
<>
95+
<ProjectWalletControls
96+
label={label}
97+
project={{
98+
id: props.project.id,
99+
publishableKey: props.project.publishableKey,
100+
services: props.project.services,
101+
teamId: props.project.teamId,
102+
}}
103+
walletAddress={walletAddress}
104+
/>
105+
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-end">
106+
<Link
107+
className="inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline"
108+
href={`/team/${props.teamSlug}/${props.project.slug}/transactions`}
109+
>
110+
View Transactions
111+
<ChevronRightIcon className="size-4" />
112+
</Link>
113+
</div>
114+
</>
101115
) : (
102116
<Alert variant="info">
103117
<CircleAlertIcon className="size-5" />
104-
<AlertTitle>Project wallet unavailable</AlertTitle>
118+
<AlertTitle>No default project wallet set</AlertTitle>
105119
<AlertDescription>
106-
We could not load the default wallet for this project. Visit the
107-
Transactions page to create or refresh your server wallets.
120+
Set a default project wallet to use for dashboard and API
121+
integrations.
108122
</AlertDescription>
123+
<AlertDialogFooter className="flex justify-start sm:justify-start pt-4">
124+
<CreateServerWallet
125+
managementAccessToken={props.managementAccessToken}
126+
project={props.project}
127+
teamSlug={props.teamSlug}
128+
setAsProjectWallet={true}
129+
/>
130+
</AlertDialogFooter>
109131
</Alert>
110132
)}
111-
112-
<div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
113-
<p className="text-muted-foreground text-sm">
114-
Manage balances, gas sponsorship, and smart account settings in
115-
Transactions.
116-
</p>
117-
<Link
118-
className="inline-flex items-center gap-1 text-sm font-medium text-primary hover:underline"
119-
href={`/team/${props.teamSlug}/${props.project.slug}/transactions`}
120-
>
121-
Open Transactions
122-
<ChevronRightIcon className="size-4" />
123-
</Link>
124-
</div>
125133
</div>
126134
</div>
127135
</section>

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/(sidebar)/components/ProjectFTUX/ProjectWalletControls.client.tsx

Lines changed: 54 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import { zodResolver } from "@hookform/resolvers/zod";
44
import { useMutation } from "@tanstack/react-query";
55
import {
6-
CopyIcon,
76
EllipsisVerticalIcon,
87
RefreshCcwIcon,
98
SendIcon,
@@ -20,6 +19,7 @@ import { sendProjectWalletTokens } from "@/actions/project-wallet/send-tokens";
2019
import type { Project } from "@/api/project/projects";
2120
import { FundWalletModal } from "@/components/blocks/fund-wallets-modal";
2221
import { SingleNetworkSelector } from "@/components/blocks/NetworkSelectors";
22+
import { WalletAddress } from "@/components/blocks/wallet-address";
2323
import { Button } from "@/components/ui/button";
2424
import {
2525
Dialog,
@@ -32,7 +32,6 @@ import {
3232
DropdownMenu,
3333
DropdownMenuContent,
3434
DropdownMenuItem,
35-
DropdownMenuSeparator,
3635
DropdownMenuTrigger,
3736
} from "@/components/ui/dropdown-menu";
3837
import {
@@ -94,66 +93,28 @@ export function ProjectWalletControls(props: ProjectWalletControlsProps) {
9493

9594
return (
9695
<div className="space-y-5">
97-
<div className="rounded-lg border border-dashed border-border/60 bg-background p-3">
96+
<div className="rounded-lg border border-border bg-background p-3">
9897
<div className="flex flex-col gap-4">
99-
<div className="flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between">
100-
<div>
101-
<p className="text-muted-foreground text-xs uppercase">
102-
Wallet address
103-
</p>
104-
<p className="font-mono text-sm break-all">{walletAddress}</p>
98+
<div className="flex flex-col gap-3 lg:flex-row">
99+
<div className="flex-1">
100+
<p className="text-muted-foreground text-xs">Address</p>
101+
<WalletAddress address={walletAddress} client={client} />
102+
</div>
103+
<div className="flex-1">
104+
<p className="text-muted-foreground text-xs">Label</p>
105+
<p className="font-mono text-sm break-all py-2">{label}</p>
105106
</div>
106-
<DropdownMenu>
107-
<DropdownMenuTrigger asChild>
108-
<Button
109-
aria-label="Open wallet actions"
110-
className="h-9 w-9"
111-
size="icon"
112-
variant="outline"
113-
>
114-
<EllipsisVerticalIcon className="size-4" />
115-
</Button>
116-
</DropdownMenuTrigger>
117-
<DropdownMenuContent align="end" className="w-48 rounded-xl">
118-
<DropdownMenuItem
119-
className="flex items-center gap-2"
120-
onSelect={() => {
121-
void handleCopyAddress();
122-
}}
123-
>
124-
<CopyIcon className="size-4 text-muted-foreground" />
125-
Copy address
126-
</DropdownMenuItem>
127-
<DropdownMenuSeparator />
128-
<DropdownMenuItem
129-
className="flex items-center gap-2"
130-
onSelect={() => setIsSendOpen(true)}
131-
>
132-
<SendIcon className="size-4 text-muted-foreground" />
133-
Send funds
134-
</DropdownMenuItem>
135-
<DropdownMenuItem
136-
className="flex items-center gap-2"
137-
onSelect={() => setIsReceiveOpen(true)}
138-
>
139-
<WalletIcon className="size-4 text-muted-foreground" />
140-
Receive funds
141-
</DropdownMenuItem>
142-
</DropdownMenuContent>
143-
</DropdownMenu>
144107
</div>
145108

146109
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
147-
<div>
148-
<p className="text-xs font-medium uppercase text-muted-foreground">
149-
Balance
150-
</p>
151-
<div className="mt-1 flex items-center gap-2">
110+
<div className="flex-1 flex-col">
111+
<p className="text-xs text-muted-foreground pb-2">Balance</p>
112+
<div className="flex items-center gap-2">
152113
<WalletIcon className="size-4 text-muted-foreground" />
153114
{balanceQuery.isLoading ? (
154115
<Spinner className="size-4" />
155116
) : balanceDisplay ? (
156-
<span className="font-semibold text-lg tracking-tight">
117+
<span className="text-md tracking-tight">
157118
{balanceDisplay}
158119
</span>
159120
) : (
@@ -174,17 +135,48 @@ export function ProjectWalletControls(props: ProjectWalletControlsProps) {
174135
)}
175136
/>
176137
</Button>
138+
<DropdownMenu>
139+
<DropdownMenuTrigger asChild>
140+
<Button
141+
aria-label="Open wallet actions"
142+
className="h-9 w-9"
143+
size="icon"
144+
variant="outline"
145+
>
146+
<EllipsisVerticalIcon className="size-4" />
147+
</Button>
148+
</DropdownMenuTrigger>
149+
<DropdownMenuContent align="end" className="w-48 rounded-xl">
150+
<DropdownMenuItem
151+
className="flex items-center gap-2"
152+
onSelect={() => setIsSendOpen(true)}
153+
>
154+
<SendIcon className="size-4 text-muted-foreground" />
155+
Send funds
156+
</DropdownMenuItem>
157+
<DropdownMenuItem
158+
className="flex items-center gap-2"
159+
onSelect={() => setIsReceiveOpen(true)}
160+
>
161+
<WalletIcon className="size-4 text-muted-foreground" />
162+
Receive funds
163+
</DropdownMenuItem>
164+
</DropdownMenuContent>
165+
</DropdownMenu>
177166
</div>
178167
</div>
179-
<SingleNetworkSelector
180-
chainId={selectedChainId}
181-
className="w-full max-w-xs rounded-lg"
182-
client={client}
183-
disableDeprecated
184-
disableChainId
185-
onChange={setSelectedChainId}
186-
placeholder="Select network"
187-
/>
168+
<div className="flex-col flex-1">
169+
<p className="text-xs text-muted-foreground pb-2">Network</p>
170+
<SingleNetworkSelector
171+
chainId={selectedChainId}
172+
className="max-w-xs rounded-lg"
173+
client={client}
174+
disableDeprecated
175+
disableChainId
176+
onChange={setSelectedChainId}
177+
placeholder="Select network"
178+
/>
179+
</div>
188180
</div>
189181
</div>
190182
</div>

0 commit comments

Comments
 (0)