From 571a8a141c6ef4bdaf8c49b462576ea31850f772 Mon Sep 17 00:00:00 2001
From: Kevin Peters <kevin@w7.xyz>
Date: Mon, 30 Oct 2023 10:24:05 -0500
Subject: [PATCH 1/3] cloud-functions: Added getSolanaEvents cloud function

The purpose of this cloud function is to fetch Solana token bridge
transfer events for a given slot range.
---
 cloud_functions/package.json           |   4 +-
 cloud_functions/scripts/deploy.sh      |   7 +
 cloud_functions/src/getSolanaEvents.ts | 363 +++++++++++++++++++++++++
 cloud_functions/src/index.ts           |   2 +
 package-lock.json                      | 324 ++++++++++------------
 5 files changed, 512 insertions(+), 188 deletions(-)
 create mode 100644 cloud_functions/src/getSolanaEvents.ts

diff --git a/cloud_functions/package.json b/cloud_functions/package.json
index 7b2d3812..3752895f 100644
--- a/cloud_functions/package.json
+++ b/cloud_functions/package.json
@@ -7,7 +7,7 @@
   "scripts": {
     "build": "tsc",
     "dev": "ts-node src/index.ts",
-    "start": "npx functions-framework --target=wormchainMonitor [--signature-type=http]",
+    "start": "npx functions-framework --target=getSolanaEvents [--signature-type=http]",
     "deploy": "bash scripts/deploy.sh",
     "gcp-build": "npm i ./dist/src/wormhole-foundation-wormhole-monitor-common-0.0.1.tgz ./dist/src/wormhole-foundation-wormhole-monitor-database-0.0.1.tgz"
   },
@@ -18,7 +18,9 @@
     "@google-cloud/functions-framework": "^3.1.3",
     "@google-cloud/pubsub": "^3.4.1",
     "@google-cloud/storage": "^6.8.0",
+    "@solana/web3.js": "^1.87.3",
     "axios": "^1.5.0",
+    "borsh": "^1.0.0",
     "dotenv": "^16.0.3",
     "firebase-admin": "^11.4.1",
     "knex": "^2.4.2",
diff --git a/cloud_functions/scripts/deploy.sh b/cloud_functions/scripts/deploy.sh
index 0b760c20..aab28384 100755
--- a/cloud_functions/scripts/deploy.sh
+++ b/cloud_functions/scripts/deploy.sh
@@ -211,6 +211,12 @@ if [ -z "$WORMCHAIN_PAGERDUTY_URL" ]; then
     echo "WORMCHAIN_PAGERDUTY_URL must be specified"
     exit 1
 fi
+
+if [ -z "$SOLANA_RPC" ]; then
+    echo "SOLANA_RPC must be specified"
+    exit 1
+fi
+
 gcloud functions --project "$GCP_PROJECT" deploy compute-tvl --entry-point computeTVL --runtime nodejs18 --trigger-http --no-allow-unauthenticated --timeout 300 --memory 1GB --region europe-west3 --set-env-vars PG_USER=$PG_USER,PG_PASSWORD=$PG_PASSWORD,PG_DATABASE=$PG_DATABASE,PG_HOST=$PG_HOST,PG_ATTEST_MESSAGE_TABLE=$PG_ATTEST_MESSAGE_TABLE,PG_TOKEN_METADATA_TABLE=$PG_TOKEN_METADATA_TABLE,PG_TOKEN_TRANSFER_TABLE=$PG_TOKEN_TRANSFER_TABLE,FIRESTORE_TVL_COLLECTION=$FIRESTORE_TVL_COLLECTION
 gcloud functions --project "$GCP_PROJECT" deploy compute-tvl-history --entry-point computeTVLHistory --runtime nodejs18 --trigger-http --no-allow-unauthenticated --timeout 540 --memory 1GB --region europe-west3 --set-env-vars PG_USER=$PG_USER,PG_PASSWORD=$PG_PASSWORD,PG_DATABASE=$PG_DATABASE,PG_HOST=$PG_HOST,PG_ATTEST_MESSAGE_TABLE=$PG_ATTEST_MESSAGE_TABLE,PG_TOKEN_METADATA_TABLE=$PG_TOKEN_METADATA_TABLE,PG_TOKEN_TRANSFER_TABLE=$PG_TOKEN_TRANSFER_TABLE,FIRESTORE_TVL_HISTORY_COLLECTION=$FIRESTORE_TVL_HISTORY_COLLECTION,PG_TOKEN_PRICE_HISTORY_TABLE=$PG_TOKEN_PRICE_HISTORY_TABLE
 gcloud functions --project "$GCP_PROJECT" deploy compute-tvl-tvm --entry-point computeTvlTvm --runtime nodejs18 --trigger-http --no-allow-unauthenticated --timeout 540 --memory 1GB --region europe-west3 --set-env-vars PG_USER=$PG_USER,PG_PASSWORD=$PG_PASSWORD,PG_DATABASE=$PG_DATABASE,PG_HOST=$PG_HOST,PG_TOKEN_METADATA_TABLE=$PG_TOKEN_METADATA_TABLE,PG_TOKEN_PRICE_HISTORY_TABLE=$PG_TOKEN_PRICE_HISTORY_TABLE,FIRESTORE_LATEST_TVLTVM_COLLECTION=$FIRESTORE_LATEST_TVLTVM_COLLECTION
@@ -219,6 +225,7 @@ gcloud functions --project "$GCP_PROJECT" deploy process-vaa --entry-point proce
 gcloud functions --project "$GCP_PROJECT" deploy refresh-todays-token-prices --entry-point refreshTodaysTokenPrices --runtime nodejs18 --trigger-http --no-allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3 --set-env-vars PG_USER=$PG_USER,PG_PASSWORD=$PG_PASSWORD,PG_DATABASE=$PG_DATABASE,PG_HOST=$PG_HOST,PG_TOKEN_METADATA_TABLE=$PG_TOKEN_METADATA_TABLE,PG_TOKEN_PRICE_HISTORY_TABLE=$PG_TOKEN_PRICE_HISTORY_TABLE
 gcloud functions --project "$GCP_PROJECT" deploy update-token-metadata --entry-point updateTokenMetadata --runtime nodejs18 --trigger-http --no-allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3 --set-env-vars PG_USER=$PG_USER,PG_PASSWORD=$PG_PASSWORD,PG_DATABASE=$PG_DATABASE,PG_HOST=$PG_HOST,PG_TOKEN_METADATA_TABLE=$PG_TOKEN_METADATA_TABLE
 gcloud functions --project "$GCP_PROJECT" deploy wormchain-monitor --entry-point wormchainMonitor --runtime nodejs18 --trigger-http --no-allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3 --set-env-vars WORMCHAIN_SLACK_CHANNEL_ID=$WORMCHAIN_SLACK_CHANNEL_ID,WORMCHAIN_SLACK_POST_URL=$WORMCHAIN_SLACK_POST_URL,WORMCHAIN_SLACK_BOT_TOKEN=$WORMCHAIN_SLACK_BOT_TOKEN,WORMCHAIN_PAGERDUTY_ROUTING_KEY=$WORMCHAIN_PAGERDUTY_ROUTING_KEY,WORMCHAIN_PAGERDUTY_URL=$WORMCHAIN_PAGERDUTY_URL
+gcloud functions --project "$GCP_PROJECT" deploy get-solana-events --entry-point getSolanaEvents --runtime nodejs18 --trigger-http --allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3 --set-env-vars SOLANA_RPC=$SOLANA_RPC
 
 if [ "$NETWORK" == "MAINNET" ]; then
     echo "Finished deploying MAINNET functions"
diff --git a/cloud_functions/src/getSolanaEvents.ts b/cloud_functions/src/getSolanaEvents.ts
new file mode 100644
index 00000000..bac1fda9
--- /dev/null
+++ b/cloud_functions/src/getSolanaEvents.ts
@@ -0,0 +1,363 @@
+import {
+  Connection,
+  ConfirmedSignatureInfo,
+  PublicKey,
+  ParsedInstruction,
+  PartiallyDecodedInstruction,
+  ParsedTransactionWithMeta,
+  SolanaJSONRPCError,
+  VersionedBlockResponse,
+} from '@solana/web3.js';
+import * as ethers from 'ethers';
+import * as bs58 from 'bs58';
+import { deserialize } from 'borsh';
+import { assertEnvironmentVariable } from '@wormhole-foundation/wormhole-monitor-common';
+
+interface EventData {
+  blockNumber: number;
+  txHash: string;
+  from: string;
+  to: string;
+  token: string;
+  amount: string;
+  isDeposit: boolean;
+}
+
+export async function getSolanaEvents(req: any, res: any) {
+  res.set('Access-Control-Allow-Origin', '*');
+  if (req.method === 'OPTIONS') {
+    // Send response to OPTIONS requests
+    res.set('Access-Control-Allow-Methods', 'GET');
+    res.set('Access-Control-Allow-Headers', 'Content-Type');
+    res.set('Access-Control-Max-Age', '3600');
+    res.sendStatus(204);
+    return;
+  }
+  if (!req.query.fromSlot) {
+    res.status(400).send('fromSlot is required');
+    return;
+  }
+  if (!req.query.toSlot) {
+    res.status(400).send('toSlot is required');
+    return;
+  }
+  try {
+    const fromSlot = Number(req.query.fromSlot);
+    const toSlot = Number(req.query.toSlot);
+    console.log(`fetching events from ${fromSlot} to ${toSlot}`);
+    // the RPC doesn't store blocks that are too old
+    const events = fromSlot < 232090284 ? [] : await _getSolanaEvents(fromSlot, toSlot);
+    console.log(`fetched ${events.length} events`);
+    res.json(events);
+  } catch (e) {
+    console.error(e);
+    res.sendStatus(500);
+  }
+}
+
+const coreBridge = new PublicKey('worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth');
+const tokenBridge = new PublicKey('wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb');
+const tokenProgram = new PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
+
+const mintAuthority = 'BCD75RNBHrJJpW4dXVagL5mPjzRLnVZq4YirJdjEYMV7';
+const transferAuthority = '7oPa2PHQdZmjSPqvpZN7MQxnC7Dcf3uL4oLqknGLk2S3';
+const custodyAuthority = 'GugU1tP7doLeTw9hQP51xRJyS8Da1fWxuiy2rVrnMD2m';
+
+enum CoreBridgeIxId {
+  PostMessage = 0x1,
+}
+
+enum TokenBridgeIxId {
+  CompleteNative = 0x2,
+  CompleteWrapped,
+  TransferWrapped,
+  TransferNative,
+  CompleteNativeWithPayload = 0x9,
+  CompleteWrappedWithPayload,
+  TransferWrappedWithPayload,
+  TransferNativeWithPayload,
+}
+
+const PostMessageDataSchema = {
+  struct: {
+    nonce: 'u32',
+    payload: { array: { type: 'u8' } },
+    consistency_level: 'u8',
+  },
+};
+
+/**
+ * Retrieves Solana events from a given slot range using the token bridge.
+ * @param fromSlot The starting slot to retrieve events from.
+ * @param toSlot The ending slot to retrieve events from.
+ * @returns An array of EventData objects representing the events that occurred within the given slot range.
+ */
+const _getSolanaEvents = async (fromSlot: number, toSlot: number) => {
+  const txs = await getParsedTransactions(fromSlot, toSlot, tokenBridge);
+  const events = txs.reduce((acc, tx) => {
+    if (!tx || !tx.blockTime || tx.meta?.err) {
+      return acc;
+    }
+    // handle the case where the token bridge instruction is a top-level instruction
+    tx.transaction.message.instructions.forEach((ix: any, index: any) => {
+      if (!isTokenBridgeIx(ix)) {
+        return;
+      }
+      const innerIx = tx.meta?.innerInstructions?.find((innerIx: any) => innerIx.index === index);
+      if (!innerIx || innerIx.instructions.length === 0) {
+        return;
+      }
+      const event = getEventData(tx, ix, innerIx.instructions);
+      if (event) {
+        acc.push(event);
+      }
+    });
+    // handle the case where the token bridge instruction is an inner instruction
+    tx.meta?.innerInstructions?.forEach((innerIx: any) => {
+      innerIx.instructions.forEach((ix: any, index: any) => {
+        if (isTokenBridgeIx(ix)) {
+          const event = getEventData(tx, ix, innerIx.instructions.slice(index + 1));
+          if (event) {
+            acc.push(event);
+          }
+        }
+      });
+    });
+    return acc;
+  }, [] as EventData[]);
+  return events;
+};
+
+const getEventData = (
+  tx: ParsedTransactionWithMeta,
+  tokenBridgeIx: PartiallyDecodedInstruction,
+  innerIxs: (ParsedInstruction | PartiallyDecodedInstruction)[]
+): EventData | undefined => {
+  const data = bs58.decode(tokenBridgeIx.data);
+  if (data.length === 0) {
+    return;
+  }
+  const tokenBridgeIxId = data[0];
+  const txHash = tx.transaction.signatures[0];
+  const blockNumber = tx.slot;
+  // search the inner instructions for token transfer instructions to get the event data
+  switch (tokenBridgeIxId) {
+    case TokenBridgeIxId.TransferNative:
+    case TokenBridgeIxId.TransferNativeWithPayload: {
+      const transferIx = innerIxs.find(
+        (ix): ix is ParsedInstruction =>
+          isTransferIx(ix) && ix.parsed.info?.authority === transferAuthority
+      );
+      if (transferIx) {
+        return {
+          blockNumber,
+          txHash,
+          to: transferIx.parsed.info?.destination,
+          from: transferIx.parsed.info?.source,
+          token: tokenBridgeIx.accounts[3]?.toString() || '', // mint account
+          amount: transferIx.parsed.info?.amount,
+          isDeposit: true,
+        };
+      }
+      break;
+    }
+    case TokenBridgeIxId.TransferWrapped:
+    case TokenBridgeIxId.TransferWrappedWithPayload: {
+      const burnIx = innerIxs.find(
+        (ix): ix is ParsedInstruction =>
+          isBurnIx(ix) && ix.parsed.info?.authority === transferAuthority
+      );
+      const coreBridgeIx = innerIxs.find(
+        (ix): ix is PartiallyDecodedInstruction =>
+          ix.programId.equals(coreBridge) && (ix as PartiallyDecodedInstruction).data !== undefined
+      );
+      const coreBridgeIxData = coreBridgeIx?.data
+        ? Buffer.from(bs58.decode(coreBridgeIx.data))
+        : undefined;
+      if (
+        burnIx &&
+        coreBridgeIxData &&
+        coreBridgeIxData.length > 0 &&
+        coreBridgeIxData[0] === CoreBridgeIxId.PostMessage
+      ) {
+        const postMessageData: any = deserialize(
+          PostMessageDataSchema,
+          coreBridgeIxData.subarray(1)
+        );
+        const payload = Buffer.from(postMessageData.payload);
+        const originChain = payload.readUint16BE(65);
+        const toChain = payload.readUInt16BE(99);
+        // if this is a wrapped token being burned and not being sent to its origin chain,
+        // then it should be included in the volume by fixing the `to` address
+        // https://docs.wormhole.com/wormhole/explore-wormhole/vaa#token-transfer
+        const to = toChain !== originChain ? tokenBridge.toString() : ethers.constants.AddressZero;
+        return {
+          blockNumber,
+          txHash,
+          to,
+          from: burnIx.parsed.info?.account,
+          token: tokenBridgeIx.accounts[4]?.toString() || '', // mint account
+          amount: burnIx.parsed.info?.amount,
+          isDeposit: false,
+        };
+      }
+      break;
+    }
+    case TokenBridgeIxId.CompleteNative:
+    case TokenBridgeIxId.CompleteNativeWithPayload: {
+      // TODO: this doesn't handle the case where the fee recipient is not the destination
+      // in this case there will be another transfer instruction with the fee recipient as the destination
+      const transferIx = innerIxs.find(
+        (ix): ix is ParsedInstruction =>
+          isTransferIx(ix) &&
+          ix.parsed.info?.authority === custodyAuthority &&
+          (tokenBridgeIxId === TokenBridgeIxId.CompleteNativeWithPayload ||
+            ix.parsed.info?.destination === tokenBridgeIx.accounts[6].toString())
+      );
+      if (transferIx) {
+        const mintAccountIndex = tokenBridgeIxId === TokenBridgeIxId.CompleteNative ? 8 : 9;
+        return {
+          blockNumber,
+          txHash,
+          to: transferIx.parsed.info?.destination,
+          from: transferIx.parsed.info?.source,
+          token: tokenBridgeIx.accounts[mintAccountIndex]?.toString() || '', // mint account
+          amount: transferIx.parsed.info?.amount,
+          isDeposit: false,
+        };
+      }
+      break;
+    }
+    case TokenBridgeIxId.CompleteWrapped:
+    case TokenBridgeIxId.CompleteWrappedWithPayload: {
+      // TODO: this doesn't handle the case where the fee recipient is not the destination
+      // in this case there will be another mint instruction with the fee recipient as the destination
+      const mintToIx = innerIxs.find(
+        (ix): ix is ParsedInstruction =>
+          isMintToIx(ix) &&
+          ix.parsed.info?.mintAuthority === mintAuthority &&
+          (tokenBridgeIxId === TokenBridgeIxId.CompleteWrappedWithPayload ||
+            ix.parsed.info?.account === tokenBridgeIx.accounts[6].toString())
+      );
+      if (mintToIx) {
+        return {
+          blockNumber,
+          txHash,
+          to: mintToIx.parsed.info?.account,
+          // to be consistent with the ethereum adapter,
+          // we set the `from` address to the zero address for minted tokens
+          from: ethers.constants.AddressZero,
+          token: mintToIx.parsed.info?.mint,
+          amount: mintToIx.parsed.info?.amount,
+          isDeposit: false,
+        };
+      }
+      break;
+    }
+  }
+};
+
+const isTokenBridgeIx = (
+  ix: ParsedInstruction | PartiallyDecodedInstruction
+): ix is PartiallyDecodedInstruction =>
+  ix.programId.equals(tokenBridge) && (ix as PartiallyDecodedInstruction).accounts !== undefined;
+
+const isMintToIx = (ix: ParsedInstruction | PartiallyDecodedInstruction): ix is ParsedInstruction =>
+  isTxOfType(ix, tokenProgram, 'mintTo');
+
+const isBurnIx = (ix: ParsedInstruction | PartiallyDecodedInstruction): ix is ParsedInstruction =>
+  isTxOfType(ix, tokenProgram, 'burn');
+
+const isTransferIx = (
+  ix: ParsedInstruction | PartiallyDecodedInstruction
+): ix is ParsedInstruction => isTxOfType(ix, tokenProgram, 'transfer');
+
+const isTxOfType = (
+  ix: ParsedInstruction | PartiallyDecodedInstruction,
+  programId: PublicKey,
+  type: string
+) => (ix as ParsedInstruction).parsed?.type === type && ix.programId.equals(programId);
+
+/**
+ * Fetches and returns an array of parsed transactions for a given address within a specified block range.
+ * @param fromSlot The starting block slot.
+ * @param toSlot The ending block slot.
+ * @param address The public key of the address to fetch transactions for.
+ * @returns An array of parsed transactions within the specified block range.
+ * @throws An error if the block range is invalid or too large, or if a transaction cannot be fetched.
+ */
+async function getParsedTransactions(
+  fromSlot: number,
+  toSlot: number,
+  address: PublicKey
+): Promise<(ParsedTransactionWithMeta | null)[]> {
+  const connection = new Connection(assertEnvironmentVariable('SOLANA_RPC'));
+  if (fromSlot > toSlot) throw new Error('invalid block range');
+  if (toSlot - fromSlot > 100_000) throw new Error('block range too large');
+
+  // identify block range by fetching signatures of the first and last transactions
+  // getSignaturesForAddress walks backwards so fromSignature occurs after toSignature
+  let toBlock: VersionedBlockResponse | null = null;
+  try {
+    toBlock = await connection.getBlock(toSlot, {
+      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 getParsedTransactions(fromSlot, toSlot - 1, address);
+    } else {
+      throw e;
+    }
+  }
+  if (!toBlock || !toBlock.blockTime || toBlock.transactions.length === 0) {
+    return getParsedTransactions(fromSlot, toSlot - 1, address);
+  }
+  const fromSignature =
+    toBlock.transactions[toBlock.transactions.length - 1].transaction.signatures[0];
+
+  let fromBlock: VersionedBlockResponse | null = null;
+  try {
+    fromBlock = await connection.getBlock(fromSlot, {
+      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 getParsedTransactions(fromSlot + 1, toSlot, address);
+    } else {
+      throw e;
+    }
+  }
+  if (!fromBlock || !fromBlock.blockTime || fromBlock.transactions.length === 0) {
+    return getParsedTransactions(fromSlot + 1, toSlot, address);
+  }
+  const toSignature = fromBlock.transactions[0].transaction.signatures[0];
+
+  // get all `address` signatures between fromTransaction and toTransaction
+  const results = [];
+  let numSignatures = 0;
+  let currSignature: string | undefined = fromSignature;
+  const limit = 100;
+  do {
+    const signatures: ConfirmedSignatureInfo[] = await connection.getSignaturesForAddress(address, {
+      before: currSignature,
+      until: toSignature,
+      limit,
+    });
+    const txs = await connection.getParsedTransactions(
+      signatures.map((s) => s.signature),
+      {
+        maxSupportedTransactionVersion: 0,
+      }
+    );
+    if (txs.length !== signatures.length) {
+      throw new Error(`failed to fetch tx for signatures`);
+    }
+    results.push(...txs);
+    numSignatures = signatures.length;
+    currSignature = signatures[signatures.length - 1].signature;
+  } while (numSignatures === limit);
+
+  return results;
+}
diff --git a/cloud_functions/src/index.ts b/cloud_functions/src/index.ts
index 82951ec0..e42b7565 100644
--- a/cloud_functions/src/index.ts
+++ b/cloud_functions/src/index.ts
@@ -24,6 +24,7 @@ export const { updateTokenMetadata } = require('./updateTokenMetadata');
 export const { getReobserveVaas } = require('./getReobserveVaas');
 export const { wormchainMonitor } = require('./wormchainMonitor');
 export const { getLatestTokenData } = require('./getLatestTokenData');
+export const { getSolanaEvents } = require('./getSolanaEvents');
 
 // Register an HTTP function with the Functions Framework that will be executed
 // when you make an HTTP request to the deployed function's endpoint.
@@ -49,3 +50,4 @@ functions.http('updateTokenMetadata', updateTokenMetadata);
 functions.http('getReobserveVaas', getReobserveVaas);
 functions.http('wormchainMonitor', wormchainMonitor);
 functions.http('latestTokenData', getLatestTokenData);
+functions.http('getSolanaEvents', getSolanaEvents);
diff --git a/package-lock.json b/package-lock.json
index 3dece2ee..1f7d8224 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,7 +32,9 @@
         "@google-cloud/functions-framework": "^3.1.3",
         "@google-cloud/pubsub": "^3.4.1",
         "@google-cloud/storage": "^6.8.0",
+        "@solana/web3.js": "^1.87.3",
         "axios": "^1.5.0",
+        "borsh": "^1.0.0",
         "dotenv": "^16.0.3",
         "firebase-admin": "^11.4.1",
         "knex": "^2.4.2",
@@ -207,6 +209,11 @@
       "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
       "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
     },
+    "cloud_functions/node_modules/borsh": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/borsh/-/borsh-1.0.0.tgz",
+      "integrity": "sha512-fSVWzzemnyfF89EPwlUNsrS5swF5CrtiN4e+h0/lLf4dz2he4L3ndM20PS9wj7ICSkXJe/TQUHdaPTq15b1mNQ=="
+    },
     "cloud_functions/node_modules/cosmjs-types": {
       "version": "0.8.0",
       "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.8.0.tgz",
@@ -2099,15 +2106,21 @@
       "license": "MIT"
     },
     "node_modules/@babel/runtime": {
-      "version": "7.21.0",
-      "license": "MIT",
+      "version": "7.23.2",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz",
+      "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==",
       "dependencies": {
-        "regenerator-runtime": "^0.13.11"
+        "regenerator-runtime": "^0.14.0"
       },
       "engines": {
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@babel/runtime/node_modules/regenerator-runtime": {
+      "version": "0.14.0",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
+      "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
+    },
     "node_modules/@babel/template": {
       "version": "7.20.7",
       "license": "MIT",
@@ -6131,34 +6144,6 @@
         "@scure/base": "~1.1.0"
       }
     },
-    "node_modules/@mysten/sui.js/node_modules/@types/node": {
-      "version": "12.20.55",
-      "license": "MIT"
-    },
-    "node_modules/@mysten/sui.js/node_modules/jayson": {
-      "version": "4.1.0",
-      "license": "MIT",
-      "dependencies": {
-        "@types/connect": "^3.4.33",
-        "@types/node": "^12.12.54",
-        "@types/ws": "^7.4.4",
-        "commander": "^2.20.3",
-        "delay": "^5.0.0",
-        "es6-promisify": "^5.0.0",
-        "eyes": "^0.1.8",
-        "isomorphic-ws": "^4.0.1",
-        "json-stringify-safe": "^5.0.1",
-        "JSONStream": "^1.3.5",
-        "uuid": "^8.3.2",
-        "ws": "^7.4.5"
-      },
-      "bin": {
-        "jayson": "bin/jayson.js"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "node_modules/@mysten/sui.js/node_modules/superstruct": {
       "version": "1.0.3",
       "license": "MIT",
@@ -6409,16 +6394,6 @@
       ],
       "license": "MIT"
     },
-    "node_modules/@noble/ed25519": {
-      "version": "1.7.1",
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://paulmillr.com/funding/"
-        }
-      ],
-      "license": "MIT"
-    },
     "node_modules/@noble/hashes": {
       "version": "1.1.5",
       "funding": [
@@ -6429,16 +6404,6 @@
       ],
       "license": "MIT"
     },
-    "node_modules/@noble/secp256k1": {
-      "version": "1.7.0",
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://paulmillr.com/funding/"
-        }
-      ],
-      "license": "MIT"
-    },
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
       "license": "MIT",
@@ -6863,50 +6828,66 @@
       }
     },
     "node_modules/@solana/web3.js": {
-      "version": "1.73.0",
-      "license": "MIT",
+      "version": "1.87.3",
+      "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.87.3.tgz",
+      "integrity": "sha512-WGLzTZpi00vP443qGK3gL+LZXQJwaWkh6bzNXYpMTCAH2Z102y3YbPWOoQzJUeRSZWSXKh7MFkA3vDMFlMvGZQ==",
       "dependencies": {
-        "@babel/runtime": "^7.12.5",
-        "@noble/ed25519": "^1.7.0",
-        "@noble/hashes": "^1.1.2",
-        "@noble/secp256k1": "^1.6.3",
+        "@babel/runtime": "^7.23.2",
+        "@noble/curves": "^1.2.0",
+        "@noble/hashes": "^1.3.1",
         "@solana/buffer-layout": "^4.0.0",
-        "agentkeepalive": "^4.2.1",
+        "agentkeepalive": "^4.3.0",
         "bigint-buffer": "^1.1.5",
-        "bn.js": "^5.0.0",
+        "bn.js": "^5.2.1",
         "borsh": "^0.7.0",
         "bs58": "^4.0.1",
-        "buffer": "6.0.1",
+        "buffer": "6.0.3",
         "fast-stable-stringify": "^1.0.0",
-        "jayson": "^3.4.4",
-        "node-fetch": "2",
-        "rpc-websockets": "^7.5.0",
+        "jayson": "^4.1.0",
+        "node-fetch": "^2.6.12",
+        "rpc-websockets": "^7.5.1",
         "superstruct": "^0.14.2"
+      }
+    },
+    "node_modules/@solana/web3.js/node_modules/@noble/curves": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
+      "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
+      "dependencies": {
+        "@noble/hashes": "1.3.2"
       },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@solana/web3.js/node_modules/@noble/hashes": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
+      "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
       "engines": {
-        "node": ">=12.20.0"
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
       }
     },
-    "node_modules/@solana/web3.js/node_modules/buffer": {
-      "version": "6.0.1",
-      "funding": [
-        {
-          "type": "github",
-          "url": "https://github.com/sponsors/feross"
-        },
-        {
-          "type": "patreon",
-          "url": "https://www.patreon.com/feross"
-        },
-        {
-          "type": "consulting",
-          "url": "https://feross.org/support"
-        }
-      ],
-      "license": "MIT",
+    "node_modules/@solana/web3.js/node_modules/node-fetch": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+      "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
       "dependencies": {
-        "base64-js": "^1.3.1",
-        "ieee754": "^1.2.1"
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
       }
     },
     "node_modules/@solana/web3.js/node_modules/superstruct": {
@@ -8602,11 +8583,10 @@
       }
     },
     "node_modules/agentkeepalive": {
-      "version": "4.2.1",
-      "license": "MIT",
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
+      "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
       "dependencies": {
-        "debug": "^4.1.0",
-        "depd": "^1.1.2",
         "humanize-ms": "^1.2.1"
       },
       "engines": {
@@ -14377,7 +14357,8 @@
     },
     "node_modules/humanize-ms": {
       "version": "1.2.1",
-      "license": "MIT",
+      "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+      "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
       "dependencies": {
         "ms": "^2.0.0"
       }
@@ -15149,8 +15130,9 @@
       }
     },
     "node_modules/jayson": {
-      "version": "3.7.0",
-      "license": "MIT",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
+      "integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
       "dependencies": {
         "@types/connect": "^3.4.33",
         "@types/node": "^12.12.54",
@@ -15162,7 +15144,6 @@
         "isomorphic-ws": "^4.0.1",
         "json-stringify-safe": "^5.0.1",
         "JSONStream": "^1.3.5",
-        "lodash": "^4.17.20",
         "uuid": "^8.3.2",
         "ws": "^7.4.5"
       },
@@ -15175,7 +15156,8 @@
     },
     "node_modules/jayson/node_modules/@types/node": {
       "version": "12.20.55",
-      "license": "MIT"
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
+      "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
     },
     "node_modules/jest": {
       "version": "27.5.1",
@@ -26894,7 +26876,8 @@
     "watcher/node_modules/@types/node": {
       "version": "12.20.55",
       "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
-      "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
+      "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==",
+      "dev": true
     },
     "watcher/node_modules/ansi-styles": {
       "version": "4.3.0",
@@ -27103,31 +27086,6 @@
       "version": "0.3.2",
       "license": "MIT"
     },
-    "watcher/node_modules/jayson": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
-      "integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
-      "dependencies": {
-        "@types/connect": "^3.4.33",
-        "@types/node": "^12.12.54",
-        "@types/ws": "^7.4.4",
-        "commander": "^2.20.3",
-        "delay": "^5.0.0",
-        "es6-promisify": "^5.0.0",
-        "eyes": "^0.1.8",
-        "isomorphic-ws": "^4.0.1",
-        "json-stringify-safe": "^5.0.1",
-        "JSONStream": "^1.3.5",
-        "uuid": "^8.3.2",
-        "ws": "^7.4.5"
-      },
-      "bin": {
-        "jayson": "bin/jayson.js"
-      },
-      "engines": {
-        "node": ">=8"
-      }
-    },
     "watcher/node_modules/jest": {
       "version": "29.3.1",
       "dev": true,
@@ -29071,9 +29029,18 @@
       "version": "0.8.0"
     },
     "@babel/runtime": {
-      "version": "7.21.0",
+      "version": "7.23.2",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz",
+      "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==",
       "requires": {
-        "regenerator-runtime": "^0.13.11"
+        "regenerator-runtime": "^0.14.0"
+      },
+      "dependencies": {
+        "regenerator-runtime": {
+          "version": "0.14.0",
+          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
+          "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA=="
+        }
       }
     },
     "@babel/template": {
@@ -31684,26 +31651,6 @@
             "@scure/base": "~1.1.0"
           }
         },
-        "@types/node": {
-          "version": "12.20.55"
-        },
-        "jayson": {
-          "version": "4.1.0",
-          "requires": {
-            "@types/connect": "^3.4.33",
-            "@types/node": "^12.12.54",
-            "@types/ws": "^7.4.4",
-            "commander": "^2.20.3",
-            "delay": "^5.0.0",
-            "es6-promisify": "^5.0.0",
-            "eyes": "^0.1.8",
-            "isomorphic-ws": "^4.0.1",
-            "json-stringify-safe": "^5.0.1",
-            "JSONStream": "^1.3.5",
-            "uuid": "^8.3.2",
-            "ws": "^7.4.5"
-          }
-        },
         "superstruct": {
           "version": "1.0.3"
         }
@@ -31816,15 +31763,9 @@
         }
       }
     },
-    "@noble/ed25519": {
-      "version": "1.7.1"
-    },
     "@noble/hashes": {
       "version": "1.1.5"
     },
-    "@noble/secp256k1": {
-      "version": "1.7.0"
-    },
     "@nodelib/fs.scandir": {
       "version": "2.1.5",
       "requires": {
@@ -32059,31 +32000,46 @@
       }
     },
     "@solana/web3.js": {
-      "version": "1.73.0",
+      "version": "1.87.3",
+      "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.87.3.tgz",
+      "integrity": "sha512-WGLzTZpi00vP443qGK3gL+LZXQJwaWkh6bzNXYpMTCAH2Z102y3YbPWOoQzJUeRSZWSXKh7MFkA3vDMFlMvGZQ==",
       "requires": {
-        "@babel/runtime": "^7.12.5",
-        "@noble/ed25519": "^1.7.0",
-        "@noble/hashes": "^1.1.2",
-        "@noble/secp256k1": "^1.6.3",
+        "@babel/runtime": "^7.23.2",
+        "@noble/curves": "^1.2.0",
+        "@noble/hashes": "^1.3.1",
         "@solana/buffer-layout": "^4.0.0",
-        "agentkeepalive": "^4.2.1",
+        "agentkeepalive": "^4.3.0",
         "bigint-buffer": "^1.1.5",
-        "bn.js": "^5.0.0",
+        "bn.js": "^5.2.1",
         "borsh": "^0.7.0",
         "bs58": "^4.0.1",
-        "buffer": "6.0.1",
+        "buffer": "6.0.3",
         "fast-stable-stringify": "^1.0.0",
-        "jayson": "^3.4.4",
-        "node-fetch": "2",
-        "rpc-websockets": "^7.5.0",
+        "jayson": "^4.1.0",
+        "node-fetch": "^2.6.12",
+        "rpc-websockets": "^7.5.1",
         "superstruct": "^0.14.2"
       },
       "dependencies": {
-        "buffer": {
-          "version": "6.0.1",
+        "@noble/curves": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
+          "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
           "requires": {
-            "base64-js": "^1.3.1",
-            "ieee754": "^1.2.1"
+            "@noble/hashes": "1.3.2"
+          }
+        },
+        "@noble/hashes": {
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
+          "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="
+        },
+        "node-fetch": {
+          "version": "2.7.0",
+          "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
+          "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
+          "requires": {
+            "whatwg-url": "^5.0.0"
           }
         },
         "superstruct": {
@@ -33178,7 +33134,9 @@
         "@google-cloud/functions-framework": "^3.1.3",
         "@google-cloud/pubsub": "^3.4.1",
         "@google-cloud/storage": "^6.8.0",
+        "@solana/web3.js": "^1.87.3",
         "axios": "^1.5.0",
+        "borsh": "^1.0.0",
         "dotenv": "^16.0.3",
         "firebase-admin": "^11.4.1",
         "knex": "^2.4.2",
@@ -33355,6 +33313,11 @@
           "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
           "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ=="
         },
+        "borsh": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/borsh/-/borsh-1.0.0.tgz",
+          "integrity": "sha512-fSVWzzemnyfF89EPwlUNsrS5swF5CrtiN4e+h0/lLf4dz2he4L3ndM20PS9wj7ICSkXJe/TQUHdaPTq15b1mNQ=="
+        },
         "cosmjs-types": {
           "version": "0.8.0",
           "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.8.0.tgz",
@@ -33768,7 +33731,8 @@
         "@types/node": {
           "version": "12.20.55",
           "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
-          "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
+          "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==",
+          "dev": true
         },
         "ansi-styles": {
           "version": "4.3.0",
@@ -33911,25 +33875,6 @@
         "is-arrayish": {
           "version": "0.3.2"
         },
-        "jayson": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
-          "integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
-          "requires": {
-            "@types/connect": "^3.4.33",
-            "@types/node": "^12.12.54",
-            "@types/ws": "^7.4.4",
-            "commander": "^2.20.3",
-            "delay": "^5.0.0",
-            "es6-promisify": "^5.0.0",
-            "eyes": "^0.1.8",
-            "isomorphic-ws": "^4.0.1",
-            "json-stringify-safe": "^5.0.1",
-            "JSONStream": "^1.3.5",
-            "uuid": "^8.3.2",
-            "ws": "^7.4.5"
-          }
-        },
         "jest": {
           "version": "29.3.1",
           "dev": true,
@@ -34723,10 +34668,10 @@
       }
     },
     "agentkeepalive": {
-      "version": "4.2.1",
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
+      "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
       "requires": {
-        "debug": "^4.1.0",
-        "depd": "^1.1.2",
         "humanize-ms": "^1.2.1"
       }
     },
@@ -38437,6 +38382,8 @@
     },
     "humanize-ms": {
       "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
+      "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
       "requires": {
         "ms": "^2.0.0"
       }
@@ -38842,7 +38789,9 @@
       }
     },
     "jayson": {
-      "version": "3.7.0",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
+      "integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
       "requires": {
         "@types/connect": "^3.4.33",
         "@types/node": "^12.12.54",
@@ -38854,13 +38803,14 @@
         "isomorphic-ws": "^4.0.1",
         "json-stringify-safe": "^5.0.1",
         "JSONStream": "^1.3.5",
-        "lodash": "^4.17.20",
         "uuid": "^8.3.2",
         "ws": "^7.4.5"
       },
       "dependencies": {
         "@types/node": {
-          "version": "12.20.55"
+          "version": "12.20.55",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
+          "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
         }
       }
     },

From 0100c61b95090768550227d986afb09cb56dc0ac Mon Sep 17 00:00:00 2001
From: Kevin Peters <kevin@w7.xyz>
Date: Wed, 1 Nov 2023 12:04:25 -0500
Subject: [PATCH 2/3] cloud-functions: Added getSuiEvents cloud function

---
 cloud_functions/package.json           |   6 +-
 cloud_functions/scripts/deploy.sh      |   1 +
 cloud_functions/src/getSolanaEvents.ts |  11 +-
 cloud_functions/src/getSuiEvents.ts    | 279 +++++++++++++++++++
 cloud_functions/src/index.ts           |   2 +
 cloud_functions/src/types.ts           |  10 +
 cloud_functions/tsconfig.json          |   3 +-
 package-lock.json                      | 353 ++++++++++++++++++++-----
 8 files changed, 580 insertions(+), 85 deletions(-)
 create mode 100644 cloud_functions/src/getSuiEvents.ts

diff --git a/cloud_functions/package.json b/cloud_functions/package.json
index 3752895f..b55dd95b 100644
--- a/cloud_functions/package.json
+++ b/cloud_functions/package.json
@@ -7,7 +7,7 @@
   "scripts": {
     "build": "tsc",
     "dev": "ts-node src/index.ts",
-    "start": "npx functions-framework --target=getSolanaEvents [--signature-type=http]",
+    "start": "npx functions-framework --target=getSuiEvents [--signature-type=http]",
     "deploy": "bash scripts/deploy.sh",
     "gcp-build": "npm i ./dist/src/wormhole-foundation-wormhole-monitor-common-0.0.1.tgz ./dist/src/wormhole-foundation-wormhole-monitor-database-0.0.1.tgz"
   },
@@ -18,6 +18,7 @@
     "@google-cloud/functions-framework": "^3.1.3",
     "@google-cloud/pubsub": "^3.4.1",
     "@google-cloud/storage": "^6.8.0",
+    "@mysten/sui.js": "^0.45.0",
     "@solana/web3.js": "^1.87.3",
     "axios": "^1.5.0",
     "borsh": "^1.0.0",
@@ -26,5 +27,8 @@
     "knex": "^2.4.2",
     "path-to-regexp": "^6.2.1",
     "pg": "^8.10.0"
+  },
+  "devDependencies": {
+    "typescript": "^5.2.2"
   }
 }
diff --git a/cloud_functions/scripts/deploy.sh b/cloud_functions/scripts/deploy.sh
index aab28384..0aecac8f 100755
--- a/cloud_functions/scripts/deploy.sh
+++ b/cloud_functions/scripts/deploy.sh
@@ -226,6 +226,7 @@ gcloud functions --project "$GCP_PROJECT" deploy refresh-todays-token-prices --e
 gcloud functions --project "$GCP_PROJECT" deploy update-token-metadata --entry-point updateTokenMetadata --runtime nodejs18 --trigger-http --no-allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3 --set-env-vars PG_USER=$PG_USER,PG_PASSWORD=$PG_PASSWORD,PG_DATABASE=$PG_DATABASE,PG_HOST=$PG_HOST,PG_TOKEN_METADATA_TABLE=$PG_TOKEN_METADATA_TABLE
 gcloud functions --project "$GCP_PROJECT" deploy wormchain-monitor --entry-point wormchainMonitor --runtime nodejs18 --trigger-http --no-allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3 --set-env-vars WORMCHAIN_SLACK_CHANNEL_ID=$WORMCHAIN_SLACK_CHANNEL_ID,WORMCHAIN_SLACK_POST_URL=$WORMCHAIN_SLACK_POST_URL,WORMCHAIN_SLACK_BOT_TOKEN=$WORMCHAIN_SLACK_BOT_TOKEN,WORMCHAIN_PAGERDUTY_ROUTING_KEY=$WORMCHAIN_PAGERDUTY_ROUTING_KEY,WORMCHAIN_PAGERDUTY_URL=$WORMCHAIN_PAGERDUTY_URL
 gcloud functions --project "$GCP_PROJECT" deploy get-solana-events --entry-point getSolanaEvents --runtime nodejs18 --trigger-http --allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3 --set-env-vars SOLANA_RPC=$SOLANA_RPC
+gcloud functions --project "$GCP_PROJECT" deploy get-sui-events --entry-point getSuiEvents --runtime nodejs18 --trigger-http --allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3
 
 if [ "$NETWORK" == "MAINNET" ]; then
     echo "Finished deploying MAINNET functions"
diff --git a/cloud_functions/src/getSolanaEvents.ts b/cloud_functions/src/getSolanaEvents.ts
index bac1fda9..fe4db2d2 100644
--- a/cloud_functions/src/getSolanaEvents.ts
+++ b/cloud_functions/src/getSolanaEvents.ts
@@ -12,16 +12,7 @@ import * as ethers from 'ethers';
 import * as bs58 from 'bs58';
 import { deserialize } from 'borsh';
 import { assertEnvironmentVariable } from '@wormhole-foundation/wormhole-monitor-common';
-
-interface EventData {
-  blockNumber: number;
-  txHash: string;
-  from: string;
-  to: string;
-  token: string;
-  amount: string;
-  isDeposit: boolean;
-}
+import { EventData } from './types';
 
 export async function getSolanaEvents(req: any, res: any) {
   res.set('Access-Control-Allow-Origin', '*');
diff --git a/cloud_functions/src/getSuiEvents.ts b/cloud_functions/src/getSuiEvents.ts
new file mode 100644
index 00000000..1ea7c63b
--- /dev/null
+++ b/cloud_functions/src/getSuiEvents.ts
@@ -0,0 +1,279 @@
+import { ethers } from 'ethers';
+import {
+  SuiClient,
+  SuiEvent,
+  SuiObjectChange,
+  SuiTransactionBlockResponse,
+  getFullnodeUrl,
+  PaginatedTransactionResponse,
+} from '@mysten/sui.js/client';
+import { normalizeSuiAddress, SUI_TYPE_ARG } from '@mysten/sui.js/utils';
+import { EventData } from './types';
+
+const wormholeMessageEventType =
+  '0x5306f64e312b581766351c07af79c72fcb1cd25147157fdc2f8ad76de9a3fb6a::publish_message::WormholeMessage';
+const tokenBridgeAddress = '0xc57508ee0d4595e5a8728974a4a93a787d38f339757230d441e895422c07aba9';
+const originalTokenBridgePackageId =
+  '0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d';
+
+export async function getSuiEvents(req: any, res: any) {
+  res.set('Access-Control-Allow-Origin', '*');
+  if (req.method === 'OPTIONS') {
+    // Send response to OPTIONS requests
+    res.set('Access-Control-Allow-Methods', 'GET');
+    res.set('Access-Control-Allow-Headers', 'Content-Type');
+    res.set('Access-Control-Max-Age', '3600');
+    res.sendStatus(204);
+    return;
+  }
+  if (!req.query.fromCheckpoint) {
+    res.status(400).send('fromCheckpoint is required');
+    return;
+  }
+  if (!req.query.toCheckpoint) {
+    res.status(400).send('toCheckpoint is required');
+    return;
+  }
+  try {
+    const fromCheckpoint = Number(req.query.fromCheckpoint);
+    const toCheckpoint = Number(req.query.toCheckpoint);
+    console.log(`fetching events from ${fromCheckpoint} to ${toCheckpoint}`);
+    const events = await _getSuiEvents(fromCheckpoint, toCheckpoint);
+    console.log(`fetched ${events.length} events`);
+    res.json(events);
+  } catch (e) {
+    console.error(e);
+    res.sendStatus(500);
+  }
+}
+
+/**
+ * Retrieves Sui events from a given checkpoint range using the token bridge.
+ * Optimized to make as few RPC calls as possible.
+ * @param fromCheckpoint The starting checkpoint to retrieve events from.
+ * @param toCheckpoint The ending checkpoint to retrieve events from.
+ * @returns An array of EventData objects representing the events that occurred within the given checkpoint range.
+ */
+const _getSuiEvents = async (
+  fromCheckpoint: number,
+  toCheckpoint: number
+): Promise<EventData[]> => {
+  const events: EventData[] = [];
+  const txBlocks = await getTransactionBlocks(fromCheckpoint, toCheckpoint, tokenBridgeAddress);
+  for (const txBlock of txBlocks) {
+    if (
+      txBlock.effects?.status.status !== 'success' ||
+      !txBlock.checkpoint ||
+      !txBlock.objectChanges ||
+      txBlock.transaction?.data.transaction.kind !== 'ProgrammableTransaction'
+    ) {
+      continue;
+    }
+    const transactions = txBlock.transaction.data.transaction.transactions;
+    for (const tx of transactions) {
+      const moveCall = 'MoveCall' in tx && tx.MoveCall;
+      if (!moveCall || moveCall.package !== originalTokenBridgePackageId) {
+        continue;
+      }
+      if (
+        (moveCall.module === 'complete_transfer_with_payload' &&
+          moveCall.function === 'authorize_transfer') ||
+        (moveCall.module === 'complete_transfer' && moveCall.function === 'authorize_transfer')
+      ) {
+        const token = moveCall.type_arguments![0];
+        // search backwards for the parse_and_verify call
+        const parseAndVerifyTx = transactions
+          .slice(
+            0,
+            transactions.findIndex((value) => value === tx)
+          )
+          .reverse()
+          .find(
+            (tx) =>
+              'MoveCall' in tx &&
+              tx.MoveCall.module === 'vaa' &&
+              tx.MoveCall.function === 'parse_and_verify'
+          );
+        if (!parseAndVerifyTx || !('MoveCall' in parseAndVerifyTx)) {
+          continue;
+        }
+        const vaaArg = parseAndVerifyTx.MoveCall.arguments?.[1];
+        if (!vaaArg || typeof vaaArg !== 'object' || !('Input' in vaaArg)) {
+          continue;
+        }
+        const vaaInput = txBlock.transaction.data.transaction.inputs[vaaArg.Input];
+        if (!vaaInput || vaaInput.type !== 'pure' || vaaInput.valueType !== 'vector<u8>') {
+          continue;
+        }
+        const vaa = Buffer.from(vaaInput.value as number[]);
+        const sigStart = 6;
+        const numSigners = vaa[5];
+        const sigLength = 66;
+        const body = vaa.subarray(sigStart + sigLength * numSigners);
+        const payload = body.subarray(51);
+        const type = payload.readUInt8(0);
+        if (type !== 1 && type !== 3) {
+          continue;
+        }
+        const amount = await denormalizeAmount(
+          token,
+          ethers.BigNumber.from(payload.subarray(1, 33))
+        );
+        const to = `0x${payload.subarray(67, 99).toString('hex')}`;
+        const event: EventData = {
+          blockNumber: Number(txBlock.checkpoint),
+          txHash: txBlock.digest,
+          // Wrapped tokens are minted from the zero address on Ethereum
+          // Override the from address to be the zero address for consistency
+          from: isWrappedToken(token, txBlock.objectChanges)
+            ? ethers.constants.AddressZero
+            : tokenBridgeAddress,
+          to,
+          token,
+          amount: amount.toString(),
+          isDeposit: false,
+        };
+        events.push(event);
+      }
+      if (
+        ((moveCall.module === 'transfer_tokens_with_payload' &&
+          moveCall.function === 'transfer_tokens_with_payload') ||
+          (moveCall.module === 'transfer_tokens' && moveCall.function === 'transfer_tokens')) &&
+        txBlock.events
+      ) {
+        const token = tx.MoveCall.type_arguments![0];
+        const payload = getWormholeMessagePayload(txBlock.events);
+        const originChain = payload.readUint16BE(65);
+        const toChain = payload.readUInt16BE(99);
+        const amount = await denormalizeAmount(
+          token,
+          ethers.BigNumber.from(payload.subarray(1, 33))
+        );
+        const isWrapped = isWrappedToken(token, txBlock.objectChanges);
+        const event: EventData = {
+          blockNumber: Number(txBlock.checkpoint),
+          txHash: txBlock.digest,
+          from: txBlock.transaction.data.sender,
+          // if this is a wrapped token being burned and not being sent to its origin chain,
+          // then it should be included in the volume by fixing the to address
+          to:
+            !isWrapped || originChain !== toChain
+              ? tokenBridgeAddress
+              : ethers.constants.AddressZero,
+          token,
+          amount: amount.toString(),
+          isDeposit: !isWrapped,
+        };
+        events.push(event);
+      }
+    }
+  }
+  return events;
+};
+
+const getWormholeMessagePayload = (events: SuiEvent[]): Buffer => {
+  const filtered = events.filter((event) => {
+    return event.type === wormholeMessageEventType;
+  });
+  // TODO: support multiple transfers in a single txBlock
+  if (filtered.length !== 1) {
+    throw new Error(`Expected exactly one wormhole message event, found ${filtered.length}`);
+  }
+  return Buffer.from((filtered[0].parsedJson as any).payload);
+};
+
+const tokenDecimalsCache: { [token: string]: number } = {};
+
+const getTokenDecimals = async (token: string): Promise<number> => {
+  if (token in tokenDecimalsCache) {
+    return tokenDecimalsCache[token];
+  }
+  const client = getClient();
+  const coinMetadata = await client.getCoinMetadata({ coinType: token });
+  if (coinMetadata === null) {
+    throw new Error(`Failed to get coin metadata for ${token}`);
+  }
+  const { decimals } = coinMetadata;
+  tokenDecimalsCache[token] = decimals;
+  return decimals;
+};
+
+const denormalizeAmount = async (
+  token: string,
+  amount: ethers.BigNumber
+): Promise<ethers.BigNumber> => {
+  const decimals = await getTokenDecimals(token);
+  if (decimals > 8) {
+    return amount.mul(ethers.BigNumber.from(10).pow(decimals - 8));
+  }
+  return amount;
+};
+
+const isWrappedToken = (token: string, objectChanges: SuiObjectChange[]) => {
+  const split = token.split('::');
+  if (split.length !== 3) {
+    throw new Error(`Invalid token ${token}`);
+  }
+  const normalized =
+    token === SUI_TYPE_ARG ? token : `${normalizeSuiAddress(split[0])}::${split[1]}::${split[2]}`;
+  const nativeKey = `0x2::dynamic_field::Field<${originalTokenBridgePackageId}::token_registry::Key<${normalized}>, ${originalTokenBridgePackageId}::native_asset::NativeAsset<${normalized}>>`;
+  const wrappedKey = `0x2::dynamic_field::Field<${originalTokenBridgePackageId}::token_registry::Key<${normalized}>, ${originalTokenBridgePackageId}::wrapped_asset::WrappedAsset<${normalized}>>`;
+  const value = objectChanges.find(
+    (change) => change.type === 'mutated' && [nativeKey, wrappedKey].includes(change.objectType)
+  );
+  if (!value) {
+    throw new Error(`Failed to find object change for token ${normalized}`);
+  }
+  return value.type === 'mutated' && value.objectType === wrappedKey;
+};
+
+export const getTransactionBlocks = async (
+  fromCheckpoint: number,
+  toCheckpoint: number,
+  changedObject: string
+): Promise<SuiTransactionBlockResponse[]> => {
+  const client = getClient();
+  const results: SuiTransactionBlockResponse[] = [];
+  let hasNextPage = false;
+  let cursor: string | null | undefined = undefined;
+  let oldestCheckpoint: string | null = null;
+  do {
+    // TODO: The public RPC doesn't support fetching events by chaining filters with a `TimeRange` filter,
+    // so we have to search backwards for our checkpoint range
+    const response: PaginatedTransactionResponse = await client.queryTransactionBlocks({
+      filter: { ChangedObject: changedObject },
+      cursor,
+      options: {
+        showEffects: true,
+        showEvents: true,
+        showInput: true,
+        showObjectChanges: true,
+      },
+    });
+    for (const txBlock of response.data) {
+      const checkpoint = txBlock.checkpoint;
+      if (!checkpoint) {
+        continue;
+      }
+      if (checkpoint >= fromCheckpoint.toString() && checkpoint <= toCheckpoint.toString()) {
+        results.push(txBlock);
+      }
+      if (oldestCheckpoint === null || checkpoint < oldestCheckpoint) {
+        oldestCheckpoint = checkpoint;
+      }
+    }
+    hasNextPage = response.hasNextPage;
+    cursor = response.nextCursor;
+  } while (
+    hasNextPage &&
+    cursor &&
+    oldestCheckpoint &&
+    oldestCheckpoint >= fromCheckpoint.toString()
+  );
+  return results;
+};
+
+const getClient = () => {
+  const url = process.env.SUI_RPC ?? getFullnodeUrl('mainnet');
+  return new SuiClient({ url });
+};
diff --git a/cloud_functions/src/index.ts b/cloud_functions/src/index.ts
index e42b7565..f0d5dd72 100644
--- a/cloud_functions/src/index.ts
+++ b/cloud_functions/src/index.ts
@@ -25,6 +25,7 @@ export const { getReobserveVaas } = require('./getReobserveVaas');
 export const { wormchainMonitor } = require('./wormchainMonitor');
 export const { getLatestTokenData } = require('./getLatestTokenData');
 export const { getSolanaEvents } = require('./getSolanaEvents');
+export const { getSuiEvents } = require('./getSuiEvents');
 
 // Register an HTTP function with the Functions Framework that will be executed
 // when you make an HTTP request to the deployed function's endpoint.
@@ -51,3 +52,4 @@ functions.http('getReobserveVaas', getReobserveVaas);
 functions.http('wormchainMonitor', wormchainMonitor);
 functions.http('latestTokenData', getLatestTokenData);
 functions.http('getSolanaEvents', getSolanaEvents);
+functions.http('getSuiEvents', getSuiEvents);
diff --git a/cloud_functions/src/types.ts b/cloud_functions/src/types.ts
index 5871455b..43d846fe 100644
--- a/cloud_functions/src/types.ts
+++ b/cloud_functions/src/types.ts
@@ -378,3 +378,13 @@ export type SlackInfo = {
   msg: string;
   bannerTxt: string;
 };
+
+export interface EventData {
+  blockNumber: number;
+  txHash: string;
+  from: string;
+  to: string;
+  token: string;
+  amount: string;
+  isDeposit: boolean;
+}
diff --git a/cloud_functions/tsconfig.json b/cloud_functions/tsconfig.json
index 7b1a4db1..f179f543 100644
--- a/cloud_functions/tsconfig.json
+++ b/cloud_functions/tsconfig.json
@@ -2,7 +2,8 @@
   "extends": "../tsconfig.base.json",
   "references": [{ "path": "../common" }, { "path": "../database" }],
   "compilerOptions": {
-    "outDir": "dist"
+    "outDir": "dist",
+    "skipLibCheck": true
   },
   "include": ["src", "src/data/*.json"]
 }
diff --git a/package-lock.json b/package-lock.json
index 1f7d8224..a1e5fc95 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,6 +32,7 @@
         "@google-cloud/functions-framework": "^3.1.3",
         "@google-cloud/pubsub": "^3.4.1",
         "@google-cloud/storage": "^6.8.0",
+        "@mysten/sui.js": "^0.45.0",
         "@solana/web3.js": "^1.87.3",
         "axios": "^1.5.0",
         "borsh": "^1.0.0",
@@ -40,6 +41,9 @@
         "knex": "^2.4.2",
         "path-to-regexp": "^6.2.1",
         "pg": "^8.10.0"
+      },
+      "devDependencies": {
+        "typescript": "^5.2.2"
       }
     },
     "cloud_functions/node_modules/@cosmjs/amino": {
@@ -194,6 +198,57 @@
       "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.31.1.tgz",
       "integrity": "sha512-n4Se1wu4GnKwztQHNFfJvUeWcpvx3o8cWhSbNs9JQShEuB3nv3R5lqFBtDCgHZF/emFQAP+ZjF8bTfCs9UBGhA=="
     },
+    "cloud_functions/node_modules/@mysten/bcs": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/@mysten/bcs/-/bcs-0.8.1.tgz",
+      "integrity": "sha512-wSEdP7QEfGQdb34g+7R0f3OdRqrv88iIABfJVDVJ6IsGLYVILreh8dZfNpZNUUyzctiyhX7zB9e/lR5qkddFPA==",
+      "dependencies": {
+        "bs58": "^5.0.0"
+      }
+    },
+    "cloud_functions/node_modules/@mysten/sui.js": {
+      "version": "0.45.0",
+      "resolved": "https://registry.npmjs.org/@mysten/sui.js/-/sui.js-0.45.0.tgz",
+      "integrity": "sha512-t+LyGWzKTH+TM3c7apvK2aohnx6hj0nxjqrqbk49X/uLxhvDKEIoTGJDQC2cfDTOg6OteYTefCNsfRauh8wAzA==",
+      "dependencies": {
+        "@mysten/bcs": "0.8.1",
+        "@noble/curves": "^1.1.0",
+        "@noble/hashes": "^1.3.1",
+        "@open-rpc/client-js": "^1.8.1",
+        "@scure/bip32": "^1.3.1",
+        "@scure/bip39": "^1.2.1",
+        "@suchipi/femver": "^1.0.0",
+        "events": "^3.3.0",
+        "superstruct": "^1.0.3",
+        "tweetnacl": "^1.0.3"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "cloud_functions/node_modules/@noble/hashes": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
+      "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "cloud_functions/node_modules/@scure/bip39": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
+      "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
+      "dependencies": {
+        "@noble/hashes": "~1.3.0",
+        "@scure/base": "~1.1.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
     "cloud_functions/node_modules/axios": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
@@ -204,6 +259,11 @@
         "proxy-from-env": "^1.1.0"
       }
     },
+    "cloud_functions/node_modules/base-x": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz",
+      "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw=="
+    },
     "cloud_functions/node_modules/bech32": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
@@ -214,6 +274,14 @@
       "resolved": "https://registry.npmjs.org/borsh/-/borsh-1.0.0.tgz",
       "integrity": "sha512-fSVWzzemnyfF89EPwlUNsrS5swF5CrtiN4e+h0/lLf4dz2he4L3ndM20PS9wj7ICSkXJe/TQUHdaPTq15b1mNQ=="
     },
+    "cloud_functions/node_modules/bs58": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz",
+      "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==",
+      "dependencies": {
+        "base-x": "^4.0.0"
+      }
+    },
     "cloud_functions/node_modules/cosmjs-types": {
       "version": "0.8.0",
       "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.8.0.tgz",
@@ -248,6 +316,27 @@
         "pbts": "bin/pbts"
       }
     },
+    "cloud_functions/node_modules/superstruct": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-1.0.3.tgz",
+      "integrity": "sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg==",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "cloud_functions/node_modules/typescript": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+      "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+      "dev": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
     "common": {
       "name": "@wormhole-foundation/wormhole-monitor-common",
       "version": "0.0.1"
@@ -6372,27 +6461,26 @@
       }
     },
     "node_modules/@noble/curves": {
-      "version": "1.0.0",
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://paulmillr.com/funding/"
-        }
-      ],
-      "license": "MIT",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
+      "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
       "dependencies": {
-        "@noble/hashes": "1.3.0"
+        "@noble/hashes": "1.3.2"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
       }
     },
     "node_modules/@noble/curves/node_modules/@noble/hashes": {
-      "version": "1.3.0",
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://paulmillr.com/funding/"
-        }
-      ],
-      "license": "MIT"
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
+      "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
     },
     "node_modules/@noble/hashes": {
       "version": "1.1.5",
@@ -6433,6 +6521,25 @@
         "node": ">= 8"
       }
     },
+    "node_modules/@open-rpc/client-js": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/@open-rpc/client-js/-/client-js-1.8.1.tgz",
+      "integrity": "sha512-vV+Hetl688nY/oWI9IFY0iKDrWuLdYhf7OIKI6U1DcnJV7r4gAgwRJjEr1QVYszUc0gjkHoQJzqevmXMGLyA0g==",
+      "dependencies": {
+        "isomorphic-fetch": "^3.0.0",
+        "isomorphic-ws": "^5.0.0",
+        "strict-event-emitter-types": "^2.0.0",
+        "ws": "^7.0.0"
+      }
+    },
+    "node_modules/@open-rpc/client-js/node_modules/isomorphic-ws": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz",
+      "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==",
+      "peerDependencies": {
+        "ws": "*"
+      }
+    },
     "node_modules/@opentelemetry/api": {
       "version": "1.4.1",
       "license": "Apache-2.0",
@@ -6713,39 +6820,36 @@
       "license": "MIT"
     },
     "node_modules/@scure/base": {
-      "version": "1.1.1",
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://paulmillr.com/funding/"
-        }
-      ],
-      "license": "MIT"
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz",
+      "integrity": "sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==",
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
     },
     "node_modules/@scure/bip32": {
-      "version": "1.3.0",
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://paulmillr.com/funding/"
-        }
-      ],
-      "license": "MIT",
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz",
+      "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==",
       "dependencies": {
-        "@noble/curves": "~1.0.0",
-        "@noble/hashes": "~1.3.0",
-        "@scure/base": "~1.1.0"
+        "@noble/curves": "~1.2.0",
+        "@noble/hashes": "~1.3.2",
+        "@scure/base": "~1.1.2"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
       }
     },
     "node_modules/@scure/bip32/node_modules/@noble/hashes": {
-      "version": "1.3.0",
-      "funding": [
-        {
-          "type": "individual",
-          "url": "https://paulmillr.com/funding/"
-        }
-      ],
-      "license": "MIT"
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
+      "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
     },
     "node_modules/@scure/bip39": {
       "version": "1.1.0",
@@ -6849,17 +6953,6 @@
         "superstruct": "^0.14.2"
       }
     },
-    "node_modules/@solana/web3.js/node_modules/@noble/curves": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
-      "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
-      "dependencies": {
-        "@noble/hashes": "1.3.2"
-      },
-      "funding": {
-        "url": "https://paulmillr.com/funding/"
-      }
-    },
     "node_modules/@solana/web3.js/node_modules/@noble/hashes": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
@@ -14964,6 +15057,15 @@
       "version": "2.0.0",
       "license": "ISC"
     },
+    "node_modules/isomorphic-fetch": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
+      "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
+      "dependencies": {
+        "node-fetch": "^2.6.1",
+        "whatwg-fetch": "^3.4.1"
+      }
+    },
     "node_modules/isomorphic-ws": {
       "version": "4.0.1",
       "license": "MIT",
@@ -23632,6 +23734,11 @@
       "version": "1.0.1",
       "license": "MIT"
     },
+    "node_modules/strict-event-emitter-types": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz",
+      "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA=="
+    },
     "node_modules/strict-uri-encode": {
       "version": "1.1.0",
       "license": "MIT",
@@ -31753,13 +31860,17 @@
       }
     },
     "@noble/curves": {
-      "version": "1.0.0",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
+      "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
       "requires": {
-        "@noble/hashes": "1.3.0"
+        "@noble/hashes": "1.3.2"
       },
       "dependencies": {
         "@noble/hashes": {
-          "version": "1.3.0"
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
+          "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="
         }
       }
     },
@@ -31783,6 +31894,25 @@
         "fastq": "^1.6.0"
       }
     },
+    "@open-rpc/client-js": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/@open-rpc/client-js/-/client-js-1.8.1.tgz",
+      "integrity": "sha512-vV+Hetl688nY/oWI9IFY0iKDrWuLdYhf7OIKI6U1DcnJV7r4gAgwRJjEr1QVYszUc0gjkHoQJzqevmXMGLyA0g==",
+      "requires": {
+        "isomorphic-fetch": "^3.0.0",
+        "isomorphic-ws": "^5.0.0",
+        "strict-event-emitter-types": "^2.0.0",
+        "ws": "^7.0.0"
+      },
+      "dependencies": {
+        "isomorphic-ws": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz",
+          "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==",
+          "requires": {}
+        }
+      }
+    },
     "@opentelemetry/api": {
       "version": "1.4.1"
     },
@@ -31936,18 +32066,24 @@
       "version": "1.2.0"
     },
     "@scure/base": {
-      "version": "1.1.1"
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.3.tgz",
+      "integrity": "sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q=="
     },
     "@scure/bip32": {
-      "version": "1.3.0",
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.2.tgz",
+      "integrity": "sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA==",
       "requires": {
-        "@noble/curves": "~1.0.0",
-        "@noble/hashes": "~1.3.0",
-        "@scure/base": "~1.1.0"
+        "@noble/curves": "~1.2.0",
+        "@noble/hashes": "~1.3.2",
+        "@scure/base": "~1.1.2"
       },
       "dependencies": {
         "@noble/hashes": {
-          "version": "1.3.0"
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
+          "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="
         }
       }
     },
@@ -32021,14 +32157,6 @@
         "superstruct": "^0.14.2"
       },
       "dependencies": {
-        "@noble/curves": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
-          "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
-          "requires": {
-            "@noble/hashes": "1.3.2"
-          }
-        },
         "@noble/hashes": {
           "version": "1.3.2",
           "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
@@ -33134,6 +33262,7 @@
         "@google-cloud/functions-framework": "^3.1.3",
         "@google-cloud/pubsub": "^3.4.1",
         "@google-cloud/storage": "^6.8.0",
+        "@mysten/sui.js": "^0.45.0",
         "@solana/web3.js": "^1.87.3",
         "axios": "^1.5.0",
         "borsh": "^1.0.0",
@@ -33141,7 +33270,8 @@
         "firebase-admin": "^11.4.1",
         "knex": "^2.4.2",
         "path-to-regexp": "^6.2.1",
-        "pg": "^8.10.0"
+        "pg": "^8.10.0",
+        "typescript": "^5.2.2"
       },
       "dependencies": {
         "@cosmjs/amino": {
@@ -33298,6 +33428,45 @@
           "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.31.1.tgz",
           "integrity": "sha512-n4Se1wu4GnKwztQHNFfJvUeWcpvx3o8cWhSbNs9JQShEuB3nv3R5lqFBtDCgHZF/emFQAP+ZjF8bTfCs9UBGhA=="
         },
+        "@mysten/bcs": {
+          "version": "0.8.1",
+          "resolved": "https://registry.npmjs.org/@mysten/bcs/-/bcs-0.8.1.tgz",
+          "integrity": "sha512-wSEdP7QEfGQdb34g+7R0f3OdRqrv88iIABfJVDVJ6IsGLYVILreh8dZfNpZNUUyzctiyhX7zB9e/lR5qkddFPA==",
+          "requires": {
+            "bs58": "^5.0.0"
+          }
+        },
+        "@mysten/sui.js": {
+          "version": "0.45.0",
+          "resolved": "https://registry.npmjs.org/@mysten/sui.js/-/sui.js-0.45.0.tgz",
+          "integrity": "sha512-t+LyGWzKTH+TM3c7apvK2aohnx6hj0nxjqrqbk49X/uLxhvDKEIoTGJDQC2cfDTOg6OteYTefCNsfRauh8wAzA==",
+          "requires": {
+            "@mysten/bcs": "0.8.1",
+            "@noble/curves": "^1.1.0",
+            "@noble/hashes": "^1.3.1",
+            "@open-rpc/client-js": "^1.8.1",
+            "@scure/bip32": "^1.3.1",
+            "@scure/bip39": "^1.2.1",
+            "@suchipi/femver": "^1.0.0",
+            "events": "^3.3.0",
+            "superstruct": "^1.0.3",
+            "tweetnacl": "^1.0.3"
+          }
+        },
+        "@noble/hashes": {
+          "version": "1.3.2",
+          "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
+          "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="
+        },
+        "@scure/bip39": {
+          "version": "1.2.1",
+          "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
+          "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
+          "requires": {
+            "@noble/hashes": "~1.3.0",
+            "@scure/base": "~1.1.0"
+          }
+        },
         "axios": {
           "version": "1.5.0",
           "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
@@ -33308,6 +33477,11 @@
             "proxy-from-env": "^1.1.0"
           }
         },
+        "base-x": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz",
+          "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw=="
+        },
         "bech32": {
           "version": "1.1.4",
           "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
@@ -33318,6 +33492,14 @@
           "resolved": "https://registry.npmjs.org/borsh/-/borsh-1.0.0.tgz",
           "integrity": "sha512-fSVWzzemnyfF89EPwlUNsrS5swF5CrtiN4e+h0/lLf4dz2he4L3ndM20PS9wj7ICSkXJe/TQUHdaPTq15b1mNQ=="
         },
+        "bs58": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz",
+          "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==",
+          "requires": {
+            "base-x": "^4.0.0"
+          }
+        },
         "cosmjs-types": {
           "version": "0.8.0",
           "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.8.0.tgz",
@@ -33346,6 +33528,17 @@
             "@types/node": ">=13.7.0",
             "long": "^4.0.0"
           }
+        },
+        "superstruct": {
+          "version": "1.0.3",
+          "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-1.0.3.tgz",
+          "integrity": "sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg=="
+        },
+        "typescript": {
+          "version": "5.2.2",
+          "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
+          "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==",
+          "dev": true
         }
       }
     },
@@ -38687,6 +38880,15 @@
     "isexe": {
       "version": "2.0.0"
     },
+    "isomorphic-fetch": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
+      "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
+      "requires": {
+        "node-fetch": "^2.6.1",
+        "whatwg-fetch": "^3.4.1"
+      }
+    },
     "isomorphic-ws": {
       "version": "4.0.1",
       "requires": {}
@@ -43982,6 +44184,11 @@
     "stream-shift": {
       "version": "1.0.1"
     },
+    "strict-event-emitter-types": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz",
+      "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA=="
+    },
     "strict-uri-encode": {
       "version": "1.1.0"
     },

From 9744c2dc74752f240e958d733694906ed47afe30 Mon Sep 17 00:00:00 2001
From: Kevin Peters <kevin@wormholelabs.xyz>
Date: Tue, 19 Mar 2024 18:35:32 -0500
Subject: [PATCH 3/3] cloud-functions: rm getSuiEvents (build issues)

---
 cloud_functions/package.json        |   3 +-
 cloud_functions/scripts/deploy.sh   |   1 -
 cloud_functions/src/getSuiEvents.ts | 279 ----------------------------
 cloud_functions/src/index.ts        |   2 -
 package-lock.json                   | 181 ------------------
 5 files changed, 1 insertion(+), 465 deletions(-)
 delete mode 100644 cloud_functions/src/getSuiEvents.ts

diff --git a/cloud_functions/package.json b/cloud_functions/package.json
index b55dd95b..58b71f06 100644
--- a/cloud_functions/package.json
+++ b/cloud_functions/package.json
@@ -7,7 +7,7 @@
   "scripts": {
     "build": "tsc",
     "dev": "ts-node src/index.ts",
-    "start": "npx functions-framework --target=getSuiEvents [--signature-type=http]",
+    "start": "npx functions-framework --target=getSolanaEvents [--signature-type=http]",
     "deploy": "bash scripts/deploy.sh",
     "gcp-build": "npm i ./dist/src/wormhole-foundation-wormhole-monitor-common-0.0.1.tgz ./dist/src/wormhole-foundation-wormhole-monitor-database-0.0.1.tgz"
   },
@@ -18,7 +18,6 @@
     "@google-cloud/functions-framework": "^3.1.3",
     "@google-cloud/pubsub": "^3.4.1",
     "@google-cloud/storage": "^6.8.0",
-    "@mysten/sui.js": "^0.45.0",
     "@solana/web3.js": "^1.87.3",
     "axios": "^1.5.0",
     "borsh": "^1.0.0",
diff --git a/cloud_functions/scripts/deploy.sh b/cloud_functions/scripts/deploy.sh
index 0aecac8f..aab28384 100755
--- a/cloud_functions/scripts/deploy.sh
+++ b/cloud_functions/scripts/deploy.sh
@@ -226,7 +226,6 @@ gcloud functions --project "$GCP_PROJECT" deploy refresh-todays-token-prices --e
 gcloud functions --project "$GCP_PROJECT" deploy update-token-metadata --entry-point updateTokenMetadata --runtime nodejs18 --trigger-http --no-allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3 --set-env-vars PG_USER=$PG_USER,PG_PASSWORD=$PG_PASSWORD,PG_DATABASE=$PG_DATABASE,PG_HOST=$PG_HOST,PG_TOKEN_METADATA_TABLE=$PG_TOKEN_METADATA_TABLE
 gcloud functions --project "$GCP_PROJECT" deploy wormchain-monitor --entry-point wormchainMonitor --runtime nodejs18 --trigger-http --no-allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3 --set-env-vars WORMCHAIN_SLACK_CHANNEL_ID=$WORMCHAIN_SLACK_CHANNEL_ID,WORMCHAIN_SLACK_POST_URL=$WORMCHAIN_SLACK_POST_URL,WORMCHAIN_SLACK_BOT_TOKEN=$WORMCHAIN_SLACK_BOT_TOKEN,WORMCHAIN_PAGERDUTY_ROUTING_KEY=$WORMCHAIN_PAGERDUTY_ROUTING_KEY,WORMCHAIN_PAGERDUTY_URL=$WORMCHAIN_PAGERDUTY_URL
 gcloud functions --project "$GCP_PROJECT" deploy get-solana-events --entry-point getSolanaEvents --runtime nodejs18 --trigger-http --allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3 --set-env-vars SOLANA_RPC=$SOLANA_RPC
-gcloud functions --project "$GCP_PROJECT" deploy get-sui-events --entry-point getSuiEvents --runtime nodejs18 --trigger-http --allow-unauthenticated --timeout 300 --memory 256MB --region europe-west3
 
 if [ "$NETWORK" == "MAINNET" ]; then
     echo "Finished deploying MAINNET functions"
diff --git a/cloud_functions/src/getSuiEvents.ts b/cloud_functions/src/getSuiEvents.ts
deleted file mode 100644
index 1ea7c63b..00000000
--- a/cloud_functions/src/getSuiEvents.ts
+++ /dev/null
@@ -1,279 +0,0 @@
-import { ethers } from 'ethers';
-import {
-  SuiClient,
-  SuiEvent,
-  SuiObjectChange,
-  SuiTransactionBlockResponse,
-  getFullnodeUrl,
-  PaginatedTransactionResponse,
-} from '@mysten/sui.js/client';
-import { normalizeSuiAddress, SUI_TYPE_ARG } from '@mysten/sui.js/utils';
-import { EventData } from './types';
-
-const wormholeMessageEventType =
-  '0x5306f64e312b581766351c07af79c72fcb1cd25147157fdc2f8ad76de9a3fb6a::publish_message::WormholeMessage';
-const tokenBridgeAddress = '0xc57508ee0d4595e5a8728974a4a93a787d38f339757230d441e895422c07aba9';
-const originalTokenBridgePackageId =
-  '0x26efee2b51c911237888e5dc6702868abca3c7ac12c53f76ef8eba0697695e3d';
-
-export async function getSuiEvents(req: any, res: any) {
-  res.set('Access-Control-Allow-Origin', '*');
-  if (req.method === 'OPTIONS') {
-    // Send response to OPTIONS requests
-    res.set('Access-Control-Allow-Methods', 'GET');
-    res.set('Access-Control-Allow-Headers', 'Content-Type');
-    res.set('Access-Control-Max-Age', '3600');
-    res.sendStatus(204);
-    return;
-  }
-  if (!req.query.fromCheckpoint) {
-    res.status(400).send('fromCheckpoint is required');
-    return;
-  }
-  if (!req.query.toCheckpoint) {
-    res.status(400).send('toCheckpoint is required');
-    return;
-  }
-  try {
-    const fromCheckpoint = Number(req.query.fromCheckpoint);
-    const toCheckpoint = Number(req.query.toCheckpoint);
-    console.log(`fetching events from ${fromCheckpoint} to ${toCheckpoint}`);
-    const events = await _getSuiEvents(fromCheckpoint, toCheckpoint);
-    console.log(`fetched ${events.length} events`);
-    res.json(events);
-  } catch (e) {
-    console.error(e);
-    res.sendStatus(500);
-  }
-}
-
-/**
- * Retrieves Sui events from a given checkpoint range using the token bridge.
- * Optimized to make as few RPC calls as possible.
- * @param fromCheckpoint The starting checkpoint to retrieve events from.
- * @param toCheckpoint The ending checkpoint to retrieve events from.
- * @returns An array of EventData objects representing the events that occurred within the given checkpoint range.
- */
-const _getSuiEvents = async (
-  fromCheckpoint: number,
-  toCheckpoint: number
-): Promise<EventData[]> => {
-  const events: EventData[] = [];
-  const txBlocks = await getTransactionBlocks(fromCheckpoint, toCheckpoint, tokenBridgeAddress);
-  for (const txBlock of txBlocks) {
-    if (
-      txBlock.effects?.status.status !== 'success' ||
-      !txBlock.checkpoint ||
-      !txBlock.objectChanges ||
-      txBlock.transaction?.data.transaction.kind !== 'ProgrammableTransaction'
-    ) {
-      continue;
-    }
-    const transactions = txBlock.transaction.data.transaction.transactions;
-    for (const tx of transactions) {
-      const moveCall = 'MoveCall' in tx && tx.MoveCall;
-      if (!moveCall || moveCall.package !== originalTokenBridgePackageId) {
-        continue;
-      }
-      if (
-        (moveCall.module === 'complete_transfer_with_payload' &&
-          moveCall.function === 'authorize_transfer') ||
-        (moveCall.module === 'complete_transfer' && moveCall.function === 'authorize_transfer')
-      ) {
-        const token = moveCall.type_arguments![0];
-        // search backwards for the parse_and_verify call
-        const parseAndVerifyTx = transactions
-          .slice(
-            0,
-            transactions.findIndex((value) => value === tx)
-          )
-          .reverse()
-          .find(
-            (tx) =>
-              'MoveCall' in tx &&
-              tx.MoveCall.module === 'vaa' &&
-              tx.MoveCall.function === 'parse_and_verify'
-          );
-        if (!parseAndVerifyTx || !('MoveCall' in parseAndVerifyTx)) {
-          continue;
-        }
-        const vaaArg = parseAndVerifyTx.MoveCall.arguments?.[1];
-        if (!vaaArg || typeof vaaArg !== 'object' || !('Input' in vaaArg)) {
-          continue;
-        }
-        const vaaInput = txBlock.transaction.data.transaction.inputs[vaaArg.Input];
-        if (!vaaInput || vaaInput.type !== 'pure' || vaaInput.valueType !== 'vector<u8>') {
-          continue;
-        }
-        const vaa = Buffer.from(vaaInput.value as number[]);
-        const sigStart = 6;
-        const numSigners = vaa[5];
-        const sigLength = 66;
-        const body = vaa.subarray(sigStart + sigLength * numSigners);
-        const payload = body.subarray(51);
-        const type = payload.readUInt8(0);
-        if (type !== 1 && type !== 3) {
-          continue;
-        }
-        const amount = await denormalizeAmount(
-          token,
-          ethers.BigNumber.from(payload.subarray(1, 33))
-        );
-        const to = `0x${payload.subarray(67, 99).toString('hex')}`;
-        const event: EventData = {
-          blockNumber: Number(txBlock.checkpoint),
-          txHash: txBlock.digest,
-          // Wrapped tokens are minted from the zero address on Ethereum
-          // Override the from address to be the zero address for consistency
-          from: isWrappedToken(token, txBlock.objectChanges)
-            ? ethers.constants.AddressZero
-            : tokenBridgeAddress,
-          to,
-          token,
-          amount: amount.toString(),
-          isDeposit: false,
-        };
-        events.push(event);
-      }
-      if (
-        ((moveCall.module === 'transfer_tokens_with_payload' &&
-          moveCall.function === 'transfer_tokens_with_payload') ||
-          (moveCall.module === 'transfer_tokens' && moveCall.function === 'transfer_tokens')) &&
-        txBlock.events
-      ) {
-        const token = tx.MoveCall.type_arguments![0];
-        const payload = getWormholeMessagePayload(txBlock.events);
-        const originChain = payload.readUint16BE(65);
-        const toChain = payload.readUInt16BE(99);
-        const amount = await denormalizeAmount(
-          token,
-          ethers.BigNumber.from(payload.subarray(1, 33))
-        );
-        const isWrapped = isWrappedToken(token, txBlock.objectChanges);
-        const event: EventData = {
-          blockNumber: Number(txBlock.checkpoint),
-          txHash: txBlock.digest,
-          from: txBlock.transaction.data.sender,
-          // if this is a wrapped token being burned and not being sent to its origin chain,
-          // then it should be included in the volume by fixing the to address
-          to:
-            !isWrapped || originChain !== toChain
-              ? tokenBridgeAddress
-              : ethers.constants.AddressZero,
-          token,
-          amount: amount.toString(),
-          isDeposit: !isWrapped,
-        };
-        events.push(event);
-      }
-    }
-  }
-  return events;
-};
-
-const getWormholeMessagePayload = (events: SuiEvent[]): Buffer => {
-  const filtered = events.filter((event) => {
-    return event.type === wormholeMessageEventType;
-  });
-  // TODO: support multiple transfers in a single txBlock
-  if (filtered.length !== 1) {
-    throw new Error(`Expected exactly one wormhole message event, found ${filtered.length}`);
-  }
-  return Buffer.from((filtered[0].parsedJson as any).payload);
-};
-
-const tokenDecimalsCache: { [token: string]: number } = {};
-
-const getTokenDecimals = async (token: string): Promise<number> => {
-  if (token in tokenDecimalsCache) {
-    return tokenDecimalsCache[token];
-  }
-  const client = getClient();
-  const coinMetadata = await client.getCoinMetadata({ coinType: token });
-  if (coinMetadata === null) {
-    throw new Error(`Failed to get coin metadata for ${token}`);
-  }
-  const { decimals } = coinMetadata;
-  tokenDecimalsCache[token] = decimals;
-  return decimals;
-};
-
-const denormalizeAmount = async (
-  token: string,
-  amount: ethers.BigNumber
-): Promise<ethers.BigNumber> => {
-  const decimals = await getTokenDecimals(token);
-  if (decimals > 8) {
-    return amount.mul(ethers.BigNumber.from(10).pow(decimals - 8));
-  }
-  return amount;
-};
-
-const isWrappedToken = (token: string, objectChanges: SuiObjectChange[]) => {
-  const split = token.split('::');
-  if (split.length !== 3) {
-    throw new Error(`Invalid token ${token}`);
-  }
-  const normalized =
-    token === SUI_TYPE_ARG ? token : `${normalizeSuiAddress(split[0])}::${split[1]}::${split[2]}`;
-  const nativeKey = `0x2::dynamic_field::Field<${originalTokenBridgePackageId}::token_registry::Key<${normalized}>, ${originalTokenBridgePackageId}::native_asset::NativeAsset<${normalized}>>`;
-  const wrappedKey = `0x2::dynamic_field::Field<${originalTokenBridgePackageId}::token_registry::Key<${normalized}>, ${originalTokenBridgePackageId}::wrapped_asset::WrappedAsset<${normalized}>>`;
-  const value = objectChanges.find(
-    (change) => change.type === 'mutated' && [nativeKey, wrappedKey].includes(change.objectType)
-  );
-  if (!value) {
-    throw new Error(`Failed to find object change for token ${normalized}`);
-  }
-  return value.type === 'mutated' && value.objectType === wrappedKey;
-};
-
-export const getTransactionBlocks = async (
-  fromCheckpoint: number,
-  toCheckpoint: number,
-  changedObject: string
-): Promise<SuiTransactionBlockResponse[]> => {
-  const client = getClient();
-  const results: SuiTransactionBlockResponse[] = [];
-  let hasNextPage = false;
-  let cursor: string | null | undefined = undefined;
-  let oldestCheckpoint: string | null = null;
-  do {
-    // TODO: The public RPC doesn't support fetching events by chaining filters with a `TimeRange` filter,
-    // so we have to search backwards for our checkpoint range
-    const response: PaginatedTransactionResponse = await client.queryTransactionBlocks({
-      filter: { ChangedObject: changedObject },
-      cursor,
-      options: {
-        showEffects: true,
-        showEvents: true,
-        showInput: true,
-        showObjectChanges: true,
-      },
-    });
-    for (const txBlock of response.data) {
-      const checkpoint = txBlock.checkpoint;
-      if (!checkpoint) {
-        continue;
-      }
-      if (checkpoint >= fromCheckpoint.toString() && checkpoint <= toCheckpoint.toString()) {
-        results.push(txBlock);
-      }
-      if (oldestCheckpoint === null || checkpoint < oldestCheckpoint) {
-        oldestCheckpoint = checkpoint;
-      }
-    }
-    hasNextPage = response.hasNextPage;
-    cursor = response.nextCursor;
-  } while (
-    hasNextPage &&
-    cursor &&
-    oldestCheckpoint &&
-    oldestCheckpoint >= fromCheckpoint.toString()
-  );
-  return results;
-};
-
-const getClient = () => {
-  const url = process.env.SUI_RPC ?? getFullnodeUrl('mainnet');
-  return new SuiClient({ url });
-};
diff --git a/cloud_functions/src/index.ts b/cloud_functions/src/index.ts
index f0d5dd72..e42b7565 100644
--- a/cloud_functions/src/index.ts
+++ b/cloud_functions/src/index.ts
@@ -25,7 +25,6 @@ export const { getReobserveVaas } = require('./getReobserveVaas');
 export const { wormchainMonitor } = require('./wormchainMonitor');
 export const { getLatestTokenData } = require('./getLatestTokenData');
 export const { getSolanaEvents } = require('./getSolanaEvents');
-export const { getSuiEvents } = require('./getSuiEvents');
 
 // Register an HTTP function with the Functions Framework that will be executed
 // when you make an HTTP request to the deployed function's endpoint.
@@ -52,4 +51,3 @@ functions.http('getReobserveVaas', getReobserveVaas);
 functions.http('wormchainMonitor', wormchainMonitor);
 functions.http('latestTokenData', getLatestTokenData);
 functions.http('getSolanaEvents', getSolanaEvents);
-functions.http('getSuiEvents', getSuiEvents);
diff --git a/package-lock.json b/package-lock.json
index a1e5fc95..86021fdc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -32,7 +32,6 @@
         "@google-cloud/functions-framework": "^3.1.3",
         "@google-cloud/pubsub": "^3.4.1",
         "@google-cloud/storage": "^6.8.0",
-        "@mysten/sui.js": "^0.45.0",
         "@solana/web3.js": "^1.87.3",
         "axios": "^1.5.0",
         "borsh": "^1.0.0",
@@ -198,34 +197,6 @@
       "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.31.1.tgz",
       "integrity": "sha512-n4Se1wu4GnKwztQHNFfJvUeWcpvx3o8cWhSbNs9JQShEuB3nv3R5lqFBtDCgHZF/emFQAP+ZjF8bTfCs9UBGhA=="
     },
-    "cloud_functions/node_modules/@mysten/bcs": {
-      "version": "0.8.1",
-      "resolved": "https://registry.npmjs.org/@mysten/bcs/-/bcs-0.8.1.tgz",
-      "integrity": "sha512-wSEdP7QEfGQdb34g+7R0f3OdRqrv88iIABfJVDVJ6IsGLYVILreh8dZfNpZNUUyzctiyhX7zB9e/lR5qkddFPA==",
-      "dependencies": {
-        "bs58": "^5.0.0"
-      }
-    },
-    "cloud_functions/node_modules/@mysten/sui.js": {
-      "version": "0.45.0",
-      "resolved": "https://registry.npmjs.org/@mysten/sui.js/-/sui.js-0.45.0.tgz",
-      "integrity": "sha512-t+LyGWzKTH+TM3c7apvK2aohnx6hj0nxjqrqbk49X/uLxhvDKEIoTGJDQC2cfDTOg6OteYTefCNsfRauh8wAzA==",
-      "dependencies": {
-        "@mysten/bcs": "0.8.1",
-        "@noble/curves": "^1.1.0",
-        "@noble/hashes": "^1.3.1",
-        "@open-rpc/client-js": "^1.8.1",
-        "@scure/bip32": "^1.3.1",
-        "@scure/bip39": "^1.2.1",
-        "@suchipi/femver": "^1.0.0",
-        "events": "^3.3.0",
-        "superstruct": "^1.0.3",
-        "tweetnacl": "^1.0.3"
-      },
-      "engines": {
-        "node": ">=16"
-      }
-    },
     "cloud_functions/node_modules/@noble/hashes": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
@@ -237,18 +208,6 @@
         "url": "https://paulmillr.com/funding/"
       }
     },
-    "cloud_functions/node_modules/@scure/bip39": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
-      "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
-      "dependencies": {
-        "@noble/hashes": "~1.3.0",
-        "@scure/base": "~1.1.0"
-      },
-      "funding": {
-        "url": "https://paulmillr.com/funding/"
-      }
-    },
     "cloud_functions/node_modules/axios": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
@@ -259,11 +218,6 @@
         "proxy-from-env": "^1.1.0"
       }
     },
-    "cloud_functions/node_modules/base-x": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz",
-      "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw=="
-    },
     "cloud_functions/node_modules/bech32": {
       "version": "1.1.4",
       "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
@@ -274,14 +228,6 @@
       "resolved": "https://registry.npmjs.org/borsh/-/borsh-1.0.0.tgz",
       "integrity": "sha512-fSVWzzemnyfF89EPwlUNsrS5swF5CrtiN4e+h0/lLf4dz2he4L3ndM20PS9wj7ICSkXJe/TQUHdaPTq15b1mNQ=="
     },
-    "cloud_functions/node_modules/bs58": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz",
-      "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==",
-      "dependencies": {
-        "base-x": "^4.0.0"
-      }
-    },
     "cloud_functions/node_modules/cosmjs-types": {
       "version": "0.8.0",
       "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.8.0.tgz",
@@ -316,14 +262,6 @@
         "pbts": "bin/pbts"
       }
     },
-    "cloud_functions/node_modules/superstruct": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-1.0.3.tgz",
-      "integrity": "sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg==",
-      "engines": {
-        "node": ">=14.0.0"
-      }
-    },
     "cloud_functions/node_modules/typescript": {
       "version": "5.2.2",
       "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
@@ -6521,25 +6459,6 @@
         "node": ">= 8"
       }
     },
-    "node_modules/@open-rpc/client-js": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmjs.org/@open-rpc/client-js/-/client-js-1.8.1.tgz",
-      "integrity": "sha512-vV+Hetl688nY/oWI9IFY0iKDrWuLdYhf7OIKI6U1DcnJV7r4gAgwRJjEr1QVYszUc0gjkHoQJzqevmXMGLyA0g==",
-      "dependencies": {
-        "isomorphic-fetch": "^3.0.0",
-        "isomorphic-ws": "^5.0.0",
-        "strict-event-emitter-types": "^2.0.0",
-        "ws": "^7.0.0"
-      }
-    },
-    "node_modules/@open-rpc/client-js/node_modules/isomorphic-ws": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz",
-      "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==",
-      "peerDependencies": {
-        "ws": "*"
-      }
-    },
     "node_modules/@opentelemetry/api": {
       "version": "1.4.1",
       "license": "Apache-2.0",
@@ -15057,15 +14976,6 @@
       "version": "2.0.0",
       "license": "ISC"
     },
-    "node_modules/isomorphic-fetch": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
-      "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
-      "dependencies": {
-        "node-fetch": "^2.6.1",
-        "whatwg-fetch": "^3.4.1"
-      }
-    },
     "node_modules/isomorphic-ws": {
       "version": "4.0.1",
       "license": "MIT",
@@ -23734,11 +23644,6 @@
       "version": "1.0.1",
       "license": "MIT"
     },
-    "node_modules/strict-event-emitter-types": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz",
-      "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA=="
-    },
     "node_modules/strict-uri-encode": {
       "version": "1.1.0",
       "license": "MIT",
@@ -31894,25 +31799,6 @@
         "fastq": "^1.6.0"
       }
     },
-    "@open-rpc/client-js": {
-      "version": "1.8.1",
-      "resolved": "https://registry.npmjs.org/@open-rpc/client-js/-/client-js-1.8.1.tgz",
-      "integrity": "sha512-vV+Hetl688nY/oWI9IFY0iKDrWuLdYhf7OIKI6U1DcnJV7r4gAgwRJjEr1QVYszUc0gjkHoQJzqevmXMGLyA0g==",
-      "requires": {
-        "isomorphic-fetch": "^3.0.0",
-        "isomorphic-ws": "^5.0.0",
-        "strict-event-emitter-types": "^2.0.0",
-        "ws": "^7.0.0"
-      },
-      "dependencies": {
-        "isomorphic-ws": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz",
-          "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==",
-          "requires": {}
-        }
-      }
-    },
     "@opentelemetry/api": {
       "version": "1.4.1"
     },
@@ -33262,7 +33148,6 @@
         "@google-cloud/functions-framework": "^3.1.3",
         "@google-cloud/pubsub": "^3.4.1",
         "@google-cloud/storage": "^6.8.0",
-        "@mysten/sui.js": "^0.45.0",
         "@solana/web3.js": "^1.87.3",
         "axios": "^1.5.0",
         "borsh": "^1.0.0",
@@ -33428,45 +33313,11 @@
           "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.31.1.tgz",
           "integrity": "sha512-n4Se1wu4GnKwztQHNFfJvUeWcpvx3o8cWhSbNs9JQShEuB3nv3R5lqFBtDCgHZF/emFQAP+ZjF8bTfCs9UBGhA=="
         },
-        "@mysten/bcs": {
-          "version": "0.8.1",
-          "resolved": "https://registry.npmjs.org/@mysten/bcs/-/bcs-0.8.1.tgz",
-          "integrity": "sha512-wSEdP7QEfGQdb34g+7R0f3OdRqrv88iIABfJVDVJ6IsGLYVILreh8dZfNpZNUUyzctiyhX7zB9e/lR5qkddFPA==",
-          "requires": {
-            "bs58": "^5.0.0"
-          }
-        },
-        "@mysten/sui.js": {
-          "version": "0.45.0",
-          "resolved": "https://registry.npmjs.org/@mysten/sui.js/-/sui.js-0.45.0.tgz",
-          "integrity": "sha512-t+LyGWzKTH+TM3c7apvK2aohnx6hj0nxjqrqbk49X/uLxhvDKEIoTGJDQC2cfDTOg6OteYTefCNsfRauh8wAzA==",
-          "requires": {
-            "@mysten/bcs": "0.8.1",
-            "@noble/curves": "^1.1.0",
-            "@noble/hashes": "^1.3.1",
-            "@open-rpc/client-js": "^1.8.1",
-            "@scure/bip32": "^1.3.1",
-            "@scure/bip39": "^1.2.1",
-            "@suchipi/femver": "^1.0.0",
-            "events": "^3.3.0",
-            "superstruct": "^1.0.3",
-            "tweetnacl": "^1.0.3"
-          }
-        },
         "@noble/hashes": {
           "version": "1.3.2",
           "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz",
           "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ=="
         },
-        "@scure/bip39": {
-          "version": "1.2.1",
-          "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz",
-          "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==",
-          "requires": {
-            "@noble/hashes": "~1.3.0",
-            "@scure/base": "~1.1.0"
-          }
-        },
         "axios": {
           "version": "1.5.0",
           "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.0.tgz",
@@ -33477,11 +33328,6 @@
             "proxy-from-env": "^1.1.0"
           }
         },
-        "base-x": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz",
-          "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw=="
-        },
         "bech32": {
           "version": "1.1.4",
           "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
@@ -33492,14 +33338,6 @@
           "resolved": "https://registry.npmjs.org/borsh/-/borsh-1.0.0.tgz",
           "integrity": "sha512-fSVWzzemnyfF89EPwlUNsrS5swF5CrtiN4e+h0/lLf4dz2he4L3ndM20PS9wj7ICSkXJe/TQUHdaPTq15b1mNQ=="
         },
-        "bs58": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz",
-          "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==",
-          "requires": {
-            "base-x": "^4.0.0"
-          }
-        },
         "cosmjs-types": {
           "version": "0.8.0",
           "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.8.0.tgz",
@@ -33529,11 +33367,6 @@
             "long": "^4.0.0"
           }
         },
-        "superstruct": {
-          "version": "1.0.3",
-          "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-1.0.3.tgz",
-          "integrity": "sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg=="
-        },
         "typescript": {
           "version": "5.2.2",
           "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz",
@@ -38880,15 +38713,6 @@
     "isexe": {
       "version": "2.0.0"
     },
-    "isomorphic-fetch": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-3.0.0.tgz",
-      "integrity": "sha512-qvUtwJ3j6qwsF3jLxkZ72qCgjMysPzDfeV240JHiGZsANBYd+EEuu35v7dfrJ9Up0Ak07D7GGSkGhCHTqg/5wA==",
-      "requires": {
-        "node-fetch": "^2.6.1",
-        "whatwg-fetch": "^3.4.1"
-      }
-    },
     "isomorphic-ws": {
       "version": "4.0.1",
       "requires": {}
@@ -44184,11 +44008,6 @@
     "stream-shift": {
       "version": "1.0.1"
     },
-    "strict-event-emitter-types": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/strict-event-emitter-types/-/strict-event-emitter-types-2.0.0.tgz",
-      "integrity": "sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA=="
-    },
     "strict-uri-encode": {
       "version": "1.1.0"
     },