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
1 change: 1 addition & 0 deletions examples/02-evm-simple-bridge/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="/favicon.ico" />
<title>CCIP Simple Bridge</title>
</head>
<body>
Expand Down
1 change: 1 addition & 0 deletions examples/02-evm-simple-bridge/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
},
"dependencies": {
"@chainlink/ccip-sdk": "1.0.0",
"@ccip-examples/shared-brand": "workspace:*",
"@ccip-examples/shared-config": "workspace:*",
"@ccip-examples/shared-components": "workspace:*",
"@ccip-examples/shared-utils": "workspace:*",
Expand Down
Binary file not shown.
3 changes: 2 additions & 1 deletion examples/02-evm-simple-bridge/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { WagmiProvider } from "wagmi";
import { RainbowKitProvider, darkTheme } from "@rainbow-me/rainbowkit";
import { useAccount, useSwitchChain } from "wagmi";

import { BRAND_COLORS } from "@ccip-examples/shared-brand";
import { wagmiConfig } from "@ccip-examples/shared-config/wagmi";
import { createDefaultQueryClient } from "@ccip-examples/shared-config/queryClient";
import type { FeeTokenOptionItem } from "@ccip-examples/shared-config";
Expand Down Expand Up @@ -145,7 +146,7 @@ export default function App() {
<WagmiProvider config={wagmiConfig}>
<RainbowKitProvider
theme={darkTheme({
accentColor: "#375BD2",
accentColor: BRAND_COLORS.primary,
borderRadius: "medium",
})}
>
Expand Down
3 changes: 2 additions & 1 deletion examples/03-multichain-bridge-dapp/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CCIP Multichain Bridge</title>
<link rel="icon" href="/favicon.ico" />
<title>Multichain Family Bridge</title>
</head>
<body>
<div id="root"></div>
Expand Down
1 change: 1 addition & 0 deletions examples/03-multichain-bridge-dapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@aptos-labs/ts-sdk": "^5.2.1",
"@aptos-labs/wallet-adapter-react": "^3.7.2",
"@chainlink/ccip-sdk": "1.0.0",
"@ccip-examples/shared-brand": "workspace:*",
"@ccip-examples/shared-config": "workspace:*",
"@ccip-examples/shared-components": "workspace:*",
"@ccip-examples/shared-utils": "workspace:*",
Expand Down
Binary file not shown.
5 changes: 3 additions & 2 deletions examples/03-multichain-bridge-dapp/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useWallet as useAptosWallet } from "@aptos-labs/wallet-adapter-react";
import { Network } from "@aptos-labs/ts-sdk";
import { useAccount, useSwitchChain } from "wagmi";

import { BRAND_COLORS } from "@ccip-examples/shared-brand";
import { wagmiConfig } from "@ccip-examples/shared-config/wagmi";
import { createDefaultQueryClient } from "@ccip-examples/shared-config/queryClient";
import { NETWORKS, type FeeTokenOptionItem } from "@ccip-examples/shared-config";
Expand Down Expand Up @@ -122,7 +123,7 @@ function AppContent() {

return (
<div className={styles.app}>
<Header title="CCIP Multichain Bridge" subtitle="EVM, Solana, and Aptos token transfers">
<Header title="Multichain Family Bridge" subtitle="EVM, Solana, and Aptos token transfers">
<HistoryButton />
</Header>
<main className={`${styles.main} ${styles.mainWide}`}>
Expand Down Expand Up @@ -177,7 +178,7 @@ export default function App() {
<WagmiProvider config={wagmiConfig}>
<RainbowKitProvider
theme={darkTheme({
accentColor: "#375BD2",
accentColor: BRAND_COLORS.primary,
borderRadius: "medium",
})}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@
font-size: var(--font-size-sm);
font-weight: 500;
color: var(--color-primary);
background-color: var(--color-primary-light);
background-color: var(--color-primary-bg);
border: 1px solid var(--color-primary);
border-radius: var(--radius-md);
cursor: pointer;
Expand Down
83 changes: 66 additions & 17 deletions examples/03-multichain-bridge-dapp/src/hooks/useSolanaTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { TransactionMessage, VersionedTransaction } from "@solana/web3.js";
import type { SolanaChain } from "@chainlink/ccip-sdk";
import { networkInfo } from "@chainlink/ccip-sdk";
import { NETWORKS } from "@ccip-examples/shared-config";
import { parseSolanaError } from "@ccip-examples/shared-utils";
import { parseSolanaError, confirmTransaction } from "@ccip-examples/shared-utils";
import type { TransactionResult, TransferMessage } from "./transferTypes.js";

export interface UseSolanaTransferParams {
Expand Down Expand Up @@ -53,25 +53,74 @@ export function useSolanaTransfer({
message: { ...message, fee },
});

onStateChange("sending");
// Retry loop covering both send AND confirm: if the user takes too long
// to sign in the wallet popup the blockhash expires. This can surface as
// either a send-time error or a confirmation-time expiration. In both
// cases we rebuild the transaction with a fresh blockhash and re-prompt.
const MAX_ATTEMPTS = 3;
let signature: string | undefined;
let confirmed = false;

// Use "confirmed" for a fresher blockhash (less likely to expire during wallet signing).
const blockhash = await solanaConnection.getLatestBlockhash("confirmed");
const messageV0 = new TransactionMessage({
payerKey: solanaPublicKey,
recentBlockhash: blockhash.blockhash,
instructions: unsignedTx.instructions,
}).compileToV0Message(unsignedTx.lookupTables);
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
onStateChange("sending");

const transaction = new VersionedTransaction(messageV0);
const signature = await sendTransaction(transaction, solanaConnection, {
skipPreflight: true,
maxRetries: 5,
});
onTxHash(signature);
const blockhash = await solanaConnection.getLatestBlockhash("confirmed");
const messageV0 = new TransactionMessage({
payerKey: solanaPublicKey,
recentBlockhash: blockhash.blockhash,
instructions: unsignedTx.instructions,
}).compileToV0Message(unsignedTx.lookupTables);

const transaction = new VersionedTransaction(messageV0);

try {
signature = await sendTransaction(transaction, solanaConnection, {
skipPreflight: true,
maxRetries: 5,
});
} catch (sendErr: unknown) {
const isBlockheightError =
sendErr instanceof Error &&
sendErr.name === "TransactionExpiredBlockheightExceededError";

if (isBlockheightError && attempt < MAX_ATTEMPTS) {
console.warn(
`Blockhash expired on send (attempt ${attempt}/${MAX_ATTEMPTS}), retrying…`
);
continue;
}
throw sendErr;
}

onTxHash(signature);

// Poll-based confirmation — uses getSignatureStatus() so no
// blockhash is needed for the confirmation step itself.
onStateChange("confirming");
const result = await confirmTransaction({
connection: solanaConnection,
signature,
lastValidBlockHeight: blockhash.lastValidBlockHeight,
});

if (result.confirmed) {
if (!result.success) throw new Error("Solana transaction failed on-chain");
confirmed = true;
break;
}

// Transaction expired before confirmation — retry with fresh tx
if (attempt < MAX_ATTEMPTS) {
console.warn(
`Transaction expired during confirmation (attempt ${attempt}/${MAX_ATTEMPTS}), retrying…`
);
continue;
}
}

onStateChange("confirming");
await solanaConnection.confirmTransaction({ signature, ...blockhash }, "confirmed");
if (!signature || !confirmed) {
throw new Error("Solana transaction could not be confirmed after multiple attempts");
}

onStateChange("tracking");
const tx = await chain.getTransaction(signature);
Expand Down
2 changes: 1 addition & 1 deletion packages/shared-brand/assets/chainlink-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/shared-brand/assets/favicon.ico
Binary file not shown.
3 changes: 2 additions & 1 deletion packages/shared-brand/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"default": "./dist/index.js"
},
"./design-tokens.css": "./src/design-tokens.css",
"./chainlink-logo.svg": "./assets/chainlink-logo.svg"
"./chainlink-logo.svg": "./assets/chainlink-logo.svg",
"./favicon.ico": "./assets/favicon.ico"
},
"scripts": {
"build": "tsc",
Expand Down
7 changes: 7 additions & 0 deletions packages/shared-brand/src/design-tokens.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
--color-primary: #0847f7;
--color-primary-dark: #0635c4;
--color-primary-light: #8aa6f9;
--color-primary-hover: #063fd4;
--color-primary-bg: #e8eeff; /* light tint for backgrounds */
--color-primary-shadow: rgba(8, 71, 247, 0.2);

/* Neutrals */
--color-dark: #0b101c; /* Chainlink dark */
Expand All @@ -32,7 +35,9 @@

/* Secondary - Status Colors */
--color-success: #217b71; /* Chainlink green */
--color-success-hover: #1a655c;
--color-warning: #f7b808; /* Chainlink yellow */
--color-warning-hover: #d9a207;
--color-error: #e54918; /* Chainlink orange */

/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */
Expand All @@ -49,6 +54,7 @@
/* Backgrounds */
--color-background: var(--color-light);
--color-background-secondary: var(--color-white);
--color-background-tertiary: #eef1f6;
--color-surface: var(--color-white);
--color-surface-elevated: var(--color-white);

Expand Down Expand Up @@ -77,6 +83,7 @@
/* ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ */

--font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
--font-sans: var(--font-family);
--font-mono: "Monaco", "Menlo", "Ubuntu Mono", monospace;

--font-size-xs: 0.75rem; /* 12px */
Expand Down
13 changes: 13 additions & 0 deletions packages/shared-brand/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,12 @@ export const BRAND_COLORS = {
primaryDark: "#0635C4",
/** Light variant */
primaryLight: "#8AA6F9",
/** Hover state for primary */
primaryHover: "#063FD4",
/** Light tint for backgrounds */
primaryBg: "#E8EEFF",
/** Primary shadow */
primaryShadow: "rgba(8, 71, 247, 0.2)",

/** Dark neutral */
dark: "#0B101C",
Expand All @@ -31,10 +37,17 @@ export const BRAND_COLORS = {

/** Success/positive states */
success: "#217B71",
/** Success hover */
successHover: "#1A655C",
/** Warning states */
warning: "#F7B808",
/** Warning hover */
warningHover: "#D9A207",
/** Error/destructive states */
error: "#E54918",

/** Tertiary background */
backgroundTertiary: "#EEF1F6",
} as const;

/** @deprecated Use BRAND_COLORS instead */
Expand Down
2 changes: 1 addition & 1 deletion packages/shared-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"types": "./dist/primitives/index.d.ts",
"import": "./dist/primitives/index.js"
},
"./styles/tokens.css": "./dist/styles/tokens.css",
"./styles/globals.css": "./dist/styles/globals.css",
"./layout": {
"types": "./dist/layout/index.d.ts",
Expand Down Expand Up @@ -52,6 +51,7 @@
"ui"
],
"dependencies": {
"@ccip-examples/shared-brand": "workspace:*",
"@ccip-examples/shared-config": "workspace:*",
"@ccip-examples/shared-utils": "workspace:*"
},
Expand Down
2 changes: 1 addition & 1 deletion packages/shared-components/src/ErrorBoundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function DefaultFallback({ error, reset }: { error: Error; reset: () => void })
style={{
padding: "0.5rem 1rem",
cursor: "pointer",
backgroundColor: "var(--color-primary, #375bd2)",
backgroundColor: "var(--color-primary, #0847f7)",
color: "white",
border: "none",
borderRadius: "4px",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
font-weight: 600;
text-transform: uppercase;
color: var(--color-primary);
background-color: var(--color-primary-light);
background-color: var(--color-primary-bg);
padding: 1px 6px;
border-radius: var(--radius-sm);
line-height: 1.4;
Expand Down
4 changes: 2 additions & 2 deletions packages/shared-components/src/bridge/BridgeForm.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@
.copyButton:hover:not(:disabled) {
color: var(--color-primary);
border-color: var(--color-primary);
background-color: var(--color-primary-light);
background-color: var(--color-primary-bg);
}

.copyButton:disabled {
Expand Down Expand Up @@ -151,7 +151,7 @@
font-size: var(--font-size-sm);
font-weight: 600;
color: var(--color-primary);
background-color: var(--color-primary-light);
background-color: var(--color-primary-bg);
border: 1px solid var(--color-primary);
border-radius: var(--radius-md);
cursor: pointer;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@

.aptosButtonConnected {
background: var(--color-background-secondary, #1a1b1f);
border-color: var(--color-primary, #375bd2);
border-color: var(--color-primary, #0847f7);
color: var(--color-text-primary);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
}

.info {
background-color: var(--color-primary-light);
background-color: var(--color-primary-bg);
border: 1px solid var(--color-primary);
color: var(--color-primary);
}
16 changes: 2 additions & 14 deletions packages/shared-components/src/styles/globals.css
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
/**
* Global styles for CCIP example frontends.
* Import tokens from same package.
* Import design tokens from shared-brand (single source of truth).
*/
@import "./tokens.css";
@import "@ccip-examples/shared-brand/design-tokens.css";

:root {
--color-metamask: #f6851b;
--color-metamask-hover: #e2761b;
--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
}

/* Global Reset */
Expand Down Expand Up @@ -41,14 +40,3 @@ body {
transform: rotate(360deg);
}
}

/* Reduced Motion Support */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Loading