Skip to content

Commit ffd6199

Browse files
committed
adjust htlc example
1 parent 086a722 commit ffd6199

1 file changed

Lines changed: 219 additions & 70 deletions

File tree

  • examples/swap-board-ml-btc/src/app/swap/[id]

examples/swap-board-ml-btc/src/app/swap/[id]/page.tsx

Lines changed: 219 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ export default function SwapPage({ params }: { params: { id: string } }) {
5151
fetchTokens()
5252

5353
// Poll for updates every 10 seconds
54-
const interval = setInterval(fetchSwap, 10000)
54+
const interval = setInterval(() => {
55+
fetchSwap()
56+
checkHTLCSpendStatus()
57+
}, 10000)
5558
return () => clearInterval(interval)
5659
}, [params.id])
5760

@@ -108,6 +111,140 @@ export default function SwapPage({ params }: { params: { id: string } }) {
108111
}
109112
}
110113

114+
const checkHTLCSpendStatus = async () => {
115+
if (!swap || !client) return
116+
117+
try {
118+
const network = (process.env.NEXT_PUBLIC_MINTLAYER_NETWORK as 'testnet' | 'mainnet') || 'testnet'
119+
const apiServer = network === 'mainnet'
120+
? 'https://api-server.mintlayer.org/api/v2'
121+
: 'https://api-server-lovelace.mintlayer.org/api/v2'
122+
123+
let needsUpdate = false
124+
const updates: any = {}
125+
let creatorHtlcSpent = false
126+
let takerHtlcSpent = false
127+
let creatorHtlcSpendTxId: string | null = null
128+
let takerHtlcSpendTxId: string | null = null
129+
130+
// Check creator's ML HTLC
131+
if (swap.creatorHtlcTxHash) {
132+
try {
133+
const outputResponse = await fetch(`${apiServer}/transaction/${swap.creatorHtlcTxHash}/output/0`)
134+
if (outputResponse.ok) {
135+
const outputData = await outputResponse.json()
136+
creatorHtlcSpent = outputData.spent === true
137+
if (creatorHtlcSpent && outputData.spent_by) {
138+
creatorHtlcSpendTxId = outputData.spent_by
139+
console.log('Creator HTLC has been spent by:', creatorHtlcSpendTxId)
140+
}
141+
}
142+
} catch (error) {
143+
console.error('Error checking creator HTLC spend status:', error)
144+
}
145+
}
146+
147+
// Check taker's ML HTLC
148+
if (swap.takerHtlcTxHash) {
149+
try {
150+
const outputResponse = await fetch(`${apiServer}/transaction/${swap.takerHtlcTxHash}/output/0`)
151+
if (outputResponse.ok) {
152+
const outputData = await outputResponse.json()
153+
takerHtlcSpent = outputData.spent === true
154+
if (takerHtlcSpent && outputData.spent_by) {
155+
takerHtlcSpendTxId = outputData.spent_by
156+
console.log('Taker HTLC has been spent by:', takerHtlcSpendTxId)
157+
}
158+
}
159+
} catch (error) {
160+
console.error('Error checking taker HTLC spend status:', error)
161+
}
162+
}
163+
164+
// Determine status based on what's been spent
165+
if (creatorHtlcSpent && takerHtlcSpent) {
166+
// Both HTLCs spent = fully completed
167+
if (swap.status !== 'fully_completed') {
168+
needsUpdate = true
169+
updates.status = 'fully_completed'
170+
}
171+
} else if (creatorHtlcSpent || takerHtlcSpent) {
172+
// One HTLC spent = first claim completed, need to extract secret
173+
if (swap.status === 'fully_completed') {
174+
// Downgrade from fully_completed to completed if only one is actually spent
175+
needsUpdate = true
176+
updates.status = 'completed'
177+
console.log('Downgrading status from fully_completed to completed - only one HTLC is spent')
178+
} else if (swap.status !== 'completed') {
179+
needsUpdate = true
180+
updates.status = 'completed'
181+
}
182+
183+
// Try to extract the secret from the claim transaction if we don't have it
184+
if (!swap.secret) {
185+
try {
186+
const spendTxId = creatorHtlcSpent ? creatorHtlcSpendTxId : takerHtlcSpendTxId
187+
const htlcTxId = creatorHtlcSpent ? swap.creatorHtlcTxHash : swap.takerHtlcTxHash
188+
189+
if (spendTxId && htlcTxId) {
190+
console.log('Attempting to extract secret from claim transaction:', spendTxId)
191+
192+
// Fetch the claim transaction to get its hex
193+
const claimTxResponse = await fetch(`${apiServer}/transaction/${spendTxId}`)
194+
if (claimTxResponse.ok) {
195+
const claimTxData = await claimTxResponse.json()
196+
const claimTxHex = claimTxData.hex
197+
198+
if (claimTxHex) {
199+
// Extract the secret using the SDK
200+
const extractedSecret = await client.extractHtlcSecret({
201+
transaction_id: spendTxId,
202+
transaction_hex: claimTxHex,
203+
format: 'hex'
204+
})
205+
206+
console.log('Successfully extracted secret:', extractedSecret)
207+
updates.secret = extractedSecret
208+
updates.claimTxHash = spendTxId
209+
updates.claimTxHex = claimTxHex
210+
needsUpdate = true
211+
}
212+
}
213+
}
214+
} catch (error) {
215+
console.error('Error extracting secret from claim transaction:', error)
216+
// Continue with status update even if secret extraction fails
217+
}
218+
}
219+
} else {
220+
// Neither HTLC is spent - downgrade status if needed
221+
if (swap.status === 'completed' || swap.status === 'fully_completed') {
222+
needsUpdate = true
223+
// Determine appropriate status based on what HTLCs exist
224+
if (swap.creatorHtlcTxHash && swap.takerHtlcTxHash) {
225+
updates.status = 'in_progress'
226+
} else if (swap.creatorHtlcTxHash || swap.takerHtlcTxHash) {
227+
updates.status = 'htlc_created'
228+
}
229+
console.log('Downgrading status - no HTLCs are actually spent on-chain')
230+
}
231+
}
232+
233+
// Update swap if needed
234+
if (needsUpdate) {
235+
console.log('Updating swap status based on spend status:', updates)
236+
await fetch(`/api/swaps/${swap.id}`, {
237+
method: 'POST',
238+
headers: { 'Content-Type': 'application/json' },
239+
body: JSON.stringify(updates)
240+
})
241+
fetchSwap()
242+
}
243+
} catch (error) {
244+
console.error('Error checking HTLC spend status:', error)
245+
}
246+
}
247+
111248
const connectWallet = async () => {
112249
try {
113250
if (client) {
@@ -889,7 +1026,7 @@ export default function SwapPage({ params }: { params: { id: string } }) {
8891026
case 'btc_htlc_created': return 'BTC HTLC created, waiting for ML HTLC'
8901027
case 'both_htlcs_created': return 'Both HTLCs created, ready to claim'
8911028
case 'in_progress': return 'Claims in progress'
892-
case 'completed': return 'First claim completed, waiting for final claim'
1029+
case 'completed': return 'First claim completed (secret revealed), waiting for final claim'
8931030
case 'fully_completed': return 'Atomic swap completed successfully'
8941031
case 'refunded': return 'ML HTLC refunded due to timeout'
8951032
case 'btc_refunded': return 'BTC HTLC refunded due to timeout'
@@ -935,7 +1072,7 @@ export default function SwapPage({ params }: { params: { id: string } }) {
9351072
</button>
9361073
)}
9371074
</div>
938-
<div className="flex items-center space-x-4">
1075+
<div className="flex items-center space-x-4 flex-wrap">
9391076
<span className={`px-3 py-1 rounded-full text-sm font-medium ${getStatusColor(swap.status)}`}>
9401077
{swap.status.toUpperCase()}
9411078
</span>
@@ -945,6 +1082,15 @@ export default function SwapPage({ params }: { params: { id: string } }) {
9451082
Connected: {userAddress.slice(0, 10)}...
9461083
</span>
9471084
)}
1085+
{(swap.status === 'in_progress' || swap.status === 'completed' || swap.status === 'both_htlcs_created') && (
1086+
<button
1087+
onClick={checkHTLCSpendStatus}
1088+
className="text-sm bg-gray-200 text-gray-700 px-3 py-1 rounded-md hover:bg-gray-300"
1089+
title="Check if HTLCs have been claimed on-chain"
1090+
>
1091+
🔄 Check Status
1092+
</button>
1093+
)}
9481094
</div>
9491095
</div>
9501096

@@ -1068,7 +1214,7 @@ export default function SwapPage({ params }: { params: { id: string } }) {
10681214

10691215
<div className="flex items-center">
10701216
<div className={`w-4 h-4 rounded-full mr-3 ${
1071-
(swap.claimTxHash || swap.btcClaimTxId) ? 'bg-green-500' : 'bg-gray-300'
1217+
(swap.claimTxHash || swap.btcClaimTxId) && swap.secret ? 'bg-green-500' : 'bg-gray-300'
10721218
}`}></div>
10731219
<span className="text-sm">
10741220
First claim completed (secret revealed)
@@ -1078,13 +1224,6 @@ export default function SwapPage({ params }: { params: { id: string } }) {
10781224
</span>
10791225
</div>
10801226

1081-
<div className="flex items-center">
1082-
<div className={`w-4 h-4 rounded-full mr-3 ${
1083-
swap.secret ? 'bg-green-500' : 'bg-gray-300'
1084-
}`}></div>
1085-
<span className="text-sm">Secret extracted</span>
1086-
</div>
1087-
10881227
<div className="flex items-center">
10891228
<div className={`w-4 h-4 rounded-full mr-3 ${
10901229
swap.status === 'fully_completed' ||
@@ -1236,74 +1375,84 @@ export default function SwapPage({ params }: { params: { id: string } }) {
12361375

12371376
{(swap.status === 'completed' || swap.status === 'fully_completed') && (
12381377
<div className="bg-green-50 p-4 rounded-md">
1239-
{(swap.claimTxHash || swap.btcClaimTxId) && (
1240-
<div className="space-y-3">
1241-
<p className="text-green-800">
1242-
{swap.status === 'fully_completed'
1243-
? "🎉 Atomic swap completed successfully! Both parties have their tokens."
1244-
: isUserCreator
1245-
? "You have claimed the taker's HTLC!"
1246-
: "The creator has claimed your HTLC!"
1378+
<div className="space-y-3">
1379+
<p className="text-green-800 font-semibold">
1380+
{swap.status === 'fully_completed'
1381+
? "🎉 Atomic swap completed successfully! Both parties have their tokens."
1382+
: "✅ First HTLC claimed! Secret has been revealed."
1383+
}
1384+
</p>
1385+
{swap.status === 'completed' && (
1386+
<p className="text-blue-800 text-sm">
1387+
{isUserCreator
1388+
? "You claimed the taker's HTLC. The taker can now use the revealed secret to claim your HTLC."
1389+
: "The creator claimed your HTLC and revealed the secret. You can now claim the creator's HTLC!"
12471390
}
12481391
</p>
1392+
)}
12491393

1250-
{/* Show ML claim transaction if it exists */}
1251-
{swap.claimTxHash && (
1252-
<p className="text-sm text-green-700">
1253-
ML Claim Transaction: {swap.claimTxHash}
1254-
</p>
1255-
)}
1394+
{/* Show ML claim transaction if it exists */}
1395+
{swap.claimTxHash && (
1396+
<p className="text-sm text-green-700">
1397+
ML Claim Transaction: {swap.claimTxHash}
1398+
</p>
1399+
)}
12561400

1257-
{/* Show BTC claim transaction if it exists */}
1258-
{swap.btcClaimTxId && (
1259-
<p className="text-sm text-green-700">
1260-
BTC Claim Transaction: {swap.btcClaimTxId}
1261-
</p>
1262-
)}
1401+
{/* Show BTC claim transaction if it exists */}
1402+
{swap.btcClaimTxId && (
1403+
<p className="text-sm text-green-700">
1404+
BTC Claim Transaction: {swap.btcClaimTxId}
1405+
</p>
1406+
)}
12631407

1264-
{swap.secret ? (
1265-
<div>
1266-
<div className="bg-white p-2 rounded border mb-3">
1267-
<p className="text-xs font-medium text-gray-700">Revealed Secret:</p>
1268-
<p className="text-xs font-mono text-gray-600 break-all">{swap.secret}</p>
1408+
{swap.secret ? (
1409+
<div>
1410+
<div className="bg-yellow-50 p-3 rounded border mb-3">
1411+
<p className="text-sm font-semibold text-gray-800 mb-2">🔑 Revealed Secret:</p>
1412+
<p className="text-sm font-mono text-gray-900 break-all bg-white p-2 rounded border border-yellow-200">{swap.secret}</p>
1413+
</div>
1414+
1415+
{isUserTaker && swap.status === 'completed' && (
1416+
<div className="space-y-2">
1417+
<p className="text-blue-800 text-sm font-medium">
1418+
👉 Now you can claim the creator's HTLC using this secret:
1419+
</p>
1420+
<button
1421+
onClick={claimWithExtractedSecret}
1422+
disabled={claimingHtlc}
1423+
className="bg-purple-600 text-white px-4 py-2 rounded-md hover:bg-purple-700 disabled:bg-gray-400 font-medium"
1424+
>
1425+
{claimingHtlc ? 'Claiming Creator HTLC...' : '🎯 Claim Creator HTLC'}
1426+
</button>
12691427
</div>
1428+
)}
12701429

1271-
{isUserTaker && (
1272-
<div className="space-y-2">
1273-
<p className="text-blue-800 text-sm">
1274-
Now you can claim the creator's HTLC using this secret:
1275-
</p>
1276-
<button
1277-
onClick={claimWithExtractedSecret}
1278-
disabled={claimingHtlc}
1279-
className="bg-purple-600 text-white px-4 py-2 rounded-md hover:bg-purple-700 disabled:bg-gray-400"
1280-
>
1281-
{claimingHtlc ? 'Claiming Creator HTLC...' : 'Claim Creator HTLC'}
1282-
</button>
1283-
</div>
1284-
)}
1430+
{isUserCreator && swap.status === 'completed' && (
1431+
<p className="text-green-700 text-sm">
1432+
⏳ Waiting for taker to claim your HTLC using the revealed secret...
1433+
</p>
1434+
)}
12851435

1286-
{isUserCreator && (
1287-
<p className="text-green-700 text-sm">
1288-
✅ Atomic swap completed! Both parties have their tokens.
1289-
</p>
1290-
)}
1291-
</div>
1292-
) : (
1293-
<div>
1294-
<p className="text-blue-800 text-sm mb-2">
1295-
{isUserTaker ? "Extract the secret to claim the creator's HTLC:" : "The taker needs to extract the secret:"}
1436+
{swap.status === 'fully_completed' && (
1437+
<p className="text-green-700 text-sm font-medium">
1438+
✅ Atomic swap completed! Both parties have their tokens.
12961439
</p>
1297-
<button
1298-
onClick={extractSecretFromClaim}
1299-
className="bg-blue-600 text-white px-3 py-1 rounded text-sm hover:bg-blue-700"
1300-
>
1301-
Extract Secret from Claim
1302-
</button>
1303-
</div>
1304-
)}
1305-
</div>
1306-
)}
1440+
)}
1441+
</div>
1442+
) : (
1443+
<div>
1444+
<p className="text-orange-800 text-sm mb-2 font-medium">
1445+
⚠️ Secret not yet extracted. Click below to extract it from the claim transaction:
1446+
</p>
1447+
<button
1448+
onClick={extractSecretFromClaim}
1449+
className="bg-blue-600 text-white px-4 py-2 rounded text-sm hover:bg-blue-700 font-medium"
1450+
>
1451+
🔍 Extract Secret from Claim
1452+
</button>
1453+
</div>
1454+
)}
1455+
</div>
13071456
</div>
13081457
)}
13091458

0 commit comments

Comments
 (0)