Skip to content

Commit 231ff99

Browse files
committed
ft_watcher: worker to invoke ft
Signed-off-by: bingyuyap <bingyu.yap.21@gmail.com>
1 parent 37cabea commit 231ff99

21 files changed

+230
-78
lines changed

common/src/consts.ts

+22-7
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
toChainId,
88
} from '@wormhole-foundation/sdk-base';
99

10-
export type Mode = 'vaa' | 'ntt';
10+
export type Mode = 'vaa' | 'ntt' | 'ft';
1111

1212
// This is defined here in an effort to keep the number and text in sync.
1313
// The default value is not exported because the getMissThreshold() function should be used to get the value.
@@ -17,9 +17,9 @@ export const MISS_THRESHOLD_LABEL = '40 minutes';
1717
export const MAX_VAA_DECIMALS = 8;
1818
export const VAA_VERSION = 1;
1919

20-
export const INITIAL_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN: {
21-
[key in Network]: { [key in Chain]?: string };
22-
} = {
20+
type NetworkChainBlockMapping = { [key in Network]: { [key in Chain]?: string } };
21+
22+
export const INITIAL_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN: NetworkChainBlockMapping = {
2323
['Mainnet']: {
2424
Ethereum: '12959638',
2525
Terra: '4810000', // not sure exactly but this should be before the first known message
@@ -84,9 +84,7 @@ export const INITIAL_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN: {
8484
['Devnet']: {},
8585
};
8686

87-
export const INITIAL_NTT_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN: {
88-
[key in Network]: { [key in Chain]?: string };
89-
} = {
87+
export const INITIAL_NTT_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN: NetworkChainBlockMapping = {
9088
['Mainnet']: {
9189
Solana: '260508723',
9290
Ethereum: '19583505',
@@ -105,6 +103,23 @@ export const INITIAL_NTT_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN: {
105103
['Devnet']: {},
106104
};
107105

106+
export const INITIAL_FT_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN: NetworkChainBlockMapping = {
107+
['Mainnet']: {},
108+
['Testnet']: {
109+
Solana: '302162456',
110+
ArbitrumSepolia: '49505590',
111+
},
112+
['Devnet']: {},
113+
};
114+
115+
export const INITIAL_DEPLOYMENT_BLOCK_BY_MODE: {
116+
[mode in Mode]: NetworkChainBlockMapping;
117+
} = {
118+
vaa: INITIAL_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN,
119+
ntt: INITIAL_NTT_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN,
120+
ft: INITIAL_FT_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN,
121+
};
122+
108123
export function getMissThreshold(date: Date, chainish: number | string | Chain | ChainId): string {
109124
// We would like chainish to really be a ChainId.
110125
let missThresholdInMins: number;

common/src/utils.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function getNetwork(): Network {
3636

3737
export function getMode(): Mode {
3838
const mode: string = assertEnvironmentVariable('MODE').toLowerCase();
39-
if (mode === 'vaa' || mode === 'ntt') {
39+
if (mode === 'vaa' || mode === 'ntt' || mode === 'ft') {
4040
return mode;
4141
}
4242
throw new Error(`Unknown mode: ${mode}`);

database/fast-transfer-schema.sql

+5-3
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,11 @@ CREATE TYPE FastTransferStatus AS ENUM ('pending', 'no_offer', 'executed', 'sett
1414
-- Market Order tracks events of when fast market orders are
1515
-- placed in the token router
1616
CREATE TABLE market_orders (
17-
fast_vaa_id VARCHAR(255) PRIMARY KEY,
18-
fast_vaa_hash VARCHAR(255),
17+
-- These two cant be primary key because on different stages they might initially be null due to the inability to find them
18+
-- To accomodate this we put a unique constraint to allow nullable
19+
-- It will eventually be filled up
20+
fast_vaa_id VARCHAR(255) UNIQUE,
21+
fast_vaa_hash VARCHAR(255) UNIQUE,
1922
amount_in BIGINT,
2023
min_amount_out BIGINT,
2124
src_chain INTEGER,
@@ -89,4 +92,3 @@ CREATE TABLE auction_history_mapping (
8992
auction_pubkey VARCHAR(255) PRIMARY KEY,
9093
index INT NOT NULL
9194
);
92-

watcher/scripts/backfill.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { ChainId, toChain } from '@wormhole-foundation/sdk-base';
3030
const lastBlockEntries = Object.entries(localDb.lastBlockByChain);
3131
for (const [chain, blockKey] of lastBlockEntries) {
3232
console.log('backfilling last block for', chain, blockKey);
33-
await remoteDb.storeLatestBlock(toChain(Number(chain) as ChainId), blockKey, false);
33+
await remoteDb.storeLatestBlock(toChain(Number(chain) as ChainId), blockKey, 'vaa');
3434
await sleep(500);
3535
}
3636
})();

watcher/scripts/backfillNear.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ const BATCH_SIZE = 100;
3535
const chain: Chain = 'Near';
3636
const provider = await getNearProvider(network, NEAR_ARCHIVE_RPC);
3737
const fromBlock = Number(
38-
(await db.getLastBlockByChain(chain, false)) ??
38+
(await db.getLastBlockByChain(chain, 'vaa')) ??
3939
INITIAL_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN[network][chain] ??
4040
0
4141
);

watcher/src/databases/BigtableDatabase.ts

+26-9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Bigtable } from '@google-cloud/bigtable';
22
import { PubSub } from '@google-cloud/pubsub';
33
import { Chain, chainToChainId } from '@wormhole-foundation/sdk-base';
44
import {
5+
Mode,
56
assertEnvironmentVariable,
67
chunkArray,
78
sleep,
@@ -38,16 +39,25 @@ export class BigtableDatabase extends Database {
3839
firestoreDb: FirebaseFirestore.Firestore;
3940
latestCollectionName: string;
4041
latestNTTCollectionName: string;
42+
latestFTCollectionName: string;
4143
pubsubSignedVAATopic: string;
44+
collectionNameByMode: { [key in Mode]: string };
4245
pubsub: PubSub;
4346
constructor() {
4447
super();
4548
this.msgTableId = assertEnvironmentVariable('BIGTABLE_TABLE_ID');
4649
this.signedVAAsTableId = assertEnvironmentVariable('BIGTABLE_SIGNED_VAAS_TABLE_ID');
4750
this.vaasByTxHashTableId = assertEnvironmentVariable('BIGTABLE_VAAS_BY_TX_HASH_TABLE_ID');
4851
this.instanceId = assertEnvironmentVariable('BIGTABLE_INSTANCE_ID');
52+
// TODO: make these const?
4953
this.latestCollectionName = assertEnvironmentVariable('FIRESTORE_LATEST_COLLECTION');
5054
this.latestNTTCollectionName = assertEnvironmentVariable('FIRESTORE_LATEST_NTT_COLLECTION');
55+
this.latestFTCollectionName = assertEnvironmentVariable('FIRESTORE_LATEST_FT_COLLECTION');
56+
this.collectionNameByMode = {
57+
vaa: this.latestCollectionName,
58+
ntt: this.latestNTTCollectionName,
59+
ft: this.latestFTCollectionName,
60+
};
5161
this.pubsubSignedVAATopic = assertEnvironmentVariable('PUBSUB_SIGNED_VAA_TOPIC');
5262
try {
5363
this.bigtable = new Bigtable();
@@ -63,11 +73,15 @@ export class BigtableDatabase extends Database {
6373
}
6474
}
6575

66-
async getLastBlockByChain(chain: Chain, isNTT: boolean): Promise<string | null> {
76+
async getLastBlockByChain(chain: Chain, mode: Mode): Promise<string | null> {
6777
const chainId = chainToChainId(chain);
68-
const lastObservedBlock = isNTT
69-
? this.firestoreDb.collection(this.latestNTTCollectionName).doc(chainId.toString())
70-
: this.firestoreDb.collection(this.latestCollectionName).doc(chainId.toString());
78+
79+
const collectionName = this.collectionNameByMode[mode];
80+
if (!collectionName) {
81+
throw new Error(`Unknown mode: ${mode}`);
82+
}
83+
84+
const lastObservedBlock = this.firestoreDb.collection(collectionName).doc(chainId.toString());
7185
const lastObservedBlockByChain = await lastObservedBlock.get();
7286
const blockKeyData = lastObservedBlockByChain.data();
7387
const lastBlockKey = blockKeyData?.lastBlockKey;
@@ -79,16 +93,19 @@ export class BigtableDatabase extends Database {
7993
return null;
8094
}
8195

82-
async storeLatestBlock(chain: Chain, lastBlockKey: string, isNTT: boolean): Promise<void> {
96+
async storeLatestBlock(chain: Chain, lastBlockKey: string, mode: Mode): Promise<void> {
8397
if (this.firestoreDb === undefined) {
8498
this.logger.error('no firestore db set');
8599
return;
86100
}
87101
const chainId = chainToChainId(chain);
88102
this.logger.info(`storing last block=${lastBlockKey} for chain=${chainId}`);
89-
const lastObservedBlock = isNTT
90-
? this.firestoreDb.collection(this.latestNTTCollectionName).doc(`${chainId.toString()}`)
91-
: this.firestoreDb.collection(this.latestCollectionName).doc(`${chainId.toString()}`);
103+
const collectionName = this.collectionNameByMode[mode];
104+
if (!collectionName) {
105+
throw new Error(`Unknown mode: ${mode}`);
106+
}
107+
108+
const lastObservedBlock = this.firestoreDb.collection(collectionName).doc(chainId.toString());
92109
await lastObservedBlock.set({ lastBlockKey });
93110
}
94111

@@ -159,7 +176,7 @@ export class BigtableDatabase extends Database {
159176
if (blockKeys.length) {
160177
const lastBlockKey = blockKeys[blockKeys.length - 1];
161178
this.logger.info(`for chain=${chain}, storing last bigtable block=${lastBlockKey}`);
162-
await this.storeLatestBlock(chain, lastBlockKey, false);
179+
await this.storeLatestBlock(chain, lastBlockKey, 'vaa');
163180
}
164181
}
165182
}

watcher/src/databases/Database.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Mode } from '@wormhole-foundation/wormhole-monitor-common';
12
import { getLogger, WormholeLogger } from '../utils/logger';
23
import { VaasByBlock } from './types';
34
import { Chain } from '@wormhole-foundation/sdk-base';
@@ -14,13 +15,13 @@ export class Database {
1415
}
1516
return filteredVaasByBlock;
1617
}
17-
async getLastBlockByChain(chain: Chain, isNTT: boolean): Promise<string | null> {
18+
async getLastBlockByChain(chain: Chain, mode: Mode): Promise<string | null> {
1819
throw new Error('Not Implemented');
1920
}
2021
async storeVaasByBlock(chain: Chain, vaasByBlock: VaasByBlock): Promise<void> {
2122
throw new Error('Not Implemented');
2223
}
23-
async storeLatestBlock(chain: Chain, lastBlockKey: string, isNTT: boolean): Promise<void> {
24+
async storeLatestBlock(chain: Chain, lastBlockKey: string, mode: Mode): Promise<void> {
2425
throw new Error('Not Implemented');
2526
}
2627
}

watcher/src/databases/JsonDatabase.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { DB_LAST_BLOCK_FILE, JSON_DB_FILE } from '../consts';
33
import { Database } from './Database';
44
import { DB, LastBlockByChain, VaasByBlock } from './types';
55
import { Chain, chainToChainId } from '@wormhole-foundation/sdk-base';
6+
import { Mode } from '@wormhole-foundation/wormhole-monitor-common';
67

78
const ENCODING = 'utf8';
89
export class JsonDatabase extends Database {
@@ -34,7 +35,7 @@ export class JsonDatabase extends Database {
3435
}
3536
}
3637

37-
async getLastBlockByChain(chain: Chain): Promise<string | null> {
38+
async getLastBlockByChain(chain: Chain, mode: Mode): Promise<string | null> {
3839
const chainId = chainToChainId(chain);
3940
const blockInfo = this.lastBlockByChain[chainId];
4041
if (blockInfo) {

watcher/src/databases/__tests__/utils.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ test('getResumeBlockByChain', async () => {
1010
const blockKey = makeBlockKey(fauxBlock, new Date().toISOString());
1111
db.lastBlockByChain = { [chainToChainId('Solana')]: blockKey };
1212
// if a chain is in the database, that number should be returned
13-
expect(await db.getLastBlockByChain('Solana')).toEqual(fauxBlock);
14-
expect(await getResumeBlockByChain('Mainnet', 'Solana', false)).toEqual(Number(fauxBlock) + 1);
13+
expect(await db.getLastBlockByChain('Solana', 'vaa')).toEqual(fauxBlock);
14+
expect(await getResumeBlockByChain('Mainnet', 'Solana', 'vaa')).toEqual(Number(fauxBlock) + 1);
1515
// if a chain is not in the database, the initial deployment block should be returned
1616
expect(INITIAL_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN['Mainnet'].Moonbeam).toBeDefined();
17-
expect(await getResumeBlockByChain('Mainnet', 'Moonbeam', false)).toEqual(
17+
expect(await getResumeBlockByChain('Mainnet', 'Moonbeam', 'vaa')).toEqual(
1818
Number(INITIAL_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN['Mainnet'].Moonbeam)
1919
);
2020
});

watcher/src/databases/utils.ts

+7-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Chain, Network, chainToChainId } from '@wormhole-foundation/sdk-base';
22
import {
3-
INITIAL_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN,
4-
INITIAL_NTT_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN,
3+
INITIAL_DEPLOYMENT_BLOCK_BY_MODE,
54
MAX_UINT_64,
5+
Mode,
66
padUint16,
77
padUint64,
88
} from '@wormhole-foundation/wormhole-monitor-common';
@@ -52,20 +52,18 @@ export const initDb = (startWatching: boolean = true): Database => {
5252
export const storeLatestBlock = async (
5353
chain: Chain,
5454
lastBlockKey: string,
55-
isNTT: boolean
55+
mode: Mode
5656
): Promise<void> => {
57-
return database.storeLatestBlock(chain, lastBlockKey, isNTT);
57+
return database.storeLatestBlock(chain, lastBlockKey, mode);
5858
};
5959

6060
export const getResumeBlockByChain = async (
6161
network: Network,
6262
chain: Chain,
63-
isNTT: boolean
63+
mode: Mode
6464
): Promise<number | null> => {
65-
const lastBlock = await database.getLastBlockByChain(chain, isNTT);
66-
const initialBlock = isNTT
67-
? INITIAL_NTT_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN[network][chain]
68-
: INITIAL_DEPLOYMENT_BLOCK_BY_NETWORK_AND_CHAIN[network][chain];
65+
const lastBlock = await database.getLastBlockByChain(chain, mode);
66+
const initialBlock = INITIAL_DEPLOYMENT_BLOCK_BY_MODE[mode][network][chain];
6967
return lastBlock !== null
7068
? Number(lastBlock) + 1
7169
: initialBlock !== undefined

watcher/src/index.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import * as dotenv from 'dotenv';
22
dotenv.config();
33

44
import { initDb } from './databases/utils';
5-
import { makeFinalizedNTTWatcher, makeFinalizedWatcher } from './watchers/utils';
65
import { Mode, getNetwork, getMode } from '@wormhole-foundation/wormhole-monitor-common';
76
import { startSupervisor } from './workers/supervisor';
87
import { Chain, Network } from '@wormhole-foundation/sdk-base';
@@ -91,10 +90,14 @@ const supportedNTTChains: Chain[] =
9190
? ['Solana', 'Sepolia', 'ArbitrumSepolia', 'BaseSepolia', 'OptimismSepolia']
9291
: ['Solana', 'Ethereum', 'Fantom', 'Arbitrum', 'Optimism', 'Base'];
9392

93+
const supportedFTChains: Chain[] = network === 'Testnet' ? ['Solana', 'ArbitrumSepolia'] : [];
94+
9495
if (mode === 'vaa') {
9596
startSupervisor(supportedChains);
9697
} else if (mode === 'ntt') {
9798
startSupervisor(supportedNTTChains);
99+
} else if (mode === 'ft') {
100+
startSupervisor(supportedFTChains);
98101
} else {
99102
throw new Error(`Unknown mode: ${mode}`);
100103
}

0 commit comments

Comments
 (0)