Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion apps/dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"scripts": {
"preinstall": "npx only-allow pnpm",
"dev": "next dev --turbopack",
"dev": "next dev",
"build": "NODE_OPTIONS=--max-old-space-size=6144 next build",
"start": "next start",
"format": "biome format ./src --write",
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { API_SERVER_URL } from "@/constants/env";
import type { UsageBillableByService } from "@3rdweb-sdk/react/hooks/useApi";
import { getAuthToken } from "../../../../../api/lib/getAuthToken";

export async function getAccountUsage() {
const token = await getAuthToken();

if (!token) {
return undefined;
}

const res = await fetch(`${API_SERVER_URL}/v1/account/usage`, {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
});

if (!res.ok) {
return undefined;
}

const json = await res.json();
return json.data as UsageBillableByService;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import type { UsageBillableByService } from "@3rdweb-sdk/react/hooks/useApi";
import { useMemo } from "react";
import { toNumber, toPercent, toSize } from "utils/number";
import { UsageCard } from "./UsageCard";

interface UsageProps {
usage: UsageBillableByService;
}

export const Usage: React.FC<UsageProps> = ({ usage: usageData }) => {
const bundlerMetrics = useMemo(() => {
const metric = {
title: "Total sponsored fees",
total: 0,
};

if (!usageData) {
return metric;
}

return {
title: metric.title,
total: usageData.billableUsd.bundler,
};
}, [usageData]);

const storageMetrics = useMemo(() => {
if (!usageData) {
return {};
}

const consumedBytes = usageData.usage.storage.sumFileSizeBytes;
const limitBytes = usageData.limits.storage;
const percent = toPercent(consumedBytes, limitBytes);

return {
total: `${toSize(consumedBytes, "MB")} / ${toSize(
limitBytes,
)} (${percent}%)`,
progress: percent,
...(usageData.billableUsd.storage > 0
? {
overage: usageData.billableUsd.storage,
}
: {}),
};
}, [usageData]);

const walletsMetrics = useMemo(() => {
if (!usageData) {
return {};
}

const numOfWallets = usageData.usage.embeddedWallets.countWalletAddresses;
const limitWallets = usageData.limits.embeddedWallets;
const percent = toPercent(numOfWallets, limitWallets);

return {
total: `${toNumber(numOfWallets)} / ${toNumber(
limitWallets,
)} (${percent}%)`,
progress: percent,
...(usageData.billableUsd.embeddedWallets > 0
? {
overage: usageData.billableUsd.embeddedWallets,
}
: {}),
};
}, [usageData]);

const rpcMetrics = useMemo(() => {
if (!usageData) {
return {};
}

return {
title: "Unlimited requests",
total: (
<span className="text-muted-foreground">
{usageData.rateLimits.rpc} Requests Per Second
</span>
),
};
}, [usageData]);

const gatewayMetrics = useMemo(() => {
if (!usageData) {
return {};
}

return {
title: "Unlimited requests",
total: (
<span className="text-muted-foreground">
{usageData.rateLimits.storage} Requests Per Second
</span>
),
};
}, [usageData]);

return (
<div className="flex grow flex-col gap-12">
<div className="grid grid-cols-1 gap-5 md:grid-cols-2 lg:grid-cols-3">
<UsageCard
{...rpcMetrics}
name="RPC"
tooltip="RPC usage is calculated by requests per second."
/>
<UsageCard
{...gatewayMetrics}
name="Storage Gateway"
tooltip="Storage gateway usage is calculated by GB per file size."
/>
<UsageCard
{...storageMetrics}
name="Storage Pinning"
tooltip="Storage pinning usage is calculated by GB per file size."
/>
<UsageCard
{...walletsMetrics}
name="Email Wallets"
tooltip="Email wallet (with managed recovery code) usage is calculated by monthly active wallets (i.e. active as defined by at least 1 user log-in via email or social within the billing period month)."
/>
<UsageCard
{...bundlerMetrics}
name="Account Abstraction"
tooltip="(Gasless, Paymaster, Bundler) usage is calculated by sponsored network fees."
/>
</div>
</div>
);
};
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { ToolTipLabel } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";
import { CircleHelpIcon } from "lucide-react";
import { toUSD } from "utils/number";

import type { JSX } from "react";
import { toUSD } from "utils/number";

interface UsageCardProps {
name: string;
Expand All @@ -23,20 +22,18 @@ export const UsageCard: React.FC<UsageCardProps> = ({
tooltip,
}) => {
return (
<div className="flex min-h-[140px] flex-col rounded-lg border border-border bg-background p-4">
<div className="flex items-center gap-2">
<h3 className="font-medium text-base">{name}</h3>
{tooltip && (
<ToolTipLabel label={tooltip}>
<CircleHelpIcon className="size-4 text-muted-foreground" />
</ToolTipLabel>
)}
</div>
<div className="relative flex min-h-[150px] flex-col rounded-lg border border-border bg-muted/50 p-4">
<h3 className="pr-10 font-medium text-lg">{name}</h3>
{tooltip && (
<ToolTipLabel label={tooltip}>
<CircleHelpIcon className="absolute top-4 right-4 size-5 text-muted-foreground hover:text-foreground" />
</ToolTipLabel>
)}

<div className="h-6" />

<div className="mt-auto flex flex-col gap-2">
{title && <p className="mb-2 font-semibold text-foreground">{title}</p>}
<div className="mt-auto flex flex-col gap-1.5">
{title && <p className="text-foreground text-sm">{title}</p>}

{total !== undefined && (
<p className="text-muted-foreground text-sm">
Expand Down
Loading
Loading