feat: OMNL Bank online production — 128 chains, Central Bank UI, settlement stack
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m22s
CI/CD Pipeline / Security Scanning (push) Successful in 2m30s
CI/CD Pipeline / Lint and Format (push) Failing after 44s
CI/CD Pipeline / Terraform Validation (push) Failing after 27s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 31s
Deploy ChainID 138 / Deploy ChainID 138 (push) Failing after 56s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 29s
Validation / validate-genesis (push) Successful in 34s
Validation / validate-terraform (push) Failing after 39s
Validation / validate-kubernetes (push) Failing after 14s
Validation / validate-smart-contracts (push) Failing after 20s
Validation / validate-security (push) Failing after 1m19s
Validation / validate-documentation (push) Failing after 27s
Verify Deployment / Verify Deployment (push) Failing after 1m13s
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m22s
CI/CD Pipeline / Security Scanning (push) Successful in 2m30s
CI/CD Pipeline / Lint and Format (push) Failing after 44s
CI/CD Pipeline / Terraform Validation (push) Failing after 27s
CI/CD Pipeline / Kubernetes Validation (push) Successful in 31s
Deploy ChainID 138 / Deploy ChainID 138 (push) Failing after 56s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 29s
Validation / validate-genesis (push) Successful in 34s
Validation / validate-terraform (push) Failing after 39s
Validation / validate-kubernetes (push) Failing after 14s
Validation / validate-smart-contracts (push) Failing after 20s
Validation / validate-security (push) Failing after 1m19s
Validation / validate-documentation (push) Failing after 27s
Verify Deployment / Verify Deployment (push) Failing after 1m13s
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -25,7 +25,6 @@ import CopyButton from '../ui/CopyButton';
|
||||
import ConfirmationModal from '../ui/ConfirmationModal';
|
||||
import Tooltip from '../ui/Tooltip';
|
||||
import LoadingSkeleton from '../ui/LoadingSkeleton';
|
||||
import ChainIcon from '../ui/ChainIcon';
|
||||
import TokenIcon from '../ui/TokenIcon';
|
||||
|
||||
interface BridgeButtonsProps {
|
||||
@@ -384,38 +383,28 @@ function BridgeButtonsConnected({
|
||||
/>
|
||||
)}
|
||||
<div className="relative">
|
||||
<div className="mb-6">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<h2 className="text-[20px] font-semibold text-white flex items-center gap-2">
|
||||
Bridge to
|
||||
{destination && (
|
||||
<>
|
||||
<ChainIcon chainId={destination.chainId} name={destination.name} size={24} />
|
||||
<span>{destination.name}</span>
|
||||
</>
|
||||
)}
|
||||
{!destination && <span>Ethereum Mainnet</span>}
|
||||
</h2>
|
||||
<Tooltip content="Refresh all balances and allowances">
|
||||
<button
|
||||
onClick={handleRefreshBalances}
|
||||
disabled={!address}
|
||||
className="p-2 text-teal-400 hover:text-teal-300 hover:bg-teal-500/20 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed border border-white/20"
|
||||
aria-label="Refresh balances"
|
||||
>
|
||||
<svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<p className="text-[#A0A0A0] text-sm mt-1">
|
||||
Wrap ETH, approve tokens, and bridge WETH9 via CCIP
|
||||
<div className="mb-6 flex items-center justify-between gap-3">
|
||||
<p className="ui-muted text-sm">
|
||||
Wrap ETH, approve tokens, and bridge WETH9 via CCIP to{' '}
|
||||
{destination?.name ?? 'Ethereum Mainnet'}
|
||||
</p>
|
||||
<Tooltip content="Refresh all balances and allowances">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRefreshBalances}
|
||||
disabled={!address}
|
||||
className="ui-btn-icon disabled:opacity-50 disabled:cursor-not-allowed shrink-0"
|
||||
aria-label="Refresh balances"
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
</button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div className="mb-8">
|
||||
<label className="flex items-center gap-2 text-[13px] font-medium mb-2 text-[#A0A0A0]">
|
||||
<label className="ui-label">
|
||||
<TokenIcon symbol="ETH" size={20} />
|
||||
<span>Amount (ETH)</span>
|
||||
<Tooltip content="Enter the amount of ETH you want to bridge. This will be wrapped to WETH9 first.">
|
||||
@@ -429,10 +418,8 @@ function BridgeButtonsConnected({
|
||||
min="0"
|
||||
value={amount}
|
||||
onChange={(e) => setAmount(e.target.value)}
|
||||
className={`w-full min-h-[48px] pl-4 pr-24 py-3 border rounded-xl focus:outline-none focus:ring-2 focus:ring-teal-500 focus:ring-offset-2 focus:ring-offset-[#252830] transition-all text-lg bg-[#1a1d24] text-white placeholder:text-[#A0A0A0] hover:border-white/30 ${
|
||||
amountError
|
||||
? 'border-red-500 focus:border-red-500'
|
||||
: 'border-white/20 focus:border-teal-500'
|
||||
className={`ui-input pl-4 pr-24 text-lg ${
|
||||
amountError ? '!border-red-500 focus:!border-red-500' : ''
|
||||
}`}
|
||||
placeholder="0.0"
|
||||
aria-invalid={!!amountError}
|
||||
@@ -444,7 +431,7 @@ function BridgeButtonsConnected({
|
||||
setAmount(ethBalance.displayValue);
|
||||
setAmountError('');
|
||||
}}
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 px-4 py-2 min-h-[36px] text-sm font-medium bg-teal-600 text-white rounded-lg hover:bg-teal-500 transition-colors"
|
||||
className="ui-btn-primary absolute right-2 top-1/2 -translate-y-1/2 !py-2 !min-h-0 text-sm"
|
||||
>
|
||||
MAX
|
||||
</button>
|
||||
@@ -461,7 +448,7 @@ function BridgeButtonsConnected({
|
||||
</div>
|
||||
|
||||
<div className="mb-8">
|
||||
<label htmlFor="bridge-recipient-address" className="block text-[13px] font-medium mb-2 text-[#A0A0A0]">
|
||||
<label htmlFor="bridge-recipient-address" className="ui-label">
|
||||
Recipient Address
|
||||
<Tooltip content="The Ethereum address that will receive the bridged tokens on the destination chain.">
|
||||
<span className="ml-2 text-white/60 cursor-help text-xs">ℹ️</span>
|
||||
@@ -474,10 +461,8 @@ function BridgeButtonsConnected({
|
||||
type="text"
|
||||
value={recipient}
|
||||
onChange={(e) => setRecipient(e.target.value)}
|
||||
className={`w-full min-h-[48px] p-4 border rounded-xl focus:outline-none focus:ring-2 focus:ring-teal-500 focus:ring-offset-2 focus:ring-offset-[#252830] transition-all font-mono text-base bg-[#1a1d24] text-white placeholder:text-[#A0A0A0] hover:border-white/30 ${
|
||||
recipientError
|
||||
? 'border-red-500 focus:border-red-500'
|
||||
: 'border-white/20 focus:border-teal-500'
|
||||
className={`ui-input font-mono pr-36 ${
|
||||
recipientError ? '!border-red-500 focus:!border-red-500' : ''
|
||||
}`}
|
||||
placeholder="0x..."
|
||||
aria-invalid={!!recipientError}
|
||||
@@ -490,7 +475,7 @@ function BridgeButtonsConnected({
|
||||
setRecipient(address);
|
||||
setRecipientError('');
|
||||
}}
|
||||
className="px-4 py-2 text-sm font-medium bg-[#252830] text-white rounded-lg hover:bg-white/10 transition-colors border border-white/20"
|
||||
className="ui-btn-secondary !py-2 text-sm"
|
||||
>
|
||||
Use my address
|
||||
</button>
|
||||
@@ -510,9 +495,9 @@ function BridgeButtonsConnected({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mb-8 p-6 bg-[#1a1d24] rounded-xl border border-white/10">
|
||||
<div className="mb-8 p-6 ui-inset rounded-xl">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<h3 className="text-[20px] font-semibold text-[#A0A0A0]">Balances & Fees</h3>
|
||||
<h3 className="text-lg font-semibold ui-muted">Balances & Fees</h3>
|
||||
{address && (
|
||||
<CopyButton text={address} className="text-xs">
|
||||
<span className="text-xs">Copy Address</span>
|
||||
@@ -520,49 +505,49 @@ function BridgeButtonsConnected({
|
||||
)}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="flex justify-between items-center p-4 bg-[#252830] rounded-lg border border-white/10 font-mono text-sm">
|
||||
<span className="font-medium text-[#A0A0A0] flex items-center gap-2">
|
||||
<div className="ui-stat-row">
|
||||
<span className="ui-muted font-medium flex items-center gap-2">
|
||||
<TokenIcon symbol="ETH" size={20} />
|
||||
ETH Balance:
|
||||
</span>
|
||||
<span className="text-white font-medium">
|
||||
{ethBalance ? ethBalance.displayValue : <LoadingSkeleton />} <span className="text-[#A0A0A0]">ETH</span>
|
||||
<span className="font-medium">
|
||||
{ethBalance ? ethBalance.displayValue : <LoadingSkeleton />} <span className="ui-muted">ETH</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center p-4 bg-[#252830] rounded-lg border border-white/10 font-mono text-sm">
|
||||
<span className="font-medium text-[#A0A0A0] flex items-center gap-2">
|
||||
<div className="ui-stat-row">
|
||||
<span className="ui-muted font-medium flex items-center gap-2">
|
||||
<TokenIcon symbol="WETH9" size={20} />
|
||||
WETH9 Balance:
|
||||
</span>
|
||||
<span className="text-white font-medium">
|
||||
<span className="font-medium">
|
||||
{address && weth9Balance !== undefined
|
||||
? `${ethers.utils.formatEther(weth9Balance.toString())}`
|
||||
: address
|
||||
? <LoadingSkeleton />
|
||||
: '0'} <span className="text-[#A0A0A0]">WETH9</span>
|
||||
: '0'} <span className="ui-muted">WETH9</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center p-4 bg-[#252830] rounded-lg border border-white/10 font-mono text-sm">
|
||||
<span className="font-medium text-[#A0A0A0] flex items-center gap-2">
|
||||
<div className="ui-stat-row">
|
||||
<span className="ui-muted font-medium flex items-center gap-2">
|
||||
<TokenIcon symbol="LINK" size={20} />
|
||||
LINK Balance:
|
||||
</span>
|
||||
<span className="text-white font-medium">
|
||||
<span className="font-medium">
|
||||
{address && linkBalance !== undefined
|
||||
? `${ethers.utils.formatEther(linkBalance.toString())}`
|
||||
: address
|
||||
? <LoadingSkeleton />
|
||||
: '0'} <span className="text-[#A0A0A0]">LINK</span>
|
||||
: '0'} <span className="ui-muted">LINK</span>
|
||||
</span>
|
||||
</div>
|
||||
{ccipFee != null && ccipFee.gt(0) && (
|
||||
<div className="flex justify-between items-center p-4 bg-[#252830] rounded-lg border border-teal-500/30 font-mono text-sm">
|
||||
<span className="font-medium text-[#A0A0A0] flex items-center gap-2">
|
||||
<div className="ui-stat-row ui-stat-row--highlight">
|
||||
<span className="ui-muted font-medium flex items-center gap-2">
|
||||
<TokenIcon symbol="LINK" size={20} />
|
||||
CCIP Fee:
|
||||
</span>
|
||||
<span className="text-white font-medium">
|
||||
{ethers.utils.formatEther(ccipFee)} <span className="text-[#A0A0A0]">LINK</span>
|
||||
<span className="font-medium">
|
||||
{ethers.utils.formatEther(ccipFee)} <span className="ui-muted">LINK</span>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
@@ -573,7 +558,7 @@ function BridgeButtonsConnected({
|
||||
<button
|
||||
onClick={() => setShowWrapModal(true)}
|
||||
disabled={!needsWrapping || !address || isWrapping}
|
||||
className="px-6 py-4 bg-[#252830] text-white rounded-xl font-semibold hover:bg-white/10 disabled:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors border border-white/20"
|
||||
className="ui-btn-secondary w-full"
|
||||
aria-label="Wrap ETH to WETH9"
|
||||
>
|
||||
{isWrapping ? (
|
||||
@@ -592,7 +577,7 @@ function BridgeButtonsConnected({
|
||||
<button
|
||||
onClick={() => setShowApproveModal(true)}
|
||||
disabled={!needsApproval || !address || isApproving}
|
||||
className="px-6 py-4 bg-[#252830] text-white rounded-xl font-semibold hover:bg-white/10 disabled:bg-gray-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors border border-white/20"
|
||||
className="ui-btn-secondary w-full"
|
||||
aria-label="Approve tokens for bridge"
|
||||
>
|
||||
{isApproving ? (
|
||||
@@ -612,7 +597,7 @@ function BridgeButtonsConnected({
|
||||
<button
|
||||
onClick={() => setShowBridgeModal(true)}
|
||||
disabled={!canBridge || !address || isBridging}
|
||||
className="w-full min-h-[56px] py-4 text-lg font-semibold bg-teal-600 text-white rounded-xl hover:bg-teal-500 disabled:bg-[#252830] disabled:opacity-50 disabled:cursor-not-allowed transition-colors focus:outline-none focus:ring-2 focus:ring-teal-500 focus:ring-offset-2 focus:ring-offset-[#252830] shadow-lg"
|
||||
className="ui-btn-primary w-full min-h-[56px] text-lg"
|
||||
aria-label="Start bridge transfer"
|
||||
>
|
||||
{isBridging ? (
|
||||
@@ -683,26 +668,11 @@ export default function BridgeButtons(props: BridgeButtonsProps) {
|
||||
const destination = CCIP_DESTINATIONS.find((d) => d.selector === destinationChainSelector);
|
||||
return (
|
||||
<div className="w-full">
|
||||
<div className="mb-6">
|
||||
<h2 className="text-[20px] font-semibold text-white flex items-center gap-2">
|
||||
Bridge to
|
||||
{destination && (
|
||||
<>
|
||||
<ChainIcon chainId={destination.chainId} name={destination.name} size={24} />
|
||||
<span>{destination.name}</span>
|
||||
</>
|
||||
)}
|
||||
{!destination && <span>Ethereum Mainnet</span>}
|
||||
</h2>
|
||||
<p className="text-[#A0A0A0] text-sm mt-1">
|
||||
Wrap ETH, approve tokens, and bridge WETH9 via CCIP
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-8 p-5 bg-amber-500/10 border border-amber-500/30 rounded-xl text-sm text-white font-medium flex items-center gap-4">
|
||||
<svg className="h-6 w-6 text-yellow-300 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<div className="ui-alert">
|
||||
<svg className="h-5 w-5 shrink-0 ui-accent-text" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
||||
</svg>
|
||||
<span>Please connect your wallet to continue</span>
|
||||
<span>Connect your wallet to bridge to {destination?.name ?? 'Ethereum Mainnet'}</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
60
frontend-dapp/src/components/layout/OmnlProductLayout.tsx
Normal file
60
frontend-dapp/src/components/layout/OmnlProductLayout.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Link, Outlet, useLocation } from 'react-router-dom';
|
||||
import { useRef } from 'react';
|
||||
import WalletConnect from '../wallet/WalletConnect';
|
||||
import WalletDisconnectNotice from '../wallet/WalletDisconnectNotice';
|
||||
|
||||
const PRODUCTS = [
|
||||
{ path: '/central-bank', label: 'Central Bank', short: 'CB' },
|
||||
{ path: '/office-24', label: 'Office 24', short: 'O24' },
|
||||
{ path: '/trade', label: 'DBIS Trade', short: 'Trade' },
|
||||
] as const;
|
||||
|
||||
export default function OmnlProductLayout() {
|
||||
const location = useLocation();
|
||||
const userInitiatedDisconnectRef = useRef(false);
|
||||
|
||||
const isActive = (path: string) => location.pathname.startsWith(path);
|
||||
|
||||
return (
|
||||
<div className="omnl-app min-h-screen flex flex-col bg-[#0b0e11]">
|
||||
<WalletDisconnectNotice userInitiatedDisconnectRef={userInitiatedDisconnectRef} />
|
||||
<header className="omnl-app-header sticky top-0 z-50 border-b border-[#2b3139] bg-[#181a20]">
|
||||
<div className="max-w-[1600px] mx-auto px-4 h-14 flex items-center gap-4">
|
||||
<Link to="/central-bank" className="flex items-center gap-2 shrink-0">
|
||||
<span className="w-8 h-8 rounded bg-[#f0b90b] text-[#181a20] font-bold flex items-center justify-center text-sm">
|
||||
O
|
||||
</span>
|
||||
<span className="font-semibold text-[#eaecef] hidden sm:inline">OMNL</span>
|
||||
</Link>
|
||||
|
||||
<nav className="flex items-center gap-1 flex-1" aria-label="OMNL products">
|
||||
{PRODUCTS.map(({ path, label }) => (
|
||||
<Link
|
||||
key={path}
|
||||
to={path}
|
||||
className={`px-4 py-2 rounded text-sm font-medium transition-colors ${
|
||||
isActive(path)
|
||||
? 'bg-[#2b3139] text-[#f0b90b]'
|
||||
: 'text-[#848e9c] hover:text-[#eaecef] hover:bg-[#1e2329]'
|
||||
}`}
|
||||
>
|
||||
{label}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<Link
|
||||
to="/"
|
||||
className="text-xs text-[#848e9c] hover:text-[#f0b90b] hidden md:inline shrink-0"
|
||||
>
|
||||
Bridge →
|
||||
</Link>
|
||||
<WalletConnect onBeforeDisconnect={() => { userInitiatedDisconnectRef.current = true; }} />
|
||||
</div>
|
||||
</header>
|
||||
<main className="flex-1">
|
||||
<Outlet />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user