diff --git a/web/packages/api/package.json b/web/packages/api/package.json index 764db5d5cf..2dfc34b084 100644 --- a/web/packages/api/package.json +++ b/web/packages/api/package.json @@ -1,6 +1,6 @@ { "name": "@snowbridge/api", - "version": "0.1.29", + "version": "0.1.30-alpha.1", "description": "Snowbridge API client", "license": "Apache-2.0", "repository": { diff --git a/web/packages/api/src/history_v2.ts b/web/packages/api/src/history_v2.ts index 99470e485c..58779023d7 100644 --- a/web/packages/api/src/history_v2.ts +++ b/web/packages/api/src/history_v2.ts @@ -1,4 +1,9 @@ -import { fetchToPolkadotTransfers, fetchToEthereumTransfers } from "./subsquid" +import { + fetchToPolkadotTransfers, + fetchToEthereumTransfers, + fetchToPolkadotTransferById, + fetchToEthereumTransferById, +} from "./subsquid" import { forwardedTopicId, getEventIndex } from "./utils" export enum TransferStatus { @@ -124,64 +129,134 @@ export type ToEthereumTransferResult = { } } -export const toPolkadotHistory = async (): Promise => { - const ethOutboundMessages = await fetchToPolkadotTransfers() - const results: ToPolkadotTransferResult[] = [] - for (const outboundMessage of ethOutboundMessages) { - const result: ToPolkadotTransferResult = { - id: outboundMessage.id, - status: TransferStatus.Pending, - info: { - when: new Date(outboundMessage.timestamp), - sourceAddress: outboundMessage.senderAddress, - beneficiaryAddress: outboundMessage.destinationAddress, - tokenAddress: outboundMessage.tokenAddress, - destinationParachain: outboundMessage.destinationParaId, - destinationFee: "", - amount: outboundMessage.amount, - }, - submitted: { - blockHash: "", - blockNumber: outboundMessage.blockNumber, - logIndex: 0, - transactionHash: outboundMessage.txHash, - transactionIndex: 0, - channelId: outboundMessage.channelId, - messageId: outboundMessage.messageId, - nonce: outboundMessage.nonce, - }, +const buildToPolkadotTransferResult = (transfer: any): ToPolkadotTransferResult => { + const result: ToPolkadotTransferResult = { + id: transfer.id, + status: TransferStatus.Pending, + info: { + when: new Date(transfer.timestamp), + sourceAddress: transfer.senderAddress, + beneficiaryAddress: transfer.destinationAddress, + tokenAddress: transfer.tokenAddress, + destinationParachain: transfer.destinationParaId, + destinationFee: "", + amount: transfer.amount, + }, + submitted: { + blockHash: "", + blockNumber: transfer.blockNumber, + logIndex: 0, + transactionHash: transfer.txHash, + transactionIndex: 0, + channelId: transfer.channelId, + messageId: transfer.messageId, + nonce: transfer.nonce, + }, + } + let inboundMessageReceived = transfer.toBridgeHubInboundQueue + if (inboundMessageReceived) { + result.inboundMessageReceived = { + extrinsic_index: "", + extrinsic_hash: "", + event_index: getEventIndex(inboundMessageReceived.id), + block_timestamp: inboundMessageReceived.timestamp, + messageId: inboundMessageReceived.messageId, + channelId: inboundMessageReceived.channelId, + nonce: inboundMessageReceived.nonce, + } + } + + const assetHubMessageProcessed = transfer.toDestination || transfer.toAssetHubMessageQueue + if (assetHubMessageProcessed) { + result.assetHubMessageProcessed = { + extrinsic_hash: "", + event_index: getEventIndex(assetHubMessageProcessed.id), + block_timestamp: assetHubMessageProcessed.timestamp, + success: assetHubMessageProcessed.success, + sibling: 0, + } + result.status = TransferStatus.Complete + if (!assetHubMessageProcessed.success) { + result.status = TransferStatus.Failed + } + } + return result +} + +const buildToEthereumTransferResult = (transfer: any): ToEthereumTransferResult => { + let bridgeHubMessageId = forwardedTopicId(transfer.id) + const result: ToEthereumTransferResult = { + id: transfer.id, + status: TransferStatus.Pending, + info: { + when: new Date(transfer.timestamp), + sourceAddress: transfer.senderAddress, + tokenAddress: transfer.tokenAddress, + beneficiaryAddress: transfer.destinationAddress, + amount: transfer.amount, + }, + submitted: { + extrinsic_index: "", + extrinsic_hash: transfer.txHash, + block_hash: "", + account_id: transfer.senderAddress, + block_num: transfer.blockNumber, + block_timestamp: transfer.timestamp, + messageId: transfer.id, + bridgeHubMessageId, + success: true, + }, + } + let bridgeHubXcmDelivered = transfer.toBridgeHubMessageQueue + if (bridgeHubXcmDelivered) { + result.bridgeHubXcmDelivered = { + block_timestamp: bridgeHubXcmDelivered.timestamp, + event_index: getEventIndex(bridgeHubXcmDelivered.id), + extrinsic_hash: "", + siblingParachain: 1000, + success: bridgeHubXcmDelivered.success, } - let inboundMessageReceived = outboundMessage.toBridgeHubInboundQueue - if (inboundMessageReceived) { - result.inboundMessageReceived = { - extrinsic_index: "", - extrinsic_hash: "", - event_index: getEventIndex(inboundMessageReceived.id), - block_timestamp: inboundMessageReceived.timestamp, - messageId: inboundMessageReceived.messageId, - channelId: inboundMessageReceived.channelId, - nonce: inboundMessageReceived.nonce, - } + if (!bridgeHubXcmDelivered.success) { + result.status = TransferStatus.Failed + return result } + } - const assetHubMessageProcessed = - outboundMessage.toDestination || outboundMessage.toAssetHubMessageQueue - if (assetHubMessageProcessed) { - result.assetHubMessageProcessed = { - extrinsic_hash: "", - event_index: getEventIndex(assetHubMessageProcessed.id), - block_timestamp: assetHubMessageProcessed.timestamp, - success: assetHubMessageProcessed.success, - sibling: 0, - } - if (!assetHubMessageProcessed.success) { - result.status = TransferStatus.Failed - continue - } + let outboundQueueAccepted = transfer.toBridgeHubOutboundQueue + if (outboundQueueAccepted) { + result.bridgeHubMessageQueued = { + block_timestamp: outboundQueueAccepted.timestamp, + event_index: getEventIndex(outboundQueueAccepted.id), + extrinsic_hash: "", + } + } - result.status = TransferStatus.Complete + let ethereumMessageDispatched = transfer.toDestination + if (ethereumMessageDispatched) { + result.ethereumMessageDispatched = { + blockNumber: ethereumMessageDispatched.blockNumber, + blockHash: "", + transactionHash: ethereumMessageDispatched.txHash, + transactionIndex: 0, + logIndex: 0, + messageId: ethereumMessageDispatched.messageId, + channelId: ethereumMessageDispatched.channelId, + nonce: ethereumMessageDispatched.nonce, + success: ethereumMessageDispatched.success, + } + result.status = TransferStatus.Complete + if (!ethereumMessageDispatched.success) { + result.status = TransferStatus.Failed } + } + return result +} +export const toPolkadotHistory = async (): Promise => { + const allTransfers = await fetchToPolkadotTransfers() + const results: ToPolkadotTransferResult[] = [] + for (const transfer of allTransfers) { + let result = buildToPolkadotTransferResult(transfer) results.push(result) } return results @@ -191,73 +266,30 @@ export const toEthereumHistory = async (): Promise = const allTransfers = await fetchToEthereumTransfers() const results: ToEthereumTransferResult[] = [] for (const transfer of allTransfers) { - let bridgeHubMessageId = forwardedTopicId(transfer.id) - const result: ToEthereumTransferResult = { - id: transfer.id, - status: TransferStatus.Pending, - info: { - when: new Date(transfer.timestamp), - sourceAddress: transfer.senderAddress, - tokenAddress: transfer.tokenAddress, - beneficiaryAddress: transfer.destinationAddress, - amount: transfer.amount, - }, - submitted: { - extrinsic_index: "", - extrinsic_hash: transfer.txHash, - block_hash: "", - account_id: transfer.senderAddress, - block_num: transfer.blockNumber, - block_timestamp: transfer.timestamp, - messageId: transfer.id, - bridgeHubMessageId, - success: true, - }, - } - let bridgeHubXcmDelivered = transfer.toBridgeHubMessageQueue - if (bridgeHubXcmDelivered) { - result.bridgeHubXcmDelivered = { - block_timestamp: bridgeHubXcmDelivered.timestamp, - event_index: getEventIndex(bridgeHubXcmDelivered.id), - extrinsic_hash: "", - siblingParachain: 1000, - success: bridgeHubXcmDelivered.success, - } - if (!bridgeHubXcmDelivered.success) { - result.status = TransferStatus.Failed - continue - } - } - - let outboundQueueAccepted = transfer.toBridgeHubOutboundQueue - if (outboundQueueAccepted) { - result.bridgeHubMessageQueued = { - block_timestamp: outboundQueueAccepted.timestamp, - event_index: getEventIndex(outboundQueueAccepted.id), - extrinsic_hash: "", - } - } - - let ethereumMessageDispatched = transfer.toDestination - if (ethereumMessageDispatched) { - result.ethereumMessageDispatched = { - blockNumber: ethereumMessageDispatched.blockNumber, - blockHash: "", - transactionHash: ethereumMessageDispatched.txHash, - transactionIndex: 0, - logIndex: 0, - messageId: ethereumMessageDispatched.messageId, - channelId: ethereumMessageDispatched.channelId, - nonce: ethereumMessageDispatched.nonce, - success: ethereumMessageDispatched.success, - } - if (!ethereumMessageDispatched.success) { - result.status = TransferStatus.Failed - continue - } - result.status = TransferStatus.Complete - } + let result = buildToEthereumTransferResult(transfer) results.push(result) } return results } + +export const toPolkadotTransferById = async ( + id: string +): Promise => { + const transfers = await fetchToPolkadotTransferById(id) + if (transfers?.length > 0) { + let result = buildToPolkadotTransferResult(transfers[0]) + return result + } + return +} + +export const toEthereumTransferById = async ( + id: string +): Promise => { + const transfers = await fetchToEthereumTransferById(id) + if (transfers?.length > 0) { + let result = buildToEthereumTransferResult(transfers[0]) + return result + } + return +} diff --git a/web/packages/api/src/subsquid.ts b/web/packages/api/src/subsquid.ts index 85be6adb51..c101f772f9 100644 --- a/web/packages/api/src/subsquid.ts +++ b/web/packages/api/src/subsquid.ts @@ -259,3 +259,160 @@ export const queryByGraphQL = async (query: string) => { let data = await response.json() return data?.data } + +/** + * Query transfer from Ethereum to Polkadot by MessageID or TxHash + +``` +curl -H 'Content-Type: application/json' \ +-X POST -d \ +'{ "query": "query { transferStatusToPolkadots(limit: 5, orderBy: blockNumber_DESC) { txHash status channelId destinationAddress messageId nonce senderAddress timestamp tokenAddress amount} }" }' \ +$graphqlApiUrl --no-progress-meter | jq "." +``` + +* @param txHash - the transaction hash on source chain +* @param status - 0:pending, 1: completed 2: failed +* @param messageId - a global index to trace the transfer in different chains +* @param toBridgeHubInboundQueue - transfer received in inbound queue on bridge hub +* @param toAssetHubMessageQueue - transfer received in message queue on asset hub +* @param toDestination - transfer received in message queue on the destination chain, if destination is asset hub then same as toAssetHubMessageQueue +* +"transferStatusToPolkadots": [ + { + + "txHash": "0x53597b6f98334a160f26182398ec3e7368be8ca7aea3eea41d288046f3a1999d", + "status": 1, + "channelId": "0xc173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539", + "destinationAddress": "0x628119c736c0e8ff28bd2f42920a4682bd6feb7b000000000000000000000000", + "messageId": "0x00d720d39256bab74c0be362005b9a50951a0909e6dabda588a5d319bfbedb65", + "nonce": 561, + "senderAddress": "0x628119c736c0e8ff28bd2f42920a4682bd6feb7b", + "timestamp": "2025-01-20T07:09:47.000000Z", + "tokenAddress": "0xba41ddf06b7ffd89d1267b5a93bfef2424eb2003", + "amount": "68554000000000000000000", + "toBridgeHubInboundQueue:": {"messageId":"0x00d720d39256bab74c0be362005b9a50951a0909e6dabda588a5d319bfbedb65",...}, + "toAssetHubMessageQueue": {"messageId":"0x00d720d39256bab74c0be362005b9a50951a0909e6dabda588a5d319bfbedb65",...}, + "toDestination": {"messageId":"0x00d720d39256bab74c0be362005b9a50951a0909e6dabda588a5d319bfbedb65",...} + } +] + **/ +export const fetchToPolkadotTransferById = async (id: string) => { + let query = `query { transferStatusToPolkadots(where: {messageId_eq: "${id}", OR: {txHash_eq: "${id}"}}) { + id + status + blockNumber + channelId + destinationAddress + destinationParaId + messageId + nonce + senderAddress + timestamp + tokenAddress + txHash + amount + toBridgeHubInboundQueue { + id + timestamp + txHash + channelId + nonce + messageId + } + toAssetHubMessageQueue { + id + success + timestamp + } + toDestination { + id + success + timestamp + } + } + }` + let result = await queryByGraphQL(query) + return result?.transferStatusToPolkadots +} + +/** + * Query the transfer from Polkadot to Ethereum by MessageID or TxHash + +``` +curl -H 'Content-Type: application/json' \ +-X POST -d \ +'{ "query": "query { transferStatusToEthereums(limit: 5, orderBy: blockNumber_DESC) { txHash status channelId destinationAddress messageId nonce senderAddress timestamp tokenAddress amount} }" }' \ +$graphqlApiUrl --no-progress-meter | jq "." +``` + +* @param txHash - the transaction hash on source chain +* @param status - 0:pending, 1: completed 2: failed +* @param messageId - a global index to trace the transfer in different chains +* @param toAssetHubMessageQueue - transfer received in message queue on asset hub +* @param toBridgeHubMessageQueue - transfer received in message queue on bridge hub +* @param toBridgeHubOutboundQueue - transfer received in outbound queue on bridge hub +* @param toDestination - transfer received on the destination chain(Ethereum) +* +"transferStatusToEthereums": [ + { + + "txHash": "0x53597b6f98334a160f26182398ec3e7368be8ca7aea3eea41d288046f3a1999d", + "status": 1, + "channelId": "0xc173fac324158e77fb5840738a1a541f633cbec8884c6a601c567d2b376a0539", + "destinationAddress": "0x628119c736c0e8ff28bd2f42920a4682bd6feb7b000000000000000000000000", + "messageId": "0x00d720d39256bab74c0be362005b9a50951a0909e6dabda588a5d319bfbedb65", + "nonce": 561, + "senderAddress": "0x628119c736c0e8ff28bd2f42920a4682bd6feb7b", + "timestamp": "2025-01-20T07:09:47.000000Z", + "tokenAddress": "0xba41ddf06b7ffd89d1267b5a93bfef2424eb2003", + "amount": "68554000000000000000000", + "toAssetHubMessageQueue": {"messageId":"0x00d720d39256bab74c0be362005b9a50951a0909e6dabda588a5d319bfbedb65",...}, + "toBridgeHubOutboundQueue:": {"messageId":"0x00d720d39256bab74c0be362005b9a50951a0909e6dabda588a5d319bfbedb65",...}, + "toDestination": {"messageId":"0x00d720d39256bab74c0be362005b9a50951a0909e6dabda588a5d319bfbedb65",...} + } +] + **/ +export const fetchToEthereumTransferById = async (id: string) => { + let query = `query { transferStatusToEthereums(where: {messageId_eq: "${id}", OR: {txHash_eq: "${id}"}}) { + id + status + blockNumber + channelId + destinationAddress + messageId + nonce + senderAddress + sourceParaId + timestamp + tokenAddress + txHash + amount + toAssetHubMessageQueue { + id + success + timestamp + } + toBridgeHubMessageQueue { + id + success + timestamp + } + toBridgeHubOutboundQueue { + id + timestamp + } + toDestination { + id + blockNumber + timestamp + txHash + success + messageId + nonce + channelId + } + } + }` + let result = await queryByGraphQL(query) + return result?.transferStatusToEthereums +} diff --git a/web/packages/contract-types/package.json b/web/packages/contract-types/package.json index 5b1e8c242e..26253ee4b5 100644 --- a/web/packages/contract-types/package.json +++ b/web/packages/contract-types/package.json @@ -1,6 +1,6 @@ { "name": "@snowbridge/contract-types", - "version": "0.1.29", + "version": "0.1.30-alpha.1", "description": "Snowbridge contract type bindings", "license": "Apache-2.0", "repository": { diff --git a/web/packages/contracts/package.json b/web/packages/contracts/package.json index 6a9154f441..64c91c5405 100644 --- a/web/packages/contracts/package.json +++ b/web/packages/contracts/package.json @@ -1,6 +1,6 @@ { "name": "@snowbridge/contracts", - "version": "0.1.29", + "version": "0.1.30-alpha.1", "description": "Snowbridge contract source and abi.", "license": "Apache-2.0", "repository": { diff --git a/web/packages/operations/src/global_transfer_history_v2.ts b/web/packages/operations/src/global_transfer_history_v2.ts index bbc3466793..c3d78a6887 100644 --- a/web/packages/operations/src/global_transfer_history_v2.ts +++ b/web/packages/operations/src/global_transfer_history_v2.ts @@ -2,15 +2,25 @@ import "dotenv/config" import { historyV2 } from "@snowbridge/api" const monitor = async () => { - const toEthereum = await historyV2.toEthereumHistory() - console.log(JSON.stringify(toEthereum, null, 2)) + const toEthereums = await historyV2.toEthereumHistory() + console.log(JSON.stringify(toEthereums, null, 2)) - const toPolkadot = await historyV2.toPolkadotHistory() - console.log(JSON.stringify(toPolkadot, null, 2)) + const toPolkadots = await historyV2.toPolkadotHistory() + console.log(JSON.stringify(toPolkadots, null, 2)) - const transfers = [...toEthereum, ...toPolkadot] + const transfers = [...toEthereums, ...toPolkadots] transfers.sort((a, b) => b.info.when.getTime() - a.info.when.getTime()) console.log(JSON.stringify(transfers, null, 2)) + + const toPolkadot = await historyV2.toPolkadotTransferById( + "0xb56662848712da9769a2122ca0d24d199ef7af7c8aedee43778dadbe1c42ebc6" + ) + console.log(JSON.stringify(toPolkadot, null, 2)) + + const toEthereum = await historyV2.toEthereumTransferById( + "0x04b7a6c7552d2890094dfe43e037cb5f5495fec2419f33b0072439a9ee7629a0" + ) + console.log(JSON.stringify(toEthereum, null, 2)) } monitor()