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
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,13 @@ export function WalletConfiguratorTextTabWalletSections({
patch("cancelDelegationSkipOnchainAcknowledgement", value)
}
/>
<TextField
label="Cancel permission — insufficient balance"
value={form.cancelDelegationInsufficientBalanceError}
onChange={(value) =>
patch("cancelDelegationInsufficientBalanceError", value)
}
/>
<TextField
id="activate-offline-permissions-title"
label="Activate offline permissions title"
Expand Down
10 changes: 10 additions & 0 deletions host/src/styleForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ export interface IStyleFormState {
cancelDelegationReject: string;
cancelDelegationSkipOnchainLabel: string;
cancelDelegationSkipOnchainAcknowledgement: string;
cancelDelegationInsufficientBalanceError: string;
activateOfflinePermissionsTitle: string;
activateOfflinePermissionsBody: string;
activateOfflinePermissionsChainsLabel: string;
Expand Down Expand Up @@ -462,6 +463,8 @@ export const ACME_PRESET: IStyleFormState = {
cancelDelegationSkipOnchainLabel: "Skip onchain cancellation",
cancelDelegationSkipOnchainAcknowledgement:
"I acknowledge that this delegation may still be used onchain by anybody that holds it, and that canceling it without submitting an onchain cancellation will only remove it from my wallet",
cancelDelegationInsufficientBalanceError:
"Insufficient balance to pay the network fee on {chainName}. Choose another payment token.",
activateOfflinePermissionsTitle: "Activate offline permissions",
activateOfflinePermissionsBody:
"This is your first time using offline permissions. You must activate the feature on your account with a one-time transaction.",
Expand Down Expand Up @@ -676,6 +679,8 @@ export const DEFAULTS_PRESET: IStyleFormState = {
cancelDelegationSkipOnchainLabel: "Skip onchain cancellation",
cancelDelegationSkipOnchainAcknowledgement:
"I acknowledge that this delegation may still be used onchain by anybody that holds it, and that canceling it without submitting an onchain cancellation will only remove it from my wallet",
cancelDelegationInsufficientBalanceError:
"Insufficient balance to pay the network fee on {chainName}. Choose another payment token.",
activateOfflinePermissionsTitle: "Activate offline permissions",
activateOfflinePermissionsBody:
"This is your first time using offline permissions. You must activate the feature on your account with a one-time transaction.",
Expand Down Expand Up @@ -1006,6 +1011,11 @@ function buildNestedCopyFromForm(form: IStyleFormState): Record<string, unknown>
"skipOnchainAcknowledgement",
form.cancelDelegationSkipOnchainAcknowledgement,
);
put(
cancelDelegation,
"insufficientBalanceError",
form.cancelDelegationInsufficientBalanceError,
);
if (Object.keys(cancelDelegation).length > 0) {
copy.cancelDelegation = cancelDelegation;
}
Expand Down
4 changes: 2 additions & 2 deletions src/circle/onrampTypes.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { EVMAccountAddress } from "@1shotapi/ows-types";
import type { EVMAccountAddress, EVMContractAddress } from "@1shotapi/ows-types";

/** Params for opening the Circle onramp fullscreen view. */
export type IOnrampOpenRequest = {
destinationAddress: EVMAccountAddress;
chainId?: number;
amount?: string;
tokenSymbol?: string;
tokenAddress?: EVMAccountAddress;
tokenAddress?: EVMContractAddress;
iconUrl?: string;
};
121 changes: 59 additions & 62 deletions src/components/AssetIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,62 +1,59 @@
import type {
EVMAccountAddress,
EVMChainId,
} from "@1shotapi/ows-types";
import { cn } from "@/lib/utils";
import { resolveAssetIconUrl } from "../lib/utils/tokenIcons";
import { SafeAssetImage } from "./SafeAssetImage";

export interface IAssetIconProps {
chainId: EVMChainId;
address: EVMAccountAddress;
symbol: string;
/** Optional host / tracked override (HTTPS). */
iconUrl?: string;
size?: "sm" | "lg";
className?: string;
}

const SIZE_CLASSES = {
sm: "size-6 text-[0.65rem]",
lg: "size-14 text-lg",
} as const;

export function AssetIcon({
chainId,
address,
symbol,
iconUrl: iconUrlOverride,
size = "sm",
className,
}: IAssetIconProps) {
const iconUrl = resolveAssetIconUrl(
chainId,
address,
symbol,
iconUrlOverride,
);
const sizeClass = SIZE_CLASSES[size];

return (
<SafeAssetImage
src={iconUrl}
className={cn(
"shrink-0 rounded-full object-cover",
sizeClass,
className,
)}
fallback={
<div
className={cn(
"bg-primary text-primary-foreground flex shrink-0 items-center justify-center rounded-full font-semibold tracking-tight",
sizeClass,
className,
)}
aria-hidden
>
<span>$</span>
</div>
}
/>
);
}
import type { EVMChainId, EVMContractAddress } from "@1shotapi/ows-types";
import { cn } from "@/lib/utils";
import { resolveAssetIconUrl } from "../lib/utils/tokenIcons";
import { SafeAssetImage } from "./SafeAssetImage";

export interface IAssetIconProps {
chainId: EVMChainId;
address: EVMContractAddress;
symbol: string;
/** Optional host / tracked override (HTTPS). */
iconUrl?: string;
size?: "sm" | "lg";
className?: string;
}

const SIZE_CLASSES = {
sm: "size-6 text-[0.65rem]",
lg: "size-14 text-lg",
} as const;

export function AssetIcon({
chainId,
address,
symbol,
iconUrl: iconUrlOverride,
size = "sm",
className,
}: IAssetIconProps) {
const iconUrl = resolveAssetIconUrl(
chainId,
address,
symbol,
iconUrlOverride,
);
const sizeClass = SIZE_CLASSES[size];

return (
<SafeAssetImage
src={iconUrl}
className={cn(
"shrink-0 rounded-full object-cover",
sizeClass,
className,
)}
fallback={
<div
className={cn(
"bg-primary text-primary-foreground flex shrink-0 items-center justify-center rounded-full font-semibold tracking-tight",
sizeClass,
className,
)}
aria-hidden
>
<span>$</span>
</div>
}
/>
);
}
103 changes: 50 additions & 53 deletions src/components/AssetIdentityMark.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,50 @@
import type {
EVMAccountAddress,
EVMChainId,
} from "@1shotapi/ows-types";
import { cn } from "@/lib/utils";
import { resolveAssetIconUrl } from "../lib/utils/tokenIcons";
import { SafeAssetImage } from "./SafeAssetImage";

export interface IAssetIdentityMarkProps {
chainId: EVMChainId;
address: EVMAccountAddress;
symbol: string;
/** Optional host / tracked override (HTTPS). */
iconUrl?: string;
chainLogoUrl?: string;
className?: string;
}

/** Large token icon with optional chain logo badge at bottom-right. */
export function AssetIdentityMark({
chainId,
address,
symbol,
iconUrl: iconUrlOverride,
chainLogoUrl,
className,
}: IAssetIdentityMarkProps) {
const iconUrl = resolveAssetIconUrl(
chainId,
address,
symbol,
iconUrlOverride,
);
const letter = (symbol.trim()[0] ?? "?").toUpperCase();

return (
<div className={cn("relative size-16 shrink-0", className)} aria-hidden>
<SafeAssetImage
src={iconUrl}
className="size-16 rounded-full object-cover"
fallback={
<div className="bg-muted text-foreground flex size-16 items-center justify-center rounded-full text-2xl font-semibold">
{letter}
</div>
}
/>
<SafeAssetImage
src={chainLogoUrl}
className="border-background absolute -right-0.5 -bottom-0.5 size-6 rounded-full border-2 object-cover"
/>
</div>
);
}
import type { EVMChainId, EVMContractAddress } from "@1shotapi/ows-types";
import { cn } from "@/lib/utils";
import { resolveAssetIconUrl } from "../lib/utils/tokenIcons";
import { SafeAssetImage } from "./SafeAssetImage";

export interface IAssetIdentityMarkProps {
chainId: EVMChainId;
address: EVMContractAddress;
symbol: string;
/** Optional host / tracked override (HTTPS). */
iconUrl?: string;
chainLogoUrl?: string;
className?: string;
}

/** Large token icon with optional chain logo badge at bottom-right. */
export function AssetIdentityMark({
chainId,
address,
symbol,
iconUrl: iconUrlOverride,
chainLogoUrl,
className,
}: IAssetIdentityMarkProps) {
const iconUrl = resolveAssetIconUrl(
chainId,
address,
symbol,
iconUrlOverride,
);
const letter = (symbol.trim()[0] ?? "?").toUpperCase();

return (
<div className={cn("relative size-16 shrink-0", className)} aria-hidden>
<SafeAssetImage
src={iconUrl}
className="size-16 rounded-full object-cover"
fallback={
<div className="bg-muted text-foreground flex size-16 items-center justify-center rounded-full text-2xl font-semibold">
{letter}
</div>
}
/>
<SafeAssetImage
src={chainLogoUrl}
className="border-background absolute -right-0.5 -bottom-0.5 size-6 rounded-full border-2 object-cover"
/>
</div>
);
}
8 changes: 4 additions & 4 deletions src/components/OnrampView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import type {
} from "@circle-fin/app-kit";
import {
ChainUtils,
EVMAccountAddress,
type EVMAccountAddress as EVMAccountAddressType,
EVMContractAddress,
type EVMAccountAddress,
type EVMChainId,
} from "@1shotapi/ows-types";
import { zeroAddress } from "viem";
Expand All @@ -27,7 +27,7 @@ export type IOnrampViewProps = IOnrampOpenRequest & {
onClose: () => void;
};

const PLACEHOLDER_TOKEN_ADDRESS = EVMAccountAddress(zeroAddress);
const PLACEHOLDER_TOKEN_ADDRESS = EVMContractAddress(zeroAddress);

/**
* Full-screen Circle AppKit onramp inside the Branding Layer shell.
Expand Down Expand Up @@ -61,7 +61,7 @@ export function OnrampView({
const [popupReady, setPopupReady] = useState(false);
const [popupOpened, setPopupOpened] = useState(false);
const [catalogTokenAddress, setCatalogTokenAddress] = useState<
EVMAccountAddressType | null
EVMContractAddress | null
>(null);
const [catalogIconUrl, setCatalogIconUrl] = useState<string | undefined>();

Expand Down
Loading
Loading