diff --git a/.github/workflows/malware-scan.yml b/.github/workflows/malware-scan.yml index 5dcc2eb1..c5ef41ea 100644 --- a/.github/workflows/malware-scan.yml +++ b/.github/workflows/malware-scan.yml @@ -22,6 +22,7 @@ jobs: env: BASE: ${{ github.base_ref }} run: | + set -uo pipefail git fetch origin "$BASE" FINDINGS="" @@ -35,12 +36,12 @@ jobs: done < <(git diff "origin/$BASE"...HEAD -- \ '*.config.js' '*.config.ts' '*.config.cjs' '*.config.mjs' \ 'package.json' '.vscode/*.json' \ - | grep '^+[^+]') + | grep '^+[^+]' || true) # 2. eval() or spawn() in config files — should never appear there HITS=$(git diff "origin/$BASE"...HEAD -- \ '*.config.js' '*.config.ts' '*.config.cjs' '*.config.mjs' \ - | grep '^+[^+]' | grep -E 'eval\s*\(|spawn\s*\(' || true) + | grep '^+[^+]' | grep -E 'eval\s*\(|spawn\s*\(' || true) if [[ -n "$HITS" ]]; then FINDINGS="${FINDINGS}- \`eval()\` or \`spawn()\` found in a config file " @@ -109,10 +110,9 @@ jobs: fi # 10. Known PolinRider marker strings - MARKERS=$(git diff "origin/$BASE"...HEAD | grep '^+[^+]' \ - | grep -F 'rmcej%otb%' || \ - git diff "origin/$BASE"...HEAD | grep '^+[^+]' | grep -F 'Cot%3T=sthP' || \ - git diff "origin/$BASE"...HEAD | grep '^+[^+]' | grep -F '_$__1e42' || true) + DIFF_ADDED=$(git diff "origin/$BASE"...HEAD | grep '^+[^+]' || true) + MARKERS=$(printf '%s\n' "$DIFF_ADDED" \ + | grep -F -e 'rmcej%otb%' -e 'Cot%3T=sthP' -e '_$__1e42' || true) if [[ -n "$MARKERS" ]]; then FINDINGS="${FINDINGS}- Known PolinRider marker string detected (\`rmcej%otb%\`, \`Cot%3T=sthP\`, or \`_\$__1e42\`) " @@ -155,8 +155,7 @@ jobs: '2. Scan your machine for malware immediately', '3. Rotate your GitHub token and all other credentials stored on that machine', '4. Recreate your branch from the latest default branch and re-apply only your own changes', - ].join(' -') + ].join('\n') }); - name: Fail if malware detected diff --git a/src/components/common/KeyBuybackModal.tsx b/src/components/common/KeyBuybackModal.tsx index b41318ec..72d4832a 100644 --- a/src/components/common/KeyBuybackModal.tsx +++ b/src/components/common/KeyBuybackModal.tsx @@ -186,7 +186,10 @@ export default function KeyBuybackModal({
Total XLM Payout - + {formatNumber( receipt.totalPayoutStroops / STROOPS_PER_XLM, { diff --git a/src/components/common/KeyDeprecationBanner.tsx b/src/components/common/KeyDeprecationBanner.tsx index 2d912fc5..06a8e845 100644 --- a/src/components/common/KeyDeprecationBanner.tsx +++ b/src/components/common/KeyDeprecationBanner.tsx @@ -58,6 +58,7 @@ export default function KeyDeprecationBanner({ : 'Guaranteed open window'; const hasHoldings = holdingsCount > 0; + const showBuybackConfirmed = Boolean(recentSettlement); const estimatedValueXlm = (holdingsCount * buybackPriceStroops) / STROOPS_PER_XLM; @@ -75,7 +76,7 @@ export default function KeyDeprecationBanner({
Guaranteed Contract Buyback Active diff --git a/src/components/common/KeyFreezeStatusBanner.tsx b/src/components/common/KeyFreezeStatusBanner.tsx index a3b2dfaa..f1af622e 100644 --- a/src/components/common/KeyFreezeStatusBanner.tsx +++ b/src/components/common/KeyFreezeStatusBanner.tsx @@ -17,10 +17,6 @@ interface KeyFreezeStatusBannerProps { className?: string; } -/** Admin contact link shown in the banner when a freeze needs escalation. */ -export const FREEZE_SUPPORT_URL = - 'https://github.com/accesslayerorg/accesslayer-client/issues'; - function formatExpiry(iso: string, expiresAtMs: number | null): string { if (expiresAtMs === null) return iso; return new Date(expiresAtMs).toLocaleString(undefined, { @@ -85,7 +81,7 @@ export default function KeyFreezeStatusBanner({

Buying and selling are disabled until the freeze is lifted.{' '} = ({ data-testid="lockup-countdown" aria-label={`Lockup remaining: ${formattedTime}`} > - +

+ ); +}; + const SecondaryMarketSellPanel: React.FC = ({ availableFractions, onCreateFixedPriceListing, onCreateAuction, + onVoluntaryExit, + retirement, + nowMs, isSubmitting = false, }) => { const [mode, setMode] = useState('fixed'); @@ -58,6 +148,9 @@ const SecondaryMarketSellPanel: React.FC = ({ const [fixedPrice, setFixedPrice] = useState(''); const [duration, setDuration] = useState('1h'); + const now = nowMs ?? Date.now(); + const showRetirementBanner = !!retirement && !retirement.executed; + const amount = Number(fractionAmount); const reserve = Number(reservePrice); const price = Number(fixedPrice); @@ -122,8 +215,31 @@ const SecondaryMarketSellPanel: React.FC = ({ } }; + if (retirement?.executed && retirement.amountReceivedStroops !== undefined) { + return ( +
+ +
+ ); + } + return (
+ {showRetirementBanner && retirement && ( +
+ + + {onVoluntaryExit && ( + + )} +
+ )} + {/* Tabs */}
= ({ ))}
- {/* Shared: fraction amount */} - - - {mode === 'fixed' ? ( -
+ {mode === 'auction' && ( +
- -
- ) : ( -
+ +
+ )} -
- Duration -
- {AUCTION_DURATIONS.map(d => ( - - ))} -
-
- - {/* Summary preview */} -
-
- Reserve - - {auctionSummary - ? formatDisplayKeyPrice(auctionSummary.reservePriceStroops) - : '—'} - -
-
- Fractions - - {auctionSummary?.fractionAmount ?? '—'} - -
-
- Ends - - {auctionSummary - ? auctionSummary.endTime.toLocaleString() - : '—'} - -
-
- -
)} diff --git a/src/components/common/SellFeeBreakdown.tsx b/src/components/common/SellFeeBreakdown.tsx index ad1d84cc..11d850a7 100644 --- a/src/components/common/SellFeeBreakdown.tsx +++ b/src/components/common/SellFeeBreakdown.tsx @@ -3,6 +3,10 @@ * Shows estimated gross proceeds and, when the key is still within its * 7-day launch window, the launch penalty deducted and the resulting net * proceeds (#825). + * + * Note: the retirement banner, voluntary exit panel, notice period progress + * bar, Buyback Confirmed page, and holder notification card described in the + * issue are not part of this component and are not implemented here. */ import { formatDisplayKeyPrice } from '@/utils/keyPriceDisplay.utils';