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
5 changes: 5 additions & 0 deletions .changeset/young-carrots-burn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"thirdweb": patch
---

ERC20 assets
21 changes: 15 additions & 6 deletions apps/dashboard/src/@/analytics/report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,11 @@ export function reportChainConfigurationAdded(properties: {
// ASSETS
// ----------------------------

type AssetContractType = "DropERC20" | "DropERC1155" | "DropERC721";
type AssetContractType =
| "DropERC20"
| "DropERC1155"
| "DropERC721"
| "ERC20Asset";

/**
* ### Why do we need to report this event?
Expand Down Expand Up @@ -334,6 +338,15 @@ export function reportAssetCreationSuccessful(properties: {
});
}

type CoinCreationStep =
| "erc20-asset:deploy-contract"
| "erc20-asset:airdrop-tokens"
| "erc20-asset:approve-airdrop-tokens"
| "drop-erc20:deploy-contract"
| "drop-erc20:set-claim-conditions"
| "drop-erc20:mint-tokens"
| "drop-erc20:airdrop-tokens";

/**
* ### Why do we need to report this event?
* - To track number of failed asset creations
Expand All @@ -355,11 +368,7 @@ export function reportAssetCreationFailed(
}
| {
assetType: "coin";
step:
| "deploy-contract"
| "set-claim-conditions"
| "mint-tokens"
| "airdrop-tokens";
step: CoinCreationStep;
}
),
) {
Expand Down
50 changes: 41 additions & 9 deletions apps/dashboard/src/@/components/blocks/NetworkSelectors.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,18 @@ export function MultiNetworkSelector(props: {
client: ThirdwebClient;
chainIds?: number[];
}) {
let { allChains, idToChain } = useAllChainsData();

if (props.chainIds && props.chainIds.length > 0) {
allChains = allChains.filter((chain) =>
props.chainIds?.includes(chain.chainId),
);
}
const { allChains, idToChain } = useAllChainsData();

const options = useMemo(() => {
let sortedChains = allChains;
let chains = allChains.filter((chain) => chain.status !== "deprecated");

if (props.chainIds && props.chainIds.length > 0) {
chains = allChains.filter((chain) =>
props.chainIds?.includes(chain.chainId),
);
}

let sortedChains = chains;

if (props.priorityChains) {
const priorityChainsSet = new Set();
Expand Down Expand Up @@ -69,7 +71,13 @@ export function MultiNetworkSelector(props: {
value: String(chain.chainId),
};
});
}, [allChains, props.priorityChains, idToChain, props.hideTestnets]);
}, [
allChains,
props.priorityChains,
idToChain,
props.hideTestnets,
props.chainIds,
]);

const searchFn = useCallback(
(option: Option, searchValue: string) => {
Expand Down Expand Up @@ -155,16 +163,38 @@ export function SingleNetworkSelector(props: {
disableDeprecated?: boolean;
placeholder?: string;
client: ThirdwebClient;
priorityChains?: number[];
}) {
const { allChains, idToChain } = useAllChainsData();

const chainsToShow = useMemo(() => {
let chains = allChains;

chains = chains.filter((chain) => chain.status !== "deprecated");

if (props.disableTestnets) {
chains = chains.filter((chain) => !chain.testnet);
}

if (props.priorityChains) {
const priorityChainsSet = new Set();
for (const chainId of props.priorityChains || []) {
priorityChainsSet.add(chainId);
}

const priorityChains = (props.priorityChains || [])
.map((chainId) => {
return idToChain.get(chainId);
})
.filter((v) => !!v);

const otherChains = allChains.filter(
(chain) => !priorityChainsSet.has(chain.chainId),
);

chains = [...priorityChains, ...otherChains];
}

if (props.chainIds) {
const chainIdSet = new Set(props.chainIds);
chains = chains.filter((chain) => chainIdSet.has(chain.chainId));
Expand All @@ -180,6 +210,8 @@ export function SingleNetworkSelector(props: {
props.chainIds,
props.disableTestnets,
props.disableDeprecated,
props.priorityChains,
idToChain,
]);

const options = useMemo(() => {
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/@/components/blocks/TokenSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ export function TokenSelector(props: {
searchPlaceholder="Search by name or symbol"
showCheck={props.showCheck}
side={props.side}
value={selectedValue}
value={tokensQuery.isPending ? undefined : selectedValue}
/>
);
}
43 changes: 29 additions & 14 deletions apps/dashboard/src/@/components/blocks/distribution-chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import { cn } from "@/lib/utils";
export type Segment = {
label: string;
percent: number;
value: string;
color: string;
};

type DistributionBarChartProps = {
segments: Segment[];
title: string;
title?: string;
titleClassName?: string;
barClassName?: string;
};

export function DistributionBarChart(props: DistributionBarChartProps) {
Expand All @@ -21,24 +24,36 @@ export function DistributionBarChart(props: DistributionBarChartProps) {

return (
<div>
<div className="mb-2 flex items-center justify-between">
<h3 className="font-medium text-sm">{props.title}</h3>
<div
className={cn(
"font-medium text-muted-foreground text-sm",
invalidTotalPercentage && "text-red-500",
)}
>
Total: {totalPercentage}%
{props.title && (
<div className="mb-2 flex items-center justify-between">
<h3 className={cn("font-medium text-sm", props.titleClassName)}>
{props.title}
</h3>
<div
className={cn(
"font-medium text-muted-foreground text-sm",
invalidTotalPercentage && "text-red-500",
)}
>
Total: {totalPercentage}%
</div>
</div>
</div>
)}

{/* Bar */}
<div className="flex h-3 overflow-hidden rounded-lg">
<div
className={cn(
"flex h-3 overflow-hidden rounded-lg",
props.barClassName,
)}
>
{props.segments.map((segment) => {
return (
<div
className="flex h-full items-center justify-center transition-all duration-200"
className={cn(
"flex h-full items-center justify-center transition-all duration-200",
segment.percent > 0 && "border-r-2 border-background",
)}
key={segment.label}
style={{
backgroundColor: segment.color,
Expand Down Expand Up @@ -67,7 +82,7 @@ export function DistributionBarChart(props: DistributionBarChartProps) {
"text-destructive-text",
)}
>
{segment.label}: {segment.percent}%
{segment.label}: {segment.value}
</p>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function MultiStepStatus<T extends string>(props: {
}) {
return (
<DynamicHeight>
<div className="space-y-4">
<div className="space-y-4 overflow-hidden">
{props.steps.map((step) => (
<div className="flex items-start space-x-3 " key={step.label}>
{step.status.type === "completed" ? (
Expand All @@ -46,7 +46,7 @@ export function MultiStepStatus<T extends string>(props: {
) : (
<CircleIcon className="mt-0.5 size-5 flex-shrink-0 text-muted-foreground/70" />
)}
<div className="flex-1">
<div className="grow">
<p
className={`font-medium ${
step.status.type === "pending"
Expand All @@ -73,7 +73,7 @@ export function MultiStepStatus<T extends string>(props: {
{step.status.type === "error"
? props.renderError?.(step, step.status.message) || (
<div className="mt-1 space-y-2">
<p className="mb-1 text-red-500 text-sm">
<p className="mb-1 text-red-500 text-sm whitespace-pre-wrap break-all">
{step.status.message}
</p>
<Button
Expand Down
20 changes: 16 additions & 4 deletions apps/dashboard/src/@/components/blocks/wallet-address.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"use client";
import { CheckIcon, CopyIcon, XIcon } from "lucide-react";
import { CheckIcon, CircleSlashIcon, CopyIcon, XIcon } from "lucide-react";
import { useMemo } from "react";
import { isAddress, type ThirdwebClient, ZERO_ADDRESS } from "thirdweb";
import { Blobbie, type SocialProfile, useSocialProfiles } from "thirdweb/react";
Expand All @@ -23,6 +23,7 @@ export function WalletAddress(props: {
className?: string;
iconClassName?: string;
client: ThirdwebClient;
fallbackIcon?: React.ReactNode;
}) {
// default back to zero address if no address provided
const address = useMemo(() => props.address || ZERO_ADDRESS, [props.address]);
Expand Down Expand Up @@ -59,9 +60,16 @@ export function WalletAddress(props: {
// special case for zero address
if (address === ZERO_ADDRESS) {
return (
<span className={cn("cursor-pointer font-mono", props.className)}>
{shortenedAddress}
</span>
<div className="flex items-center gap-2 py-2">
<CircleSlashIcon
className={cn("size-6 text-muted-foreground/70", props.iconClassName)}
/>
<span
className={cn("cursor-pointer font-mono text-sm", props.className)}
>
{shortenedAddress}
</span>
</div>
);
}

Expand All @@ -82,6 +90,7 @@ export function WalletAddress(props: {
iconClassName={props.iconClassName}
profiles={profiles.data || []}
thirdwebClient={props.client}
fallbackIcon={props.fallbackIcon}
/>
)}
<span className="cursor-pointer font-mono">
Expand Down Expand Up @@ -173,6 +182,7 @@ function WalletAvatar(props: {
profiles: SocialProfile[];
thirdwebClient: ThirdwebClient;
iconClassName?: string;
fallbackIcon?: React.ReactNode;
}) {
const avatar = useMemo(() => {
return props.profiles.find(
Expand All @@ -199,6 +209,8 @@ function WalletAvatar(props: {
className={cn("size-5 object-cover", props.iconClassName)}
src={resolvedAvatarSrc}
/>
) : props.fallbackIcon ? (
props.fallbackIcon
) : (
<Blobbie
address={props.address}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ const contractTypeToAssetTypeRecord: Record<string, string | undefined> = {
DropERC20: "Coin",
DropERC721: "NFT Collection",
DropERC1155: "NFT Collection",
ERC20Asset: "Coin",
};

const NetworkFilterCell = React.memo(function NetworkFilterCell({
Expand Down
1 change: 1 addition & 0 deletions apps/dashboard/src/@/components/ui/CopyAddressButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export function CopyAddressButton(props: {
copyIconPosition={props.copyIconPosition}
textToCopy={props.address}
textToShow={shortenedAddress}
iconClassName={props.iconClassName}
tooltip={props.tooltip || "Copy Address"}
variant={props.variant}
/>
Expand Down
8 changes: 7 additions & 1 deletion apps/dashboard/src/@/components/ui/tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export function TabButtons(props: {
shadowColor?: string;
tabIconClassName?: string;
hideBottomLine?: boolean;
bottomLineClassName?: string;
}) {
const { containerRef, lineRef, activeTabRef } =
useUnderline<HTMLButtonElement>();
Expand All @@ -106,7 +107,12 @@ export function TabButtons(props: {
<div className={cn("relative", props.containerClassName)}>
{/* Bottom line */}
{!props.hideBottomLine && (
<div className="absolute right-0 bottom-0 left-0 h-[1px] bg-border" />
<div
className={cn(
"absolute right-0 bottom-0 left-0 h-[1px] bg-border",
props.bottomLineClassName,
)}
/>
)}

<ScrollShadow
Expand Down
1 change: 0 additions & 1 deletion apps/dashboard/src/@/contexts/error-handler.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
"use client";

import { CircleAlertIcon, InfoIcon } from "lucide-react";
import Link from "next/link";
import { createContext, useCallback, useContext, useState } from "react";
Expand Down
7 changes: 6 additions & 1 deletion apps/dashboard/src/@/hooks/project-contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ export function useAddContractToProject() {
contractAddress: string;
chainId: string;
deploymentType: "asset" | undefined;
contractType: "DropERC20" | "DropERC721" | "DropERC1155" | undefined;
contractType:
| "ERC20Asset"
| "DropERC721"
| "DropERC1155"
| "DropERC20"
| undefined;
}) => {
const res = await apiServerProxy({
body: JSON.stringify({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type ContractPageMetadata = {
isAccount: boolean;
isAccountPermissionsSupported: boolean;
functionSelectors: string[];
showClaimRewards: boolean;
};

export async function getContractPageMetadata(contract: ThirdwebContract) {
Expand Down
Loading
Loading