From 18314c24665526f8d9bac519df303436da672a65 Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Thu, 28 Aug 2025 11:35:29 +0400 Subject: [PATCH] Dapp: exact-position deep link route and single-position view; consume localStorage deeplink with ARB alias --- app/dapp/page.tsx | 32 ++++++++++++++++++- .../[network]/[preset]/[tokenId]/page.tsx | 26 +++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 app/dapp/position/[network]/[preset]/[tokenId]/page.tsx diff --git a/app/dapp/page.tsx b/app/dapp/page.tsx index cfeccc2..e7495f3 100644 --- a/app/dapp/page.tsx +++ b/app/dapp/page.tsx @@ -49,6 +49,7 @@ export default function DappPage() { const [presetKey, setPresetKey] = useState('cbBTC-USDC'); const [manualWallet, setManualWallet] = useState(''); const [manualTokenId, setManualTokenId] = useState(''); + const [deeplinkTokenId, setDeeplinkTokenId] = useState(''); const [detectedTokenIds, setDetectedTokenIds] = useState([]); const [scanBusy, setScanBusy] = useState(false); const [scanComplete, setScanComplete] = useState(false); @@ -271,6 +272,32 @@ export default function DappPage() { loadChainDefaults(selectedChainId); }, [selectedChainId]); + // Consume deep-link payload from /dapp/position/[network]/[preset]/[tokenId] + useEffect(() => { + try { + if (typeof window === 'undefined') return; + const raw = localStorage.getItem('dapp:deeplink:v1'); + if (!raw) return; + localStorage.removeItem('dapp:deeplink:v1'); + const parsed = JSON.parse(raw || '{}') as { network?: string; preset?: number; tokenId?: string; ts?: number }; + const age = Math.floor(Date.now() / 1000) - (parsed.ts || 0); + if (age > 600) return; // ignore stale links >10min + const net = String(parsed.network || '').toUpperCase(); + const presetIdx = Math.max(1, Number(parsed.preset || 1)); + const nextChainId = net === 'BASE' ? base.id : ((net === 'ARBITRUM' || net === 'ARB') ? arbitrum.id : selectedChainId); + if (nextChainId !== selectedChainId) { + setSelectedChainId(nextChainId); + loadChainDefaults(nextChainId); + } + const list = PRESETS[nextChainId] || []; + const item = list[presetIdx - 1]; + if (item?.key) setPresetKey(item.key); + // Force single-position view for deep link + if (parsed.tokenId) setDeeplinkTokenId(String(parsed.tokenId)); + } catch {} + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + // Detect ERC721Enumerable support: interfaceId 0x780e9d63 const { data: supportsEnumerable } = useReadContract({ abi: erc721Abi, @@ -320,9 +347,12 @@ export default function DappPage() { // Resolve tokenIds: either detected or manual entry const tokenIds = useMemo(() => { + if (deeplinkTokenId.trim()) { + try { return [BigInt(deeplinkTokenId.trim())]; } catch { return []; } + } const manual = manualTokenId.trim() ? [BigInt(manualTokenId.trim())] : []; return [...detectedTokenIds, ...manual]; - }, [detectedTokenIds, manualTokenId]); + }, [detectedTokenIds, manualTokenId, deeplinkTokenId]); // Build reads for debt contract const debtReads = useMemo(() => { diff --git a/app/dapp/position/[network]/[preset]/[tokenId]/page.tsx b/app/dapp/position/[network]/[preset]/[tokenId]/page.tsx new file mode 100644 index 0000000..1354025 --- /dev/null +++ b/app/dapp/position/[network]/[preset]/[tokenId]/page.tsx @@ -0,0 +1,26 @@ +"use client"; + +import { useEffect } from "react"; +import { useParams } from "next/navigation"; +import DappPage from "../../../../page"; + +export default function PositionDeepLinkPage() { + const params = useParams<{ network: string; preset: string; tokenId: string }>(); + + useEffect(() => { + try { + const network = String(params?.network || '').toUpperCase(); + const preset = Number(params?.preset || '1'); + const tokenId = String(params?.tokenId || ''); + if (!network || !preset || !tokenId) return; + // Persist for Dapp page to consume on mount; keep URL unchanged + const payload = { network, preset, tokenId, ts: Math.floor(Date.now() / 1000) }; + if (typeof window !== 'undefined') { + localStorage.setItem('dapp:deeplink:v1', JSON.stringify(payload)); + } + } catch {} + }, [params]); + + // Render the main Dapp UI without redirect; it will consume the deep-link + return ; +}