Skip to content

Commit 1926e9e

Browse files
committed
Add project wallet controls with send/receive actions
Introduces ProjectWalletControls component to manage project wallet actions, including copying the address, sending, and receiving funds. Adds a modal for sending tokens with form validation and credential caching. Refactors ProjectFTUX to use the new controls and implements the sendProjectWalletTokens server action.
1 parent 945ac3d commit 1926e9e

File tree

3 files changed

+680
-19
lines changed

3 files changed

+680
-19
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"use server";
2+
3+
import { apiServerProxy } from "@/actions/proxies";
4+
5+
export async function sendProjectWalletTokens(options: {
6+
walletAddress: string;
7+
recipientAddress: string;
8+
chainId: number;
9+
quantityWei: string;
10+
publishableKey: string;
11+
teamId: string;
12+
tokenAddress?: string;
13+
secretKey: string;
14+
vaultAccessToken?: string;
15+
}) {
16+
const {
17+
walletAddress,
18+
recipientAddress,
19+
chainId,
20+
quantityWei,
21+
publishableKey,
22+
teamId,
23+
tokenAddress,
24+
secretKey,
25+
vaultAccessToken,
26+
} = options;
27+
28+
if (!secretKey) {
29+
return {
30+
error: "A project secret key is required to send funds.",
31+
ok: false,
32+
} as const;
33+
}
34+
35+
const response = await apiServerProxy<{
36+
result?: { transactionIds: string[] };
37+
error?: { message?: string };
38+
}>({
39+
body: JSON.stringify({
40+
chainId,
41+
from: walletAddress,
42+
recipients: [
43+
{
44+
address: recipientAddress,
45+
quantity: quantityWei,
46+
},
47+
],
48+
...(tokenAddress ? { tokenAddress } : {}),
49+
}),
50+
headers: {
51+
"Content-Type": "application/json",
52+
"x-client-id": publishableKey,
53+
"x-team-id": teamId,
54+
...(secretKey ? { "x-secret-key": secretKey } : {}),
55+
...(vaultAccessToken ? { "x-vault-access-token": vaultAccessToken } : {}),
56+
},
57+
method: "POST",
58+
pathname: "/v1/wallets/send",
59+
});
60+
61+
if (!response.ok) {
62+
return {
63+
error: response.error || "Failed to submit transfer request.",
64+
ok: false,
65+
} as const;
66+
}
67+
68+
return {
69+
ok: true,
70+
transactionIds: response.data?.result?.transactionIds ?? [],
71+
} as const;
72+
}

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

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ 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 { CopyTextButton } from "@/components/ui/CopyTextButton";
1110
import { CodeServer } from "@/components/ui/code/code.server";
1211
import { UnderlineLink } from "@/components/ui/UnderlineLink";
1312
import { DotNetIcon } from "@/icons/brand-icons/DotNetIcon";
@@ -27,6 +26,7 @@ import {
2726
} from "@/lib/server/project-wallet";
2827
import { ClientIDSection } from "./ClientIDSection";
2928
import { IntegrateAPIKeyCodeTabs } from "./IntegrateAPIKeyCodeTabs";
29+
import { ProjectWalletControls } from "./ProjectWalletControls.client";
3030
import { SecretKeySection } from "./SecretKeySection";
3131

3232
export async function ProjectFTUX(props: {
@@ -64,9 +64,6 @@ function ProjectWalletSection(props: {
6464
const defaultLabel = getProjectWalletLabel(props.project.name);
6565
const walletAddress = props.wallet?.address;
6666
const label = props.wallet?.label ?? defaultLabel;
67-
const shortenedAddress = walletAddress
68-
? `${walletAddress.slice(0, 6)}...${walletAddress.slice(-4)}`
69-
: undefined;
7067

7168
return (
7269
<section>
@@ -90,21 +87,16 @@ function ProjectWalletSection(props: {
9087
</div>
9188

9289
{walletAddress ? (
93-
<div className="flex flex-col gap-3 rounded-lg border border-dashed border-border/60 bg-background p-3 md:flex-row md:items-center md:justify-between">
94-
<div>
95-
<p className="text-muted-foreground text-xs uppercase">
96-
Wallet address
97-
</p>
98-
<p className="font-mono text-sm break-all">{walletAddress}</p>
99-
</div>
100-
<CopyTextButton
101-
className="w-full md:w-auto"
102-
copyIconPosition="right"
103-
textToCopy={walletAddress}
104-
textToShow={shortenedAddress ?? walletAddress}
105-
tooltip="Copy wallet address"
106-
/>
107-
</div>
90+
<ProjectWalletControls
91+
label={label}
92+
project={{
93+
id: props.project.id,
94+
publishableKey: props.project.publishableKey,
95+
services: props.project.services,
96+
teamId: props.project.teamId,
97+
}}
98+
walletAddress={walletAddress}
99+
/>
108100
) : (
109101
<Alert variant="info">
110102
<CircleAlertIcon className="size-5" />

0 commit comments

Comments
 (0)