From 49b2c2a4f62940c61fc53bdcb4ea76fb8c5dd884 Mon Sep 17 00:00:00 2001 From: Paul Noel Date: Thu, 21 Nov 2024 14:16:26 -0600 Subject: [PATCH] watcher: fix tests --- .../__tests__/AlgorandWatcher.test.ts | 20 +- .../watchers/__tests__/AptosWatcher.test.ts | 13 +- .../__tests__/ArbitrumWatcher.test.ts | 2 +- .../watchers/__tests__/BaseWatcher.test.ts | 4 +- .../__tests__/CosmwasmWatcher.test.ts | 16 +- .../src/watchers/__tests__/EVMWatcher.test.ts | 8 +- .../__tests__/OptimismWatcher.test.ts | 2 +- .../watchers/__tests__/SolanaWatcher.test.ts | 26 +- .../src/watchers/__tests__/SuiWatcher.test.ts | 2 +- .../src/watchers/__tests__/parseVaa.test.ts | 604 ++++++++++++++++++ 10 files changed, 653 insertions(+), 44 deletions(-) create mode 100644 watcher/src/watchers/__tests__/parseVaa.test.ts diff --git a/watcher/src/watchers/__tests__/AlgorandWatcher.test.ts b/watcher/src/watchers/__tests__/AlgorandWatcher.test.ts index 634575eb..c3b8d99f 100644 --- a/watcher/src/watchers/__tests__/AlgorandWatcher.test.ts +++ b/watcher/src/watchers/__tests__/AlgorandWatcher.test.ts @@ -16,13 +16,16 @@ test('getFinalizedBlockNumber', async () => { test('getMessagesForBlocks', async () => { const watcher = new AlgorandWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(25692450, 25692450); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(25692450, 25692450); expect(messages).toMatchObject({ '25692450/2022-12-21T02:00:40.000Z': [] }); }); test('getMessagesForBlocks initial block', async () => { const watcher = new AlgorandWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(initialAlgorandBlock, initialAlgorandBlock); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks( + initialAlgorandBlock, + initialAlgorandBlock + ); expect(messages).toMatchObject({ '22931277/2022-08-19T15:10:48.000Z': [ '2RBQLCETCLFV4F3PQ7IHEWVWQV3MCP4UM5S5OFZM23XMC2O2DJ6A:8/67e93fa6c8ac5c819990aa7340c0c16b508abb1178be9b30d024b8ac25193d45/1', @@ -32,13 +35,16 @@ test('getMessagesForBlocks initial block', async () => { test('getMessagesForBlocks indexer pagination support', async () => { const watcher = new AlgorandWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(initialAlgorandBlock, 27069946); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks( + initialAlgorandBlock, + 27069946 + ); expect(Object.keys(messages).length).toEqual(420); }); test('getMessagesForBlocks seq < 192', async () => { const watcher = new AlgorandWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(25428873, 25428873); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(25428873, 25428873); expect(messages).toMatchObject({ '25428873/2022-12-09T18:10:08.000Z': [ 'M6QPEZ42P5O23II7SCWZTNZ7MHBSOH6KUNAPMH5YL3XHGNTEFUSQ:8/67e93fa6c8ac5c819990aa7340c0c16b508abb1178be9b30d024b8ac25193d45/191', @@ -48,7 +54,7 @@ test('getMessagesForBlocks seq < 192', async () => { test('getMessagesForBlocks seq = 192', async () => { const watcher = new AlgorandWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(25433218, 25433218); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(25433218, 25433218); expect(messages).toMatchObject({ '25433218/2022-12-09T22:40:55.000Z': [ '3PJPDBGTQK6JXAQEJNOYFE4NLLKFMCTKRY5FYNAXSEBDO25XUUJQ:8/67e93fa6c8ac5c819990aa7340c0c16b508abb1178be9b30d024b8ac25193d45/192', @@ -58,7 +64,7 @@ test('getMessagesForBlocks seq = 192', async () => { test('getMessagesForBlocks seq > 383', async () => { const watcher = new AlgorandWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(26856742, 26856742); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(26856742, 26856742); expect(messages).toMatchObject({ '26856742/2023-02-09T09:05:04.000Z': [ 'LJNYXPG5VLJNNTBLSZSHLZQ7XQWTSUPKGA7APVI53J3MAKHQN72Q:8/67e93fa6c8ac5c819990aa7340c0c16b508abb1178be9b30d024b8ac25193d45/384', @@ -68,6 +74,6 @@ test('getMessagesForBlocks seq > 383', async () => { test('getMessagesForBlocks on known empty block', async () => { const watcher = new AlgorandWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(23761195, 23761195); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(23761195, 23761195); expect(messages).toMatchObject({ '23761195/2022-09-28T21:42:30.000Z': [] }); }); diff --git a/watcher/src/watchers/__tests__/AptosWatcher.test.ts b/watcher/src/watchers/__tests__/AptosWatcher.test.ts index cf6ffe84..e553f01f 100644 --- a/watcher/src/watchers/__tests__/AptosWatcher.test.ts +++ b/watcher/src/watchers/__tests__/AptosWatcher.test.ts @@ -16,7 +16,7 @@ test('getFinalizedSequenceNumber', async () => { test('getMessagesForSequenceNumbers', async () => { const watcher = new AptosWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(0, 1); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(0, 1); expect(messages).toMatchObject({ '1095891/2022-10-19T00:55:54.676Z/0': [ '0x27b5808a7cfdb688e02be192ed334da683975b7487e8be7a09670b3088b58908:22/0000000000000000000000000000000000000000000000000000000000000001/0', @@ -32,12 +32,11 @@ test('getMessagesForSequenceNumbers', async () => { // test that block number, timestamp, and sequence number are all strictly increasing const latestSequenceNumber = await watcher.getFinalizedBlockNumber(); - const messageKeys = Object.keys( - await watcher.getMessagesForBlocks( - latestSequenceNumber - watcher.maximumBatchSize + 1, - latestSequenceNumber - ) - ).sort(); + const { vaasByBlock } = await watcher.getMessagesForBlocks( + latestSequenceNumber - watcher.maximumBatchSize + 1, + latestSequenceNumber + ); + const messageKeys = Object.keys(vaasByBlock).sort(); console.log(messageKeys); expect(messageKeys.length).toBe(watcher.maximumBatchSize); expect(Date.parse(messageKeys.at(-1)!.split('/')[1])).toBeLessThan(Date.now()); diff --git a/watcher/src/watchers/__tests__/ArbitrumWatcher.test.ts b/watcher/src/watchers/__tests__/ArbitrumWatcher.test.ts index 76b09e04..b1ab6b08 100644 --- a/watcher/src/watchers/__tests__/ArbitrumWatcher.test.ts +++ b/watcher/src/watchers/__tests__/ArbitrumWatcher.test.ts @@ -24,7 +24,7 @@ test('getFinalizedBlockNumber', async () => { test('getMessagesForBlocks', async () => { const watcher = new ArbitrumWatcher('Mainnet'); - const vaasByBlock = await watcher.getMessagesForBlocks(114500582, 114500584); + const { vaasByBlock } = await watcher.getMessagesForBlocks(114500582, 114500584); const entries = Object.entries(vaasByBlock); expect(entries.length).toEqual(3); expect(entries.filter(([block, vaas]) => vaas.length === 0).length).toEqual(2); diff --git a/watcher/src/watchers/__tests__/BaseWatcher.test.ts b/watcher/src/watchers/__tests__/BaseWatcher.test.ts index 610b978e..f621921c 100644 --- a/watcher/src/watchers/__tests__/BaseWatcher.test.ts +++ b/watcher/src/watchers/__tests__/BaseWatcher.test.ts @@ -14,7 +14,7 @@ test('getFinalizedBlockNumber', async () => { test('getMessagesForBlocks', async () => { const watcher = new EVMWatcher('Mainnet', 'Base'); - const vaasByBlock = await watcher.getMessagesForBlocks(1544175, 1544185); + const { vaasByBlock } = await watcher.getMessagesForBlocks(1544175, 1544185); expect(vaasByBlock).toMatchObject({ '1544175/2023-07-20T18:28:17.000Z': [], '1544176/2023-07-20T18:28:19.000Z': [], @@ -32,7 +32,7 @@ test('getMessagesForBlocks', async () => { test('getMessagesForBlockWithWHMsg', async () => { const watcher = new EVMWatcher('Mainnet', 'Base'); - const vaasByBlock = await watcher.getMessagesForBlocks(1557420, 1557429); + const { vaasByBlock } = await watcher.getMessagesForBlocks(1557420, 1557429); expect(vaasByBlock).toMatchObject({ '1557420/2023-07-21T01:49:47.000Z': [], '1557421/2023-07-21T01:49:49.000Z': [], diff --git a/watcher/src/watchers/__tests__/CosmwasmWatcher.test.ts b/watcher/src/watchers/__tests__/CosmwasmWatcher.test.ts index 2a134ce7..70dba269 100644 --- a/watcher/src/watchers/__tests__/CosmwasmWatcher.test.ts +++ b/watcher/src/watchers/__tests__/CosmwasmWatcher.test.ts @@ -17,7 +17,7 @@ test.skip('getFinalizedBlockNumber(terra2)', async () => { test.skip('getMessagesForBlocks(terra2)', async () => { const watcher = new TerraExplorerWatcher('Mainnet', 'Terra2'); - const vaasByBlock = await watcher.getMessagesForBlocks(10847656, 10847657); + const { vaasByBlock } = await watcher.getMessagesForBlocks(10847656, 10847657); const entries = Object.entries(vaasByBlock); expect(entries.length).toEqual(2); expect(entries.filter(([block, vaas]) => vaas.length === 0).length).toEqual(1); @@ -38,7 +38,7 @@ test('getFinalizedBlockNumber(terra explorer)', async () => { test('getMessagesForBlocks(terra explorer)', async () => { const watcher = new TerraExplorerWatcher('Mainnet', 'Terra'); - const vaasByBlock = await watcher.getMessagesForBlocks(14506733, 14506740); + const { vaasByBlock } = await watcher.getMessagesForBlocks(14506733, 14506740); const entries = Object.entries(vaasByBlock); expect(entries.length).toEqual(2); expect(entries.filter(([block, vaas]) => vaas.length === 0).length).toEqual(1); @@ -54,7 +54,7 @@ test('getMessagesForBlocks(terra explorer)', async () => { // flaky rpc, skip test.skip('getMessagesForBlocks(terra explorer, no useful info)', async () => { const watcher = new TerraExplorerWatcher('Mainnet', 'Terra'); - const vaasByBlock = await watcher.getMessagesForBlocks(10975000, 10975010); + const { vaasByBlock } = await watcher.getMessagesForBlocks(10975000, 10975010); const entries = Object.entries(vaasByBlock); expect(entries.length).toEqual(1); expect(entries.filter(([block, vaas]) => vaas.length === 0).length).toEqual(1); @@ -70,7 +70,7 @@ test('getFinalizedBlockNumber(xpla)', async () => { test('getMessagesForBlocks(xpla)', async () => { const watcher = new CosmwasmWatcher('Mainnet', 'Xpla'); - const vaasByBlock = await watcher.getMessagesForBlocks(1645812, 1645813); + const { vaasByBlock } = await watcher.getMessagesForBlocks(1645812, 1645813); const entries = Object.entries(vaasByBlock); expect(entries.length).toEqual(2); expect(entries.filter(([block, vaas]) => vaas.length === 0).length).toEqual(1); @@ -91,7 +91,7 @@ test('getFinalizedBlockNumber(injective)', async () => { test.skip('getMessagesForBlocks(injective)', async () => { const watcher = new InjectiveExplorerWatcher('Mainnet'); - const vaasByBlock = await watcher.getMessagesForBlocks(61720293, 61720294); + const { vaasByBlock } = await watcher.getMessagesForBlocks(61720293, 61720294); const entries = Object.entries(vaasByBlock); // console.log(entries); // Leave this in for future debugging expect(entries.length).toEqual(2); @@ -115,7 +115,7 @@ test.skip('getFinalizedBlockNumber(sei)', async () => { // skipped because the SeiExplorerWatcher is used test.skip('getMessagesForBlocks(sei)', async () => { const watcher = new CosmwasmWatcher('Mainnet', 'Sei'); - const vaasByBlock = await watcher.getMessagesForBlocks(18907686, 18907687); + const { vaasByBlock } = await watcher.getMessagesForBlocks(18907686, 18907687); const entries = Object.entries(vaasByBlock); expect(entries.length).toEqual(2); expect(entries.filter(([block, vaas]) => vaas.length === 0).length).toEqual(1); @@ -138,7 +138,7 @@ test('getFinalizedBlockNumber(sei explorer)', async () => { // skipped because it takes more and more time to paginate back test.skip('getMessagesForBlocks(sei explorer)', async () => { const watcher = new SeiExplorerWatcher('Mainnet'); - const vaasByBlock = await watcher.getMessagesForBlocks(19061244, 19061245); + const { vaasByBlock } = await watcher.getMessagesForBlocks(19061244, 19061245); const entries = Object.entries(vaasByBlock); expect(entries.length).toEqual(1); expect(entries.filter(([block, vaas]) => vaas.length === 0).length).toEqual(0); @@ -161,7 +161,7 @@ test('getFinalizedBlockNumber(wormchain)', async () => { test('getMessagesForBlocks(wormchain)', async () => { const watcher = new WormchainWatcher('Mainnet'); - const vaasByBlock = await watcher.getMessagesForBlocks(8978585, 8978585); + const { vaasByBlock } = await watcher.getMessagesForBlocks(8978585, 8978585); const entries = Object.entries(vaasByBlock); expect(entries.length).toEqual(1); expect(entries.filter(([block, vaas]) => vaas.length === 0).length).toEqual(0); diff --git a/watcher/src/watchers/__tests__/EVMWatcher.test.ts b/watcher/src/watchers/__tests__/EVMWatcher.test.ts index a1c10b12..3a2b3df2 100644 --- a/watcher/src/watchers/__tests__/EVMWatcher.test.ts +++ b/watcher/src/watchers/__tests__/EVMWatcher.test.ts @@ -72,7 +72,7 @@ test('getFinalizedBlockNumber', async () => { test('getMessagesForBlocks', async () => { const watcher = new EVMWatcher('Mainnet', 'Avalanche'); - const vaasByBlock = await watcher.getMessagesForBlocks(46997500, 46997599); + const { vaasByBlock } = await watcher.getMessagesForBlocks(46997500, 46997599); const entries = Object.entries(vaasByBlock); expect(entries.length).toEqual(100); expect(entries.filter(([block, vaas]) => vaas.length === 0).length).toEqual(99); @@ -111,7 +111,7 @@ test('getBlock by number (Celo compatibility)', async () => { test('getMessagesForBlocks (Celo compatibility)', async () => { const watcher = new EVMWatcher('Mainnet', 'Celo'); - const vaasByBlock = await watcher.getMessagesForBlocks(13322450, 13322549); + const { vaasByBlock } = await watcher.getMessagesForBlocks(13322450, 13322549); const entries = Object.entries(vaasByBlock); expect(entries.length).toEqual(100); expect(entries.filter(([block, vaas]) => vaas.length === 0).length).toEqual(98); @@ -142,7 +142,7 @@ test('getBlock by number (Karura compatibility)', async () => { test('getMessagesForBlocks (Karura compatibility)', async () => { const watcher = new EVMWatcher('Mainnet', 'Karura'); - const vaasByBlock = await watcher.getMessagesForBlocks(4582511, 4582513); + const { vaasByBlock } = await watcher.getMessagesForBlocks(4582511, 4582513); const entries = Object.entries(vaasByBlock); expect(entries.length).toEqual(3); expect(entries[0][0]).toEqual('4582511/2023-06-19T15:54:48.000Z'); @@ -155,7 +155,7 @@ test('getMessagesForBlocks (Karura compatibility)', async () => { test('getMessagesForBlocks (Karura compatibility 2)', async () => { const watcher = new EVMWatcher('Mainnet', 'Karura'); await watcher.getFinalizedBlockNumber(); // This has the side effect of initializing the latestFinalizedBlockNumber - const vaasByBlock = await watcher.getMessagesForBlocks(4595356, 4595358); + const { vaasByBlock } = await watcher.getMessagesForBlocks(4595356, 4595358); const entries = Object.entries(vaasByBlock); expect(entries.length).toEqual(3); }); diff --git a/watcher/src/watchers/__tests__/OptimismWatcher.test.ts b/watcher/src/watchers/__tests__/OptimismWatcher.test.ts index ac3e023f..55dd73d5 100644 --- a/watcher/src/watchers/__tests__/OptimismWatcher.test.ts +++ b/watcher/src/watchers/__tests__/OptimismWatcher.test.ts @@ -17,7 +17,7 @@ test('getFinalizedBlockNumber', async () => { test('getMessagesForBlocks', async () => { const watcher = new EVMWatcher('Mainnet', 'Optimism'); - const vaasByBlock = await watcher.getMessagesForBlocks(105235070, 105235080); + const { vaasByBlock } = await watcher.getMessagesForBlocks(105235070, 105235080); expect(vaasByBlock).toMatchObject({ '105235070/2023-06-06T16:28:37.000Z': [], '105235071/2023-06-06T16:28:39.000Z': [], diff --git a/watcher/src/watchers/__tests__/SolanaWatcher.test.ts b/watcher/src/watchers/__tests__/SolanaWatcher.test.ts index ae4c2ddb..e8e2d83f 100644 --- a/watcher/src/watchers/__tests__/SolanaWatcher.test.ts +++ b/watcher/src/watchers/__tests__/SolanaWatcher.test.ts @@ -16,7 +16,7 @@ test('getFinalizedBlockNumber', async () => { test('getMessagesForBlocks - single block', async () => { const watcher = new SolanaWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(170799004, 170799004); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(170799004, 170799004); expect(Object.keys(messages).length).toBe(1); expect(messages).toMatchObject({ '170799004/2023-01-04T16:43:43.000Z': [ @@ -32,21 +32,21 @@ test('getMessagesForBlocks - single block', async () => { // temporary skip due to SolanaJSONRPCError: failed to get confirmed block: Block 171774030 cleaned up, does not exist on node. First available block: 176896202 test('getMessagesForBlocks - fromSlot is skipped slot', async () => { const watcher = new SolanaWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(171774030, 171774032); // 171774024 - 171774031 are skipped + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(171774030, 171774032); // 171774024 - 171774031 are skipped expect(Object.keys(messages).length).toBe(1); expect(messages).toMatchObject({ '171774032/2023-01-10T13:36:39.000Z': [] }); }); test('getMessagesForBlocks - toSlot is skipped slot', async () => { const watcher = new SolanaWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(171774023, 171774025); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(171774023, 171774025); expect(messages).toMatchObject({ '171774025/2023-01-10T13:36:34.000Z': [] }); }); test('getMessagesForBlocks - empty block', async () => { // Even if there are no messages, last block should still be returned const watcher = new SolanaWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(170979766, 170979766); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(170979766, 170979766); expect(Object.keys(messages).length).toBe(1); expect(messages).toMatchObject({ '170979766/2023-01-05T18:40:25.000Z': [] }); }); @@ -58,25 +58,25 @@ test('getMessagesForBlocks - block with no transactions', async () => { 'solana: invalid block range' ); - let messages = await watcher.getMessagesForBlocks(174108661, 174108861); + let { vaasByBlock: messages } = await watcher.getMessagesForBlocks(174108661, 174108861); expect(Object.keys(messages).length).toBe(1); expect(Object.values(messages).flat().length).toBe(0); - messages = await watcher.getMessagesForBlocks(174108863, 174109061); + ({ vaasByBlock: messages } = await watcher.getMessagesForBlocks(174108863, 174109061)); expect(Object.keys(messages).length).toBe(1); expect(Object.values(messages).flat().length).toBe(0); }); test('getMessagesForBlocks - multiple blocks', async () => { const watcher = new SolanaWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(171050470, 171050474); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(171050470, 171050474); expect(Object.keys(messages).length).toBe(2); expect(Object.values(messages).flat().length).toBe(2); }); test('getMessagesForBlocks - multiple blocks, last block empty', async () => { const watcher = new SolanaWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(170823000, 170825000); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(170823000, 170825000); expect(Object.keys(messages).length).toBe(3); expect(Object.values(messages).flat().length).toBe(2); // 2 messages, last block has no message }); @@ -84,16 +84,16 @@ test('getMessagesForBlocks - multiple blocks, last block empty', async () => { test('getMessagesForBlocks - multiple blocks containing more than `getSignaturesLimit` WH transactions', async () => { const watcher = new SolanaWatcher('Mainnet'); watcher.getSignaturesLimit = 10; - const messages = await watcher.getMessagesForBlocks(171582367, 171583452); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(171582367, 171583452); expect(Object.keys(messages).length).toBe(3); expect(Object.values(messages).flat().length).toBe(3); }); test('getMessagesForBlocks - multiple calls', async () => { const watcher = new SolanaWatcher('Mainnet'); - const messages1 = await watcher.getMessagesForBlocks(171773021, 171773211); - const messages2 = await watcher.getMessagesForBlocks(171773212, 171773250); - const messages3 = await watcher.getMessagesForBlocks(171773251, 171773500); + const { vaasByBlock: messages1 } = await watcher.getMessagesForBlocks(171773021, 171773211); + const { vaasByBlock: messages2 } = await watcher.getMessagesForBlocks(171773212, 171773250); + const { vaasByBlock: messages3 } = await watcher.getMessagesForBlocks(171773251, 171773500); const allMessageKeys = [ ...Object.keys(messages1), ...Object.keys(messages2), @@ -105,7 +105,7 @@ test('getMessagesForBlocks - multiple calls', async () => { test('getMessagesForBlocks - handle failed transactions', async () => { const watcher = new SolanaWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(94401321, 94501321); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(94401321, 94501321); expect(Object.keys(messages).length).toBe(6); expect(Object.values(messages).flat().length).toBe(5); expect( diff --git a/watcher/src/watchers/__tests__/SuiWatcher.test.ts b/watcher/src/watchers/__tests__/SuiWatcher.test.ts index 074d2874..6158f66f 100644 --- a/watcher/src/watchers/__tests__/SuiWatcher.test.ts +++ b/watcher/src/watchers/__tests__/SuiWatcher.test.ts @@ -19,7 +19,7 @@ test('getFinalizedSequenceNumber', async () => { // works backwards. This will cause a 429 until we clear that up. test.skip('getMessagesForBlocks', async () => { const watcher = new SuiWatcher('Mainnet'); - const messages = await watcher.getMessagesForBlocks(1581997, 1581997); + const { vaasByBlock: messages } = await watcher.getMessagesForBlocks(1581997, 1581997); console.log(messages); const entries = Object.entries(messages); expect(entries.length).toEqual(46); diff --git a/watcher/src/watchers/__tests__/parseVaa.test.ts b/watcher/src/watchers/__tests__/parseVaa.test.ts new file mode 100644 index 00000000..63239fe7 --- /dev/null +++ b/watcher/src/watchers/__tests__/parseVaa.test.ts @@ -0,0 +1,604 @@ +// import { +// ParsedAttestMetaVaa, +// ParsedTokenTransferVaa, +// TokenBridgePayload, +// encode, +// hex, +// hexToUint8Array, +// parseAttestMetaPayload, +// parseTokenTransferPayload, +// parseVaa, +// tryHexToNativeAssetString, +// } from '@certusone/wormhole-sdk'; +import { + ChainId, + chain, + chainToChainId, + contracts, + encoding, + toChain, + toChainId, +} from '@wormhole-foundation/sdk-base'; +import { + UniversalAddress, + VAA, + deserialize, + deserializePayload, +} from '@wormhole-foundation/sdk-definitions'; +import { + TokenTransfer, + createAttestMessage, + createTokenMetadata, + getNativeAddress, +} from '@wormhole-foundation/wormhole-monitor-database'; +import { + getMissThreshold, + isTokenBridgeEmitter, + normalizeCompileInstruction, + padUint16, + universalAddress_stripped, +} from '@wormhole-foundation/wormhole-monitor-common'; +// import { getPostedMessage } from '@certusone/wormhole-sdk/lib/cjs/solana/wormhole'; +import { + Commitment, + ConfirmedSignatureInfo, + Connection, + Message, + MessageV0, + PublicKey, + SolanaJSONRPCError, + VersionedBlockResponse, +} from '@solana/web3.js'; +import { RPCS_BY_CHAIN } from '../../consts'; +import { deserializePostMessage } from '@wormhole-foundation/sdk-solana-core'; +import { blindDeserializePayload } from '@wormhole-foundation/sdk'; +// import { AlgorandAddress } from '@wormhole-foundation/sdk-algorand'; +import '@wormhole-foundation/sdk/addresses'; +import { token } from '@coral-xyz/anchor/dist/cjs/utils'; + +jest.setTimeout(60000); + +// test('parseVAA', async () => { +// const vaa = +// '01000000030d002520841fd6a9417e6153d50a90f70b264600f8ede7ff13cc6dfadfdff6c50e5b0b6128765ec2a6d214df37e3271c47b3b70c0709af5a4c7a4e128bb440ee328200023b1441013537f5f4abfd8dc8db7c6ab6c755cb79dab49154abf13e3e36731a2e1532b222d1baec5f32c6bea7ea02d801fbf39c4bf9f4be112e02f3d034c08ba1010365196cf4d9cd3fe663533cb387bc935a05ee41aa9c8543ea651be4f2954b338b65a91cd8f4c31887649f2777c977cc36289e2a34f9a30e4763e39f972cd5e8a2000476871c6753b827c5273c2af0d7d172401151613ac2154d849b7c0206d560d0074dc048bf8c130cb6d076421e6efa3ed7edca33c2f5ac1ce35b57bad8d492f7ab0106dc8533b9bba525713f16915847ec2ab1663534cbfdbe3bf4bec6c414c862169924d047b2c349580feedc3e0f16c5e047ed60b98334e9a7fd95daad9bdc7038d200070420bf19fbeeffe60095a3650ca00df9b01181768c21e9b3dd00a40e06eec84116a40e590cd061e8bc7707b191d656426691343561431fb458985d456260cdf70009001392c4b71f6ca80e7458cc41767e019d02353e3137d7440cbb7d1b776c816e4e9654ce6d7b1a09f7c44daec3f9b32737efd3911e0224d5fe52d601ec58f19f010c3ab45148ab0d27fcd5470f2cb9bdf7ba98f9038a3f830deaf82326603b345bef02242fffde2d4d1ed8291f51bcee0a95e0cf66e688189a7815196565f7619e71000d510050bdc29c59e46dc3b5a0a8a7a3ca71d6cd7eb4eac1bed1ff482f651e8fa1464e2f6635c242ff91f0c28702d13af057fc6f9483e567102f66f4380e1d0960010f09965038f2fb49b667d474bbf411cb89cebc3138354585be1d0b6a54829c49f877c66a078c99f7008e761e09d561a6ebddfdf12fa5f73f5f622173963fc1bd280010c4e893e32327bbcba511e67a67f2e1e33a4b77fecb7f52a33b042f5e8c281f9f549c3d3ade8bfe19b809f3850204e46f184135162fba8b83b6b448e6555ee49200116773748635f0a1a0443cbbb9cba7cdde8ab4382dbf3ae4e4ecbeb56ddf51d59b06fd296edad6e6853f139155045a8a12ae5bde3fe1e22383ed3e554af39b5bee0012e9fc42d87e006f1dd2909516b4a51ce437559ac64f37e42afcb25d633c26a3970a998c8cbd154d3d359cd4d898aa87c37b04a085f0245e480d988fd0a96f5f250164a7f29f000000000002000000000000000000000000aada05bd399372f0b0463744c09113c137636f6a00000000000000440101000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000db58580000000000000000300000000000002de0000000000000000000000003a920ddbc41181846785ae9ee89929ffd26deeae0000000000000000000000003a920ddbc41181846785ae9ee89929ffd26deeae0000'; +// // The old way of parsing VAAs: +// const parsed = parseVaa(Buffer.from(vaa, 'hex')); +// expect(parsed).toBeDefined(); +// console.log(`version: ${parsed.version}`); +// console.log(`guardianSetIndex: ${parsed.guardianSetIndex}`); +// console.log(`timestamp: ${parsed.timestamp}`); +// console.log(`nonce: ${parsed.nonce}`); +// console.log(`emitterChain: ${parsed.emitterChain}`); +// console.log(`emitterAddress: ${parsed.emitterAddress.toString('hex')}`); +// console.log(`sequence: ${parsed.sequence}`); +// console.log(`consistencyLevel: ${parsed.consistencyLevel}`); +// console.log(`payload: ${parsed.payload.toString('hex')}`); +// console.log(`hash: ${parsed.hash.toString('hex')}`); + +// // The new way of parsing VAAs: +// const newParsedVaa = deserialize('Uint8Array', vaa); +// expect(newParsedVaa).toBeDefined(); +// // expect(newParsedVaa.version).toBe(parsed.version); +// expect(newParsedVaa.guardianSet).toBe(parsed.guardianSetIndex); +// expect(newParsedVaa.timestamp).toBe(parsed.timestamp); +// expect(newParsedVaa.nonce).toBe(parsed.nonce); +// expect(toChainId(newParsedVaa.emitterChain)).toBe(parsed.emitterChain); +// expect(universalAddress_stripped(newParsedVaa.emitterAddress)).toBe( +// parsed.emitterAddress.toString('hex') +// ); +// expect(newParsedVaa.sequence).toBe(parsed.sequence); +// expect(newParsedVaa.consistencyLevel).toBe(parsed.consistencyLevel); +// expect(Buffer.from(newParsedVaa.payload).toString('hex')).toBe(parsed.payload.toString('hex')); +// expect(Buffer.from(newParsedVaa.hash).toString('hex')).toBe(parsed.hash.toString('hex')); +// }); + +// test('parseTokenTransferPayload', async () => { +// const vaaBytes = +// '01000000040d00a6636646d215b9fe9ab6650378a38607504fb8358680b13d576fabfddee480782e4baf0cc3937e112f58b47d8bb2f06a228e971b04b80a7dc0cf46f99b0c27ed01019ef3bb8e22a8b680431ea61eeee9ebeb317e59b664a91af5ed0f5d4ba8841d6b7490718ccebde26d01a1dce75c608bbe1bbe3a23e2aefd220f913ad1741c8de00102aadc991c4cfc32cbd41afbbb9e36f56a5a776a66d45e4f2696efb45e9ffbfc696a0cef45610cc9a578e4804f407da4950093bb8be75c1ed0135dc88c26d594e700046ce342dc8cb81d9001dba77ab681e3ced17579f987d8d3fd2e771dec3857065822d6d35d229e89c9ae87c193f36b6d5fcbfb8ad7feb6f77b3f5a1636816038970005a22dbfabd18dc83e01834a9a46ec8914d1f9ee0131f54a44817588e7c9898de04a4e8091c4dc38f563fdb3c05ea8622025653c1fa3c39ac63e7ac79107d97a6401062ce4b786896c645e6b46e5d1b5ac9deb787cc6bf863f8072dc4cddbd66e9306f3290110dfecc8fe2b8d6c68a7569817c3491333c0e2d14c60eb4a7207b03065b0107946b9acddeae95549fac515c44b0fe477349eb2ac2143b5d992d1a6e2f73b4965d076fe16e18e3f7c681e0fc601c19f78e8dd0babea64c234ed5106749d5446f000a58273cf0ea0dcb286ed688ec0b67de623b31ae5d43516499d9788e196f1f3fcc4b8aaeee2e17cdea6b390e6034663c432620bfa969863ae5314ae6d39fc2e138000c701fec1194e76a2c60c70eb1be5cb90e4f531699f39bc4800ac3a0c90a48e8c62de29995fa0b75277d3626d0c2bb657913c96ffe10710b4a19a018a2ad977c54010ded90fcbefceac0be71e43cd7396e8ba48c013a1c07d1040f2571d73222ab1c730207cd14d7cd0452a40be2fc6df6c8eab44e1cf08fc9f0c5ee583bc27e6782b10010805584dc7ff6e33b33acea12f5ef1dcea6eecfbdc747b22541d9fe89d06d04992a0a19488a7a43c70f2d19aef0ebd42ed614c84bbb970e207d59ec1896fc835c011121091ca89f68660f1cd6c73287a35af1d5d56136370e7e06de24f106637d9d3946e0985ff224c9456175d8cc2902a0b4db1f38a7885fe2c8043c6ba4bfb434ad01129b633ed7640d212c2c84bc35fce49a22e87078744fcbff2d7b61959012cb00bb50b256e2f364e3c0ee1a2da7091e68b45bd059341e24b66d5b9aa45610ada66e0066212183aeee000000050000000000000000000000005a58505a96d1dbf8df91cb21b54419fc36e93fde0000000000065fed0f01000000000000000000000000000000000000000000000000000000000000e1c80000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf12700005000000000000000000000000267fedf6baf664a995396aa9bc4d08b3c788dfdd000a0000000000000000000000000000000000000000000000000000000000000000'; +// // The old way of parsing VAAs: +// const parsed = parseVaa(Buffer.from(vaaBytes, 'hex')); +// expect(parsed).toBeDefined(); +// console.log(`version: ${parsed.version}`); +// console.log(`guardianSetIndex: ${parsed.guardianSetIndex}`); +// console.log(`timestamp: ${parsed.timestamp}`); +// console.log(`nonce: ${parsed.nonce}`); +// console.log(`emitterChain: ${parsed.emitterChain}`); +// console.log(`emitterAddress: ${parsed.emitterAddress.toString('hex')}`); +// console.log(`sequence: ${parsed.sequence}`); +// console.log(`consistencyLevel: ${parsed.consistencyLevel}`); +// console.log(`payload: ${parsed.payload.toString('hex')}`); +// console.log(`hash: ${parsed.hash.toString('hex')}`); + +// Get the values from the old processVaa function: +// const vaa = deserialize('Uint8Array', vaaBytes); +// const vaa = parseVaa(Buffer.from(vaaBytes, 'hex')); +// console.log(`vaa: ${vaa}`); +// if (vaa.payload.length > 0) { +// const payloadType = vaa.payload[0]; // First byte is the payload type +// console.log(`payloadType: ${payloadType}`); +// const payloadBuffer = Buffer.from(vaa.payload); +// if ( +// payloadType === TokenBridgePayload.Transfer || +// payloadType === TokenBridgePayload.TransferWithPayload +// ) { +// // const payload = parseTokenTransferPayload(payloadBuffer); +// const payload = parseTokenTransferPayload(vaa.payload); +// console.log( +// `payload: ${JSON.stringify(payload, (key, value) => +// typeof value === 'bigint' ? value.toString() : value +// )}` +// ); +// const tokenTransferVaa: ParsedTokenTransferVaa = { ...vaa, ...payload }; +// console.log( +// `tokenTransferVaa: ${JSON.stringify(tokenTransferVaa, (key, value) => +// typeof value === 'bigint' ? value.toString() : value +// )}` +// ); +// const tokenTransfer = oldCreateTokenTransfer(tokenTransferVaa); +// console.log(`tokenTransfer: ${JSON.stringify(tokenTransfer)}`); +// } else if (payloadType === TokenBridgePayload.AttestMeta) { +// const payload = parseAttestMetaPayload(vaa.payload); +// const attestMetaVaa: ParsedAttestMetaVaa = { ...vaa, ...payload }; +// const attestMessage = createAttestMessage(attestMetaVaa); +// const tokenMetadata = createTokenMetadata(attestMetaVaa); +// } +// } + +// const ttPayload = parseTokenTransferPayload(parsed.payload); +// // The new way of parsing VAAs: +// const newParsedVaa: VAA<'TokenBridge:Transfer'> = deserialize('TokenBridge:Transfer', vaaBytes); +// expect(newParsedVaa).toBeDefined(); +// // expect(newParsedVaa.version).toBe(parsed.version); +// console.log('consistencyLevel', newParsedVaa.consistencyLevel); +// console.log('emitterAddress', newParsedVaa.emitterAddress.toString()); +// console.log('emitterChain', newParsedVaa.emitterChain); +// console.log('guardianSet', newParsedVaa.guardianSet); +// console.log('hash', newParsedVaa.hash); +// console.log('nonce', newParsedVaa.nonce); +// console.log('payload', newParsedVaa.payload); +// console.log('payloadLiteral', newParsedVaa.payloadLiteral); +// console.log('payloadName', newParsedVaa.payloadName); +// console.log('protocolName', newParsedVaa.protocolName); +// console.log('sequence', newParsedVaa.sequence); +// console.log('signatures', newParsedVaa.signatures); +// console.log('timestamp', newParsedVaa.timestamp); +// expect(newParsedVaa.consistencyLevel).toBe(parsed.consistencyLevel); +// expect(universalAddress_stripped(newParsedVaa.emitterAddress)).toBe( +// parsed.emitterAddress.toString('hex') +// ); +// expect(toChainId(newParsedVaa.emitterChain)).toBe(parsed.emitterChain); +// expect(newParsedVaa.guardianSet).toBe(parsed.guardianSetIndex); +// expect(Buffer.from(newParsedVaa.hash)).toEqual(parsed.hash); +// expect(newParsedVaa.nonce).toBe(parsed.nonce); + +// expect(newParsedVaa.payload.token.amount).toBe(ttPayload.amount); +// expect(newParsedVaa.payload.token.address.address).toEqual( +// new Uint8Array(ttPayload.tokenAddress) +// ); +// expect(toChainId(newParsedVaa.payload.token.chain)).toBe(ttPayload.tokenChain); +// expect(newParsedVaa.payload.to.address.address).toEqual(new Uint8Array(ttPayload.to)); +// expect(toChainId(newParsedVaa.payload.to.chain)).toBe(ttPayload.toChain); +// expect(newParsedVaa.payload.fee).toBe(ttPayload.fee); + +// expect(newParsedVaa.payloadLiteral).toBe('TokenBridge:Transfer'); +// expect(newParsedVaa.payloadName).toBe('Transfer'); +// const module = isTokenBridgeEmitter( +// newParsedVaa.emitterChain, +// universalAddress_stripped(newParsedVaa.emitterAddress) +// ) +// ? 'TokenBridge' +// : 'null'; +// expect(newParsedVaa.protocolName).toBe(module); +// expect(newParsedVaa.sequence).toBe(parsed.sequence); +// expect(newParsedVaa.timestamp).toBe(parsed.timestamp); +// }); + +// test('parseAttest', async () => { +// const vaaBytes = +// '01000000040d004ce7f39063e9dc297dcc7ea26b61c7527d6c591e1712f0f8d24d0bd113cf810f4ec8a8d10dd87b48ea788129a4177242664faa391a6e06d03bc82ec949910fb70002a0a0d9f77a13b9646c3d194df0489d9eccfc363d5733dc219e084df3505ca93338bf08689dc624bf77ddd51a21099517d7cd966604e1b93f691dc6296aff0f0401033343056f243193afa99ae8593af27cab115020e9f6bba63b6aa971f3743fd3830f04604a446b37394f34948e1310f8e3ede87690d19c89a216136a476eb23bcc0104ab466f6e89c1f40e34aba9a8d741714a1fb95c80d5efbda37a0a57d18dd8988801ff0ed0ee86a068bea04ade9f064287593e1a948a86691ee698c243c89b65140006faac540570a014c710b370ffb72651af63ec8d0f810d8f48bd31ceb4e086e17443df2f6de716227ff5b79792d2e0117dae2c7c8f5e9db62e2e988b6f33e4ec310107497a1efccdab88743a6e9c891b34334f8295b154f37af32b86214868840398d335640ad04a08011d203f6c71e335ae2fc23a76816c9d78e18ec3bac1c0023ab600098fc55d14a3201b95fd1d8d69aff32c8bdd8656a6cff68cbfae19416f9b61b59c163763cbac70a92987b2c2bdac13ef838e1fa71dbc3e060d29c3922ea875db60010cad6d3a64487bd09c425d833d1d79286fbe90d56921b44dee1b614d2c5f28618a3327046c9085c9b3a9e4baef6443b5d43c6bea8e1412f6f9227c9c8172febcfe010dee3b0e65095d75751992427df365c0e31c2db09a3ba8f8b137155d59b3da05236a936186fb6de8aa52ab059d593487aa41a6918397d0753aeb7b07686db7fbee000e17dd065299f6b4fde1141c1669209d10df247706d51115c3bd191bb9fb945f95010f6ab21c955518e34be3001769fc58ead018b7fcfa00fed74fa12c563f6a4e001039a0828fb9601242f536d5c389efd1df281cbea754fe6d217342b4c97883238c690ba5e4d4aba1b71112f425e4081e8deb0ac36c803ab92b0c711414559cb70700114e4f664a46221bb36f5c8eac3d08ef031f1c4ffc0d9eea349aea206de18a9f361ed44c5991d726b36e95372eb4afe0e523b305c246642e9835f610d3a00cd5420012f3e17cac2cd362aedff7d4be179c687603f6f68b69fc087be3578cfe8028c8365efe88a58d6b85273e6dfbf25ebde52620e8a7e8c0238fc160759798d87f64be016627ad576a26010000020000000000000000000000003ee18b2214aff97000d974cf647e7c347e8fa5850000000000040a530102000000000000000000000000e54efba49a2362e0ff3396c8d7b5dbec1359ec6f000200436c61696d202455534445207265776172642061742068747470733a2f2f75732320757364652d7265776172642e636f6d000000000000000000000000000000'; +// // The old way of parsing VAAs: +// const parsed = parseVaa(Buffer.from(vaaBytes, 'hex')); +// expect(parsed).toBeDefined(); +// const attestMeta = parseAttestMetaPayload(parsed.payload); +// // The new way of parsing VAAs: +// const newParsedVaa: VAA<'TokenBridge:AttestMeta'> = deserialize( +// 'TokenBridge:AttestMeta', +// vaaBytes +// ); +// expect(newParsedVaa).toBeDefined(); +// // expect(newParsedVaa.version).toBe(parsed.version); +// // console.log('consistencyLevel', newParsedVaa.consistencyLevel); +// // console.log('emitterAddress', newParsedVaa.emitterAddress.toString()); +// // console.log('emitterChain', newParsedVaa.emitterChain); +// // console.log('guardianSet', newParsedVaa.guardianSet); +// // console.log('hash', newParsedVaa.hash); +// // console.log('nonce', newParsedVaa.nonce); +// console.log('payload', newParsedVaa.payload); +// // console.log('payloadLiteral', newParsedVaa.payloadLiteral); +// // console.log('payloadName', newParsedVaa.payloadName); +// // console.log('protocolName', newParsedVaa.protocolName); +// // console.log('sequence', newParsedVaa.sequence); +// // console.log('signatures', newParsedVaa.signatures); +// // console.log('timestamp', newParsedVaa.timestamp); +// expect(newParsedVaa.consistencyLevel).toBe(parsed.consistencyLevel); +// expect(universalAddress_stripped(newParsedVaa.emitterAddress)).toBe( +// parsed.emitterAddress.toString('hex') +// ); +// expect(toChainId(newParsedVaa.emitterChain)).toBe(parsed.emitterChain); +// expect(newParsedVaa.guardianSet).toBe(parsed.guardianSetIndex); +// expect(Buffer.from(newParsedVaa.hash)).toEqual(parsed.hash); +// expect(newParsedVaa.nonce).toBe(parsed.nonce); + +// expect(toChainId(newParsedVaa.payload.token.chain)).toBe(attestMeta.tokenChain); +// expect(newParsedVaa.payload.token.address.address).toEqual( +// new Uint8Array(attestMeta.tokenAddress) +// ); +// expect(newParsedVaa.payload.decimals).toBe(attestMeta.decimals); +// expect(newParsedVaa.payload.symbol).toBe(attestMeta.symbol); +// expect(newParsedVaa.payload.name.replace(/\0/g, '')).toBe(attestMeta.name); + +// expect(newParsedVaa.payloadLiteral).toBe('TokenBridge:AttestMeta'); +// expect(newParsedVaa.payloadName).toBe('AttestMeta'); +// const module = isTokenBridgeEmitter( +// newParsedVaa.emitterChain, +// universalAddress_stripped(newParsedVaa.emitterAddress) +// ) +// ? 'TokenBridge' +// : 'null'; +// expect(newParsedVaa.protocolName).toBe(module); +// expect(newParsedVaa.sequence).toBe(parsed.sequence); +// expect(newParsedVaa.timestamp).toBe(parsed.timestamp); +// const attestMessage = createAttestMessage(newParsedVaa); +// console.log(`attestMessage: ${JSON.stringify(attestMessage)}`); +// const tokenMetadata = createTokenMetadata(newParsedVaa); +// console.log(`tokenMetadata: ${JSON.stringify(tokenMetadata)}`); +// }); + +// test('CCTP parseVAA', async () => { +// const vaa = +// '01000000030d002520841fd6a9417e6153d50a90f70b264600f8ede7ff13cc6dfadfdff6c50e5b0b6128765ec2a6d214df37e3271c47b3b70c0709af5a4c7a4e128bb440ee328200023b1441013537f5f4abfd8dc8db7c6ab6c755cb79dab49154abf13e3e36731a2e1532b222d1baec5f32c6bea7ea02d801fbf39c4bf9f4be112e02f3d034c08ba1010365196cf4d9cd3fe663533cb387bc935a05ee41aa9c8543ea651be4f2954b338b65a91cd8f4c31887649f2777c977cc36289e2a34f9a30e4763e39f972cd5e8a2000476871c6753b827c5273c2af0d7d172401151613ac2154d849b7c0206d560d0074dc048bf8c130cb6d076421e6efa3ed7edca33c2f5ac1ce35b57bad8d492f7ab0106dc8533b9bba525713f16915847ec2ab1663534cbfdbe3bf4bec6c414c862169924d047b2c349580feedc3e0f16c5e047ed60b98334e9a7fd95daad9bdc7038d200070420bf19fbeeffe60095a3650ca00df9b01181768c21e9b3dd00a40e06eec84116a40e590cd061e8bc7707b191d656426691343561431fb458985d456260cdf70009001392c4b71f6ca80e7458cc41767e019d02353e3137d7440cbb7d1b776c816e4e9654ce6d7b1a09f7c44daec3f9b32737efd3911e0224d5fe52d601ec58f19f010c3ab45148ab0d27fcd5470f2cb9bdf7ba98f9038a3f830deaf82326603b345bef02242fffde2d4d1ed8291f51bcee0a95e0cf66e688189a7815196565f7619e71000d510050bdc29c59e46dc3b5a0a8a7a3ca71d6cd7eb4eac1bed1ff482f651e8fa1464e2f6635c242ff91f0c28702d13af057fc6f9483e567102f66f4380e1d0960010f09965038f2fb49b667d474bbf411cb89cebc3138354585be1d0b6a54829c49f877c66a078c99f7008e761e09d561a6ebddfdf12fa5f73f5f622173963fc1bd280010c4e893e32327bbcba511e67a67f2e1e33a4b77fecb7f52a33b042f5e8c281f9f549c3d3ade8bfe19b809f3850204e46f184135162fba8b83b6b448e6555ee49200116773748635f0a1a0443cbbb9cba7cdde8ab4382dbf3ae4e4ecbeb56ddf51d59b06fd296edad6e6853f139155045a8a12ae5bde3fe1e22383ed3e554af39b5bee0012e9fc42d87e006f1dd2909516b4a51ce437559ac64f37e42afcb25d633c26a3970a998c8cbd154d3d359cd4d898aa87c37b04a085f0245e480d988fd0a96f5f250164a7f29f000000000002000000000000000000000000aada05bd399372f0b0463744c09113c137636f6a00000000000000440101000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000000000000000000000000000000000000db58580000000000000000300000000000002de0000000000000000000000003a920ddbc41181846785ae9ee89929ffd26deeae0000000000000000000000003a920ddbc41181846785ae9ee89929ffd26deeae0000'; +// // The old way of parsing VAAs: +// const parsed = parseVaa(Buffer.from(vaa, 'hex')); +// expect(parsed).toBeDefined(); +// console.log(`version: ${parsed.version}`); +// console.log(`guardianSetIndex: ${parsed.guardianSetIndex}`); +// console.log(`timestamp: ${parsed.timestamp}`); +// console.log(`nonce: ${parsed.nonce}`); +// console.log(`emitterChain: ${parsed.emitterChain}`); +// console.log(`emitterAddress: ${parsed.emitterAddress.toString('hex')}`); +// console.log(`sequence: ${parsed.sequence}`); +// console.log(`consistencyLevel: ${parsed.consistencyLevel}`); +// console.log(`payload: ${parsed.payload.toString('hex')}`); +// console.log(`hash: ${parsed.hash.toString('hex')}`); + +// // The new way of parsing VAAs: +// const newParsedVaa: VAA<'Uint8Array'> = deserialize('Uint8Array', vaa); +// expect(newParsedVaa).toBeDefined(); +// // expect(newParsedVaa.version).toBe(parsed.version); +// expect(newParsedVaa.guardianSet).toBe(parsed.guardianSetIndex); +// expect(newParsedVaa.timestamp).toBe(parsed.timestamp); +// expect(newParsedVaa.nonce).toBe(parsed.nonce); +// expect(toChainId(newParsedVaa.emitterChain)).toBe(parsed.emitterChain); +// expect(universalAddress_stripped(newParsedVaa.emitterAddress)).toBe( +// parsed.emitterAddress.toString('hex') +// ); +// expect(newParsedVaa.sequence).toBe(parsed.sequence); +// expect(newParsedVaa.consistencyLevel).toBe(parsed.consistencyLevel); +// newParsedVaa.signatures; +// expect(Buffer.from(newParsedVaa.payload).toString('hex')).toBe(parsed.payload.toString('hex')); +// expect(Buffer.from(newParsedVaa.hash).toString('hex')).toBe(parsed.hash.toString('hex')); +// }); + +test('blindDeserialize', async () => { + const vaaBytes = + '01000000040d00a6636646d215b9fe9ab6650378a38607504fb8358680b13d576fabfddee480782e4baf0cc3937e112f58b47d8bb2f06a228e971b04b80a7dc0cf46f99b0c27ed01019ef3bb8e22a8b680431ea61eeee9ebeb317e59b664a91af5ed0f5d4ba8841d6b7490718ccebde26d01a1dce75c608bbe1bbe3a23e2aefd220f913ad1741c8de00102aadc991c4cfc32cbd41afbbb9e36f56a5a776a66d45e4f2696efb45e9ffbfc696a0cef45610cc9a578e4804f407da4950093bb8be75c1ed0135dc88c26d594e700046ce342dc8cb81d9001dba77ab681e3ced17579f987d8d3fd2e771dec3857065822d6d35d229e89c9ae87c193f36b6d5fcbfb8ad7feb6f77b3f5a1636816038970005a22dbfabd18dc83e01834a9a46ec8914d1f9ee0131f54a44817588e7c9898de04a4e8091c4dc38f563fdb3c05ea8622025653c1fa3c39ac63e7ac79107d97a6401062ce4b786896c645e6b46e5d1b5ac9deb787cc6bf863f8072dc4cddbd66e9306f3290110dfecc8fe2b8d6c68a7569817c3491333c0e2d14c60eb4a7207b03065b0107946b9acddeae95549fac515c44b0fe477349eb2ac2143b5d992d1a6e2f73b4965d076fe16e18e3f7c681e0fc601c19f78e8dd0babea64c234ed5106749d5446f000a58273cf0ea0dcb286ed688ec0b67de623b31ae5d43516499d9788e196f1f3fcc4b8aaeee2e17cdea6b390e6034663c432620bfa969863ae5314ae6d39fc2e138000c701fec1194e76a2c60c70eb1be5cb90e4f531699f39bc4800ac3a0c90a48e8c62de29995fa0b75277d3626d0c2bb657913c96ffe10710b4a19a018a2ad977c54010ded90fcbefceac0be71e43cd7396e8ba48c013a1c07d1040f2571d73222ab1c730207cd14d7cd0452a40be2fc6df6c8eab44e1cf08fc9f0c5ee583bc27e6782b10010805584dc7ff6e33b33acea12f5ef1dcea6eecfbdc747b22541d9fe89d06d04992a0a19488a7a43c70f2d19aef0ebd42ed614c84bbb970e207d59ec1896fc835c011121091ca89f68660f1cd6c73287a35af1d5d56136370e7e06de24f106637d9d3946e0985ff224c9456175d8cc2902a0b4db1f38a7885fe2c8043c6ba4bfb434ad01129b633ed7640d212c2c84bc35fce49a22e87078744fcbff2d7b61959012cb00bb50b256e2f364e3c0ee1a2da7091e68b45bd059341e24b66d5b9aa45610ada66e0066212183aeee000000050000000000000000000000005a58505a96d1dbf8df91cb21b54419fc36e93fde0000000000065fed0f01000000000000000000000000000000000000000000000000000000000000e1c80000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf12700005000000000000000000000000267fedf6baf664a995396aa9bc4d08b3c788dfdd000a0000000000000000000000000000000000000000000000000000000000000000'; + const newParsedVaa = deserialize('Uint8Array', vaaBytes); + expect(newParsedVaa).toBeDefined(); + const deserial2 = blindDeserializePayload(newParsedVaa.payload); + console.log( + `deserial: ${JSON.stringify(deserial2, (key, value) => + typeof value === 'bigint' ? value.toString() : value + )}` + ); + if (deserial2[0][0] === 'TokenBridge:Transfer') { + } + console.log(vaaBytes.length); + const paddedChainId = padUint16(toChainId('Solana').toString()); + console.log(`paddedChainId: ${paddedChainId}`); +}); + +// test('Encoding', async () => { +// const vaaBytes = +// '01000000040d00a6636646d215b9fe9ab6650378a38607504fb8358680b13d576fabfddee480782e4baf0cc3937e112f58b47d8bb2f06a228e971b04b80a7dc0cf46f99b0c27ed01019ef3bb8e22a8b680431ea61eeee9ebeb317e59b664a91af5ed0f5d4ba8841d6b7490718ccebde26d01a1dce75c608bbe1bbe3a23e2aefd220f913ad1741c8de00102aadc991c4cfc32cbd41afbbb9e36f56a5a776a66d45e4f2696efb45e9ffbfc696a0cef45610cc9a578e4804f407da4950093bb8be75c1ed0135dc88c26d594e700046ce342dc8cb81d9001dba77ab681e3ced17579f987d8d3fd2e771dec3857065822d6d35d229e89c9ae87c193f36b6d5fcbfb8ad7feb6f77b3f5a1636816038970005a22dbfabd18dc83e01834a9a46ec8914d1f9ee0131f54a44817588e7c9898de04a4e8091c4dc38f563fdb3c05ea8622025653c1fa3c39ac63e7ac79107d97a6401062ce4b786896c645e6b46e5d1b5ac9deb787cc6bf863f8072dc4cddbd66e9306f3290110dfecc8fe2b8d6c68a7569817c3491333c0e2d14c60eb4a7207b03065b0107946b9acddeae95549fac515c44b0fe477349eb2ac2143b5d992d1a6e2f73b4965d076fe16e18e3f7c681e0fc601c19f78e8dd0babea64c234ed5106749d5446f000a58273cf0ea0dcb286ed688ec0b67de623b31ae5d43516499d9788e196f1f3fcc4b8aaeee2e17cdea6b390e6034663c432620bfa969863ae5314ae6d39fc2e138000c701fec1194e76a2c60c70eb1be5cb90e4f531699f39bc4800ac3a0c90a48e8c62de29995fa0b75277d3626d0c2bb657913c96ffe10710b4a19a018a2ad977c54010ded90fcbefceac0be71e43cd7396e8ba48c013a1c07d1040f2571d73222ab1c730207cd14d7cd0452a40be2fc6df6c8eab44e1cf08fc9f0c5ee583bc27e6782b10010805584dc7ff6e33b33acea12f5ef1dcea6eecfbdc747b22541d9fe89d06d04992a0a19488a7a43c70f2d19aef0ebd42ed614c84bbb970e207d59ec1896fc835c011121091ca89f68660f1cd6c73287a35af1d5d56136370e7e06de24f106637d9d3946e0985ff224c9456175d8cc2902a0b4db1f38a7885fe2c8043c6ba4bfb434ad01129b633ed7640d212c2c84bc35fce49a22e87078744fcbff2d7b61959012cb00bb50b256e2f364e3c0ee1a2da7091e68b45bd059341e24b66d5b9aa45610ada66e0066212183aeee000000050000000000000000000000005a58505a96d1dbf8df91cb21b54419fc36e93fde0000000000065fed0f01000000000000000000000000000000000000000000000000000000000000e1c80000000000000000000000000d500b1d8e8ef31e21c99d1db9a6444d3adf12700005000000000000000000000000267fedf6baf664a995396aa9bc4d08b3c788dfdd000a0000000000000000000000000000000000000000000000000000000000000000'; +// const newParsedVaa = deserialize('Uint8Array', vaaBytes); +// expect(newParsedVaa).toBeDefined(); +// const n: number = newParsedVaa.nonce; +// console.log(`nonce: ${newParsedVaa.nonce}`); +// expect(stringToChainId('Solana')).toBe(stringToChainId('1')); +// expect(stringToChainId('nonsense')).toBe(undefined); +// expect(stringToChainId('5150')).toBe(undefined); +// expect(stringToChainId('1')).toBe(stringToChainId('Solana')); +// console.log(`encoded nonce: ${encode('uint32', n)}, myEncode: ${myEncode(n, 4)}`); +// expect(encode('uint32', n)).toBe(myEncode(n, 4)); +// expect(encode('uint32', newParsedVaa.timestamp)).toBe(myEncode(newParsedVaa.timestamp, 4)); +// expect(encode('uint16', toChainId(newParsedVaa.emitterChain))).toBe( +// myEncode(toChainId(newParsedVaa.emitterChain), 2) +// ); +// expect(encode('uint64', newParsedVaa.sequence)).toBe(myEncode(newParsedVaa.sequence, 8)); +// console.log(`emitterAddress: ${encode('bytes32', hex(newParsedVaa.emitterAddress.toString()))}`); +// expect(encode('bytes32', hex(newParsedVaa.emitterAddress.toString()))).toBe( +// Buffer.from(encoding.bytes.zpad(newParsedVaa.emitterAddress.toUint8Array(), 32)).toString('hex') +// ); +// console.log(`encoded 1: ${encode('uint32', 1)}, myEncode 1: ${myEncode(1, 4)}`); +// console.log( +// `encoded 1000000: ${encode('uint32', 1000000)}, myEncode 1000000: ${myEncode(1000000, 4)}` +// ); +// console.log(`using sdkv2 for nonce: ${encoding.bignum.toBytes(newParsedVaa.nonce)}`); +// console.log(`using sdkv2 for nonce: ${encoding.bignum.toString(BigInt(newParsedVaa.nonce))}`); +// console.log(`using sdkv2 for 1: ${encoding.bignum.toBytes(1)}`); +// const tokenAddress = '0000000000000000000000005a58505a96d1dbf8df91cb21b54419fc36e93fde'; +// console.log(`hexToUint8Array: ${hexToUint8Array(tokenAddress)}`); +// console.log(`encoding.hex.decode: ${encoding.hex.decode(tokenAddress)}`); +// expect(hexToUint8Array(tokenAddress)).toEqual(encoding.hex.decode(tokenAddress)); +// }); + +// test('Solana ', async () => { +// // VAA +// // 3ShRAQBHrCDcJLQiz9NphNnLqX3LUcwb2B9J4BgnBqGhPgeQCsgUzduJzvcGcKGQhUczNx1PaXjCoaGFeq2tcFnG +// // 1/5c790ae482b0101dd0b27fb0880e3043fd8e29cca03b89362169a7e25f57a293/12557 +// // Block 263085934 +// // 4/30/2024, 8:31:27 AM +// const retries = 5; +// const slotNumber: number = 263085934; +// const SIG_LIMIT = 1000; +// const RPC = RPCS_BY_CHAIN['Mainnet'].Solana!; +// const PROGRAM_ID = contracts.coreBridge('Mainnet', 'Solana'); +// const COMMITMENT: Commitment = 'finalized'; +// const connection: Connection = new Connection(RPC, COMMITMENT); +// let toBlock: VersionedBlockResponse; +// let fromBlock: VersionedBlockResponse; +// try { +// toBlock = await findNextValidBlock(connection, slotNumber + 1, -1, retries); +// fromBlock = await findNextValidBlock(connection, slotNumber - 1, 1, retries); +// } catch (e) { +// throw new Error('solana: invalid block range: ' + (e as Error).message); +// } +// const fromSignature = toBlock.transactions[0].transaction.signatures[0]; +// const toSignature = +// fromBlock.transactions[fromBlock.transactions.length - 1].transaction.signatures[0]; + +// // get all core bridge signatures between fromTransaction and toTransaction +// let numSignatures = SIG_LIMIT; +// let currSignature: string | undefined = fromSignature; +// while (numSignatures === SIG_LIMIT) { +// const signatures: ConfirmedSignatureInfo[] = await connection.getSignaturesForAddress( +// new PublicKey(PROGRAM_ID), +// { +// before: currSignature, +// until: toSignature, +// limit: SIG_LIMIT, +// } +// ); + +// console.log(`processing ${signatures.length} transactions`); + +// // In order to determine if a transaction has a Wormhole message, we normalize and iterate +// // through all instructions in the transaction. Only PostMessage instructions are relevant +// // when looking for messages. PostMessageUnreliable instructions are ignored because there +// // are no data availability guarantees (ie the associated message accounts may have been +// // reused, overwriting previous data). Then, the message account is the account given by +// // the second index in the instruction's account key indices. From here, we can fetch the +// // message data from the account and parse out the emitter and sequence. +// const results = await connection.getTransactions( +// signatures.map((s) => s.signature), +// { +// maxSupportedTransactionVersion: 0, +// } +// ); + +// if (results.length !== signatures.length) { +// throw new Error(`solana: failed to fetch tx for signatures`); +// } + +// for (const res of results) { +// if (res?.meta?.err) { +// // skip errored txs +// continue; +// } +// if (!res || !res.blockTime) { +// throw new Error( +// `solana: failed to fetch tx for signature ${res?.transaction.signatures[0] || 'unknown'}` +// ); +// } + +// const message = res.transaction.message; +// let accountKeys = isLegacyMessage(message) ? message.accountKeys : message.staticAccountKeys; + +// // If the message contains an address table lookup, we need to resolve the addresses +// // before looking for the programIdIndex +// if (message.addressTableLookups.length > 0) { +// const lookupPromises = message.addressTableLookups.map(async (atl) => { +// const lookupTableAccount = await connection +// .getAddressLookupTable(atl.accountKey) +// .then((res) => res.value); + +// if (!lookupTableAccount) +// throw new Error('lookupTableAccount is null, cant resolve addresses'); + +// // Important to return the addresses in the order they're specified in the +// // address table lookup object. Note writable comes first, then readable. +// return [ +// atl.accountKey, +// atl.writableIndexes.map((i) => lookupTableAccount.state.addresses[i]), +// atl.readonlyIndexes.map((i) => lookupTableAccount.state.addresses[i]), +// ] as [PublicKey, PublicKey[], PublicKey[]]; +// }); + +// // Lookup all addresses in parallel +// const lookups = await Promise.all(lookupPromises); + +// // Ensure the order is maintained for lookups +// // Static, Writable, Readable +// // ref: https://github.com/gagliardetto/solana-go/blob/main/message.go#L414-L464 +// const writable: PublicKey[] = []; +// const readable: PublicKey[] = []; +// for (const atl of message.addressTableLookups) { +// const table = lookups.find((l) => l[0].equals(atl.accountKey)); +// if (!table) throw new Error('Could not find address table lookup'); +// writable.push(...table[1]); +// readable.push(...table[2]); +// } + +// accountKeys.push(...writable.concat(readable)); +// } + +// const programIdIndex = accountKeys.findIndex((i) => i.toBase58() === PROGRAM_ID); +// const instructions = message.compiledInstructions; +// const innerInstructions = +// res.meta?.innerInstructions?.flatMap((i) => +// i.instructions.map(normalizeCompileInstruction) +// ) || []; + +// const whInstructions = innerInstructions +// .concat(instructions) +// .filter((i) => i.programIdIndex === programIdIndex); + +// const vaaKeys: string[] = []; +// for (const instruction of whInstructions) { +// // skip if not postMessage instruction +// const instructionId = instruction.data; +// if (instructionId[0] !== 0x08 && instructionId[0] !== 0x01) continue; + +// const accountId = accountKeys[instruction.accountKeyIndexes[1]]; +// const { +// message: { emitterAddress: oldEA, sequence: oldSeq }, +// } = await getPostedMessage(connection, accountId.toBase58(), COMMITMENT); +// console.log(`old emitterAddress: ${oldEA.toString('hex')}, old sequence: ${oldSeq}`); + +// const acctInfo = await connection.getAccountInfo(accountId, COMMITMENT); +// if (!acctInfo?.data) throw new Error('No data found in message account'); +// const { emitterAddress, sequence } = deserializePostMessage(new Uint8Array(acctInfo.data)); +// console.log( +// `new emitterAddress: ${universalAddress_stripped( +// emitterAddress +// )}, new sequence: ${sequence}` +// ); +// } +// } + +// numSignatures = signatures.length; +// currSignature = signatures.at(-1)?.signature; +// } +// }); + +// test('Algorand', async () => { +// const tokenAddr: string = '000000000000000000000000000000000000000000000000000000000dc3f639'; +// const oldWay = tryHexToNativeAssetString(tokenAddr, 8); + +// // const newWay = new UniversalAddress(tokenAddr).toNative('Algorand').toBigInt().toString(); +// // expect(oldWay).toBe(newWay); + +// const ethTokenAddress = '000000000000000000000000e5597f0723eeaba1b26948e06f008bf0fc1e37e6'; +// const oldNative = await getNativeAddress(2, ethTokenAddress); +// const oldNative2 = tryHexToNativeAssetString(ethTokenAddress, 2); +// console.log(`oldNative: ${oldNative}, oldNative2: ${oldNative2}`); +// }); + +test.only('getMissThreshold', async () => { + const now = new Date(); + const misses = [ + { + id: '00035/18446744073644466320/00000000000000000000000024850c6f61c438823f01b7a3bf2b89b72174fa9d/00000000000000000001', + chain: '35', + block: 65085295, + emitter: '00000000000000000000000024850c6f61c438823f01b7a3bf2b89b72174fa9d', + seq: '1', + timestamp: '2024-06-13T17:48:22.000Z', + txHash: '0x39d80bd8fb1dad557561777f0ee8988802c14f0ac951ac87721276ceed801d73', + hasSignedVaa: 0, + }, + { + id: '00035/18446744073644466459/00000000000000000000000024850c6f61c438823f01b7a3bf2b89b72174fa9d/00000000000000000000', + chain: '35', + block: 65085156, + emitter: '00000000000000000000000024850c6f61c438823f01b7a3bf2b89b72174fa9d', + seq: '0', + timestamp: '2024-06-13T17:43:44.000Z', + txHash: '0xe4a65ca0d7f16cabb2f921c4381d69f589ab16c3248c51883dcc62d3885da572', + hasSignedVaa: 0, + }, + ]; + const filteredMisses = misses.filter( + (message) => message.timestamp < getMissThreshold(now, message.chain) + ); + console.log(`filteredMisses: ${JSON.stringify(filteredMisses)}`); +}); + +function stringToChainId(input: string): ChainId | undefined { + try { + if (Number.isNaN(Number(input))) { + return toChainId(input); + } + return toChainId(Number(input)); + } catch (e) { + return undefined; + } +} + +function myEncode(value: bigint | number, numBytes: number): string { + const interim: Uint8Array = encoding.bignum.toBytes(value, numBytes); + return Buffer.from(interim).toString('hex'); +} + +// function encodeBytes32(value: Uint8Array): string { +// return Buffer.from(encoding.bytes.zpad(value, 32)).toString('hex'); +// } + +// export const oldCreateTokenTransfer = (vaa: ParsedTokenTransferVaa): TokenTransfer => ({ +// timestamp: vaa.timestamp.toString(), +// emitter_chain: vaa.emitterChain, +// emitter_address: vaa.emitterAddress.toString('hex'), +// sequence: vaa.sequence.toString(), +// amount: vaa.amount.toString(), +// token_address: vaa.tokenAddress.toString('hex'), +// token_chain: vaa.tokenChain, +// to_address: vaa.to.toString('hex'), +// to_chain: vaa.toChain, +// payload_type: Number(vaa.payloadType), +// fee: vaa.fee !== null ? vaa.fee.toString() : null, +// from_address: vaa.fromAddress !== null ? vaa.fromAddress.toString('hex') : null, +// module: 'TokenBridge', +// }); + +async function findNextValidBlock( + connection: Connection, + slot: number, + next: number, + retries: number +): Promise { + // identify block range by fetching signatures of the first and last transactions + // getSignaturesForAddress walks backwards so fromSignature occurs after toSignature + if (retries === 0) throw new Error(`No block found after exhausting retries`); + + let block: VersionedBlockResponse | null = null; + try { + block = await connection.getBlock(slot, { maxSupportedTransactionVersion: 0 }); + } catch (e) { + if (e instanceof SolanaJSONRPCError && (e.code === -32007 || e.code === -32009)) { + // failed to get confirmed block: slot was skipped or missing in long-term storage + return findNextValidBlock(connection, slot + next, next, retries - 1); + } else { + throw e; + } + } + + if (!block || !block.blockTime || block.transactions.length === 0) { + return findNextValidBlock(connection, slot + next, next, retries - 1); + } + + return block; +} + +const isLegacyMessage = (message: Message | MessageV0): message is Message => { + return message.version === 'legacy'; +};