Procházet zdrojové kódy

Stacks Implementation (#4565)

* feat: squashed stacks implementation

* chore: wip

* chore: wip

* chore: wip pre-polling

* feat: update to polling style

* feat: add reobserve

* docs: update comment

* chore: remove port remnants

* fix: update event logic and contracts

* chore: remove unwanted changes

* chore: clean up test dir

* docs: update readme

* chore: remove contract files for later merging in via separate pr

* Revert "chore: remove contract files for later merging in via separate pr"

This reverts commit 0e3fdaf31697ea92fa8f46d2b64862044f71eb8a.

* fix: add more integration progress

* chore: ai progress

* chore: update readme

* chore: update readme

* chore: remove file

* chore: wip

* chore: update scripts for spy test

* fix: add tx_status check for processing stacks transactions in the watcher

* chore: remove outdated test dir (#6)

Co-authored-by: janniks <janniks@users.noreply.github.com>

* refactor(audit): minor updates (#7)

* build: remove guardian dependency from stacks node

* refactor: make default parameters configurable

---------

Co-authored-by: janniks <janniks@users.noreply.github.com>

* fix: update contracts

* fix: ntt eth deploy updates

* chore: rename tmp notes

* fix: cherry pick watcher updates

* chore: update parameters

* fix(audit): update emitter address

* fix: pull in updates

* fix(audit): switch to state contract tracked owner var

* fix: update state contract changes

* chore: update notes

* fix(audit): add active contract fetching

* fix(audit): add max bigint checks, closes #4

* fix(audit): add missing fetch method

* fix(audit): fix incorrect clarity byte

* fix(audit): check burn block confirmation height for reobserve requests, closes #2

* fix: switch to confirmed height rather than processed height

* fix: remove stacks port remnant

* fix: add stacks defaults directly in node config

* chore: remove addressed comments, l1 finalizers not needed for stacks

* chore: update contracts

* fix: add missing c32 file

* added POC for stacks native api

* fixed c32

* reverted variable renaming

* test: update integration style tests

* refactor: move to Tilt tests with it cases

* refactor: update stacks test setup

* refactor: update stacks k8s

* chore: update test files

* test: update spy handling

* chore: update contracts

* test: fix resolve check

* chore: update logs

* fix: remove submodule

* fix: remove submodule

* fix: update integration tests

* fix: switch to latest node develop branch

* fix: type errors

* chore: remove old notes

* refactor: rename native mode

* chore: udpate to v3 endpoints

* fix: add working hacknet

* fix: update contracts

* fix: cleanup config files

* fix: add working nakamoto tx hacknet

* fix: working wormhole message with node only

* fix: minor updates

* fix: update failure checks

* refactor: streamline VAA monitoring

* test: update integration test

* test: add nonce to contract name

* test: fix faulty tests

* test: use pr build for stacks node

---------

Co-authored-by: janniks <janniks@users.noreply.github.com>
Co-authored-by: Roberto De Ioris <roberto.deioris@gmail.com>
Co-authored-by: jannik-stacks <237011140+jannik-stacks@users.noreply.github.com>

* chore: remove unwanted file

* fix: update pr according to repo guidelines

* chore: revert go.mod

* fix: pin dockerfiles

* chore: make linter happy

* chore: revert gitignore

* fix: add txid to message pub

* chore: cleanup duplicate go mod

* chore: undo package change

* chore: repin dockerfiles

* chore: add lock files

* chore: add lockfiles in dockerfile

* refactor: make success check more verbose

* refactor: ignore uncommitted events

* fix: add reobservation flag for stacks message pubs

* fix: remove unused l1finalizers

* refactor: fix lint errors

* docs: update stacks readmes

* chore: rename stacks test packages

---------

Co-authored-by: janniks <6362150+janniks@users.noreply.github.com>
Co-authored-by: janniks <janniks@users.noreply.github.com>
Co-authored-by: Roberto De Ioris <roberto.deioris@gmail.com>
jannik-stacks před 2 týdny
rodič
revize
df2c5873b6
60 změnil soubory, kde provedl 21883 přidání a 2 odebrání
  1. 119 1
      Tiltfile
  2. 605 0
      devnet/stacks-bitcoin.yaml
  3. 72 0
      devnet/stacks-broadcaster.yaml
  4. 328 0
      devnet/stacks-node.yaml
  5. 106 0
      devnet/stacks-signer.yaml
  6. 45 0
      devnet/stacks-stacker.yaml
  7. 39 1
      node/cmd/guardiand/node.go
  8. 66 0
      node/pkg/watchers/stacks/README.md
  9. 323 0
      node/pkg/watchers/stacks/clarity.go
  10. 46 0
      node/pkg/watchers/stacks/config.go
  11. 296 0
      node/pkg/watchers/stacks/fetch.go
  12. 105 0
      node/pkg/watchers/stacks/fixture_stacks_simulate.json
  13. 49 0
      node/pkg/watchers/stacks/fixture_stacks_tenure_blocks.json
  14. 9 0
      node/pkg/watchers/stacks/fixture_stacks_tenure_info.json
  15. 632 0
      node/pkg/watchers/stacks/watcher.go
  16. 1 0
      stacks/.gitignore
  17. 28 0
      stacks/Dockerfile
  18. 28 0
      stacks/README.md
  19. 13 0
      stacks/broadcaster/Dockerfile
  20. 135 0
      stacks/broadcaster/common.ts
  21. 1367 0
      stacks/broadcaster/package-lock.json
  22. 25 0
      stacks/broadcaster/package.json
  23. 133 0
      stacks/broadcaster/tx-broadcaster.ts
  24. 622 0
      stacks/contracts/dependencies/amm-pool-v2-01.clar
  25. 194 0
      stacks/contracts/dependencies/amm-registry-v2-01.clar
  26. 97 0
      stacks/contracts/dependencies/amm-vault-v2-01.clar
  27. 68 0
      stacks/contracts/dependencies/clarity-stacks-helper.clar
  28. 81 0
      stacks/contracts/dependencies/clarity-stacks.clar
  29. 89 0
      stacks/contracts/dependencies/code-body-prover.clar
  30. 58 0
      stacks/contracts/dependencies/executor-dao.clar
  31. 5 0
      stacks/contracts/dependencies/extension-trait.clar
  32. 133 0
      stacks/contracts/dependencies/hk-cursor-v2.clar
  33. 11 0
      stacks/contracts/dependencies/hk-ecc-v1.clar
  34. 27 0
      stacks/contracts/dependencies/hk-merkle-tree-keccak160-v1.clar
  35. 108 0
      stacks/contracts/dependencies/liquidity-locker.clar
  36. 5 0
      stacks/contracts/dependencies/proposal-trait.clar
  37. 299 0
      stacks/contracts/dependencies/self-listing-helper-v3.clar
  38. 182 0
      stacks/contracts/dependencies/token-amm-pool-v2-01.clar
  39. 6 0
      stacks/contracts/dependencies/trait-flash-loan-user.clar
  40. 31 0
      stacks/contracts/dependencies/trait-semi-fungible.clar
  41. 26 0
      stacks/contracts/dependencies/trait-sip-010.clar
  42. 93 0
      stacks/contracts/wormhole-core-proxy-v2.clar
  43. 288 0
      stacks/contracts/wormhole-core-state.clar
  44. 1120 0
      stacks/contracts/wormhole-core-v4.clar
  45. 61 0
      stacks/contracts/wormhole-trait-core-v2.clar
  46. 16 0
      stacks/contracts/wormhole-trait-export-v1.clar
  47. 36 0
      stacks/contracts/wormhole-trait-governance-v1.clar
  48. 11 0
      stacks/stacker/Dockerfile
  49. 135 0
      stacks/stacker/common.ts
  50. 1366 0
      stacks/stacker/package-lock.json
  51. 24 0
      stacks/stacker/package.json
  52. 321 0
      stacks/stacker/stacking.ts
  53. 23 0
      stacks/stacks-test.yaml
  54. 7 0
      stacks/test/Dockerfile
  55. 543 0
      stacks/test/integration.test.ts
  56. 55 0
      stacks/test/lib/constants.ts
  57. 925 0
      stacks/test/lib/helpers.ts
  58. 10196 0
      stacks/test/package-lock.json
  59. 34 0
      stacks/test/package.json
  60. 17 0
      stacks/test/vitest.config.ts

+ 119 - 1
Tiltfile

@@ -75,6 +75,7 @@ config.define_bool("ibc_relayer", False, "Enable IBC relayer between cosmos chai
 config.define_bool("redis", False, "Enable a redis instance")
 config.define_bool("generic_relayer", False, "Enable the generic relayer off-chain component")
 config.define_bool("query_server", False, "Enable cross-chain query server")
+config.define_bool("stacks", False, "Enable Stacks component")
 
 cfg = config.parse()
 num_guardians = int(cfg.get("num", "1"))
@@ -102,6 +103,7 @@ btc = cfg.get("btc", False)
 redis = cfg.get('redis', ci)
 generic_relayer = cfg.get("generic_relayer", ci)
 query_server = cfg.get("query_server", ci)
+stacks = cfg.get("stacks", ci)
 
 if ci:
     guardiand_loglevel = cfg.get("guardiand_loglevel", "warn")
@@ -362,6 +364,18 @@ def build_node_yaml():
                     "http://wormchain:1317"
                 ]
 
+            if stacks:
+                container["command"] += [
+                    "--stacksStateContract",
+                    "ST5ZW3BC07M4P27KFJ6JJ6PKTB1NW79SH0BVYB3W.wormhole-core-state",
+                    "--stacksRPC",
+                    "http://stacks-node:20443",
+                    "--stacksRPCAuthToken",
+                    "12345",
+                    "--stacksBitcoinBlockPollInterval",
+                    "2s"
+                ]
+
     return encode_yaml_stream(node_yaml_with_replicas)
 
 k8s_yaml_with_ns(build_node_yaml())
@@ -385,6 +399,8 @@ if wormchain:
     guardian_resource_deps = guardian_resource_deps + ["wormchain", "wormchain-deploy"]
 if sui:
     guardian_resource_deps = guardian_resource_deps + ["sui"]
+if stacks:
+    guardian_resource_deps = guardian_resource_deps + ["stacks-node"]
 
 k8s_resource(
     "guardian",
@@ -666,7 +682,6 @@ if ci_tests:
                     "BOOTSTRAP_PEERS", str(ccqBootstrapPeers)),
                     "MAX_WORKERS", max_workers))
     )
-
     # separate resources to parallelize docker builds
     k8s_resource(
         "sdk-ci-tests",
@@ -1029,3 +1044,106 @@ if query_server:
         labels = ["query-server"],
         trigger_mode = trigger_mode
     )
+
+if stacks:
+    # Build Stacks node image
+    docker_build(
+        ref = "stacks-node",
+        context = "./stacks",
+        dockerfile = "stacks/Dockerfile",
+        target = "stacks-node",
+    )
+
+    # Build Stacks signer image
+    docker_build(
+        ref = "stacks-signer",
+        context = "./stacks",
+        dockerfile = "stacks/Dockerfile",
+        target = "stacks-signer",
+    )
+
+    # Build stacker image
+    docker_build(
+        ref = "stacks-stacker",
+        context = "./stacks/stacker",
+        dockerfile = "stacks/stacker/Dockerfile"
+    )
+
+    # Build broadcaster image
+    docker_build(
+        ref = "stacks-broadcaster",
+        context = "./stacks/broadcaster",
+        dockerfile = "stacks/broadcaster/Dockerfile"
+    )
+
+    # Build Stacks integration test suite
+    docker_build(
+        ref = "stacks-test",
+        context = "./stacks",
+        dockerfile = "stacks/test/Dockerfile",
+    )
+
+    # Deploy Bitcoin services
+    k8s_yaml_with_ns("devnet/stacks-bitcoin.yaml")
+    k8s_resource(
+        "bitcoin-node",
+        port_forwards = [
+            port_forward(18443, name = "Bitcoin RPC [:18443]", host = webHost),
+            port_forward(18444, name = "Bitcoin P2P [:18444]", host = webHost)
+        ],
+        labels = ["stacks"],
+        trigger_mode = trigger_mode,
+    )
+    k8s_resource(
+        "bitcoin-miner",
+        resource_deps = ["bitcoin-node"],
+        labels = ["stacks"],
+        trigger_mode = trigger_mode,
+    )
+
+    # Deploy Stacks node
+    k8s_yaml_with_ns("devnet/stacks-node.yaml")
+    k8s_resource(
+        "stacks-node",
+        resource_deps = ["bitcoin-miner"],
+        port_forwards = [
+            port_forward(20443, name = "Stacks Node RPC [:20443]", host = webHost)
+        ],
+        labels = ["stacks"],
+        trigger_mode = trigger_mode,
+    )
+
+    # Deploy Stacks signer
+    k8s_yaml_with_ns("devnet/stacks-signer.yaml")
+    k8s_resource(
+        "stacks-signer",
+        labels = ["stacks"],
+        trigger_mode = trigger_mode,
+    )
+
+    # Deploy Stacker (establishes PoX anchor blocks)
+    k8s_yaml_with_ns("devnet/stacks-stacker.yaml")
+    k8s_resource(
+        "stacks-stacker",
+        resource_deps = ["stacks-node"],
+        labels = ["stacks"],
+        trigger_mode = trigger_mode,
+    )
+
+    # Deploy TX broadcaster
+    k8s_yaml_with_ns("devnet/stacks-broadcaster.yaml")
+    k8s_resource(
+        "stacks-broadcaster",
+        resource_deps = ["stacks-node", "stacks-signer"],
+        labels = ["stacks"],
+        trigger_mode = trigger_mode,
+    )
+
+    # Deploy Stacks integration test suite
+    k8s_yaml_with_ns("stacks/stacks-test.yaml")
+    k8s_resource(
+        "stacks-test",
+        resource_deps = ["stacks-broadcaster"], # After Nakamoto
+        labels = ["stacks"],
+        trigger_mode = trigger_mode,
+    )

+ 605 - 0
devnet/stacks-bitcoin.yaml

@@ -0,0 +1,605 @@
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+    name: bitcoin-config
+data:
+    bitcoin.conf: |
+        regtest=1 #chain=regtest
+
+        [regtest]
+        printtoconsole=1
+        disablewallet=0
+        txindex=1
+
+        # Specify a non-default location to store blockchain data.
+        datadir=/root/.bitcoin
+
+        # [network]
+        discover=0
+        dns=0
+        dnsseed=0
+        listenonion=0
+
+        # [rpc]
+        rpcserialversion=0
+        # Accept command line and JSON-RPC commands.
+        server=1
+        # Accept public REST requests.
+        rest=1
+        rpcbind=0.0.0.0:18443
+        rpcallowip=0.0.0.0/0
+        rpcallowip=::/0
+        rpcuser=hacknet
+        rpcpassword=hacknet
+        bind=0.0.0.0:18444
+
+        # [debug]
+        # Reduce verbose logging - disable bench, validation, and other noisy categories
+        debug=0
+        debugexclude=bench,validation,leveldb,estimatefee,mempool,http,rpc
+
+        # [wallet]
+        addresstype=legacy
+        changetype=legacy
+        fallbackfee=0.00001
+
+    miner.sh: |
+        #!/bin/env bash
+
+        # Removed 'set -e' to allow graceful error handling instead of immediate exit
+        trap "exit" INT TERM
+        trap "kill 0" EXIT
+
+        DEFAULT_TIMEOUT=$(($(date +%s) + 30)) # set the default mining timeout to the current epoch +30s
+        DEFINED_ADDRESSES=$(compgen -A variable | grep "STACKS.*.BTC_ADDR") # retrieve env vars matching STACKS*BTC_ADDRESS
+        DEFINED_WALLETS=$(compgen -A variable | grep "STACKS.*.BTC_WALLET") # retrieve env vars matching STACKS*BTC_WALLET
+        mapfile -t ADDRESSES < <(printf '%s\n' "$DEFINED_ADDRESSES" | tr ' ' '\n') # convert the compgen output to an array
+        mapfile -t WALLETS < <(printf '%s\n' "$DEFINED_WALLETS" | tr ' ' '\n') # convert the compgen output to an array
+        NUM_MINERS=${#ADDRESSES[@]} # use the same value for total miners throughout script
+        RESERVED_BLOCKS=100 # during initial block mining, reserve 100 blocks to mine at the end so the earlier blocks have received rewards by the epoch 2.0 block
+
+        function debug_log(){
+            # Debug logging with timestamp
+            local message="${1}"
+            local timestamp=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
+            echo "$timestamp [DEBUG] $message"
+        }
+
+        function test_rpc_connection(){
+            # Test RPC connection to Bitcoin node
+            debug_log "Testing RPC connection to Bitcoin node..."
+            local result=""
+            result=$(bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet ping 2>&1)
+            local exit_code=$?
+
+            if [ $exit_code -eq 0 ]; then
+                debug_log "RPC connection successful"
+                return 0
+            else
+                debug_log "RPC connection failed: $result"
+                return 1
+            fi
+        }
+
+        function get_height(){
+            # return the current block height, or -1 in case of error
+            bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet getblockcount 2>/dev/null || echo "-1"
+            true
+        }
+
+        function get_mining_info(){
+            # canary check for getmininginfo if `chain` == `regtest`. else return a failure
+            local mining_info=""
+            local chain=""
+            mining_info=$(bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet -rpcwait getmininginfo 2> /dev/null)
+            chain=$(echo "${mining_info}" | awk '/chain/ { gsub(/[",]/,""); print $2}')
+            if [ "$chain" == "regtest" ];then
+                return 0
+            fi
+            return 1
+        }
+
+        function get_wallet_info(){
+            # returns if a wallet exists, with debug logging
+            echo "[func] Get wallet info"
+            local wallet=${1}
+            echo "    - checking for wallet (${wallet})"
+            debug_log "Attempting to get wallet info for: $wallet"
+
+            local result=""
+            result=$(bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet -rpcwait -rpcwallet="${wallet}" getwalletinfo 2>&1)
+            local ret="$?"
+
+            if [ "$ret" -eq "0" ]; then
+                echo "    - successfully retrieved named wallet"
+                debug_log "Wallet $wallet exists and is accessible"
+                return 0
+            else
+                debug_log "Wallet $wallet not accessible: $result"
+                return $ret
+            fi
+        }
+
+        function create_wallet(){
+            # creates a wallet with a given name, handles already exists case
+            echo "[func] Create wallet"
+            local wallet=${1}
+            echo "    - creating wallet (${wallet}) as legacy wallet with load_on_startup=true"
+            debug_log "Executing createwallet command for: $wallet"
+
+            local result=""
+            result=$(bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet -rpcwait -named createwallet wallet_name="${wallet}" descriptors=false load_on_startup=true 2>&1)
+            local exit_code=$?
+
+            debug_log "Createwallet result for $wallet: exit_code=$exit_code, output=$result"
+
+            if [ $exit_code -eq 0 ]; then
+                echo "    - successfully created wallet"
+                debug_log "Wallet $wallet created successfully"
+            elif echo "$result" | grep -q "already exists"; then
+                echo "    - wallet DB already exists, checking if it's a legacy wallet..."
+                debug_log "Wallet $wallet already exists on disk, attempting to load it"
+
+                # Try to load the existing wallet
+                local load_result=""
+                load_result=$(bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet -rpcwait loadwallet "${wallet}" 2>&1)
+                local load_exit_code=$?
+
+                if [ $load_exit_code -eq 0 ]; then
+                    debug_log "Wallet $wallet loaded, checking if it's legacy or descriptor wallet"
+
+                    # Check if it's a descriptor wallet by trying importaddress
+                    local test_addr="bcrt1qtest123456789"  # dummy address for testing
+                    local import_test=""
+                    import_test=$(bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet -rpcwallet="${wallet}" importaddress "${test_addr}" "" false 2>&1)
+
+                    if echo "$import_test" | grep -q "Only legacy wallets"; then
+                        echo "    - existing wallet is a descriptor wallet, deleting and recreating as legacy..."
+                        debug_log "Wallet $wallet is descriptor wallet, will delete and recreate"
+
+                        # Unload the wallet first
+                        bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet unloadwallet "${wallet}" > /dev/null 2>&1
+
+                        # Delete wallet directory
+                        rm -rf "/root/.bitcoin/regtest/${wallet}"
+                        debug_log "Deleted descriptor wallet directory: /root/.bitcoin/regtest/${wallet}"
+
+                        # Retry creating as legacy wallet
+                        result=$(bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet -rpcwait -named createwallet wallet_name="${wallet}" descriptors=false load_on_startup=true 2>&1)
+                        exit_code=$?
+
+                        if [ $exit_code -eq 0 ]; then
+                            echo "    - successfully created legacy wallet after deleting descriptor wallet"
+                            debug_log "Wallet $wallet recreated as legacy wallet"
+                        else
+                            echo "    - ERROR: failed to recreate wallet as legacy: $result"
+                            debug_log "Failed to recreate wallet $wallet: $result"
+                        fi
+                    else
+                        echo "    - existing wallet is already a legacy wallet, ready to use"
+                        debug_log "Wallet $wallet is already a legacy wallet"
+                    fi
+                else
+                    echo "    - warning: could not load existing wallet: $load_result"
+                    debug_log "Failed to load wallet $wallet: $load_result"
+                fi
+            else
+                echo "    - warning: wallet creation failed (exit code: $exit_code), output: $result"
+                debug_log "Wallet creation failed for $wallet, attempting to load existing wallet"
+                load_wallet "${wallet}"
+            fi
+        }
+
+        function load_wallet(){
+            # loads a wallet with a given name, handles errors gracefully
+            echo "[func] Load wallet"
+            local wallet=${1}
+            echo "    - loading wallet (${wallet})"
+            debug_log "Executing loadwallet command for: $wallet"
+
+            local result=""
+            result=$(bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet -rpcwait loadwallet "${wallet}" 2>&1)
+            local exit_code=$?
+
+            debug_log "Loadwallet result for $wallet: exit_code=$exit_code, output=$result"
+
+            if [ $exit_code -eq 0 ]; then
+                echo "    - successfully loaded wallet"
+                debug_log "Wallet $wallet loaded successfully"
+            else
+                echo "    - warning: wallet loading failed (exit code: $exit_code), output: $result"
+                debug_log "Wallet loading failed for $wallet, continuing anyway (might already be loaded)"
+            fi
+        }
+
+        function unload_wallet(){
+            # unloads a wallet with a given name
+            echo "[func] Unload wallet"
+            local wallet=${1}
+            echo "    - unloading wallet (${wallet})"
+            bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet -rpcwait unloadwallet "${wallet}" > /dev/null 2>&1
+            echo "    - successfully unloaded wallet"
+        }
+
+        function import_address(){
+            # imports an address to a wallet so the miner can send the btc from the coinbase transaction
+            echo "[func] Import address"
+            local wallet=${1}
+            local address=${2}
+            echo "    - importing address (${address}) to wallet (${wallet})"
+
+            # Try importing address with timeout and error handling
+            local result=""
+            result=$(timeout 30 bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet -rpcwait -rpcwallet="${wallet}" importaddress "${address}" 2>&1)
+            local exit_code=$?
+
+            if [ $exit_code -eq 0 ]; then
+                echo "    - successfully imported address to wallet"
+            else
+                echo "    - warning: failed to import address (exit code: $exit_code), output: $result"
+                echo "    - continuing anyway as address might already be imported"
+            fi
+        }
+
+        function mine_bitcoin(){
+            # mines bitcoins to an address. this will be used to generate mining rewards for the stacks miners
+            local num_blocks=${1}
+            local address=${2}
+            bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet -rpcwait generatetoaddress "${num_blocks}" "${address}" > /dev/null 2>&1
+        }
+
+        function wait_until_height(){
+            # Sleep until bitcoind reaches a given height
+            echo "[func] wait until height"
+            local target=${1}
+            echo "    - waiting for bitcoin chain to reach height: ${target}"
+            while :; do
+                local height=""
+                height=$(get_height)
+                if [ "$height" -lt "0" ]; then
+                    echo "    - unable to determine blockheight. sleeping 5s"
+                    sleep 5
+                    continue
+                fi
+                if [ "$height" -ge "$target" ]; then
+                    echo "    - target height (${target}) reached. exiting function"
+                    return 0
+                fi
+                echo "    - target: ${target}, height: ${height}. sleeping 1s"
+                sleep 1
+            done
+        }
+
+        function get_stacks_epoch(){
+            # Determine current Stacks epoch based on Bitcoin block height
+            local height=${1}
+            if [ "$height" -ge "$STACKS_32_HEIGHT" ]; then
+                echo "3.2"
+            elif [ "$height" -ge "$STACKS_30_HEIGHT" ]; then
+                echo "3.0-Nakamoto"
+            elif [ "$height" -ge "$STACKS_25_HEIGHT" ]; then
+                echo "2.5"
+            elif [ "$height" -ge "$STACKS_2_05_HEIGHT" ]; then
+                echo "2.05"
+            else
+                echo "2.0"
+            fi
+        }
+
+        function get_mining_interval_for_height(){
+            # Get the mining interval based on current height
+            local height=${1}
+            if [ "$height" -ge "$STACKS_32_HEIGHT" ]; then
+                echo "$MINE_INTERVAL_EPOCH3"
+            elif [ "$height" -ge "$STACKS_25_HEIGHT" ]; then
+                echo "$MINE_INTERVAL_EPOCH25"
+            elif [ "$height" -ge "$STACKS_2_05_HEIGHT" ]; then
+                echo "$MINE_INTERVAL_EPOCH2_05"
+            else
+                echo "$MINE_INTERVAL"
+            fi
+        }
+
+        function log_height_and_epoch(){
+            # Log current height, epoch, and mining info
+            local height=${1}
+            local prefix=${2:-""}
+            local epoch=$(get_stacks_epoch "$height")
+            local interval=$(get_mining_interval_for_height "$height")
+            local timestamp=$(date -u '+%Y-%m-%dT%H:%M:%SZ')
+            echo "$timestamp ${prefix}Height: $height | Expected Epoch: $epoch | Mining interval: ${interval}s"
+        }
+
+        function build_miners(){
+            # Build the mining environment before actual mining starts
+            echo "[func] Build miners"
+            debug_log "Starting miner setup process"
+            echo "    - detected (${NUM_MINERS}) miner(s)"
+            debug_log "Detected $NUM_MINERS miners to set up"
+
+            echo "    - creating wallets and importing addresses"
+            for ((i=0; i<${NUM_MINERS}; i++)); do
+                local ADDRESS_ENV_VAR=${ADDRESSES[$i]}
+                local WALLET_ENV_VAR=${WALLETS[$i]}
+                local WALLET=${!WALLET_ENV_VAR}
+                local ADDRESS=${!ADDRESS_ENV_VAR}
+
+                echo "      - Setting up miner $((i+1))/$NUM_MINERS: wallet=${WALLET}, address=${ADDRESS}"
+                debug_log "Processing miner $((i+1)): env_var=$WALLET_ENV_VAR, wallet=$WALLET, address=$ADDRESS"
+
+                debug_log "Checking if wallet $WALLET exists..."
+                if ! get_wallet_info "${WALLET}"; then
+                    debug_log "Wallet $WALLET does not exist, creating it"
+                    create_wallet "${WALLET}"
+                else
+                    debug_log "Wallet $WALLET already exists and is accessible"
+                fi
+
+                debug_log "Importing address $ADDRESS to wallet $WALLET"
+                import_address "${WALLET}" "${ADDRESS}"
+
+                echo "      - Completed setup for miner $((i+1))/$NUM_MINERS (wallet remains loaded)"
+                debug_log "Finished processing miner $((i+1))"
+            done
+            echo "    - wallet setup complete"
+            debug_log "All miner wallets configured successfully"
+        }
+
+        echo "Starting Bitcoin mining service..."
+        debug_log "Bitcoin miner service initialization starting"
+
+        # Wait for Bitcoin node to be ready with connection testing
+        echo "Testing RPC connection and waiting for Bitcoin node to be ready..."
+        while true; do
+            debug_log "Testing RPC connection..."
+            if test_rpc_connection; then
+                debug_log "RPC connection successful, testing mining info..."
+                if get_mining_info; then
+                    debug_log "Bitcoin node is fully ready"
+                    break
+                else
+                    debug_log "RPC works but mining info not ready yet"
+                fi
+            else
+                debug_log "RPC connection not ready yet"
+            fi
+
+            CURRENT_HEIGHT=$(get_height)
+            log_height_and_epoch "$CURRENT_HEIGHT" "Waiting for Bitcoin node... "
+            sleep 5
+        done
+
+        echo "Bitcoin node is ready, building miners..."
+        debug_log "Proceeding to build miners"
+        build_miners
+
+        echo "Starting mining process..."
+        CURRENT_HEIGHT=$(get_height)
+        TARGET_HEIGHT=$((STACKS_2_05_HEIGHT - 1))
+
+        if [ "$CURRENT_HEIGHT" -lt "$TARGET_HEIGHT" ]; then
+            echo "Mining initial blocks from $CURRENT_HEIGHT to $TARGET_HEIGHT"
+            BLOCKS_TO_MINE=$((TARGET_HEIGHT - CURRENT_HEIGHT))
+            ADDRESS_INDEX=0
+
+            for ((i=0; i<${BLOCKS_TO_MINE}; i++)); do
+                CURRENT_HEIGHT=$(get_height)
+                log_height_and_epoch "$CURRENT_HEIGHT" "Initial mining ($((i+1))/$BLOCKS_TO_MINE) - "
+
+                # Rotate through miner addresses
+                ADDRESS_ENV_VAR=${ADDRESSES[$ADDRESS_INDEX]}
+                ADDRESS=${!ADDRESS_ENV_VAR}
+                mine_bitcoin 1 "${ADDRESS}"
+                ADDRESS_INDEX=$(((ADDRESS_INDEX + 1) % NUM_MINERS))
+
+                NEW_HEIGHT=$(get_height)
+                log_height_and_epoch "$NEW_HEIGHT" "Block mined! - "
+                sleep "${MINE_INTERVAL}"
+            done
+        fi
+
+        echo "Initial mining complete. Entering continuous mining mode..."
+
+        # Continuous mining with block height and epoch monitoring
+        ADDRESS_INDEX=0
+        while true; do
+            CURRENT_HEIGHT=$(get_height)
+
+            # Get mining interval for current height
+            INTERVAL=$(get_mining_interval_for_height "$CURRENT_HEIGHT")
+
+            # Rotate through miner addresses
+            ADDRESS_ENV_VAR=${ADDRESSES[$ADDRESS_INDEX]}
+            ADDRESS=${!ADDRESS_ENV_VAR}
+
+            mine_bitcoin 1 "${ADDRESS}"
+            ADDRESS_INDEX=$(((ADDRESS_INDEX + 1) % NUM_MINERS))
+
+            NEW_HEIGHT=$(get_height)
+            log_height_and_epoch "$NEW_HEIGHT" "Block mined! - "
+            sleep "${INTERVAL}"
+        done
+
+---
+apiVersion: v1
+kind: Service
+metadata:
+    name: bitcoin-node
+    labels:
+        app: bitcoin-node
+spec:
+    ports:
+        - name: rpc
+          port: 18443
+          targetPort: 18443
+          protocol: TCP
+        - name: p2p
+          port: 18444
+          targetPort: 18444
+          protocol: TCP
+    selector:
+        app: bitcoin-node
+
+---
+# Bitcoin Node StatefulSet
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+    name: bitcoin-node
+    labels:
+        app: bitcoin-node
+spec:
+    serviceName: bitcoin-node
+    replicas: 1
+    selector:
+        matchLabels:
+            app: bitcoin-node
+    template:
+        metadata:
+            labels:
+                app: bitcoin-node
+        spec:
+            containers:
+                - name: bitcoin-node
+                  image: bitcoin/bitcoin:25.2@sha256:14b4777166cba8de36b62ce72801038760a8f490122781b66d40592c8c69ebda
+                  ports:
+                      - containerPort: 18443
+                        name: rpc
+                        protocol: TCP
+                      - containerPort: 18444
+                        name: p2p
+                        protocol: TCP
+                  volumeMounts:
+                      - name: bitcoin-config
+                        mountPath: /root/.bitcoin/bitcoin.conf
+                        subPath: bitcoin.conf
+                      - name: bitcoin-data
+                        mountPath: /root/.bitcoin/regtest
+                  command:
+                      - /bin/bash
+                      - -c
+                      - |
+                          set -e
+                          bitcoind
+                  readinessProbe:
+                      exec:
+                          command:
+                              - /bin/sh
+                              - -c
+                              - bitcoin-cli -rpcwait getblockcount
+                      initialDelaySeconds: 10
+                      periodSeconds: 5
+                      timeoutSeconds: 3
+                      successThreshold: 1
+                      failureThreshold: 10
+                  livenessProbe:
+                      exec:
+                          command:
+                              - /bin/sh
+                              - -c
+                              - bitcoin-cli -rpcwait getblockcount
+                      initialDelaySeconds: 30
+                      periodSeconds: 30
+                      timeoutSeconds: 5
+                      failureThreshold: 3
+            volumes:
+                - name: bitcoin-config
+                  configMap:
+                      name: bitcoin-config
+                      defaultMode: 0755
+    volumeClaimTemplates:
+        - metadata:
+              name: bitcoin-data
+          spec:
+              accessModes: ["ReadWriteOnce"]
+              resources:
+                  requests:
+                      storage: 10Gi
+---
+# Bitcoin Miner Deployment
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+    name: bitcoin-miner
+    labels:
+        app: bitcoin-miner
+spec:
+    replicas: 1
+    selector:
+        matchLabels:
+            app: bitcoin-miner
+    template:
+        metadata:
+            labels:
+                app: bitcoin-miner
+        spec:
+            containers:
+                - name: bitcoin-miner
+                  image: bitcoin/bitcoin:25.2@sha256:14b4777166cba8de36b62ce72801038760a8f490122781b66d40592c8c69ebda
+                  env:
+                      - name: STACKS_MINER_1_BTC_ADDR
+                        value: "moARgPEufCG3BXqpZLR9wLZZufzeASFjxY"
+                      - name: STACKS_MINER_1_BTC_WALLET
+                        value: "stacks-miner-1"
+                      - name: MINE_INTERVAL
+                        value: "2"
+                      - name: MINE_INTERVAL_EPOCH2_05
+                        value: "5"
+                      - name: MINE_INTERVAL_EPOCH25
+                        value: "5"
+                      - name: MINE_INTERVAL_EPOCH3
+                        value: "30"
+                      - name: STACKS_2_05_HEIGHT
+                        value: "203"
+                      - name: STACKS_25_HEIGHT
+                        value: "209"
+                      - name: STACKS_30_HEIGHT
+                        value: "232"
+                      - name: STACKS_32_HEIGHT
+                        value: "234"
+                      - name: PAUSE_HEIGHT
+                        value: "999999999999"
+                      - name: PAUSE_TIMER
+                        value: "86400000"
+                  volumeMounts:
+                      - name: bitcoin-config
+                        mountPath: /root/.bitcoin/bitcoin.conf
+                        subPath: bitcoin.conf
+                      - name: bitcoin-config
+                        mountPath: /miner.sh
+                        subPath: miner.sh
+                  command:
+                      - /bin/bash
+                      - -c
+                      - |
+                          chmod +x /miner.sh
+                          /miner.sh
+                  readinessProbe:
+                      exec:
+                          command:
+                              - /bin/sh
+                              - -c
+                              - |
+                                  HEIGHT=$(bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet getblockcount 2>/dev/null || echo 0)
+                                  [ "$HEIGHT" -ge 100 ]
+                      initialDelaySeconds: 15
+                      periodSeconds: 5
+                      timeoutSeconds: 3
+                      successThreshold: 1
+                      failureThreshold: 60
+                  livenessProbe:
+                      exec:
+                          command:
+                              - /bin/sh
+                              - -c
+                              - bitcoin-cli -rpcconnect=bitcoin-node -rpcuser=hacknet -rpcpassword=hacknet getblockcount
+                      initialDelaySeconds: 60
+                      periodSeconds: 30
+                      timeoutSeconds: 5
+                      failureThreshold: 3
+            volumes:
+                - name: bitcoin-config
+                  configMap:
+                      name: bitcoin-config
+                      defaultMode: 0755

+ 72 - 0
devnet/stacks-broadcaster.yaml

@@ -0,0 +1,72 @@
+---
+# TX Broadcaster Deployment
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: stacks-broadcaster
+  labels:
+    app: stacks-broadcaster
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: stacks-broadcaster
+  template:
+    metadata:
+      labels:
+        app: stacks-broadcaster
+    spec:
+      containers:
+        - name: tx-broadcaster
+          image: stacks-broadcaster
+          env:
+            - name: STACKS_CORE_RPC_HOST
+              value: "stacks-node"
+            - name: STACKS_CORE_RPC_PORT
+              value: "20443"
+            - name: NAKAMOTO_BLOCK_INTERVAL
+              value: "2"
+            - name: STACKS_25_HEIGHT
+              value: "209"
+            - name: STACKS_30_HEIGHT
+              value: "232"
+            - name: POX_PREPARE_LENGTH
+              value: "5"
+            - name: POX_REWARD_LENGTH
+              value: "20"
+            - name: PAUSE_HEIGHT
+              value: "999999999999"
+            # Private keys for broadcasting transactions
+            - name: ACCOUNT_KEYS
+              value: "e26e611fc92fe535c5e2e58a6a446375bb5e3b471440af21bbe327384befb50a01,e3ebd73a51da9a2ab0c6679145420876bf4338554a8972e3ab200cef7adbec6001"
+            # Signer keys for reference
+            - name: STACKING_KEYS
+              value: "41634762d89dfa09133a4a8e9c1378d0161d29cd0a9433b51f1e3d32947a73dc01"
+          readinessProbe:
+            exec:
+              command:
+                - /bin/sh
+                - -c
+                - "test -f /tmp/ready"
+            initialDelaySeconds: 30
+            periodSeconds: 10
+            timeoutSeconds: 5
+            successThreshold: 1
+            failureThreshold: 3
+          livenessProbe:
+            exec:
+              command:
+                - /bin/sh
+                - -c
+                - "pgrep -f tx-broadcaster.ts"
+            initialDelaySeconds: 60
+            periodSeconds: 30
+            timeoutSeconds: 10
+            failureThreshold: 3
+          resources:
+            requests:
+              memory: "256Mi"
+              cpu: "100m"
+            limits:
+              memory: "512Mi"
+              cpu: "250m"

+ 328 - 0
devnet/stacks-node.yaml

@@ -0,0 +1,328 @@
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: stacks-node-config
+data:
+  stacks-node.toml: |
+    # stacks-node hacknet miner config
+    # event observers:
+    #   - stacks-signer
+    [node]
+    name = "${MINER_NAME}"
+    rpc_bind = "0.0.0.0:20443"
+    p2p_bind = "0.0.0.0:20444"
+    prometheus_bind = "0.0.0.0:9153"
+    data_url = "http://${STACKS_NODE_IP}:20443"
+    p2p_address = "${STACKS_NODE_IP}:20443"
+    working_dir = "/data/chainstate"
+    seed = "${MINER_SEED}"
+    local_peer_seed = "${MINER_SEED}"
+    miner = true
+    stacker = true
+    wait_time_for_microblocks = 0
+    mine_microblocks = false
+    use_test_genesis_chainstate = true
+    pox_sync_sample_secs = 0
+    wait_time_for_blocks = 200
+    microblock_frequency = 1000
+    txindex = true
+
+    fault_injection_block_push_fail_probability = ${BLOCK_PUSH_FAIL_PROBABILITY}
+
+    [miner]
+    first_attempt_time_ms = 180_000
+    subsequent_attempt_time_ms = 360_000
+    microblock_attempt_time_ms = 10
+    mining_key = "${MINER_SEED}"
+    activated_vrf_key_path = "/data/chainstate/saved_vrf_key.json"
+    block_reward_recipient = "${REWARD_RECIPIENT}"
+
+    [connection_options]
+    public_ip_address = "${POD_IP}:20444"
+    auth_token = "12345"
+    timeout = 15
+    connect_timeout = 15
+    handshake_timeout = 15
+    idle_timeout = 15
+    dns_timeout = 15
+    private_neighbors = true
+
+    [burnchain]
+    rpc_port = ${BITCOIN_RPC_PORT}
+    peer_port = ${BITCOIN_PEER_PORT}
+    pox_prepare_length = ${POX_PREPARE_LENGTH}
+    pox_reward_length = ${POX_REWARD_LENGTH}
+    chain = "bitcoin"
+    mode = "nakamoto-neon"
+    poll_time_secs = 1
+    magic_bytes = "T3"
+    burn_fee_cap = 20_000
+    peer_host = "${BITCOIN_PEER_HOST}"
+    username = "${BITCOIN_RPC_USER}"
+    password = "${BITCOIN_RPC_PASS}"
+    wallet_name = "${BITCOIN_WALLET}"
+
+    # Add stacks-signer as an event observer
+    [[events_observer]]
+    endpoint = "${SIGNER_HOST}:${SIGNER_PORT}"
+    events_keys = ["stackerdb", "block_proposal", "burn_blocks"]
+    timeout_ms = 10_000
+
+    [[burnchain.epochs]]
+    start_height = 0
+    epoch_name = "1.0"
+
+    [[burnchain.epochs]]
+    start_height = ${STACKS_20_HEIGHT}
+    epoch_name = "2.0"
+
+    [[burnchain.epochs]]
+    start_height = ${STACKS_2_05_HEIGHT}
+    epoch_name = "2.05"
+
+    [[burnchain.epochs]]
+    start_height = ${STACKS_21_HEIGHT}
+    epoch_name = "2.1"
+
+    [[burnchain.epochs]]
+    start_height = ${STACKS_22_HEIGHT}
+    epoch_name = "2.2"
+
+    [[burnchain.epochs]]
+    start_height = ${STACKS_23_HEIGHT}
+    epoch_name = "2.3"
+
+    [[burnchain.epochs]]
+    start_height = ${STACKS_24_HEIGHT}
+    epoch_name = "2.4"
+
+    [[burnchain.epochs]]
+    start_height = ${STACKS_25_HEIGHT}
+    epoch_name = "2.5"
+
+    [[burnchain.epochs]]
+    start_height = ${STACKS_30_HEIGHT}
+    epoch_name = "3.0"
+
+    [[burnchain.epochs]]
+    start_height = ${STACKS_31_HEIGHT}
+    epoch_name = "3.1"
+
+    [[burnchain.epochs]]
+    start_height = ${STACKS_32_HEIGHT}
+    epoch_name = "3.2"
+
+    [[ustx_balance]]
+    # PRIVATE KEY 714a5bf161a680ebb2670c5ea6e8bcd75f299eae234412af0cf12d21e11ae09901
+    address = "ST5ZW3BC07M4P27KFJ6JJ6PKTB1NW79SH0BVYB3W" # Stacks integration test account
+    amount = 10000000000000000
+
+    [[ustx_balance]]
+    address = "ST2SBXRBJJTH7GV5J93HJ62W2NRRQ46XYBK92Y039" # Deployer Account
+    amount = 10000000000000000
+
+    [[ustx_balance]]
+    # This is a 2-3 multi-sig address controlled using the above three
+    # addresses. The resulting multi-sig address below was created using the SignerWallet struct.
+    address = "SN3R84XZYA63QS28932XQF3G1J8R9PC3W76P9CSQS"
+    amount = 10000000000000000
+
+    [[ustx_balance]]
+    address = "ST24VB7FBXCBV6P0SRDSPSW0Y2J9XHDXNHW9Q8S7H" # Signer 1 Account
+    amount = 10000000000000000
+
+    [[ustx_balance]]
+    address = "ST2XAK68AR2TKBQBFNYSK9KN2AY9CVA91A7CSK63Z" # Signer 2 Account
+    amount = 10000000000000000
+
+    [[ustx_balance]]
+    address = "ST1J9R0VMA5GQTW65QVHW1KVSKD7MCGT27X37A551" # Signer 3 Account
+    amount = 10000000000000000
+
+    [[ustx_balance]]
+    address = "ST1YEHRRYJ4GF9CYBFFN0ZVCXX1APSBEEQ5KEDN7M" # Stacker 1
+    amount = 10000000000000000
+
+    [[ustx_balance]]
+    address = "ST1WNJTS9JM1JYGK758B10DBAMBZ0K23ADP392SBV" # Stacker 2
+    amount = 10000000000000000
+
+    [[ustx_balance]]
+    address = "ST1MDWBDVDGAANEH9001HGXQA6XRNK7PX7A7X8M6R" # Stacker 3
+    amount = 10000000000000000
+
+    [[ustx_balance]]
+    address = "ST332DWHNM323264X869MKXFZABSE5WZ60EA07TJ1" # Tester 1
+    amount = 10000000000000000
+
+    [[ustx_balance]]
+    address = "ST2FY5WGSFA209NFHDT08NCB8Y9J3P1H19YR2D674" # Tester 2
+    amount = 10000000000000000
+
+    [[ustx_balance]]
+    address = "ST3SW0AXHXFDHGQY2XMMDHN6T7VPY395WS7ZRGQCD" # Tester 3
+    amount = 10000000000000000
+
+---
+# Stacks Miner 1 Service
+apiVersion: v1
+kind: Service
+metadata:
+  name: stacks-node
+  labels:
+    app: stacks-node
+spec:
+  ports:
+    - name: rpc
+      port: 20443
+      targetPort: 20443
+      protocol: TCP
+    - name: p2p
+      port: 20444
+      targetPort: 20444
+      protocol: TCP
+    - name: prometheus
+      port: 9153
+      targetPort: 9153
+      protocol: TCP
+  selector:
+    app: stacks-node
+
+---
+# Stacks Node StatefulSet
+apiVersion: apps/v1
+kind: StatefulSet
+metadata:
+  name: stacks-node
+  labels:
+    app: stacks-node
+spec:
+  serviceName: stacks-node
+  replicas: 1
+  selector:
+    matchLabels:
+      app: stacks-node
+  template:
+    metadata:
+      labels:
+        app: stacks-node
+    spec:
+      containers:
+        - name: stacks-node
+          image: stacks-node
+          securityContext:
+            runAsUser: 0
+          ports:
+            - containerPort: 20443
+              name: rpc
+              protocol: TCP
+            - containerPort: 20444
+              name: p2p
+              protocol: TCP
+            - containerPort: 9153
+              name: prometheus
+              protocol: TCP
+          volumeMounts:
+            - name: config-template
+              mountPath: /data/config.toml.in
+              subPath: stacks-node.toml
+            - name: chainstate
+              mountPath: /data/chainstate
+          env:
+            - name: POD_IP
+              valueFrom:
+                fieldRef:
+                  fieldPath: status.podIP
+            - name: MINER_NAME
+              value: "stacks-node"
+            - name: STACKS_NODE_IP
+              valueFrom:
+                fieldRef:
+                  fieldPath: status.podIP
+            - name: MINER_SEED
+              value: "23ad69119000a241706486b9349556bdc6dfabdf9d9131b153a57c6b0330fb0d01"
+            - name: REWARD_RECIPIENT
+              value: "ST1XVSVQN0KP5SDYFNT8E5TXWVW0XZVQEDBMCJ3XM"
+            - name: BITCOIN_PEER_HOST
+              value: "bitcoin-node"
+            - name: BITCOIN_RPC_PORT
+              value: "18443"
+            - name: BITCOIN_PEER_PORT
+              value: "18444"
+            - name: BITCOIN_RPC_USER
+              value: "hacknet"
+            - name: BITCOIN_RPC_PASS
+              value: "hacknet"
+            - name: BITCOIN_WALLET
+              value: "stacks-miner-1"
+            - name: SIGNER_HOST
+              value: "stacks-signer"
+            - name: SIGNER_PORT
+              value: "30000"
+            - name: BLOCK_PUSH_FAIL_PROBABILITY
+              value: "0"
+            - name: STACKS_20_HEIGHT
+              value: "0"
+            - name: STACKS_2_05_HEIGHT
+              value: "203"
+            - name: STACKS_21_HEIGHT
+              value: "204"
+            - name: STACKS_22_HEIGHT
+              value: "206"
+            - name: STACKS_23_HEIGHT
+              value: "207"
+            - name: STACKS_24_HEIGHT
+              value: "208"
+            - name: STACKS_25_HEIGHT
+              value: "209"
+            - name: STACKS_30_HEIGHT
+              value: "232"
+            - name: STACKS_31_HEIGHT
+              value: "233"
+            - name: STACKS_32_HEIGHT
+              value: "234"
+            - name: POX_PREPARE_LENGTH
+              value: "5"
+            - name: POX_REWARD_LENGTH
+              value: "20"
+          command:
+            - /bin/bash
+            - -c
+            - |
+              cd /data/
+              set -e
+              perl -pe 's/\$\{?([A-Za-z_][A-Za-z0-9_]*)\}?/$ENV{$1}/ge' < config.toml.in > config.toml
+              exec stacks-node start --config config.toml 2>&1
+          readinessProbe:
+            httpGet:
+              path: /v2/info
+              port: 20443
+            initialDelaySeconds: 30
+            periodSeconds: 10
+            timeoutSeconds: 5
+            successThreshold: 1
+            failureThreshold: 10
+          livenessProbe:
+            httpGet:
+              path: /v2/info
+              port: 20443
+            initialDelaySeconds: 60
+            periodSeconds: 30
+            timeoutSeconds: 10
+            failureThreshold: 3
+          resources:
+            requests:
+              memory: "4Gi"
+              cpu: "2000m"
+            limits:
+              memory: "8Gi"
+              cpu: "4000m"
+      volumes:
+        - name: config-template
+          configMap:
+            name: stacks-node-config
+        - name: config
+          emptyDir: {}
+        - name: chainstate
+          emptyDir: {}

+ 106 - 0
devnet/stacks-signer.yaml

@@ -0,0 +1,106 @@
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: stacks-signer-config
+data:
+  config.toml: |
+    # stacks-signer config
+    stacks_private_key = "41634762d89dfa09133a4a8e9c1378d0161d29cd0a9433b51f1e3d32947a73dc01"
+    node_host = "stacks-node:20443"
+    endpoint = "0.0.0.0:30000"
+    network = "testnet"
+    auth_password = "12345"
+    db_path = "/data/signer/stacks-signer.sqlite"
+    metrics_endpoint = "0.0.0.0:9153"
+
+---
+# Stacks Signer 1 Service
+apiVersion: v1
+kind: Service
+metadata:
+  name: stacks-signer
+  labels:
+    app: stacks-signer
+spec:
+  ports:
+    - name: signer
+      port: 30000
+      targetPort: 30000
+      protocol: TCP
+    - name: metrics
+      port: 9153
+      targetPort: 9153
+      protocol: TCP
+  selector:
+    app: stacks-signer
+
+---
+# Stacks Signer Deployment
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: stacks-signer
+  labels:
+    app: stacks-signer
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: stacks-signer
+  template:
+    metadata:
+      labels:
+        app: stacks-signer
+    spec:
+      containers:
+        - name: stacks-signer
+          image: stacks-signer
+          ports:
+            - containerPort: 30000
+              name: signer
+              protocol: TCP
+            - containerPort: 9153
+              name: metrics
+              protocol: TCP
+          volumeMounts:
+            - name: config
+              mountPath: /data/config.toml
+              subPath: config.toml
+            - name: signer-data
+              mountPath: /data/signer
+          command:
+            - /bin/bash
+            - -c
+            - |
+              mkdir -p /data/signer
+              cd /data/
+              exec stacks-signer run --config config.toml 2>&1
+          readinessProbe:
+            tcpSocket:
+              port: 30000
+            initialDelaySeconds: 15
+            periodSeconds: 10
+            timeoutSeconds: 5
+            successThreshold: 1
+            failureThreshold: 10
+          livenessProbe:
+            tcpSocket:
+              port: 30000
+            initialDelaySeconds: 30
+            periodSeconds: 30
+            timeoutSeconds: 10
+            failureThreshold: 3
+          resources:
+            requests:
+              memory: "512Mi"
+              cpu: "250m"
+            limits:
+              memory: "1Gi"
+              cpu: "500m"
+      volumes:
+        - name: config
+          configMap:
+            name: stacks-signer-config
+        - name: signer-data
+          emptyDir: {}

+ 45 - 0
devnet/stacks-stacker.yaml

@@ -0,0 +1,45 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: stacks-stacker
+  labels:
+    app: stacks-stacker
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: stacks-stacker
+  template:
+    metadata:
+      labels:
+        app: stacks-stacker
+    spec:
+      containers:
+        - name: stacker
+          image: stacks-stacker
+          env:
+            - name: STACKS_CORE_RPC_HOST
+              value: "stacks-node"
+            - name: STACKS_CORE_RPC_PORT
+              value: "20443"
+            - name: STACKING_CYCLES
+              value: "1"
+            - name: STACKING_KEYS
+              value: "41634762d89dfa09133a4a8e9c1378d0161d29cd0a9433b51f1e3d32947a73dc01"
+            - name: STACKING_SLOT_DISTRO
+              value: "2"
+            - name: STACKS_25_HEIGHT
+              value: "209"
+            - name: STACKS_30_HEIGHT
+              value: "232"
+            - name: POX_PREPARE_LENGTH
+              value: "5"
+            - name: POX_REWARD_LENGTH
+              value: "20"
+            - name: STACKING_INTERVAL
+              value: "2"
+            - name: POST_TX_WAIT
+              value: "10"
+            - name: SERVICE_NAME
+              value: "stacker"

+ 39 - 1
node/cmd/guardiand/node.go

@@ -17,6 +17,7 @@ import (
 	"github.com/certusone/wormhole/node/pkg/guardiansigner"
 	"github.com/certusone/wormhole/node/pkg/watchers"
 	"github.com/certusone/wormhole/node/pkg/watchers/ibc"
+	"github.com/certusone/wormhole/node/pkg/watchers/stacks"
 	ethcrypto "github.com/ethereum/go-ethereum/crypto"
 
 	"github.com/certusone/wormhole/node/pkg/watchers/cosmwasm"
@@ -242,6 +243,11 @@ var (
 	polygonSepoliaRPC      *string
 	polygonSepoliaContract *string
 
+	stacksRPC                      *string
+	stacksRPCAuthToken             *string
+	stacksStateContract            *string
+	stacksBitcoinBlockPollInterval *string
+
 	logLevel                *string
 	publicRpcLogDetailStr   *string
 	publicRpcLogToTelemetry *bool
@@ -295,7 +301,6 @@ var (
 	// Global variable used to store enabled Chain IDs for Transfer Verification. Contents are parsed from
 	// transferVerifierEnabledChainIDs.
 	txVerifierChains []vaa.ChainID
-
 	// featureFlags are additional static flags that should be published in P2P heartbeats.
 	featureFlags  []string
 	notaryEnabled *bool
@@ -535,6 +540,11 @@ func init() {
 	transferVerifierEnabledChainIDs = NodeCmd.Flags().UintSlice("transferVerifierEnabledChainIDs", make([]uint, 0), "Transfer Verifier will be enabled for these chain IDs (comma-separated)")
 
 	notaryEnabled = NodeCmd.Flags().Bool("notaryEnabled", false, "Run the notary")
+
+	stacksRPC = NodeCmd.Flags().String("stacksRPC", "", "Stacks Node/API URL")
+	stacksRPCAuthToken = NodeCmd.Flags().String("stacksRPCAuthToken", "", "Stacks RPC Authorization Token")
+	stacksStateContract = NodeCmd.Flags().String("stacksStateContract", "", "Stacks contract address for the Wormhole core contract")
+	stacksBitcoinBlockPollInterval = NodeCmd.Flags().String("stacksBitcoinBlockPollInterval", "2s", "Stacks Bitcoin block poll interval")
 }
 
 var (
@@ -1887,6 +1897,34 @@ func runNode(cmd *cobra.Command, args []string) {
 
 	}
 
+	if shouldStart(stacksStateContract) && shouldStart(stacksRPC) {
+		logger.Info("Configuring Stacks watcher",
+			zap.String("contract", *stacksStateContract),
+			zap.String("rpc_url", *stacksRPC))
+
+		rpcURL := *stacksRPC
+		if rpcURL == "" {
+			// Default to stacks service name with API port if not specified
+			rpcURL = "http://stacks-node:3999"
+		}
+
+		bitcoinBlockPollInterval, err := time.ParseDuration(*stacksBitcoinBlockPollInterval)
+		if err != nil {
+			logger.Fatal("Invalid stacksBitcoinBlockPollInterval", zap.String("value", *stacksBitcoinBlockPollInterval), zap.Error(err))
+		}
+
+		wc := &stacks.WatcherConfig{
+			NetworkID:                "stacks",
+			ChainID:                  vaa.ChainIDStacks,
+			StateContract:            *stacksStateContract,
+			RPCURL:                   rpcURL,
+			RPCAuthToken:             *stacksRPCAuthToken,
+			BitcoinBlockPollInterval: bitcoinBlockPollInterval,
+		}
+
+		watcherConfigs = append(watcherConfigs, wc)
+	}
+
 	var ibcWatcherConfig *node.IbcWatcherConfig = nil
 	if shouldStart(ibcWS) {
 		ibcWatcherConfig = &node.IbcWatcherConfig{

+ 66 - 0
node/pkg/watchers/stacks/README.md

@@ -0,0 +1,66 @@
+# Stacks Watcher
+
+This package implements the watcher for the Stacks blockchain.
+
+Responsibility: Observe message publication events from the Wormhole Core contract on the Stacks blockchain.
+
+## High-level architecture
+
+The Stacks watcher uses a polling-based approach to monitor the Stacks blockchain:
+
+- **Bitcoin Block Anchoring**: Uses Bitcoin blocks (burn blocks) as the anchor point for confirmation.
+- **BlockPoller**: Polls the Stacks RPC API for new Bitcoin blocks every 2 seconds.
+- **Confirmation**: Considers a block final after 6 Bitcoin block confirmations.
+- **ObsvReqProcessor**: Processes observation requests to re-observe specific transactions.
+
+## Processing Flow
+
+The watcher maintains two key tracking points:
+
+- Latest Bitcoin block height seen
+- Last processed Bitcoin block height
+
+When new blocks are found, it:
+
+1. Polls for new Bitcoin (burn) blocks
+2. Processes Bitcoin blocks that have reached sufficient confirmation (6 blocks)
+3. Fetches all Stacks blocks anchored to those Bitcoin blocks
+4. Processes transactions in those Stacks blocks
+5. Examines events in those transactions to find Wormhole message publication events
+6. Extracts message data and creates MessagePublication objects
+
+## Re-observation Support
+
+The watcher can reprocess specific transactions when requested. It:
+
+1. Fetches the transaction details using the transaction ID
+2. Processes the transaction to look for Wormhole events
+3. Publishes any valid messages found
+
+## Configuration
+
+The watcher requires the following configuration:
+
+- Stacks RPC URL: The URL of the Stacks blockchain API
+- Contract Address: The address of the Wormhole Core contract on Stacks
+
+## API Endpoints
+
+The watcher interacts with the Stacks Node RPC API endpoints:
+
+- `/v3/tenures/info` - Get current tenure information
+- `/v3/tenures/blocks/{consensusHash}` - Get blocks for a tenure by consensus hash
+- `/v3/tenures/blocks/height/{height}` - Get tenure blocks by Bitcoin block height
+- `/v3/blocks/replay/{blockHash}` - Get block transactions with simulation results (includes `vm_error` field for failed transactions)
+- `/v3/transaction/{txID}` - Get transaction details by ID
+
+**Note:** The v3 block replay endpoint includes a `vm_error` field (added in stacks-core PR #6575) which contains the runtime error message for failed transactions. This field is null for successful transactions.
+
+## Error Handling
+
+The watcher handles the following error conditions, however generally errors are ignored and subsequent blocks/transactions are still processed.
+
+- RPC connection failures - Will retry on the next polling interval
+- Block processing failures - Logs errors and continues with the next block
+- Transaction processing failures - Logs errors and continues with the next transaction
+- Event processing failures - Logs errors and continues with the next event

+ 323 - 0
node/pkg/watchers/stacks/clarity.go

@@ -0,0 +1,323 @@
+package stacks
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"io"
+	"math/big"
+)
+
+// Define an enum called ClarityType with the following values:
+// 0x00: 128-bit signed integer
+// 0x01: 128-bit unsigned integer
+// 0x02: buffer
+// 0x03: boolean true
+// 0x04: boolean false
+// 0x05: standard principal
+// 0x06: contract principal
+// 0x07: Ok response
+// 0x08: Err response
+// 0x09: None option
+// 0x0a: Some option
+// 0x0b: List
+// 0x0c: Tuple
+// 0x0d: StringASCII
+// 0x0e: StringUTF8
+type ClarityType uint8 //enums:enum
+
+const (
+	Int128Signed ClarityType = iota
+	Int128Unsigned
+	Buffer
+	BooleanTrue
+	BooleanFalse
+	StandardPrincipal
+	ContractPrincipal
+	OkResponse
+	ErrResponse
+	NoneOption
+	SomeOption
+	ListType
+	TupleType
+	StringASCIIType
+	StringUTF8Type
+)
+
+type ClarityValue interface {
+	ClarityDecode(*bytes.Reader) error
+}
+
+type Int128 struct {
+	Value *big.Int
+}
+
+func (i *Int128) ClarityDecode(r *bytes.Reader) error {
+	buf := make([]byte, 16)
+	_, err := io.ReadFull(r, buf)
+	if err != nil {
+		return err
+	}
+	i.Value = new(big.Int).SetBytes(buf)
+	return nil
+}
+
+type UInt128 struct {
+	Value *big.Int
+}
+
+func (u *UInt128) ClarityDecode(r *bytes.Reader) error {
+	buf := make([]byte, 16)
+	_, err := io.ReadFull(r, buf)
+	if err != nil {
+		return err
+	}
+	u.Value = new(big.Int).SetBytes(buf)
+	return nil
+}
+
+type ClarityBuffer struct {
+	Length uint32
+	Data   []byte
+}
+
+func (b *ClarityBuffer) ClarityDecode(r *bytes.Reader) error {
+	err := binary.Read(r, binary.BigEndian, &b.Length)
+	if err != nil {
+		return err
+	}
+	b.Data = make([]byte, b.Length)
+	_, err = r.Read(b.Data)
+	return err
+}
+
+type Bool struct {
+	Value bool
+}
+
+func (b *Bool) ClarityDecode(r *bytes.Reader) error {
+	var val uint8
+	err := binary.Read(r, binary.BigEndian, &val)
+	if err != nil {
+		return err
+	}
+	b.Value = val != 0
+	return nil
+}
+
+type ClarityPrincipal struct {
+	Version byte
+	Hash160 [20]byte
+}
+
+func (p *ClarityPrincipal) ClarityDecode(r *bytes.Reader) error {
+	err := binary.Read(r, binary.BigEndian, &p.Version)
+	if err != nil {
+		return err
+	}
+	_, err = io.ReadFull(r, p.Hash160[:])
+	return err
+}
+
+type ClarityContractPrincipal struct {
+	ClarityPrincipal
+	Name string
+}
+
+func (c *ClarityContractPrincipal) ClarityDecode(r *bytes.Reader) error {
+	err := c.ClarityPrincipal.ClarityDecode(r)
+	if err != nil {
+		return err
+	}
+	var nameLength uint8
+	err = binary.Read(r, binary.BigEndian, &nameLength)
+	if err != nil {
+		return err
+	}
+	nameBytes := make([]byte, nameLength)
+	_, err = io.ReadFull(r, nameBytes)
+	if err != nil {
+		return err
+	}
+	c.Name = string(nameBytes)
+	return nil
+}
+
+type Response struct {
+	IsOk   bool
+	Result ClarityValue
+}
+
+func (res *Response) ClarityDecode(r *bytes.Reader) error {
+	var err error
+	res.Result, err = DecodeClarityValue(r)
+	res.IsOk = err == nil
+	return err
+}
+
+type Option struct {
+	IsSome bool
+	Value  ClarityValue
+}
+
+func (opt *Option) ClarityDecode(r *bytes.Reader) error {
+	var err error
+	opt.Value, err = DecodeClarityValue(r)
+	opt.IsSome = err == nil
+	return err
+}
+
+type List struct {
+	Length uint32
+	Values []ClarityValue
+}
+
+func (l *List) ClarityDecode(r *bytes.Reader) error {
+	err := binary.Read(r, binary.BigEndian, &l.Length)
+	if err != nil {
+		return err
+	}
+	l.Values = make([]ClarityValue, l.Length)
+	for i := uint32(0); i < l.Length; i++ {
+		l.Values[i], err = DecodeClarityValue(r)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+type Tuple struct {
+	Length uint32
+	Values map[string]ClarityValue
+}
+
+func (t *Tuple) ClarityDecode(r *bytes.Reader) error {
+	err := binary.Read(r, binary.BigEndian, &t.Length)
+	if err != nil {
+		return err
+	}
+	t.Values = make(map[string]ClarityValue)
+	for i := uint32(0); i < t.Length; i++ {
+		var nameLength uint8
+		err = binary.Read(r, binary.BigEndian, &nameLength)
+		if err != nil {
+			return err
+		}
+		nameBytes := make([]byte, nameLength)
+		_, err = io.ReadFull(r, nameBytes)
+		if err != nil {
+			return err
+		}
+		name := string(nameBytes)
+		t.Values[name], err = DecodeClarityValue(r)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+type StringASCII struct {
+	Length uint32
+	Value  string
+}
+
+func (s *StringASCII) ClarityDecode(r *bytes.Reader) error {
+	err := binary.Read(r, binary.BigEndian, &s.Length)
+	if err != nil {
+		return err
+	}
+	valueBytes := make([]byte, s.Length)
+	_, err = io.ReadFull(r, valueBytes)
+	if err != nil {
+		return err
+	}
+	s.Value = string(valueBytes)
+	return nil
+}
+
+type StringUTF8 struct {
+	Length uint32
+	Value  string
+}
+
+func (s *StringUTF8) ClarityDecode(r *bytes.Reader) error {
+	err := binary.Read(r, binary.BigEndian, &s.Length)
+	if err != nil {
+		return err
+	}
+	valueBytes := make([]byte, s.Length)
+	_, err = io.ReadFull(r, valueBytes)
+	if err != nil {
+		return err
+	}
+	s.Value = string(valueBytes)
+	return nil
+}
+
+func DecodeClarityValue(r *bytes.Reader) (ClarityValue, error) {
+	var typeID uint8
+	err := binary.Read(r, binary.BigEndian, &typeID)
+	if err != nil {
+		return nil, err
+	}
+
+	switch typeID {
+	case 0x00:
+		val := &Int128{}
+		err := val.ClarityDecode(r)
+		return val, err
+	case 0x01:
+		val := &UInt128{}
+		err := val.ClarityDecode(r)
+		return val, err
+	case 0x02:
+		val := &ClarityBuffer{}
+		err := val.ClarityDecode(r)
+		return val, err
+	case 0x03, 0x04:
+		val := &Bool{}
+		// r.Seek(-1, 1) // patch: Seek back 1 byte from current position if we need to decode the value
+		// err := val.ClarityDecode(r)
+		val.Value = typeID == 0x03
+		// return val, err
+		return val, nil
+	case 0x05:
+		val := &ClarityPrincipal{}
+		err := val.ClarityDecode(r)
+		return val, err
+	case 0x06:
+		val := &ClarityContractPrincipal{}
+		err := val.ClarityDecode(r)
+		return val, err
+	case 0x07, 0x08:
+		val := &Response{}
+		err := val.ClarityDecode(r)
+		val.IsOk = typeID == 0x07
+		return val, err
+	case 0x09, 0x0a:
+		val := &Option{}
+		err := val.ClarityDecode(r)
+		val.IsSome = typeID == 0x0a
+		return val, err
+	case 0x0b:
+		val := &List{}
+		err := val.ClarityDecode(r)
+		return val, err
+	case 0x0c:
+		val := &Tuple{}
+		err := val.ClarityDecode(r)
+		return val, err
+	case 0x0d:
+		val := &StringASCII{}
+		err := val.ClarityDecode(r)
+		return val, err
+	case 0x0e:
+		val := &StringUTF8{}
+		err := val.ClarityDecode(r)
+		return val, err
+	default:
+		return nil, errors.New("unknown type ID")
+	}
+}

+ 46 - 0
node/pkg/watchers/stacks/config.go

@@ -0,0 +1,46 @@
+package stacks
+
+import (
+	"time"
+
+	"github.com/certusone/wormhole/node/pkg/common"
+	gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
+	"github.com/certusone/wormhole/node/pkg/query"
+	"github.com/certusone/wormhole/node/pkg/supervisor"
+	"github.com/certusone/wormhole/node/pkg/watchers"
+	"github.com/certusone/wormhole/node/pkg/watchers/interfaces"
+	"github.com/wormhole-foundation/wormhole/sdk/vaa"
+)
+
+// WatcherConfig defines the configuration for the Stacks watcher
+type WatcherConfig struct {
+	NetworkID     watchers.NetworkID // human readable name
+	ChainID       vaa.ChainID        // ChainID
+	RPCURL        string             // Stacks RPC URL
+	RPCAuthToken  string             // Stacks RPC Authorization Token
+	StateContract string             // Stacks contract address for the Wormhole core (state) contract
+
+	// Optional configurable parameters (zero values will use defaults)
+	BitcoinBlockPollInterval time.Duration `mapstructure:"bitcoinBlockPollInterval"` // How often to poll for new Bitcoin blocks
+}
+
+func (wc *WatcherConfig) GetNetworkID() watchers.NetworkID {
+	return wc.NetworkID
+}
+
+func (wc *WatcherConfig) GetChainID() vaa.ChainID {
+	return wc.ChainID
+}
+
+//nolint:unparam // error is always nil here but the return type is required to satisfy the interface.
+func (wc *WatcherConfig) Create(
+	msgC chan<- *common.MessagePublication,
+	obsvReqC <-chan *gossipv1.ObservationRequest,
+	_ <-chan *query.PerChainQueryInternal, // queryReqC - not used for Stacks
+	_ chan<- *query.PerChainQueryResponseInternal, // queryResponseC - not used for Stacks
+	_ chan<- *common.GuardianSet, // setC - not used for Stacks
+	_ common.Environment, // env - not used for Stacks
+) (supervisor.Runnable, interfaces.Reobserver, error) {
+	watcher := NewWatcher(wc.RPCURL, wc.RPCAuthToken, wc.StateContract, wc.BitcoinBlockPollInterval, msgC, obsvReqC)
+	return watcher.Run, watcher, nil
+}

+ 296 - 0
node/pkg/watchers/stacks/fetch.go

@@ -0,0 +1,296 @@
+package stacks
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"strings"
+
+	"github.com/certusone/wormhole/node/pkg/common"
+)
+
+type (
+	StacksStxTransferEvent struct {
+		Amount    string `json:"amount"`
+		Memo      string `json:"memo"`
+		Recipient string `json:"recipient"`
+		Sender    string `json:"sender"`
+	}
+
+	StacksContractEvent struct {
+		ContractIdentifier string                 `json:"contract_identifier"`
+		RawValue           string                 `json:"raw_value"`
+		Topic              string                 `json:"topic"`
+		Value              map[string]interface{} `json:"value"`
+	}
+
+	StacksEvent struct {
+		Committed        bool                    `json:"committed"`
+		EventIndex       uint64                  `json:"event_index"`
+		TxID             string                  `json:"txid"`
+		Type             string                  `json:"type"`
+		StxTransferEvent *StacksStxTransferEvent `json:"stx_transfer_event,omitempty"`
+		ContractEvent    *StacksContractEvent    `json:"contract_event,omitempty"`
+	}
+
+	StacksV3TenureBlock struct {
+		BlockId       string `json:"block_id"`
+		BlockHash     string `json:"block_hash"`
+		ParentBlockId string `json:"parent_block_id"`
+		Height        uint64 `json:"height"`
+	}
+
+	StacksV3TenureBlocksResponse struct {
+		ConsensusHash   string                `json:"consensus_hash"`
+		BurnBlockHeight uint64                `json:"burn_block_height"`
+		BurnBlockHash   string                `json:"burn_block_hash"`
+		StacksBlocks    []StacksV3TenureBlock `json:"stacks_blocks"`
+	}
+
+	StacksV3TenureBlockTransaction struct {
+		TxId                 string                 `json:"txid"`
+		TxIndex              uint32                 `json:"tx_index"`
+		Data                 map[string]interface{} `json:"data,omitempty"`                   // Transaction data structure
+		Hex                  string                 `json:"hex,omitempty"`                    // Raw transaction hex
+		Result               map[string]interface{} `json:"result,omitempty"`                 // Transaction execution result
+		ResultHex            string                 `json:"result_hex,omitempty"`             // Transaction execution result in hex
+		StxBurned            uint64                 `json:"stx_burned,omitempty"`             // STX burned in transaction
+		ExecutionCost        map[string]interface{} `json:"execution_cost,omitempty"`         // Execution cost breakdown
+		Events               []StacksEvent          `json:"events,omitempty"`                 // Transaction events
+		PostConditionAborted bool                   `json:"post_condition_aborted,omitempty"` // Whether the post-condition was aborted
+		VmError              *string                `json:"vm_error,omitempty"`               // Runtime error message if transaction failed (null when successful)
+	}
+
+	StacksV3TenureBlockReplayResponse struct {
+		BlockId         string                           `json:"block_id"`
+		BlockHash       string                           `json:"block_hash"`
+		BlockHeight     uint64                           `json:"block_height"`
+		ParentBlockId   string                           `json:"parent_block_id"`
+		ConsensusHash   string                           `json:"consensus_hash"`
+		Fees            uint64                           `json:"fees"`
+		TxMerkleRoot    string                           `json:"tx_merkle_root"`
+		StateIndexRoot  string                           `json:"state_index_root"`
+		Timestamp       uint64                           `json:"timestamp"`
+		MinerSignature  string                           `json:"miner_signature"`
+		SignerSignature []string                         `json:"signer_signature"`
+		Transactions    []StacksV3TenureBlockTransaction `json:"transactions"`
+		ValidMerkleRoot bool                             `json:"valid_merkle_root"`
+	}
+
+	StacksV3TransactionResponse struct {
+		IndexBlockHash string `json:"index_block_hash"`
+		Tx             string `json:"tx"`
+		Result         string `json:"result"`
+	}
+
+	StacksV2PoxEpoch struct {
+		EpochID     string `json:"epoch_id"`
+		StartHeight uint64 `json:"start_height"`
+		EndHeight   uint64 `json:"end_height"`
+	}
+
+	StacksV2PoxResponse struct {
+		ContractID                  string             `json:"contract_id"`
+		FirstBurnchainBlockHeight   uint64             `json:"first_burnchain_block_height"`
+		CurrentBurnchainBlockHeight uint64             `json:"current_burnchain_block_height"`
+		PreparePhaseBlockLength     uint64             `json:"prepare_phase_block_length"`
+		RewardPhaseBlockLength      uint64             `json:"reward_phase_block_length"`
+		Epochs                      []StacksV2PoxEpoch `json:"epochs"`
+	}
+
+	StacksV2InfoPoxAnchor struct {
+		AnchorBlockHash string `json:"anchor_block_hash"`
+		AnchorBlockTxid string `json:"anchor_block_txid"`
+	}
+
+	StacksV2InfoResponse struct {
+		PeerVersion            uint64                 `json:"peer_version"`
+		PoxConsensus           string                 `json:"pox_consensus"`
+		BurnBlockHeight        uint64                 `json:"burn_block_height"`
+		StablePoxConsensus     string                 `json:"stable_pox_consensus"`
+		StableBurnBlockHeight  uint64                 `json:"stable_burn_block_height"`
+		ServerVersion          string                 `json:"server_version"`
+		NetworkID              uint64                 `json:"network_id"`
+		ParentNetworkID        uint64                 `json:"parent_network_id"`
+		StacksTipHeight        uint64                 `json:"stacks_tip_height"`
+		StacksTip              string                 `json:"stacks_tip"`
+		StacksTipConsensusHash string                 `json:"stacks_tip_consensus_hash"`
+		GenesisChainStateHash  string                 `json:"genesis_chainstate_hash"`
+		UnanchoredTip          *string                `json:"unanchored_tip"`
+		UnanchoredSeq          *uint64                `json:"unanchored_seq"`
+		TenureHeight           uint64                 `json:"tenure_height"`
+		ExitAtBlockHeight      *uint64                `json:"exit_at_block_height"`
+		IsFullySynced          bool                   `json:"is_fully_synced"`
+		NodePublicKey          string                 `json:"node_public_key"`
+		NodePublicKeyHash      string                 `json:"node_public_key_hash"`
+		LastPoxAnchor          *StacksV2InfoPoxAnchor `json:"last_pox_anchor"`
+		Stackerdbs             []string               `json:"stackerdbs"`
+	}
+)
+
+// Fetches a tenure and its blocks by Bitcoin (burn) block height
+func (w *Watcher) fetchTenureBlocksByBurnHeight(ctx context.Context, height uint64) (*StacksV3TenureBlocksResponse, error) {
+	url := fmt.Sprintf("%s/v3/tenures/blocks/height/%d", w.rpcURL, height)
+
+	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to fetch Bitcoin (burn) block: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
+	}
+
+	body, err := common.SafeRead(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response body: %w", err)
+	}
+
+	var tenureBlocks StacksV3TenureBlocksResponse
+	if err := json.Unmarshal(body, &tenureBlocks); err != nil {
+		return nil, fmt.Errorf("failed to parse response: %w", err)
+	}
+
+	return &tenureBlocks, nil
+}
+
+// Fetches block replay data including all transactions for a given block
+// Uses the v3 blocks/replay endpoint which includes vm_error for failed transactions
+func (w *Watcher) fetchStacksBlockReplay(ctx context.Context, blockId string) (*StacksV3TenureBlockReplayResponse, error) {
+	url := fmt.Sprintf("%s/v3/blocks/replay/%s", w.rpcURL, blockId)
+
+	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	if w.rpcAuthToken != "" {
+		req.Header.Set("Authorization", w.rpcAuthToken)
+	}
+
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to fetch block replay: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
+	}
+
+	body, err := common.SafeRead(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response body: %w", err)
+	}
+
+	var replay StacksV3TenureBlockReplayResponse
+	if err := json.Unmarshal(body, &replay); err != nil {
+		return nil, fmt.Errorf("failed to parse response: %w", err)
+	}
+
+	return &replay, nil
+}
+
+// Fetches a transaction by its txid
+func (w *Watcher) fetchStacksTransactionByTxId(ctx context.Context, txID string) (*StacksV3TransactionResponse, error) {
+	txID = strings.TrimPrefix(txID, "0x")
+	url := fmt.Sprintf("%s/v3/transaction/%s", w.rpcURL, txID)
+
+	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to fetch transaction: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
+	}
+
+	body, err := common.SafeRead(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response body: %w", err)
+	}
+
+	var tx StacksV3TransactionResponse
+	if err := json.Unmarshal(body, &tx); err != nil {
+		return nil, fmt.Errorf("failed to parse node transaction response: %w", err)
+	}
+
+	return &tx, nil
+}
+
+// Fetches PoX (Proof of Transfer) information including epoch data
+func (w *Watcher) fetchPoxInfo(ctx context.Context) (*StacksV2PoxResponse, error) {
+	url := fmt.Sprintf("%s/v2/pox", w.rpcURL)
+
+	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to fetch PoX info: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
+	}
+
+	body, err := common.SafeRead(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response body: %w", err)
+	}
+
+	var poxInfo StacksV2PoxResponse
+	if err := json.Unmarshal(body, &poxInfo); err != nil {
+		return nil, fmt.Errorf("failed to parse PoX info response: %w", err)
+	}
+
+	return &poxInfo, nil
+}
+
+// Fetches node information from the Stacks node
+func (w *Watcher) fetchNodeInfo(ctx context.Context) (*StacksV2InfoResponse, error) {
+	url := fmt.Sprintf("%s/v2/info", w.rpcURL)
+
+	req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
+	if err != nil {
+		return nil, fmt.Errorf("failed to create request: %w", err)
+	}
+
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return nil, fmt.Errorf("failed to fetch Stacks node info: %w", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
+	}
+
+	body, err := common.SafeRead(resp.Body)
+	if err != nil {
+		return nil, fmt.Errorf("failed to read response body: %w", err)
+	}
+
+	var nodeInfo StacksV2InfoResponse
+	if err := json.Unmarshal(body, &nodeInfo); err != nil {
+		return nil, fmt.Errorf("failed to parse Stacks node info response: %w", err)
+	}
+
+	return &nodeInfo, nil
+}

+ 105 - 0
node/pkg/watchers/stacks/fixture_stacks_simulate.json

@@ -0,0 +1,105 @@
+{
+    "block_id": "8ba4f594878590a519935c6272456d31c479c398bcec88e1ad83c64468c999e5",
+    "block_hash": "1eaef849c3cb178279f27af3db595ad3914d23aece533f22912d1d87c22aae3a",
+    "parent_block_id": "190f786ff64ce61fdb25b75ab6fb2f4a36d2d0c85d9a732c0bdb64f4effc464f",
+    "consensus_hash": "c81f64866e26a6a6ee7d697f22eb55796d8c78a3",
+    "fees": 300,
+    "tx_merkle_root": "99178a7cf8fb09d864eb0e06ad541c0be69732e43efd3339ebfc71fc1ba38cb7",
+    "state_index_root": "84a43d0d714cf732cbf2717fc4c67516533c2192347ae90fe92b88bb4cb41706",
+    "timestamp": 1744604312,
+    "miner_signature": "006ab76f8943df0128658cbaaed10bc159fdb84e2a6e5bd2aa14495ddd4deee5917368a7cdf218629c91042cdfb12ac00a1017e6a811505b1d5c39788e2d0d2376",
+    "signer_signature": [
+        "000e47f34c25ee9e53f41dfaa1ed802b88c73fadcbdff52eb1d29fa5f8d7791d151a308319a3d96e87e2cb321d21f50fdd477490f034c754815e130113eb9cbd52",
+        "0045ada75fbb3b0064ac7861f9c08426f56babdcab4f822113890377e089c64d4334d0deba7a3cd4525f4250113f193bc50217018d338d229290b9e7f36b5d44e8"
+    ],
+    "transactions": [
+        {
+            "txid": "8f16abb34505ae2fa06834596c017274631b9a0a70236c68cbc8adb66c5e7724",
+            "tx_index": 0,
+            "data": {
+                "version": "Testnet",
+                "chain_id": 2147483648,
+                "auth": {
+                    "Standard": {
+                        "Singlesig": {
+                            "hash_mode": "P2PKH",
+                            "signer": "841f9b4dd30ae02fd816cc225c59d1b00b9c3a3a",
+                            "nonce": 312328,
+                            "tx_fee": 300,
+                            "key_encoding": "Compressed",
+                            "signature": "01f99efb9944afe7f0e7337998cdd1b0838e3fce534a78d54d1cd3c483e5bf857f38a1bc88d9bac2a3f0766a40f6113d42e16dfa572d3280898c0d7fe53afc32af"
+                        }
+                    }
+                },
+                "anchor_mode": "Any",
+                "post_condition_mode": "Deny",
+                "post_conditions": [],
+                "payload": {
+                    "TokenTransfer": [
+                        {
+                            "Standard": [
+                                26,
+                                [
+                                    180,
+                                    253,
+                                    174,
+                                    152,
+                                    182,
+                                    75,
+                                    156,
+                                    214,
+                                    201,
+                                    67,
+                                    111,
+                                    59,
+                                    150,
+                                    85,
+                                    88,
+                                    150,
+                                    106,
+                                    254,
+                                    137,
+                                    11
+                                ]
+                            ]
+                        },
+                        1000,
+                        "00000000000000000000000000000000000000000000000000000000000000000000"
+                    ]
+                }
+            },
+            "hex": "80800000000400841f9b4dd30ae02fd816cc225c59d1b00b9c3a3a000000000004c408000000000000012c0001f99efb9944afe7f0e7337998cdd1b0838e3fce534a78d54d1cd3c483e5bf857f38a1bc88d9bac2a3f0766a40f6113d42e16dfa572d3280898c0d7fe53afc32af03020000000000051ab4fdae98b64b9cd6c9436f3b965558966afe890b00000000000003e800000000000000000000000000000000000000000000000000000000000000000000",
+            "result": {
+                "Response": {
+                    "committed": true,
+                    "data": {
+                        "Bool": true
+                    }
+                }
+            },
+            "stx_burned": 0,
+            "execution_cost": {
+                "write_length": 0,
+                "write_count": 0,
+                "read_length": 0,
+                "read_count": 0,
+                "runtime": 0
+            },
+            "events": [
+                {
+                    "committed": true,
+                    "event_index": 0,
+                    "stx_transfer_event": {
+                        "amount": "1000",
+                        "memo": "00000000000000000000000000000000000000000000000000000000000000000000",
+                        "recipient": "ST2TFVBMRPS5SSNP98DQKQ5JNB2B6NZM91C4K3P7B",
+                        "sender": "ST221Z6TDTC5E0BYR2V624Q2ST6R0Q71T78WTAX6H"
+                    },
+                    "txid": "0x8f16abb34505ae2fa06834596c017274631b9a0a70236c68cbc8adb66c5e7724",
+                    "type": "stx_transfer_event"
+                }
+            ]
+        }
+    ],
+    "valid": true
+}

+ 49 - 0
node/pkg/watchers/stacks/fixture_stacks_tenure_blocks.json

@@ -0,0 +1,49 @@
+{
+    "consensus_hash": "c81f64866e26a6a6ee7d697f22eb55796d8c78a3",
+    "burn_block_height": 35901,
+    "burn_block_hash": "7c7d030c1e86ec0fbfc54d6d679c863d2de70b630e2fde038610a138174b7683",
+    "stacks_blocks": [
+        {
+            "block_id": "8ec85141cf934e8eeee29619ac6c81488690824b975033d0feb175f4ed43600e",
+            "block_hash": "172b69b6695a220fa951808854ee89536905f03b914e2ca51fe277d7cb51eff6",
+            "parent_block_id": "199679ef3dba8cd798542040247795d427d8e7f183701be508ae5c826ca4df36",
+            "height": 965359
+        },
+        {
+            "block_id": "d490e67f05d310ed0f59e5e2a3d1416769d5f67dfbedf94817ef8037d974523b",
+            "block_hash": "bfc7b36d18052e045127868d1f52f3b9eb6bcf10ba958fcd6f6aeb3b593aec32",
+            "parent_block_id": "8ec85141cf934e8eeee29619ac6c81488690824b975033d0feb175f4ed43600e",
+            "height": 965360
+        },
+        {
+            "block_id": "52d7b563c2b764fd4fdf716f6b61e672a217cf4088c66e5912c055bfe7c5312a",
+            "block_hash": "a6ae0e771cb4b8b8fad1b35d8c8b2738688d64624301b6fef2f112aaae6f0ea4",
+            "parent_block_id": "d490e67f05d310ed0f59e5e2a3d1416769d5f67dfbedf94817ef8037d974523b",
+            "height": 965361
+        },
+        {
+            "block_id": "84582dcf24197a096f92f6b3ced3acacc30eab3f5270edf10bd4344df99f0d87",
+            "block_hash": "a6a9829f2384e3cd1d266807eae2f6f014626b1f994b7f2a56bd69815bd6e40d",
+            "parent_block_id": "52d7b563c2b764fd4fdf716f6b61e672a217cf4088c66e5912c055bfe7c5312a",
+            "height": 965362
+        },
+        {
+            "block_id": "13bc822ae9288036d918cb5de5514151ef7403992e10f5f16465eed9406d277e",
+            "block_hash": "b2e6cc61c594491ff3b5c690487d0282752ae413d5a6da2b636159557ac8d070",
+            "parent_block_id": "84582dcf24197a096f92f6b3ced3acacc30eab3f5270edf10bd4344df99f0d87",
+            "height": 965363
+        },
+        {
+            "block_id": "190f786ff64ce61fdb25b75ab6fb2f4a36d2d0c85d9a732c0bdb64f4effc464f",
+            "block_hash": "3001233565ba62dc5cb59cbe3ede97691b520c4045db1ace871d7e7435d75487",
+            "parent_block_id": "13bc822ae9288036d918cb5de5514151ef7403992e10f5f16465eed9406d277e",
+            "height": 965364
+        },
+        {
+            "block_id": "8ba4f594878590a519935c6272456d31c479c398bcec88e1ad83c64468c999e5",
+            "block_hash": "1eaef849c3cb178279f27af3db595ad3914d23aece533f22912d1d87c22aae3a",
+            "parent_block_id": "190f786ff64ce61fdb25b75ab6fb2f4a36d2d0c85d9a732c0bdb64f4effc464f",
+            "height": 965365
+        }
+    ]
+}

+ 9 - 0
node/pkg/watchers/stacks/fixture_stacks_tenure_info.json

@@ -0,0 +1,9 @@
+{
+    "consensus_hash": "82dabaee6f445d0047ebb3757575737bb0262dc5",
+    "tenure_start_block_id": "8ea305f48e2f23b80cae05353dd361ef533514f856efdead10ec3e892aa5f003",
+    "parent_consensus_hash": "bfd28d01a26a6961c959917e20e89b908b9c747d",
+    "parent_tenure_start_block_id": "6594abc9e881d00623542906ad73db2cc8f0493ddce5beea442bfca006a82fb9",
+    "tip_block_id": "8ea305f48e2f23b80cae05353dd361ef533514f856efdead10ec3e892aa5f003",
+    "tip_height": 965766,
+    "reward_cycle": 41
+}

+ 632 - 0
node/pkg/watchers/stacks/watcher.go

@@ -0,0 +1,632 @@
+package stacks
+
+import (
+	"bytes"
+	"context"
+	"encoding/hex"
+	"fmt"
+	"math"
+	"math/big"
+	"strings"
+	"sync/atomic"
+	"time"
+
+	"github.com/certusone/wormhole/node/pkg/common"
+	"github.com/certusone/wormhole/node/pkg/p2p"
+	gossipv1 "github.com/certusone/wormhole/node/pkg/proto/gossip/v1"
+	"github.com/certusone/wormhole/node/pkg/readiness"
+	"github.com/certusone/wormhole/node/pkg/supervisor"
+	"github.com/wormhole-foundation/wormhole/sdk/vaa"
+	"go.uber.org/zap"
+)
+
+/// OVERVIEW
+// The Stacks watcher monitors the Stacks blockchain for cross-chain Wormhole message events.
+// It uses Bitcoin blocks (burn blocks) as the anchor point for confirmation and processes
+// Stacks blocks that are anchored to confirmed Bitcoin blocks.
+//
+// Core Components and Process Flow:
+// - Public Methods:
+//    - Run: Main entry point that starts the block poller and observation request handler
+//    - Reobserve: Implements reobservation support for previously emitted messages
+//
+// - Execution Flow:
+//    - runBlockPoller: Polls for new Bitcoin blocks and processes confirmed blocks
+//    - process...: (`processCoreEvent` is the main/final function)
+//      - Bitcoin Block → Stacks Blocks → Transactions → Events → Message Data
+//
+// API Interaction, aka fetch methods are in `fetch.go`.
+
+// Safe overflow checking constants for BigInt validation
+var (
+	maxUint32BigInt = big.NewInt(math.MaxUint32)
+	maxUint64BigInt = new(big.Int).SetUint64(math.MaxUint64)
+	maxUint8BigInt  = big.NewInt(math.MaxUint8)
+	maxInt64        = uint64(math.MaxInt64)
+)
+
+type (
+	Watcher struct {
+		rpcURL        string
+		rpcAuthToken  string
+		stateContract string
+
+		bitcoinBlockPollInterval time.Duration
+
+		msgC          chan<- *common.MessagePublication
+		obsvReqC      <-chan *gossipv1.ObservationRequest
+		readinessSync readiness.Component
+
+		nakamotoBitcoinHeight atomic.Uint64 // We can't process blocks before this height
+
+		stableBitcoinHeight    atomic.Uint64
+		latestBitcoinHeight    atomic.Uint64
+		processedBitcoinHeight atomic.Uint64
+	}
+
+	MessageData struct {
+		EmitterAddress   vaa.Address
+		Nonce            uint32
+		Sequence         uint64
+		ConsistencyLevel uint8
+		Payload          []byte
+	}
+)
+
+func NewWatcher(
+	rpcURL string,
+	rpcAuthToken string,
+	contract string,
+	bitcoinBlockPollInterval time.Duration,
+	msgC chan<- *common.MessagePublication,
+	obsvReqC <-chan *gossipv1.ObservationRequest,
+) *Watcher {
+	w := &Watcher{
+		rpcURL:                   rpcURL,
+		rpcAuthToken:             rpcAuthToken,
+		stateContract:            contract,
+		bitcoinBlockPollInterval: bitcoinBlockPollInterval,
+		msgC:                     msgC,
+		obsvReqC:                 obsvReqC,
+		readinessSync:            common.MustConvertChainIdToReadinessSyncing(vaa.ChainIDStacks),
+	}
+
+	w.latestBitcoinHeight.Store(0)
+	w.processedBitcoinHeight.Store(0)
+
+	return w
+}
+
+/// WATCHER PUBLIC METHODS
+
+func (w *Watcher) Run(ctx context.Context) error {
+	logger := supervisor.Logger(ctx)
+
+	logger.Info("Starting Stacks watcher",
+		zap.String("rpc_url", w.rpcURL),
+		zap.String("contract", w.stateContract))
+
+	errC := make(chan error)
+
+	// Start block poller
+	common.RunWithScissors(ctx, errC, "stacksBlockPoller", w.runBlockPoller)
+
+	// Handle observation requests
+	common.RunWithScissors(ctx, errC, "stacksObsvReqWorker", func(ctx context.Context) error {
+		for {
+			select {
+			case <-ctx.Done():
+				return nil
+			case req := <-w.obsvReqC:
+				logger.Info("received observation request",
+					zap.String("tx_hash", hex.EncodeToString(req.TxHash)))
+				continue
+			}
+		}
+	})
+
+	// Set initial readiness state
+	readiness.SetReady(w.readinessSync)
+
+	// Wait for error or context cancellation
+	select {
+	case <-ctx.Done():
+		logger.Info("context cancelled, stopping Stacks watcher")
+		return nil
+	case err := <-errC:
+		return err
+	}
+}
+
+// Reobserve implements the interfaces.Reobserver interface.
+func (w *Watcher) Reobserve(ctx context.Context, chainID vaa.ChainID, txID []byte, customEndpoint string) (uint32, error) {
+	logger := supervisor.Logger(ctx)
+
+	// Verify this request is for our chain
+	if chainID != vaa.ChainIDStacks {
+		return 0, fmt.Errorf("unexpected chain ID: %v", chainID)
+	}
+
+	txIdString := hex.EncodeToString(txID)
+	logger.Info("Received reobservation request",
+		zap.String("tx_id", txIdString),
+		zap.String("custom_endpoint", customEndpoint))
+
+	// Process the transaction
+	err := w.reobserveStacksTransactionByTxId(ctx, txIdString, logger)
+	if err != nil {
+		logger.Error("Failed to reobserve transaction",
+			zap.String("tx_hash", txIdString),
+			zap.Error(err))
+		return 0, err
+	}
+
+	// Return 1 to indicate we processed the request
+	return 1, nil
+}
+
+/// RUN
+
+// Polls for new Bitcoin (burn) blocks and processes confirmed blocks
+func (w *Watcher) runBlockPoller(ctx context.Context) error {
+	logger := supervisor.Logger(ctx)
+
+	logger.Info("Starting Stacks block poller",
+		zap.String("rpc_url", w.rpcURL),
+		zap.String("contract", w.stateContract),
+		zap.Duration("poll_interval", w.bitcoinBlockPollInterval))
+
+	poxInfo, err := w.fetchPoxInfo(ctx)
+	if err != nil {
+		return fmt.Errorf("failed to fetch PoX info: %w", err)
+	}
+
+	var nakamotoEpoch *StacksV2PoxEpoch
+	for _, epoch := range poxInfo.Epochs {
+		if epoch.EpochID == "Epoch30" {
+			nakamotoEpoch = &epoch
+			break
+		}
+	}
+
+	if nakamotoEpoch == nil {
+		return fmt.Errorf("failed to find Nakamoto epoch (Epoch30) in PoX info")
+	}
+
+	w.nakamotoBitcoinHeight.Store(nakamotoEpoch.StartHeight)
+
+	nodeInfo, err := w.fetchNodeInfo(ctx)
+	if err != nil {
+		return fmt.Errorf("failed to fetch node info: %w", err)
+	}
+
+	// Set to stable or nakamoto height, whichever is higher
+	// Act as if all blocks up to the stable burn block height have been processed
+	if nakamotoEpoch.StartHeight > nodeInfo.StableBurnBlockHeight {
+		w.processedBitcoinHeight.Store(nakamotoEpoch.StartHeight)
+	} else {
+		w.processedBitcoinHeight.Store(nodeInfo.StableBurnBlockHeight)
+	}
+
+	logger.Info("Initialized Stacks watcher with stable Bitcoin (burn) block",
+		zap.Uint64("stable_bitcoin_block_height", nodeInfo.StableBurnBlockHeight))
+
+	// Convert StableBurnBlockHeight to int64 with overflow check
+	stableHeight := nodeInfo.StableBurnBlockHeight
+	if stableHeight > maxInt64 {
+		return fmt.Errorf("stable burn block height %d exceeds maximum int64 value", stableHeight)
+	}
+
+	p2p.DefaultRegistry.SetNetworkStats(vaa.ChainIDStacks, &gossipv1.Heartbeat_Network{
+		Height:          int64(stableHeight), // #nosec G115 -- checked above
+		ContractAddress: w.stateContract,
+	})
+
+	timer := time.NewTimer(w.bitcoinBlockPollInterval)
+	defer timer.Stop()
+
+	// Poll loop
+	for {
+		select {
+		case <-ctx.Done():
+			return nil
+		case <-timer.C:
+			nodeInfo, err := w.fetchNodeInfo(ctx)
+			if err != nil {
+				logger.Error("Failed to fetch node info",
+					zap.Error(err))
+				timer.Reset(w.bitcoinBlockPollInterval)
+				continue
+			}
+
+			previousStableBitcoinHeight := w.stableBitcoinHeight.Load()
+
+			// We have a new stable Bitcoin (burn) block height
+			if nodeInfo.StableBurnBlockHeight > previousStableBitcoinHeight {
+				logger.Info("Found new stable Bitcoin (burn) block",
+					zap.Uint64("previous_stable_height", previousStableBitcoinHeight),
+					zap.Uint64("stable_height", nodeInfo.StableBurnBlockHeight))
+
+				w.stableBitcoinHeight.Store(nodeInfo.StableBurnBlockHeight)
+
+				// Convert StableBurnBlockHeight to int64 with overflow check
+				newStableHeight := nodeInfo.StableBurnBlockHeight
+				if newStableHeight > maxInt64 {
+					logger.Error("Stable burn block height exceeds maximum int64 value",
+						zap.Uint64("height", newStableHeight))
+					timer.Reset(w.bitcoinBlockPollInterval)
+					continue
+				}
+
+				p2p.DefaultRegistry.SetNetworkStats(vaa.ChainIDStacks, &gossipv1.Heartbeat_Network{
+					Height:          int64(newStableHeight), // #nosec G115 -- checked above
+					ContractAddress: w.stateContract,
+				})
+
+				bitcoinFromHeight := w.processedBitcoinHeight.Load() + 1
+
+				logger.Info("Processing Bitcoin (burn) blocks",
+					zap.Uint64("from_height", bitcoinFromHeight),
+					zap.Uint64("to_height", nodeInfo.StableBurnBlockHeight))
+
+				// Processing loop
+				for height := bitcoinFromHeight; height <= nodeInfo.StableBurnBlockHeight; height++ {
+					tenure, err := w.fetchTenureBlocksByBurnHeight(ctx, height)
+					if err != nil {
+						logger.Error("Failed to fetch Bitcoin (burn) block",
+							zap.Uint64("height", height),
+							zap.Error(err))
+						break
+					}
+
+					w.processBitcoinBlock(ctx, tenure, logger)
+					w.processedBitcoinHeight.Store(height)
+				}
+			}
+
+			timer.Reset(w.bitcoinBlockPollInterval)
+		}
+	}
+}
+
+/// PROCESS
+
+// Processes all Stacks blocks anchored to the given Bitcoin (burn) block
+func (w *Watcher) processBitcoinBlock(ctx context.Context, tenureBlocks *StacksV3TenureBlocksResponse, logger *zap.Logger) {
+	logger.Info("Processing Bitcoin (burn) block",
+		zap.Uint64("bitcoin_block_height", tenureBlocks.BurnBlockHeight),
+		zap.String("bitcoin_block_hash", tenureBlocks.BurnBlockHash))
+
+	// Process each Stacks block anchored to this burn block
+	for _, block := range tenureBlocks.StacksBlocks {
+		logger.Info("Processing Stacks block", zap.String("stacks_block_id", block.BlockId))
+
+		// Fetch and process the Stacks block
+		if err := w.processStacksBlock(ctx, block.BlockId, logger); err != nil {
+			logger.Error("Failed to process Stacks block",
+				zap.String("stacks_block_id", block.BlockId),
+				zap.Error(err))
+			// Continue processing other blocks even if one fails
+		}
+	}
+}
+
+// Fetches and processes all transactions in a Stacks block
+func (w *Watcher) processStacksBlock(ctx context.Context, blockHash string, logger *zap.Logger) error {
+	replay, err := w.fetchStacksBlockReplay(ctx, blockHash)
+	if err != nil {
+		return fmt.Errorf("failed to fetch Stacks block replay: %w", err)
+	}
+
+	for _, tx := range replay.Transactions {
+		if err := w.processStacksTransaction(ctx, &tx, replay, false, logger); err != nil {
+			logger.Error("Failed to process transaction",
+				zap.String("tx_id", tx.TxId),
+				zap.Error(err))
+			// Continue processing other transactions even if one fails
+		}
+	}
+
+	return nil
+}
+
+// Processes a single transaction from a Stacks block
+func (w *Watcher) processStacksTransaction(_ context.Context, tx *StacksV3TenureBlockTransaction, replay *StacksV3TenureBlockReplayResponse, isReobservation bool, logger *zap.Logger) error {
+	logger.Info("Processing Stacks transaction", zap.String("tx_id", tx.TxId))
+
+	// non-okay response
+	if !strings.HasPrefix(tx.ResultHex, "0x07") { // (ok) is 0x07...
+		return fmt.Errorf("transaction %s failed due to response hex: %s", tx.TxId, tx.ResultHex)
+	}
+
+	// abort_by_response
+	if !isTransactionResultCommitted(tx.Result) {
+		return fmt.Errorf("transaction %s failed due to response: %v", tx.TxId, tx.Result)
+	}
+
+	// abort_by_post_condition
+	if tx.PostConditionAborted {
+		return fmt.Errorf("transaction %s failed due to post-condition aborted", tx.TxId)
+	}
+
+	// other runtime error
+	if tx.VmError != nil {
+		return fmt.Errorf("transaction %s failed due to runtime error: %s", tx.TxId, *tx.VmError)
+	}
+
+	// success
+
+	wormholeEvents := 0
+	for _, event := range tx.Events {
+		// Skip events that don't match our criteria
+		if !event.Committed ||
+			event.Type != "contract_event" ||
+			event.ContractEvent == nil ||
+			event.ContractEvent.ContractIdentifier != w.stateContract ||
+			event.ContractEvent.Topic != "print" {
+			continue
+		}
+
+		logger.Info("Found Wormhole message event",
+			zap.String("tx_id", tx.TxId),
+			zap.Uint64("event_index", event.EventIndex))
+
+		hexStr := strings.TrimPrefix(event.ContractEvent.RawValue, "0x")
+		hexBytes, err := hex.DecodeString(hexStr)
+		if err != nil {
+			logger.Error("Failed to decode raw value hex",
+				zap.String("tx_id", tx.TxId),
+				zap.Uint64("event_index", event.EventIndex),
+				zap.String("hex", event.ContractEvent.RawValue),
+				zap.Error(err))
+			continue
+		}
+
+		clarityValue, err := DecodeClarityValue(bytes.NewReader(hexBytes))
+		if err != nil {
+			logger.Error("Failed to decode clarity value",
+				zap.String("tx_id", tx.TxId),
+				zap.Uint64("event_index", event.EventIndex),
+				zap.Error(err))
+			continue
+		}
+
+		logger.Debug("Decoded clarity value",
+			zap.String("tx_id", tx.TxId),
+			zap.Uint64("event_index", event.EventIndex),
+			zap.String("type", fmt.Sprintf("%T", clarityValue)))
+
+		// Process the core event
+		if err := w.processCoreEvent(clarityValue, tx.TxId, replay.Timestamp, isReobservation); err == nil {
+			wormholeEvents++
+		} else {
+			logger.Error("Failed to process core event",
+				zap.String("tx_id", tx.TxId),
+				zap.Uint64("event_index", event.EventIndex),
+				zap.Error(err))
+			// Continue processing other events even if one fails
+		}
+	}
+
+	logger.Info("Finished processing transaction events",
+		zap.String("tx_id", tx.TxId),
+		zap.Int("wormhole_events_processed", wormholeEvents))
+
+	return nil
+}
+
+// Processes a single transaction by its txid (used for reobservations)
+func (w *Watcher) reobserveStacksTransactionByTxId(ctx context.Context, txId string, logger *zap.Logger) error {
+	logger.Info("Processing transaction by txid", zap.String("tx_id", txId))
+
+	transaction, err := w.fetchStacksTransactionByTxId(ctx, txId)
+	if err != nil {
+		return fmt.Errorf("failed to fetch transaction: %w", err)
+	}
+
+	replay, err := w.fetchStacksBlockReplay(ctx, transaction.IndexBlockHash)
+	if err != nil {
+		return fmt.Errorf("failed to fetch block replay: %w", err)
+	}
+
+	stableBitcoinBlockHeight := w.stableBitcoinHeight.Load()
+	if replay.BlockHeight > stableBitcoinBlockHeight {
+		return fmt.Errorf("block replay height %d is greater than stable Bitcoin (burn) block height %d", replay.BlockHeight, stableBitcoinBlockHeight)
+	}
+
+	var tx *StacksV3TenureBlockTransaction
+	for i := range replay.Transactions {
+		if replay.Transactions[i].TxId == txId {
+			tx = &replay.Transactions[i]
+			break
+		}
+	}
+
+	if tx == nil {
+		return fmt.Errorf("transaction %s not found in block replay", txId)
+	}
+
+	// Process the transaction using the same processing function used in polling
+	if err := w.processStacksTransaction(ctx, tx, replay, true, logger); err != nil {
+		return fmt.Errorf("failed to process transaction: %w", err)
+	}
+
+	logger.Info("Successfully processed transaction for reobservation",
+		zap.String("tx_id", txId))
+
+	return nil
+}
+
+// Processes a core contract event tuple and extracts message fields
+func (w *Watcher) processCoreEvent(clarityValue ClarityValue, txId string, timestamp uint64, isReobservation bool) error {
+	// Cast to tuple
+	eventTuple, isTuple := clarityValue.(*Tuple)
+	if !isTuple {
+		return fmt.Errorf("expected tuple type but got %T", clarityValue)
+	}
+
+	// Extract the event name
+	eventName, err := extractEventName(eventTuple)
+	if err != nil {
+		return fmt.Errorf("failed to extract event name: %w", err)
+	}
+
+	// Check if this is a post-message event
+	if eventName != "post-message" {
+		return fmt.Errorf("expected 'post-message' event but got '%s'", eventName)
+	}
+
+	// Extract the core message fields
+	msgData, err := extractMessageData(eventTuple)
+	if err != nil {
+		return fmt.Errorf("failed to extract message data: %w", err)
+	}
+
+	// Convert txId to bytes
+	txIdBytes, err := hex.DecodeString(strings.TrimPrefix(txId, "0x"))
+	if err != nil {
+		return fmt.Errorf("failed to decode transaction ID hex: %w", err)
+	}
+
+	// Convert timestamp to int64 with overflow check
+	if timestamp > maxInt64 {
+		return fmt.Errorf("timestamp %d exceeds maximum int64 value", timestamp)
+	}
+
+	// Create the complete MessagePublication
+	msgPub := &common.MessagePublication{
+		TxID:             txIdBytes,
+		Timestamp:        time.Unix(int64(timestamp), 0), // #nosec G115 -- checked above
+		EmitterChain:     vaa.ChainIDStacks,
+		EmitterAddress:   msgData.EmitterAddress,
+		ConsistencyLevel: msgData.ConsistencyLevel,
+		Nonce:            msgData.Nonce,
+		Payload:          msgData.Payload,
+		Sequence:         msgData.Sequence,
+		IsReobservation:  isReobservation,
+	}
+
+	// Submit the message to the channel for processing
+	w.msgC <- msgPub
+
+	return nil
+}
+
+/// HELPERS
+
+func isTransactionResultCommitted(result map[string]interface{}) bool {
+	if result == nil {
+		return false
+	}
+
+	response, parsed := result["Response"].(map[string]interface{})
+	if !parsed {
+		return false
+	}
+
+	committed, parsed := response["committed"].(bool)
+	return parsed && committed
+}
+
+// Extracts the event name from an event tuple
+func extractEventName(eventTuple *Tuple) (string, error) {
+	eventNameVal, ok := eventTuple.Values["event"]
+	if !ok {
+		return "", fmt.Errorf("missing 'event' field in tuple")
+	}
+
+	// Check if event is a StringASCII or StringUTF8
+	var eventName string
+	if strVal, ok := eventNameVal.(*StringASCII); ok {
+		eventName = strVal.Value
+	} else if strVal, ok := eventNameVal.(*StringUTF8); ok {
+		eventName = strVal.Value
+	} else {
+		return "", fmt.Errorf("'event' field is not a string type: %T", eventNameVal)
+	}
+
+	return eventName, nil
+}
+
+// Extracts core message fields from an event tuple
+func extractMessageData(eventTuple *Tuple) (*MessageData, error) {
+	// Get the data field which should contain the message
+	dataVal, ok := eventTuple.Values["data"]
+	if !ok {
+		return nil, fmt.Errorf("missing 'data' field in tuple")
+	}
+
+	// Cast data to tuple
+	msgTuple, ok := dataVal.(*Tuple)
+	if !ok {
+		return nil, fmt.Errorf("'data' field is not a tuple: %T", dataVal)
+	}
+
+	// Extract message fields
+	emitterVal, ok := msgTuple.Values["emitter"]
+	if !ok {
+		return nil, fmt.Errorf("missing 'emitter' field in message")
+	}
+
+	emitterBuffer, ok := emitterVal.(*ClarityBuffer)
+	if !ok || emitterBuffer.Length != 32 {
+		return nil, fmt.Errorf("'emitter' field is not a 32-byte buffer: %T", emitterVal)
+	}
+
+	// Convert buffer to wormhole address
+	emitterAddr := vaa.Address{}
+	copy(emitterAddr[:], emitterBuffer.Data[:])
+
+	nonceVal, ok := msgTuple.Values["nonce"]
+	if !ok {
+		return nil, fmt.Errorf("missing 'nonce' field in message")
+	}
+
+	nonceUint, ok := nonceVal.(*UInt128)
+	if !ok || nonceUint.Value.Cmp(maxUint32BigInt) > 0 {
+		return nil, fmt.Errorf("invalid 'nonce' field: %T", nonceVal)
+	}
+
+	sequenceVal, ok := msgTuple.Values["sequence"]
+	if !ok {
+		return nil, fmt.Errorf("missing 'sequence' field in message")
+	}
+
+	sequenceUint, ok := sequenceVal.(*UInt128)
+	if !ok || sequenceUint.Value.Cmp(maxUint64BigInt) > 0 {
+		return nil, fmt.Errorf("invalid 'sequence' field: %T", sequenceVal)
+	}
+
+	consistencyLevelVal, ok := msgTuple.Values["consistency-level"]
+	if !ok {
+		return nil, fmt.Errorf("missing 'consistency-level' field in message")
+	}
+
+	consistencyLevelUint, ok := consistencyLevelVal.(*UInt128)
+	if !ok || consistencyLevelUint.Value.Cmp(maxUint8BigInt) > 0 {
+		return nil, fmt.Errorf("invalid 'consistency-level' field: %T", consistencyLevelVal)
+	}
+
+	payloadVal, ok := msgTuple.Values["payload"]
+	if !ok {
+		return nil, fmt.Errorf("missing 'payload' field in message")
+	}
+
+	payload, ok := payloadVal.(*ClarityBuffer)
+	if !ok || payload.Length > 8192 {
+		return nil, fmt.Errorf("invalid 'payload' field: %T", payloadVal)
+	}
+
+	// Extract values with safe conversions (already validated above against max values)
+	nonceValue := nonceUint.Value.Uint64()
+	consistencyLevelValue := consistencyLevelUint.Value.Uint64()
+
+	// Return just the core message fields
+	return &MessageData{
+		EmitterAddress:   emitterAddr,
+		Nonce:            uint32(nonceValue), // #nosec G115 -- validated against maxUint32BigInt above
+		Sequence:         sequenceUint.Value.Uint64(),
+		ConsistencyLevel: uint8(consistencyLevelValue), // #nosec G115 -- validated against maxUint8BigInt above
+		Payload:          payload.Data,
+	}, nil
+}

+ 1 - 0
stacks/.gitignore

@@ -0,0 +1 @@
+dist

+ 28 - 0
stacks/Dockerfile

@@ -0,0 +1,28 @@
+FROM rust@sha256:234b03300ecaae65919eb8cb13ca9b36588b8dff5b1c69581e361f9f25654db1 AS builder
+
+ARG STACKS_CORE_REPO=https://github.com/stacks-network/stacks-core.git
+ARG STACKS_CORE_BASE_BRANCH=develop
+
+RUN echo "Building Stacks Core from branch: $STACKS_CORE_BASE_BRANCH"
+
+RUN git clone --branch $STACKS_CORE_BASE_BRANCH --single-branch --depth=1 $STACKS_CORE_REPO /code/stacks-core
+WORKDIR /code/stacks-core
+RUN apt-get update && apt-get install -y git libclang-dev llvm
+
+RUN cargo build --features monitoring_prom,slog_json --bin stacks-node --bin stacks-signer
+
+FROM debian@sha256:936abff852736f951dab72d91a1b6337cf04217b2a77a5eaadc7c0f2f1ec1758 AS stacks-node
+RUN apt-get update \
+    && apt-get install -y ca-certificates --no-install-recommends \
+    && apt-get clean && rm -rf /var/lib/apt/lists/*
+RUN mkdir /data
+COPY --from=builder /code/stacks-core/target/debug/stacks-node /usr/local/bin/stacks-node
+STOPSIGNAL SIGTERM
+
+FROM debian@sha256:936abff852736f951dab72d91a1b6337cf04217b2a77a5eaadc7c0f2f1ec1758 AS stacks-signer
+RUN apt-get update \
+    && apt-get install -y ca-certificates --no-install-recommends \
+    && apt-get clean && rm -rf /var/lib/apt/lists/*
+RUN mkdir /data
+COPY --from=builder /code/stacks-core/target/debug/stacks-signer /usr/local/bin/stacks-signer
+STOPSIGNAL SIGTERM

+ 28 - 0
stacks/README.md

@@ -0,0 +1,28 @@
+# Stacks
+
+## High-level architecture
+
+The Stacks watcher is a component that monitors the Stacks blockchain and processes transactions to find Wormhole message publication events.
+
+The Stacks watcher uses a polling-based approach to monitor the Stacks blockchain:
+
+- **Bitcoin Block Anchoring**: Uses Bitcoin blocks (burn blocks) as the anchor point for confirmation.
+- **BlockPoller**: Polls the Stacks RPC API for new Bitcoin blocks every 2 seconds.
+- **Confirmation**: Considers a block final after 6 Bitcoin block confirmations.
+- **ObsvReqProcessor**: Processes observation requests to re-observe specific transactions.
+
+## Processing Flow
+
+The watcher maintains two key tracking points:
+
+- Latest Bitcoin block height seen
+- Last processed Bitcoin block height
+
+When new blocks are found, it:
+
+1. Polls for new Bitcoin (burn) blocks
+2. Processes Bitcoin blocks that have reached sufficient confirmation (6 blocks)
+3. Fetches all Stacks blocks anchored to those Bitcoin blocks
+4. Processes transactions in those Stacks blocks
+5. Examines events in those transactions to find Wormhole message publication events
+6. Extracts message data and creates MessagePublication objects

+ 13 - 0
stacks/broadcaster/Dockerfile

@@ -0,0 +1,13 @@
+FROM node@sha256:101246ec9f5f803f58d6c1eb1d40477c7a99049718a5849ae78229cc6f9198b2
+
+WORKDIR /app
+
+# Copy package.json and install dependencies
+COPY package.json package-lock.json ./
+RUN npm ci
+
+# Copy application source files
+COPY tx-broadcaster.ts common.ts ./
+
+# Default command - runs the transaction broadcaster
+CMD ["npx", "tsx", "tx-broadcaster.ts"]

+ 135 - 0
stacks/broadcaster/common.ts

@@ -0,0 +1,135 @@
+import { StackingClient } from '@stacks/stacking';
+import { StacksTestnet } from '@stacks/network';
+import {
+  getAddressFromPrivateKey,
+  TransactionVersion,
+  createStacksPrivateKey,
+  StacksPrivateKey,
+} from '@stacks/transactions';
+import { getPublicKeyFromPrivate, publicKeyToBtcAddress } from '@stacks/encryption';
+import {
+  InfoApi,
+  Configuration,
+  BlocksApi,
+  TransactionsApi,
+  SmartContractsApi,
+  AccountsApi,
+} from '@stacks/blockchain-api-client';
+import pino, { Logger } from 'pino';
+
+const serviceName = process.env.SERVICE_NAME || 'JS';
+export let logger: Logger;
+if (process.env.STACKS_LOG_JSON === '1') {
+  logger = pino({
+    level: process.env.LOG_LEVEL || 'info',
+    name: serviceName,
+  });
+} else {
+  logger = pino({
+    name: serviceName,
+    level: process.env.LOG_LEVEL || 'info',
+    transport: {
+      target: 'pino-pretty',
+    },
+    // @ts-ignore
+    options: {
+      colorize: true,
+    },
+  });
+}
+
+export const nodeUrl = `http://${process.env.STACKS_CORE_RPC_HOST}:${process.env.STACKS_CORE_RPC_PORT}`;
+export const network = new StacksTestnet({ url: nodeUrl });
+const apiConfig = new Configuration({
+  basePath: nodeUrl,
+});
+export const infoApi = new InfoApi(apiConfig);
+export const blocksApi = new BlocksApi(apiConfig);
+export const txApi = new TransactionsApi(apiConfig);
+export const contractsApi = new SmartContractsApi(apiConfig);
+export const accountsApi = new AccountsApi(apiConfig);
+
+export const EPOCH_30_START = parseEnvInt('STACKS_30_HEIGHT', true);
+export const EPOCH_25_START = parseEnvInt('STACKS_25_HEIGHT', true);
+export const POX_PREPARE_LENGTH = parseEnvInt('POX_PREPARE_LENGTH', true);
+export const POX_REWARD_LENGTH = parseEnvInt('POX_REWARD_LENGTH', true);
+
+export type Account = {
+  privKey: string;
+  pubKey: string;
+  stxAddress: string;
+  btcAddr: string;
+  signerPrivKey: StacksPrivateKey;
+  signerPubKey: string;
+  targetSlots: number;
+  index: number;
+  client: StackingClient;
+  logger: Logger;
+};
+
+export const getAccounts = (stackingKeys: string[], stackingSlotDistribution: number[]) =>
+  stackingKeys.map((privKey, index) => {
+    const pubKey = getPublicKeyFromPrivate(privKey);
+    const stxAddress = getAddressFromPrivateKey(privKey, TransactionVersion.Testnet);
+    const signerPrivKey = createStacksPrivateKey(privKey);
+    const signerPubKey = getPublicKeyFromPrivate(signerPrivKey.data);
+    return {
+      privKey,
+      pubKey,
+      stxAddress,
+      btcAddr: publicKeyToBtcAddress(pubKey),
+      signerPrivKey: signerPrivKey,
+      signerPubKey: signerPubKey,
+      targetSlots: stackingSlotDistribution[index]!,
+      index,
+      client: new StackingClient(stxAddress, network),
+      logger: logger.child({
+        account: stxAddress,
+        index: index,
+      }),
+    };
+  });
+
+export const MAX_U128 = 2n ** 128n - 1n;
+export const maxAmount = MAX_U128;
+
+export async function waitForSetup(stackingKeys: string[], stackingSlotDistribution: number[]) {
+  try {
+    await getAccounts(stackingKeys, stackingSlotDistribution)[0].client.getPoxInfo();
+  } catch (error) {
+    if (/(ECONNREFUSED|ENOTFOUND|SyntaxError)/.test(error.cause?.message)) {
+      console.log(`Stacks node not ready, waiting...`);
+    }
+    await new Promise(resolve => setTimeout(resolve, 3000));
+    return waitForSetup(stackingKeys, stackingSlotDistribution);
+  }
+}
+
+export function parseEnvInt<T extends boolean = false>(
+  envKey: string,
+  required?: T
+): T extends true ? number : number | undefined {
+  let value = process.env[envKey];
+  if (typeof value === 'undefined') {
+    if (required) {
+      throw new Error(`Missing required env var: ${envKey}`);
+    }
+    return undefined as T extends true ? number : number | undefined;
+  }
+  return parseInt(value, 10);
+}
+
+export function burnBlockToRewardCycle(burnBlock: number) {
+  const cycleLength = BigInt(POX_REWARD_LENGTH);
+  return Number(BigInt(burnBlock) / cycleLength) + 1;
+}
+
+export const EPOCH_30_START_CYCLE = burnBlockToRewardCycle(EPOCH_30_START);
+
+export function isPreparePhase(burnBlock: number) {
+  return POX_REWARD_LENGTH - (burnBlock % POX_REWARD_LENGTH) < POX_PREPARE_LENGTH;
+}
+
+export function didCrossPreparePhase(lastBurnHeight: number, newBurnHeight: number) {
+  return isPreparePhase(newBurnHeight) && !isPreparePhase(lastBurnHeight);
+}

+ 1367 - 0
stacks/broadcaster/package-lock.json

@@ -0,0 +1,1367 @@
+{
+  "name": "broadcaster",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "broadcaster",
+      "version": "1.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "@stacks/blockchain-api-client": "7.8.2",
+        "@stacks/common": "6.11.4-pr.36558cf.0",
+        "@stacks/encryption": "6.11.4-pr.36558cf.0",
+        "@stacks/network": "6.11.4-pr.36558cf.0",
+        "@stacks/stacking": "6.11.4-pr.36558cf.0",
+        "@stacks/transactions": "6.11.4-pr.36558cf.0",
+        "pino": "^8.19.0",
+        "pino-pretty": "^10.3.1"
+      },
+      "devDependencies": {
+        "@stacks/prettier-config": "^0.0.10",
+        "tsx": "4.7.1"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
+      "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
+      "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
+      "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
+      "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
+      "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
+      "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
+      "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
+      "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
+      "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
+      "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
+      "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
+      "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
+      "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
+      "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
+      "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
+      "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
+      "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
+      "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
+      "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
+      "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
+      "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
+      "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
+      "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@noble/hashes": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.5.tgz",
+      "integrity": "sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/@noble/secp256k1": {
+      "version": "1.7.1",
+      "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz",
+      "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/@scure/base": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz",
+      "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@scure/bip39": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz",
+      "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "~1.1.1",
+        "@scure/base": "~1.1.0"
+      }
+    },
+    "node_modules/@socket.io/component-emitter": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+      "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
+      "license": "MIT"
+    },
+    "node_modules/@stacks/blockchain-api-client": {
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/@stacks/blockchain-api-client/-/blockchain-api-client-7.8.2.tgz",
+      "integrity": "sha512-yGPFJTnuZ89S5tOCx9Dook6A4ghzR7RDTE3zpcfgae2wAenN2LPgQYGGNONKeLuxKl1giPiiT0ywv/MYR60YnA==",
+      "license": "GPL-3.0",
+      "dependencies": {
+        "@stacks/stacks-blockchain-api-types": "*",
+        "@types/ws": "7.4.7",
+        "cross-fetch": "3.1.5",
+        "eventemitter3": "4.0.7",
+        "jsonrpc-lite": "2.2.0",
+        "socket.io-client": "4.7.3",
+        "ws": "8.16.0"
+      }
+    },
+    "node_modules/@stacks/common": {
+      "version": "6.11.4-pr.36558cf.0",
+      "resolved": "https://registry.npmjs.org/@stacks/common/-/common-6.11.4-pr.36558cf.0.tgz",
+      "integrity": "sha512-xusHrEty66bLiKFB8AoB3mskHsbmsQ99PJe6jAwbrOYCMYsi3rmUBaSd2Puhg/Bjw4DY+T5xLJeZXBWDytP9xw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/bn.js": "^5.1.0",
+        "@types/node": "^18.0.4"
+      }
+    },
+    "node_modules/@stacks/encryption": {
+      "version": "6.11.4-pr.36558cf.0",
+      "resolved": "https://registry.npmjs.org/@stacks/encryption/-/encryption-6.11.4-pr.36558cf.0.tgz",
+      "integrity": "sha512-rBeLtfu0HxqZpSb29iLXfa0MJQM0FX4wP9c11iJCNt+Wkld53cDhcmxA5PMngGQYKws0fTFT3ZDsHtFpTdi4Xg==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "1.1.5",
+        "@noble/secp256k1": "1.7.1",
+        "@scure/bip39": "1.1.0",
+        "@stacks/common": "^6.11.4-pr.36558cf.0",
+        "@types/node": "^18.0.4",
+        "base64-js": "^1.5.1",
+        "bs58": "^5.0.0",
+        "ripemd160-min": "^0.0.6",
+        "varuint-bitcoin": "^1.1.2"
+      }
+    },
+    "node_modules/@stacks/network": {
+      "version": "6.11.4-pr.36558cf.0",
+      "resolved": "https://registry.npmjs.org/@stacks/network/-/network-6.11.4-pr.36558cf.0.tgz",
+      "integrity": "sha512-liLhuUlqx0EqydAEgf7trWt90QiDwRsZQHqgaJPzCDWiN3I4yoN6gpT0jQ2CUNL49NRDZWK1X3XUYDKx2w/0yw==",
+      "license": "MIT",
+      "dependencies": {
+        "@stacks/common": "^6.11.4-pr.36558cf.0",
+        "cross-fetch": "^3.1.5"
+      }
+    },
+    "node_modules/@stacks/prettier-config": {
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/@stacks/prettier-config/-/prettier-config-0.0.10.tgz",
+      "integrity": "sha512-MrYWGEgO/mYR8TOZIKknQEHbFQZ5VyAD/s8eF2Yxr6Lgalt2alVEh+6ODehVP2uepkyXPmJzLbaQYs8/L4E78Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prettier": "2.5"
+      }
+    },
+    "node_modules/@stacks/stacking": {
+      "version": "6.11.4-pr.36558cf.0",
+      "resolved": "https://registry.npmjs.org/@stacks/stacking/-/stacking-6.11.4-pr.36558cf.0.tgz",
+      "integrity": "sha512-BK3da72woKdqwrSi5KrQrp7pfEBSrJKDE/gn/yt5RRkYcX38jbUoNauJqbNCHaPgKdPlYVzYnmtbe4f5YUQ+Ng==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "1.1.5",
+        "@scure/base": "1.1.1",
+        "@stacks/common": "^6.11.4-pr.36558cf.0",
+        "@stacks/encryption": "^6.11.4-pr.36558cf.0",
+        "@stacks/network": "^6.11.4-pr.36558cf.0",
+        "@stacks/stacks-blockchain-api-types": "^0.61.0",
+        "@stacks/transactions": "^6.11.4-pr.36558cf.0",
+        "bs58": "^5.0.0"
+      }
+    },
+    "node_modules/@stacks/stacking/node_modules/@scure/base": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
+      "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/@stacks/stacking/node_modules/@stacks/stacks-blockchain-api-types": {
+      "version": "0.61.0",
+      "resolved": "https://registry.npmjs.org/@stacks/stacks-blockchain-api-types/-/stacks-blockchain-api-types-0.61.0.tgz",
+      "integrity": "sha512-yPOfTUboo5eA9BZL/hqMcM71GstrFs9YWzOrJFPeP4cOO1wgYvAcckgBRbgiE3NqeX0A7SLZLDAXLZbATuRq9w==",
+      "license": "ISC"
+    },
+    "node_modules/@stacks/stacks-blockchain-api-types": {
+      "version": "7.14.1",
+      "resolved": "https://registry.npmjs.org/@stacks/stacks-blockchain-api-types/-/stacks-blockchain-api-types-7.14.1.tgz",
+      "integrity": "sha512-65hvhXxC+EUqHJAQsqlBCqXB+zwfxZICSKYJugdg6BCp9I9qniyfz5XyQeC4RMVo0tgEoRdS/b5ZCFo5kLWmxA==",
+      "license": "ISC"
+    },
+    "node_modules/@stacks/transactions": {
+      "version": "6.11.4-pr.36558cf.0",
+      "resolved": "https://registry.npmjs.org/@stacks/transactions/-/transactions-6.11.4-pr.36558cf.0.tgz",
+      "integrity": "sha512-l9Yoiks/+P5TGgXZELuCebS1YG3jBaStrbZydhb15nstCtX3OvU/GTfq6XVuy/K4TlMbeb6vrlV+t6gSMxcDSA==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "1.1.5",
+        "@noble/secp256k1": "1.7.1",
+        "@stacks/common": "^6.11.4-pr.36558cf.0",
+        "@stacks/network": "^6.11.4-pr.36558cf.0",
+        "c32check": "^2.0.0",
+        "lodash.clonedeep": "^4.5.0"
+      }
+    },
+    "node_modules/@types/bn.js": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz",
+      "integrity": "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/node": {
+      "version": "18.19.130",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz",
+      "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~5.26.4"
+      }
+    },
+    "node_modules/@types/ws": {
+      "version": "7.4.7",
+      "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz",
+      "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/abort-controller": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+      "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+      "license": "MIT",
+      "dependencies": {
+        "event-target-shim": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=6.5"
+      }
+    },
+    "node_modules/atomic-sleep": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
+      "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/base-x": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.1.tgz",
+      "integrity": "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==",
+      "license": "MIT"
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "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/bs58": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz",
+      "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==",
+      "license": "MIT",
+      "dependencies": {
+        "base-x": "^4.0.0"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "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",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
+    "node_modules/c32check": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/c32check/-/c32check-2.0.0.tgz",
+      "integrity": "sha512-rpwfAcS/CMqo0oCqDf3r9eeLgScRE3l/xHDCXhM3UyrfvIn7PrLq63uHh7yYbv8NzaZn5MVsVhIRpQ+5GZ5HyA==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "^1.1.2",
+        "base-x": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/colorette": {
+      "version": "2.0.20",
+      "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+      "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+      "license": "MIT"
+    },
+    "node_modules/cross-fetch": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
+      "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
+      "license": "MIT",
+      "dependencies": {
+        "node-fetch": "2.6.7"
+      }
+    },
+    "node_modules/dateformat": {
+      "version": "4.6.3",
+      "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
+      "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/end-of-stream": {
+      "version": "1.4.5",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+      "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+      "license": "MIT",
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/engine.io-client": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz",
+      "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.1",
+        "engine.io-parser": "~5.2.1",
+        "ws": "~8.17.1",
+        "xmlhttprequest-ssl": "~2.0.0"
+      }
+    },
+    "node_modules/engine.io-client/node_modules/ws": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+      "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/engine.io-parser": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+      "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
+      "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.19.12",
+        "@esbuild/android-arm": "0.19.12",
+        "@esbuild/android-arm64": "0.19.12",
+        "@esbuild/android-x64": "0.19.12",
+        "@esbuild/darwin-arm64": "0.19.12",
+        "@esbuild/darwin-x64": "0.19.12",
+        "@esbuild/freebsd-arm64": "0.19.12",
+        "@esbuild/freebsd-x64": "0.19.12",
+        "@esbuild/linux-arm": "0.19.12",
+        "@esbuild/linux-arm64": "0.19.12",
+        "@esbuild/linux-ia32": "0.19.12",
+        "@esbuild/linux-loong64": "0.19.12",
+        "@esbuild/linux-mips64el": "0.19.12",
+        "@esbuild/linux-ppc64": "0.19.12",
+        "@esbuild/linux-riscv64": "0.19.12",
+        "@esbuild/linux-s390x": "0.19.12",
+        "@esbuild/linux-x64": "0.19.12",
+        "@esbuild/netbsd-x64": "0.19.12",
+        "@esbuild/openbsd-x64": "0.19.12",
+        "@esbuild/sunos-x64": "0.19.12",
+        "@esbuild/win32-arm64": "0.19.12",
+        "@esbuild/win32-ia32": "0.19.12",
+        "@esbuild/win32-x64": "0.19.12"
+      }
+    },
+    "node_modules/event-target-shim": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+      "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/eventemitter3": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+      "license": "MIT"
+    },
+    "node_modules/events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.x"
+      }
+    },
+    "node_modules/fast-copy": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz",
+      "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==",
+      "license": "MIT"
+    },
+    "node_modules/fast-redact": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
+      "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/fast-safe-stringify": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+      "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
+      "license": "MIT"
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/get-tsconfig": {
+      "version": "4.13.0",
+      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
+      "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "resolve-pkg-maps": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+      }
+    },
+    "node_modules/help-me": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
+      "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
+      "license": "MIT"
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "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": "BSD-3-Clause"
+    },
+    "node_modules/joycon": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
+      "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/jsonrpc-lite": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/jsonrpc-lite/-/jsonrpc-lite-2.2.0.tgz",
+      "integrity": "sha512-/cbbSxtZWs1O7R4tWqabrCM/t3N8qKUZMAg9IUqpPvUs6UyRvm6pCNYkskyKN/XU0UgffW+NY2ZRr8t0AknX7g==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.clonedeep": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+      "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
+      "license": "MIT"
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/node-fetch": {
+      "version": "2.6.7",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+      "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/on-exit-leak-free": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
+      "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "license": "ISC",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/pino": {
+      "version": "8.21.0",
+      "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz",
+      "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==",
+      "license": "MIT",
+      "dependencies": {
+        "atomic-sleep": "^1.0.0",
+        "fast-redact": "^3.1.1",
+        "on-exit-leak-free": "^2.1.0",
+        "pino-abstract-transport": "^1.2.0",
+        "pino-std-serializers": "^6.0.0",
+        "process-warning": "^3.0.0",
+        "quick-format-unescaped": "^4.0.3",
+        "real-require": "^0.2.0",
+        "safe-stable-stringify": "^2.3.1",
+        "sonic-boom": "^3.7.0",
+        "thread-stream": "^2.6.0"
+      },
+      "bin": {
+        "pino": "bin.js"
+      }
+    },
+    "node_modules/pino-abstract-transport": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz",
+      "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==",
+      "license": "MIT",
+      "dependencies": {
+        "readable-stream": "^4.0.0",
+        "split2": "^4.0.0"
+      }
+    },
+    "node_modules/pino-pretty": {
+      "version": "10.3.1",
+      "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.3.1.tgz",
+      "integrity": "sha512-az8JbIYeN/1iLj2t0jR9DV48/LQ3RC6hZPpapKPkb84Q+yTidMCpgWxIT3N0flnBDilyBQ1luWNpOeJptjdp/g==",
+      "license": "MIT",
+      "dependencies": {
+        "colorette": "^2.0.7",
+        "dateformat": "^4.6.3",
+        "fast-copy": "^3.0.0",
+        "fast-safe-stringify": "^2.1.1",
+        "help-me": "^5.0.0",
+        "joycon": "^3.1.1",
+        "minimist": "^1.2.6",
+        "on-exit-leak-free": "^2.1.0",
+        "pino-abstract-transport": "^1.0.0",
+        "pump": "^3.0.0",
+        "readable-stream": "^4.0.0",
+        "secure-json-parse": "^2.4.0",
+        "sonic-boom": "^3.0.0",
+        "strip-json-comments": "^3.1.1"
+      },
+      "bin": {
+        "pino-pretty": "bin.js"
+      }
+    },
+    "node_modules/pino-std-serializers": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz",
+      "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==",
+      "license": "MIT"
+    },
+    "node_modules/prettier": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
+      "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "prettier": "bin-prettier.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
+    "node_modules/process-warning": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz",
+      "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==",
+      "license": "MIT"
+    },
+    "node_modules/pump": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
+      "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+      "license": "MIT",
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "node_modules/quick-format-unescaped": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
+      "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
+      "license": "MIT"
+    },
+    "node_modules/readable-stream": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+      "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+      "license": "MIT",
+      "dependencies": {
+        "abort-controller": "^3.0.0",
+        "buffer": "^6.0.3",
+        "events": "^3.3.0",
+        "process": "^0.11.10",
+        "string_decoder": "^1.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/real-require": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
+      "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12.13.0"
+      }
+    },
+    "node_modules/resolve-pkg-maps": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+      "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+      }
+    },
+    "node_modules/ripemd160-min": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz",
+      "integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "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/safe-stable-stringify": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+      "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/secure-json-parse": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
+      "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/socket.io-client": {
+      "version": "4.7.3",
+      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.3.tgz",
+      "integrity": "sha512-nU+ywttCyBitXIl9Xe0RSEfek4LneYkJxCeNnKCuhwoH4jGXO1ipIUw/VA/+Vvv2G1MTym11fzFC0SxkrcfXDw==",
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.2",
+        "engine.io-client": "~6.5.2",
+        "socket.io-parser": "~4.2.4"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/socket.io-parser": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
+      "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.1"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/sonic-boom": {
+      "version": "3.8.1",
+      "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz",
+      "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==",
+      "license": "MIT",
+      "dependencies": {
+        "atomic-sleep": "^1.0.0"
+      }
+    },
+    "node_modules/split2": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+      "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">= 10.x"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/thread-stream": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz",
+      "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==",
+      "license": "MIT",
+      "dependencies": {
+        "real-require": "^0.2.0"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+      "license": "MIT"
+    },
+    "node_modules/tsx": {
+      "version": "4.7.1",
+      "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.1.tgz",
+      "integrity": "sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "~0.19.10",
+        "get-tsconfig": "^4.7.2"
+      },
+      "bin": {
+        "tsx": "dist/cli.mjs"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "5.26.5",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+      "license": "MIT"
+    },
+    "node_modules/varuint-bitcoin": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz",
+      "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "^5.1.1"
+      }
+    },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "license": "ISC"
+    },
+    "node_modules/ws": {
+      "version": "8.16.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
+      "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/xmlhttprequest-ssl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
+      "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    }
+  }
+}

+ 25 - 0
stacks/broadcaster/package.json

@@ -0,0 +1,25 @@
+{
+  "name": "@wormhole-foundation/stacks-broadcaster",
+  "private": true,
+  "version": "1.0.0",
+  "description": "",
+  "type": "module",
+  "keywords": [],
+  "author": "",
+  "license": "MIT",
+  "dependencies": {
+    "@stacks/blockchain-api-client": "7.8.2",
+    "@stacks/common": "6.11.4-pr.36558cf.0",
+    "@stacks/encryption": "6.11.4-pr.36558cf.0",
+    "@stacks/network": "6.11.4-pr.36558cf.0",
+    "@stacks/stacking": "6.11.4-pr.36558cf.0",
+    "@stacks/transactions": "6.11.4-pr.36558cf.0",
+    "pino": "^8.19.0",
+    "pino-pretty": "^10.3.1"
+  },
+  "devDependencies": {
+    "@stacks/prettier-config": "^0.0.10",
+    "tsx": "4.7.1"
+  },
+  "prettier": "@stacks/prettier-config"
+}

+ 133 - 0
stacks/broadcaster/tx-broadcaster.ts

@@ -0,0 +1,133 @@
+import { StacksTestnet } from '@stacks/network';
+import { StackingClient } from '@stacks/stacking';
+import {
+  TransactionVersion,
+  getAddressFromPrivateKey,
+  getNonce,
+  makeSTXTokenTransfer,
+  broadcastTransaction,
+  StacksTransaction,
+} from '@stacks/transactions';
+import { writeFileSync } from 'fs';
+import { logger } from './common';
+
+const broadcastInterval = parseInt(process.env.NAKAMOTO_BLOCK_INTERVAL ?? '2');
+const url = `http://${process.env.STACKS_CORE_RPC_HOST}:${process.env.STACKS_CORE_RPC_PORT}`;
+const network = new StacksTestnet({ url });
+const EPOCH_30_START = parseInt(process.env.STACKS_30_HEIGHT ?? '0');
+const PAUSE_HEIGHT = parseInt(process.env.PAUSE_HEIGHT ?? '999999999999');
+const PAUSE_TIMER = parseInt(process.env.PAUSE_TIMER ?? '86400000');
+
+const accounts = process.env.ACCOUNT_KEYS!.split(',').map(privKey => ({
+  privKey,
+  stxAddress: getAddressFromPrivateKey(privKey, TransactionVersion.Testnet),
+}));
+
+const client = new StackingClient(accounts[0].stxAddress, network);
+
+async function run() {
+  const poxInfo = await client.getPoxInfo();
+  if (poxInfo.current_burnchain_block_height == PAUSE_HEIGHT) {
+    logger.info(
+      `Pause height reached : (current=${poxInfo.current_burnchain_block_height}), (pause=${PAUSE_HEIGHT})`
+    );
+    logger.info(
+      `sleeping for ${PAUSE_TIMER}`
+    )
+    await new Promise(resolve => setTimeout(resolve, PAUSE_TIMER));
+  }
+  const accountNonces = await Promise.all(
+    accounts.map(async account => {
+      const nonce = await getNonce(account.stxAddress, network);
+      return { ...account, nonce };
+    })
+  );
+
+  // Send from account with lowest nonce
+  accountNonces.sort((a, b) => Number(a.nonce) - Number(b.nonce));
+  const sender = accountNonces[0];
+  const recipient = accountNonces[1];
+
+  logger.info(
+    `Sending stx-transfer from ${sender.stxAddress} (nonce=${sender.nonce}) to ${recipient.stxAddress}`
+  );
+
+  const tx = await makeSTXTokenTransfer({
+    recipient: recipient.stxAddress,
+    amount: 1000,
+    senderKey: sender.privKey,
+    network,
+    nonce: sender.nonce,
+    fee: 300,
+    anchorMode: 'any',
+  });
+  await broadcast(tx, sender.stxAddress);
+}
+
+async function broadcast(tx: StacksTransaction, sender?: string) {
+  const txType = tx.payload.payloadType;
+  const label = sender ? accountLabel(sender) : 'Unknown';
+  const broadcastResult = await broadcastTransaction(tx, network);
+  if (broadcastResult.error) {
+    logger.error({ ...broadcastResult, account: label }, `Error broadcasting ${txType}`);
+    return false;
+  } else {
+    if (label.includes('Flooder')) return true;
+    logger.debug(`Broadcast ${txType} from ${label} tx=${broadcastResult.txid}`);
+    return true;
+  }
+}
+
+async function waitForNakamoto() {
+  while (true) {
+    try {
+      const poxInfo = await client.getPoxInfo();
+      if (poxInfo.current_burnchain_block_height! <= EPOCH_30_START) {
+        logger.info(
+          `Nakamoto not activated yet, waiting... (current=${poxInfo.current_burnchain_block_height}), (epoch3=${EPOCH_30_START})`
+        );
+      }  else {
+        logger.info(
+          `Nakamoto activation height reached, ready to submit txs for Nakamoto block production`
+        );
+        break;
+      }
+    } catch (error) {
+      if (/(ECONNREFUSED|ENOTFOUND|SyntaxError)/.test(error.cause?.message)) {
+        logger.info(
+          `Stacks node not ready, waiting...`
+        );
+      } else {
+        logger.error('Error getting pox info:', error);
+      }
+    }
+    await new Promise(resolve => setTimeout(resolve, 3000));
+  }
+}
+
+function accountLabel(address: string) {
+  const accountIndex = accounts.findIndex(account => account.stxAddress === address);
+  if (accountIndex !== -1) {
+    return `Account #${accountIndex}`;
+  }
+  return `Unknown (${address})`;
+}
+
+async function loop() {
+  await waitForNakamoto();
+
+  // Signal readiness to Kubernetes
+  writeFileSync('/tmp/ready', 'true');
+  logger.info('Nakamoto activated, broadcaster is ready');
+
+  while (true) {
+    try {
+      await run();
+    } catch (e) {
+      console.log(e);
+      logger.error('Error submitting stx-transfer tx:', e);
+    }
+    await new Promise(resolve => setTimeout(resolve, broadcastInterval * 1000));
+  }
+}
+loop();

+ 622 - 0
stacks/contracts/dependencies/amm-pool-v2-01.clar

@@ -0,0 +1,622 @@
+(use-trait ft-trait .trait-sip-010.sip-010-trait)
+(define-constant ERR-NOT-AUTHORIZED (err u1000))
+(define-constant ERR-POOL-ALREADY-EXISTS (err u2000))
+(define-constant ERR-INVALID-POOL (err u2001))
+(define-constant ERR-BLOCKLISTED (err u2002))
+(define-constant ERR-INVALID-LIQUIDITY (err u2003))
+(define-constant ERR-PERCENT-GREATER-THAN-ONE (err u2004))
+(define-constant ERR-EXCEEDS-MAX-SLIPPAGE (err u2005))
+(define-constant ERR-ORACLE-NOT-ENABLED (err u2006))
+(define-constant ERR-ORACLE-AVERAGE-BIGGER-THAN-ONE (err u2007))
+(define-constant ERR-PAUSED (err u2008))
+(define-constant ERR-SWITCH-THRESHOLD-BIGGER-THAN-ONE (err u2009))
+(define-constant ERR-NO-LIQUIDITY (err u2010))
+(define-constant ERR-MAX-IN-RATIO (err u2011))
+(define-constant ERR-MAX-OUT-RATIO (err u2012))
+(define-data-var paused bool true)
+(define-read-only (is-dao-or-extension)
+    (ok (asserts! (or (is-eq tx-sender .executor-dao) (contract-call? .executor-dao is-extension contract-caller)) ERR-NOT-AUTHORIZED)))
+(define-read-only (is-blocklisted-or-default (sender principal))
+	(contract-call? .amm-registry-v2-01 is-blocklisted-or-default sender))
+(define-read-only (get-switch-threshold)
+    (contract-call? .amm-registry-v2-01 get-switch-threshold))
+(define-read-only (get-pool-details-by-id (pool-id uint))
+    (contract-call? .amm-registry-v2-01 get-pool-details-by-id pool-id))
+(define-read-only (get-pool-details (token-x principal) (token-y principal) (factor uint))
+    (contract-call? .amm-registry-v2-01 get-pool-details token-x token-y factor))
+(define-read-only (get-pool-exists (token-x principal) (token-y principal) (factor uint))
+    (contract-call? .amm-registry-v2-01 get-pool-exists token-x token-y factor))
+(define-read-only (is-paused)
+	(var-get paused))
+(define-read-only (get-balances (token-x principal) (token-y principal) (factor uint))
+	(let (
+			(pool (try! (get-pool-details token-x token-y factor)))
+		)
+		(ok {balance-x: (get balance-x pool), balance-y: (get balance-y pool)})))
+(define-read-only (get-start-block (token-x principal) (token-y principal) (factor uint))
+	(ok (get start-block (try! (get-pool-details token-x token-y factor)))))
+(define-read-only (get-end-block (token-x principal) (token-y principal) (factor uint))
+	(ok (get end-block (try! (get-pool-details token-x token-y factor)))))
+(define-read-only (get-max-in-ratio (token-x principal) (token-y principal) (factor uint))
+	(ok (get max-in-ratio (try! (get-pool-details token-x token-y factor)))))
+(define-read-only (get-max-out-ratio (token-x principal) (token-y principal) (factor uint))
+	(ok (get max-out-ratio (try! (get-pool-details token-x token-y factor)))))
+(define-read-only (check-pool-status (token-x principal) (token-y principal) (factor uint))
+	(let (
+			(pool (try! (get-pool-details token-x token-y factor)))
+		)
+		(ok (asserts! (and (>= tenure-height (get start-block pool)) (<= tenure-height (get end-block pool))) ERR-NOT-AUTHORIZED))))
+(define-read-only (get-oracle-enabled (token-x principal) (token-y principal) (factor uint))
+	(ok (get oracle-enabled (try! (get-pool-details token-x token-y factor)))))
+(define-read-only (get-oracle-average (token-x principal) (token-y principal) (factor uint))
+	(ok (get oracle-average (try! (get-pool-details token-x token-y factor)))))
+(define-read-only (get-oracle-resilient (token-x principal) (token-y principal) (factor uint))
+    (let (
+            (exists (is-some (get-pool-exists token-x token-y factor)))
+            (pool (if exists (try! (get-pool-details token-x token-y factor)) (try! (get-pool-details token-y token-x factor))))
+            (oracle-instant (try! (get-oracle-instant token-x token-y factor))))
+        (asserts! (get oracle-enabled pool) ERR-ORACLE-NOT-ENABLED)
+        (ok (+ (mul-down (- ONE_8 (get oracle-average pool)) oracle-instant)
+            (mul-down (get oracle-average pool) (if (is-eq (get oracle-resilient pool) u0) oracle-instant (get oracle-resilient pool)))))))
+(define-read-only (get-oracle-instant (token-x principal) (token-y principal) (factor uint))
+	(let (
+			(exists (is-some (get-pool-exists token-x token-y factor)))
+			(pool (if exists (try! (get-pool-details token-x token-y factor)) (try! (get-pool-details token-y token-x factor))))
+		)
+		(asserts! (get oracle-enabled pool) ERR-ORACLE-NOT-ENABLED)
+		(if exists
+			(ok (get-price-internal (get balance-x pool) (get balance-y pool) factor))
+			(ok (get-price-internal (get balance-y pool) (get balance-x pool) factor)))))
+(define-read-only (get-price (token-x principal) (token-y principal) (factor uint))
+	(let (
+			(pool (try! (get-pool-details token-x token-y factor)))
+		)
+		(ok (get-price-internal (get balance-x pool) (get balance-y pool) factor))))
+(define-read-only (get-threshold-x (token-x principal) (token-y principal) (factor uint))
+	(ok (get threshold-x (try! (get-pool-details token-x token-y factor)))))
+(define-read-only (get-threshold-y (token-x principal) (token-y principal) (factor uint))
+	(ok (get threshold-y (try! (get-pool-details token-x token-y factor)))))
+(define-read-only (get-fee-rebate (token-x principal) (token-y principal) (factor uint))
+	(ok (get fee-rebate (try! (get-pool-details token-x token-y factor)))))
+(define-read-only (get-fee-rate-x (token-x principal) (token-y principal) (factor uint))
+	(ok (get fee-rate-x (try! (get-pool-details token-x token-y factor)))))
+(define-read-only (get-fee-rate-y (token-x principal) (token-y principal) (factor uint))
+	(ok (get fee-rate-y (try! (get-pool-details token-x token-y factor)))))
+(define-read-only (get-pool-owner (token-x principal) (token-y principal) (factor uint))
+	(ok (get pool-owner (try! (get-pool-details token-x token-y factor)))))
+(define-read-only (get-y-given-x (token-x principal) (token-y principal) (factor uint) (dx uint))
+	(let (
+			(pool (try! (get-pool-details token-x token-y factor)))
+			(threshold (get threshold-x pool))
+			(dy (if (>= dx threshold)
+				(get-y-given-x-internal (get balance-x pool) (get balance-y pool) factor dx)
+				(div-down (mul-down dx (get-y-given-x-internal (get balance-x pool) (get balance-y pool) factor threshold)) threshold)))
+		)
+		(asserts! (< dx (mul-down (get balance-x pool) (get max-in-ratio pool))) ERR-MAX-IN-RATIO)
+		(asserts! (< dy (mul-down (get balance-y pool) (get max-out-ratio pool))) ERR-MAX-OUT-RATIO)
+		(ok dy)))
+(define-read-only (get-x-given-y (token-x principal) (token-y principal) (factor uint) (dy uint))
+	(let (
+			(pool (try! (get-pool-details token-x token-y factor)))
+			(threshold (get threshold-y pool))
+			(dx (if (>= dy threshold)
+					(get-x-given-y-internal (get balance-x pool) (get balance-y pool) factor dy)
+					(div-down (mul-down dy (get-x-given-y-internal (get balance-x pool) (get balance-y pool) factor threshold)) threshold)))
+		)
+		(asserts! (< dy (mul-down (get balance-y pool) (get max-in-ratio pool))) ERR-MAX-IN-RATIO)
+		(asserts! (< dx (mul-down (get balance-x pool) (get max-out-ratio pool))) ERR-MAX-OUT-RATIO)
+		(ok dx)))
+(define-read-only (get-y-in-given-x-out (token-x principal) (token-y principal) (factor uint) (dx uint))
+	(let (
+			(pool (try! (get-pool-details token-x token-y factor)))
+			(threshold (get threshold-x pool))
+			(dy (if (>= dx threshold)
+					(get-y-in-given-x-out-internal (get balance-x pool) (get balance-y pool) factor dx)
+					(div-down (mul-down dx (get-y-in-given-x-out-internal (get balance-x pool) (get balance-y pool) factor threshold)) threshold)))
+		)
+		(asserts! (< dy (mul-down (get balance-y pool) (get max-in-ratio pool))) ERR-MAX-IN-RATIO)
+		(asserts! (< dx (mul-down (get balance-x pool) (get max-out-ratio pool))) ERR-MAX-OUT-RATIO)
+		(ok dy)))
+(define-read-only (get-x-in-given-y-out (token-x principal) (token-y principal) (factor uint) (dy uint))
+	(let (
+			(pool (try! (get-pool-details token-x token-y factor)))
+			(threshold (get threshold-y pool))
+			(dx (if (>= dy threshold)
+					(get-x-in-given-y-out-internal (get balance-x pool) (get balance-y pool) factor dy)
+					(div-down (mul-down dy (get-x-in-given-y-out-internal (get balance-x pool) (get balance-y pool) factor threshold)) threshold)))
+		)
+		(asserts! (< dx (mul-down (get balance-x pool) (get max-in-ratio pool))) ERR-MAX-IN-RATIO)
+		(asserts! (< dy (mul-down (get balance-y pool) (get max-out-ratio pool))) ERR-MAX-OUT-RATIO)
+		(ok dx)))
+(define-read-only (get-x-given-price (token-x principal) (token-y principal) (factor uint) (price uint))
+	(let (
+			(pool (try! (get-pool-details token-x token-y factor)))
+		)
+		(asserts! (< price (get-price-internal (get balance-x pool) (get balance-y pool) factor)) ERR-NO-LIQUIDITY)
+		(ok (get-x-given-price-internal (get balance-x pool) (get balance-y pool) factor price))))
+(define-read-only (get-y-given-price (token-x principal) (token-y principal) (factor uint) (price uint))
+	(let (
+			(pool (try! (get-pool-details token-x token-y factor)))
+		)
+		(asserts! (> price (get-price-internal (get balance-x pool) (get balance-y pool) factor)) ERR-NO-LIQUIDITY)
+		(ok (get-y-given-price-internal (get balance-x pool) (get balance-y pool) factor price))))
+(define-read-only (get-token-given-position (token-x principal) (token-y principal) (factor uint) (dx uint) (max-dy (optional uint)))
+	(let (
+			(pool (try! (get-pool-details token-x token-y factor)))
+			(dy (default-to u340282366920938463463374607431768211455 max-dy))
+		)
+		(asserts! (and (> dx u0) (> dy u0))  ERR-NO-LIQUIDITY)
+		(ok (get-token-given-position-internal (get balance-x pool) (get balance-y pool) factor (get total-supply pool) dx dy))))
+(define-read-only (get-position-given-mint (token-x principal) (token-y principal) (factor uint) (token-amount uint))
+	(let (
+			(pool (try! (get-pool-details token-x token-y factor)))
+		)
+		(asserts! (> (get total-supply pool) u0) ERR-NO-LIQUIDITY)
+		(ok (get-position-given-mint-internal (get balance-x pool) (get balance-y pool) factor (get total-supply pool) token-amount))))
+(define-read-only (get-position-given-burn (token-x principal) (token-y principal) (factor uint) (token-amount uint))
+	(let (
+			(pool (try! (get-pool-details token-x token-y factor)))
+		)
+		(asserts! (> (get total-supply pool) u0) ERR-NO-LIQUIDITY)
+		(ok (get-position-given-burn-internal (get balance-x pool) (get balance-y pool) factor (get total-supply pool) token-amount))))
+(define-read-only (get-helper (token-x principal) (token-y principal) (factor uint) (dx uint))
+	(if (is-some (get-pool-exists token-x token-y factor))
+		(get-y-given-x token-x token-y factor dx)
+		(get-x-given-y token-y token-x factor dx)))
+(define-read-only (get-helper-a (token-x principal) (token-y principal) (token-z principal) (factor-x uint) (factor-y uint) (dx uint))
+	(get-helper token-y token-z factor-y (try! (get-helper token-x token-y factor-x dx))))
+(define-read-only (get-helper-b
+		(token-x principal) (token-y principal) (token-z principal) (token-w principal)
+		(factor-x uint) (factor-y uint) (factor-z uint)
+		(dx uint))
+	(get-helper token-z token-w factor-z (try! (get-helper-a token-x token-y token-z factor-x factor-y dx))))
+(define-read-only (get-helper-c
+		(token-x principal) (token-y principal) (token-z principal) (token-w principal) (token-v principal)
+		(factor-x uint) (factor-y uint) (factor-z uint) (factor-w uint)
+		(dx uint))
+	(get-helper-a token-z token-w token-v factor-z factor-w (try! (get-helper-a token-x token-y token-z factor-x factor-y dx))))
+(define-read-only (fee-helper (token-x principal) (token-y principal) (factor uint))
+	(if (is-some (get-pool-exists token-x token-y factor))
+		(get-fee-rate-x token-x token-y factor)
+		(get-fee-rate-y token-y token-x factor)))
+(define-read-only (fee-helper-a (token-x principal) (token-y principal) (token-z principal) (factor-x uint) (factor-y uint))
+	(ok (+ (try! (fee-helper token-x token-y factor-x)) (try! (fee-helper token-y token-z factor-y)))))
+(define-read-only (fee-helper-b
+		(token-x principal) (token-y principal) (token-z principal) (token-w principal)
+		(factor-x uint) (factor-y uint) (factor-z uint))
+	(ok (+ (try! (fee-helper-a token-x token-y token-z factor-x factor-y)) (try! (fee-helper token-z token-w factor-z)))))
+(define-read-only (fee-helper-c
+		(token-x principal) (token-y principal) (token-z principal) (token-w principal) (token-v principal)
+		(factor-x uint) (factor-y uint) (factor-z uint) (factor-w uint))
+	(ok (+ (try! (fee-helper-a token-x token-y token-z factor-x factor-y)) (try! (fee-helper-a token-z token-w token-v factor-z factor-w)))))
+(define-read-only (get-invariant (balance-x uint) (balance-y uint) (t uint))
+    (if (>= t (get-switch-threshold))
+        (+ (mul-down (- ONE_8 t) (+ balance-x balance-y)) (mul-down t (mul-down balance-x balance-y)))
+        (+ (pow-down balance-x (- ONE_8 t)) (pow-down balance-y (- ONE_8 t)))))
+(define-read-only (get-max-ratio-limit)
+    (contract-call? .amm-registry-v2-01 get-max-ratio-limit))
+(define-public (pause (new-paused bool))
+	(begin
+		(try! (is-dao-or-extension))
+		(ok (var-set paused new-paused))))
+(define-public (set-start-block (token-x principal) (token-y principal) (factor uint) (new-start-block uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (is-dao-or-extension))) ERR-NOT-AUTHORIZED)
+        (as-contract (contract-call? .amm-registry-v2-01 set-start-block token-x token-y factor new-start-block))))
+(define-public (set-end-block (token-x principal) (token-y principal) (factor uint) (new-end-block uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (is-dao-or-extension))) ERR-NOT-AUTHORIZED)
+        (as-contract (contract-call? .amm-registry-v2-01 set-end-block token-x token-y factor new-end-block))))
+(define-public (set-max-in-ratio (token-x principal) (token-y principal) (factor uint) (new-max-in-ratio uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (is-dao-or-extension))) ERR-NOT-AUTHORIZED)
+        (as-contract (contract-call? .amm-registry-v2-01 set-max-in-ratio token-x token-y factor new-max-in-ratio))))
+(define-public (set-max-out-ratio (token-x principal) (token-y principal) (factor uint) (new-max-out-ratio uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (is-dao-or-extension))) ERR-NOT-AUTHORIZED)
+        (as-contract (contract-call? .amm-registry-v2-01 set-max-out-ratio token-x token-y factor new-max-out-ratio))))
+(define-public (set-oracle-enabled (token-x principal) (token-y principal) (factor uint) (enabled bool))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (is-dao-or-extension))) ERR-NOT-AUTHORIZED)
+        (as-contract (contract-call? .amm-registry-v2-01 set-oracle-enabled token-x token-y factor enabled))))
+(define-public (set-oracle-average (token-x principal) (token-y principal) (factor uint) (new-oracle-average uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (is-dao-or-extension))) ERR-NOT-AUTHORIZED)
+        (as-contract (contract-call? .amm-registry-v2-01 set-oracle-average token-x token-y factor new-oracle-average))))
+(define-public (set-threshold-x (token-x principal) (token-y principal) (factor uint) (new-threshold uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (is-dao-or-extension))) ERR-NOT-AUTHORIZED)
+        (as-contract (contract-call? .amm-registry-v2-01 set-threshold-x token-x token-y factor new-threshold))))
+(define-public (set-threshold-y (token-x principal) (token-y principal) (factor uint) (new-threshold uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (is-dao-or-extension))) ERR-NOT-AUTHORIZED)
+        (as-contract (contract-call? .amm-registry-v2-01 set-threshold-y token-x token-y factor new-threshold))))
+(define-public (set-fee-rate-x (token-x principal) (token-y principal) (factor uint) (fee-rate-x uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (is-dao-or-extension))) ERR-NOT-AUTHORIZED)
+        (as-contract (contract-call? .amm-registry-v2-01 set-fee-rate-x token-x token-y factor fee-rate-x))))
+(define-public (set-fee-rate-y (token-x principal) (token-y principal) (factor uint) (fee-rate-y uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (asserts! (or (is-eq tx-sender (get pool-owner pool)) (is-ok (is-dao-or-extension))) ERR-NOT-AUTHORIZED)
+        (as-contract (contract-call? .amm-registry-v2-01 set-fee-rate-y token-x token-y factor fee-rate-y))))
+(define-public (create-pool (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (factor uint) (pool-owner principal) (dx uint) (dy uint))
+    (begin
+				(asserts! (not (is-blocklisted-or-default tx-sender)) ERR-BLOCKLISTED)
+        (as-contract (try! (contract-call? .amm-registry-v2-01 create-pool token-x-trait token-y-trait factor pool-owner)))
+        (add-to-position token-x-trait token-y-trait factor dx (some dy))))
+(define-public (add-to-position (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (factor uint) (dx uint) (max-dy (optional uint)))
+    (let (
+            (token-x (contract-of token-x-trait))
+            (token-y (contract-of token-y-trait))
+            (pool (try! (get-pool-details token-x token-y factor)))
+            (balance-x (get balance-x pool))
+            (balance-y (get balance-y pool))
+            (total-supply (get total-supply pool))
+            (add-data (try! (get-token-given-position token-x token-y factor dx max-dy)))
+            (new-supply (get token add-data))
+            (dy (get dy add-data))
+            (pool-updated (merge pool { total-supply: (+ new-supply total-supply), balance-x: (+ balance-x dx), balance-y: (+ balance-y dy) }))
+            (sender tx-sender))
+        (asserts! (not (is-paused)) ERR-PAUSED)
+        (asserts! (and (> dx u0) (> dy u0)) ERR-INVALID-LIQUIDITY)
+        (asserts! (>= (default-to u340282366920938463463374607431768211455 max-dy) dy) ERR-EXCEEDS-MAX-SLIPPAGE)
+        (try! (contract-call? token-x-trait transfer-fixed dx sender .amm-vault-v2-01 none))
+        (try! (contract-call? token-y-trait transfer-fixed dy sender .amm-vault-v2-01 none))
+        (as-contract (try! (contract-call? .amm-registry-v2-01 update-pool token-x token-y factor pool-updated)))
+        (as-contract (try! (contract-call? .token-amm-pool-v2-01 mint-fixed (get pool-id pool) new-supply sender)))
+        (print { object: "pool", action: "liquidity-added", data: pool-updated, dx: dx, dy: dy, token-x: token-x, token-y: token-y, sender: sender })
+        (ok {supply: new-supply, dx: dx, dy: dy})))
+(define-public (reduce-position (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (factor uint) (percent uint))
+    (let (
+            (token-x (contract-of token-x-trait))
+            (token-y (contract-of token-y-trait))
+            (pool (try! (get-pool-details token-x token-y factor)))
+            (balance-x (get balance-x pool))
+            (balance-y (get balance-y pool))
+            (total-shares (unwrap-panic (contract-call? .token-amm-pool-v2-01 get-balance-fixed (get pool-id pool) tx-sender)))
+            (shares (if (is-eq percent ONE_8) total-shares (mul-down total-shares percent)))
+            (total-supply (get total-supply pool))
+            (reduce-data (try! (get-position-given-burn token-x token-y factor shares)))
+            (dx (get dx reduce-data))
+            (dy (get dy reduce-data))
+            (pool-updated (merge pool { total-supply: (if (<= total-supply shares) u0 (- total-supply shares)), balance-x: (if (<= balance-x dx) u0 (- balance-x dx)), balance-y: (if (<= balance-y dy) u0 (- balance-y dy)) }))
+            (sender tx-sender))
+				(asserts! (not (is-blocklisted-or-default tx-sender)) ERR-BLOCKLISTED)
+        (asserts! (not (is-paused)) ERR-PAUSED)
+        (asserts! (<= percent ONE_8) ERR-PERCENT-GREATER-THAN-ONE)
+        (as-contract (try! (contract-call? .amm-vault-v2-01 transfer-ft-two token-x-trait dx token-y-trait dy sender)))
+        (as-contract (try! (contract-call? .amm-registry-v2-01 update-pool token-x token-y factor pool-updated)))
+        (as-contract (try! (contract-call? .token-amm-pool-v2-01 burn-fixed (get pool-id pool) shares sender)))
+        (print { object: "pool", action: "liquidity-removed", data: pool-updated, dx: dx, dy: dy, token-x: token-x, token-y: token-y, sender: sender })
+        (ok {dx: dx, dy: dy})))
+(define-public (swap-x-for-y (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (factor uint) (dx uint) (min-dy (optional uint)))
+    (let (
+            (token-x (contract-of token-x-trait))
+            (token-y (contract-of token-y-trait))
+            (pool (try! (get-pool-details token-x token-y factor)))
+            (balance-x (get balance-x pool))
+            (balance-y (get balance-y pool))
+            (fee (mul-up dx (get fee-rate-x pool)))
+            (dx-net-fees (if (<= dx fee) u0 (- dx fee)))
+            (fee-rebate (mul-down fee (get fee-rebate pool)))
+            (dy (try! (get-y-given-x token-x token-y factor dx-net-fees)))
+            (pool-updated (merge pool {
+                balance-x: (+ balance-x dx-net-fees fee-rebate),
+                balance-y: (if (<= balance-y dy) u0 (- balance-y dy)),
+                oracle-resilient: (if (get oracle-enabled pool) (try! (get-oracle-resilient token-x token-y factor)) u0)
+                }))
+            (sender tx-sender))
+				(asserts! (not (is-blocklisted-or-default tx-sender)) ERR-BLOCKLISTED)
+        (asserts! (not (is-paused)) ERR-PAUSED)
+        (try! (check-pool-status token-x token-y factor))
+        (asserts! (> dx u0) ERR-INVALID-LIQUIDITY)
+        (asserts! (<= (div-down dy dx-net-fees) (get-price-internal balance-x balance-y factor)) ERR-INVALID-LIQUIDITY)
+        (asserts! (<= (default-to u0 min-dy) dy) ERR-EXCEEDS-MAX-SLIPPAGE)
+        (try! (contract-call? token-x-trait transfer-fixed dx sender .amm-vault-v2-01 none))
+        (and (> dy u0) (as-contract (try! (contract-call? .amm-vault-v2-01 transfer-ft token-y-trait dy sender))))
+        (as-contract (try! (contract-call? .amm-vault-v2-01 add-to-reserve token-x (- fee fee-rebate))))
+        (as-contract (try! (contract-call? .amm-registry-v2-01 update-pool token-x token-y factor pool-updated)))
+        (print { object: "pool", action: "swap-x-for-y", data: pool-updated, dx: dx, dy: dy, token-x: token-x, token-y: token-y, sender: sender, fee: fee, fee-rebate: fee-rebate })
+        (ok {dx: dx-net-fees, dy: dy})))
+(define-public (swap-y-for-x (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (factor uint) (dy uint) (min-dx (optional uint)))
+    (let (
+            (token-x (contract-of token-x-trait))
+            (token-y (contract-of token-y-trait))
+            (pool (try! (get-pool-details token-x token-y factor)))
+            (balance-x (get balance-x pool))
+            (balance-y (get balance-y pool))
+            (fee (mul-up dy (get fee-rate-y pool)))
+            (dy-net-fees (if (<= dy fee) u0 (- dy fee)))
+            (fee-rebate (mul-down fee (get fee-rebate pool)))
+            (dx (try! (get-x-given-y token-x token-y factor dy-net-fees)))
+            (pool-updated (merge pool {
+                balance-x: (if (<= balance-x dx) u0 (- balance-x dx)),
+                balance-y: (+ balance-y dy-net-fees fee-rebate),
+                oracle-resilient: (if (get oracle-enabled pool) (try! (get-oracle-resilient token-x token-y factor)) u0)
+                }))
+            (sender tx-sender))
+				(asserts! (not (is-blocklisted-or-default tx-sender)) ERR-BLOCKLISTED)
+        (asserts! (not (is-paused)) ERR-PAUSED)
+        (try! (check-pool-status token-x token-y factor))
+        (asserts! (> dy u0) ERR-INVALID-LIQUIDITY)
+        (asserts! (>= (div-down dy-net-fees dx) (get-price-internal balance-x balance-y factor)) ERR-INVALID-LIQUIDITY)
+        (asserts! (<= (default-to u0 min-dx) dx) ERR-EXCEEDS-MAX-SLIPPAGE)
+        (try! (contract-call? token-y-trait transfer-fixed dy sender .amm-vault-v2-01 none))
+        (and (> dx u0) (as-contract (try! (contract-call? .amm-vault-v2-01 transfer-ft token-x-trait dx sender))))
+        (as-contract (try! (contract-call? .amm-vault-v2-01 add-to-reserve token-y (- fee fee-rebate))))
+        (as-contract (try! (contract-call? .amm-registry-v2-01 update-pool token-x token-y factor pool-updated)))
+        (print { object: "pool", action: "swap-y-for-x", data: pool-updated, dx: dx, dy: dy, token-x: token-x, token-y: token-y, sender: sender, fee: fee, fee-rebate: fee-rebate })
+        (ok {dx: dx, dy: dy-net-fees})))
+(define-public (swap-helper (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (factor uint) (dx uint) (min-dy (optional uint)))
+	(if (is-some (get-pool-exists (contract-of token-x-trait) (contract-of token-y-trait) factor))
+		(ok (get dy (try! (swap-x-for-y token-x-trait token-y-trait factor dx min-dy))))
+		(ok (get dx (try! (swap-y-for-x token-y-trait token-x-trait factor dx min-dy))))))
+(define-public (swap-helper-a (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (token-z-trait <ft-trait>) (factor-x uint) (factor-y uint) (dx uint) (min-dz (optional uint)))
+	(swap-helper token-y-trait token-z-trait factor-y (try! (swap-helper token-x-trait token-y-trait factor-x dx none)) min-dz))
+(define-public (swap-helper-b
+		(token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (token-z-trait <ft-trait>) (token-w-trait <ft-trait>)
+		(factor-x uint) (factor-y uint) (factor-z uint)
+		(dx uint) (min-dw (optional uint)))
+	(swap-helper token-z-trait token-w-trait factor-z (try! (swap-helper-a token-x-trait token-y-trait token-z-trait factor-x factor-y dx none)) min-dw))
+(define-public (swap-helper-c
+		(token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (token-z-trait <ft-trait>) (token-w-trait <ft-trait>) (token-v-trait <ft-trait>)
+		(factor-x uint) (factor-y uint) (factor-z uint) (factor-w uint)
+		(dx uint) (min-dv (optional uint)))
+	(swap-helper-a token-z-trait token-w-trait token-v-trait factor-z factor-w (try! (swap-helper-a token-x-trait token-y-trait token-z-trait factor-x factor-y dx none)) min-dv))
+(define-private (get-price-internal (balance-x uint) (balance-y uint) (factor uint))
+    (if (>= factor (get-switch-threshold))
+        (div-down (+ (- ONE_8 factor) (mul-down factor balance-y)) (+ (- ONE_8 factor) (mul-down factor balance-x)))
+        (pow-down (div-down balance-y balance-x) factor)))
+(define-private (get-y-given-x-internal (balance-x uint) (balance-y uint) (t uint) (dx uint))
+    (if (>= t (get-switch-threshold))
+        (let (
+                (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))))
+            (div-down (+ (mul-down t-comp dx) (mul-down t (mul-down dx balance-y))) (+ t-comp (mul-down t (+ balance-x dx)))))
+        (let (
+                (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+                (t-comp-num-uncapped (div-up ONE_8 t-comp))
+                (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND))
+                (x-pow (pow-up balance-x t-comp))
+                (y-pow (pow-up balance-y t-comp))
+                (x-dx-pow (pow-down (+ balance-x dx) t-comp))
+                (add-term (+ x-pow y-pow))
+                (term (if (<= add-term x-dx-pow) u0 (- add-term x-dx-pow)))
+                (final-term (pow-up term t-comp-num)))
+            (if (<= balance-y final-term) u0 (- balance-y final-term)))))
+(define-private (get-x-given-y-internal (balance-x uint) (balance-y uint) (t uint) (dy uint))
+    (if (>= t (get-switch-threshold))
+        (let (
+                (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))))
+            (div-down (+ (mul-down t-comp dy) (mul-down t (mul-down dy balance-x))) (+ t-comp (mul-down t (+ balance-y dy)))))
+        (let (
+                (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+                (t-comp-num-uncapped (div-up ONE_8 t-comp))
+                (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND))
+                (x-pow (pow-up balance-x t-comp))
+                (y-pow (pow-up balance-y t-comp))
+                (y-dy-pow (pow-down (+ balance-y dy) t-comp))
+                (add-term (+ x-pow y-pow))
+                (term (if (<= add-term y-dy-pow) u0 (- add-term y-dy-pow)))
+                (final-term (pow-up term t-comp-num)))
+            (if (<= balance-x final-term) u0 (- balance-x final-term)))))
+(define-private (get-y-in-given-x-out-internal (balance-x uint) (balance-y uint) (t uint) (dx uint))
+    (if (>= t (get-switch-threshold))
+        (let (
+                (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))))
+            (div-down (+ (mul-down t-comp dx) (mul-down t (mul-down dx balance-y))) (+ t-comp (mul-down t (- balance-x dx)))))
+        (let (
+                (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+                (t-comp-num-uncapped (div-down ONE_8 t-comp))
+                (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND))
+                (x-pow (pow-down balance-x t-comp))
+                (y-pow (pow-down balance-y t-comp))
+                (x-dx-pow (pow-up (if (<= balance-x dx) u0 (- balance-x dx)) t-comp))
+                (add-term (+ x-pow y-pow))
+                (term (if (<= add-term x-dx-pow) u0 (- add-term x-dx-pow)))
+                (final-term (pow-down term t-comp-num)))
+            (if (<= final-term balance-y) u0 (- final-term balance-y)))))
+(define-private (get-x-in-given-y-out-internal (balance-x uint) (balance-y uint) (t uint) (dy uint))
+    (if (>= t (get-switch-threshold))
+        (let (
+                (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t))))
+            (div-down (+ (mul-down t-comp dy) (mul-down t (mul-down dy balance-x))) (+ t-comp (mul-down t (- balance-y dy)))))
+        (let (
+                (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+                (t-comp-num-uncapped (div-down ONE_8 t-comp))
+                (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND))
+                (x-pow (pow-down balance-x t-comp))
+                (y-pow (pow-down balance-y t-comp))
+                (y-dy-pow (pow-up (if (<= balance-y dy) u0 (- balance-y dy)) t-comp))
+                (add-term (+ x-pow y-pow))
+                (term (if (<= add-term y-dy-pow) u0 (- add-term y-dy-pow)))
+                (final-term (pow-down term t-comp-num)))
+            (if (<= final-term balance-x) u0 (- final-term balance-x)))))
+(define-private (get-x-given-price-internal (balance-x uint) (balance-y uint) (t uint) (price uint))
+    (if (>= t (get-switch-threshold))
+        (let (
+                (power (pow-down (div-down (get-price-internal balance-x balance-y t) price) u50000000)))
+            (mul-down balance-x (if (<= power ONE_8) u0 (- power ONE_8))))
+        (let (
+                (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+                (t-comp-num-uncapped (div-down ONE_8 t-comp))
+                (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND))
+                (numer (+ ONE_8 (pow-down (get-price-internal balance-x balance-y t) (div-down t-comp t))))
+                (denom (+ ONE_8 (pow-down price (div-down t-comp t))))
+                (lead-term (pow-down (div-down numer denom) t-comp-num)))
+            (if (<= lead-term ONE_8) u0 (mul-up balance-x (- lead-term ONE_8))))))
+(define-private (get-y-given-price-internal (balance-x uint) (balance-y uint) (t uint) (price uint))
+    (if (>= t (get-switch-threshold))
+        (let (
+                (power (pow-down (div-down price (get-price-internal balance-x balance-y t)) u50000000)))
+            (mul-down balance-y (if (<= power ONE_8) u0 (- power ONE_8))))
+        (let (
+                (t-comp (if (<= ONE_8 t) u0 (- ONE_8 t)))
+                (t-comp-num-uncapped (div-down ONE_8 t-comp))
+                (t-comp-num (if (< t-comp-num-uncapped MILD_EXPONENT_BOUND) t-comp-num-uncapped MILD_EXPONENT_BOUND))
+                (numer (+ ONE_8 (pow-down (get-price-internal balance-x balance-y t) (div-down t-comp t))))
+                (denom (+ ONE_8 (pow-down price (div-down t-comp t))))
+                (lead-term (pow-down (div-down numer denom) t-comp-num)))
+            (if (<= ONE_8 lead-term) u0 (mul-up balance-y (- ONE_8 lead-term))))))
+(define-private (get-token-given-position-internal (balance-x uint) (balance-y uint) (t uint) (total-supply uint) (dx uint) (dy uint))
+	(if (is-eq total-supply u0)
+		{token: (get-invariant dx dy t), dy: dy}
+		{token: (div-down (mul-down total-supply dx) balance-x), dy: (div-down (mul-down balance-y dx) balance-x)}))
+(define-private (get-position-given-mint-internal (balance-x uint) (balance-y uint) (t uint) (total-supply uint) (token-amount uint))
+	(let (
+			(token-div-supply (div-down token-amount total-supply))
+		)
+		{dx: (mul-down balance-x token-div-supply), dy: (mul-down balance-y token-div-supply)}))
+(define-private (get-position-given-burn-internal (balance-x uint) (balance-y uint) (t uint) (total-supply uint) (token-amount uint))
+	(get-position-given-mint-internal balance-x balance-y t total-supply token-amount))
+(define-constant ONE_8 u100000000) ;; 8 decimal places
+(define-constant MAX_POW_RELATIVE_ERROR u4)
+(define-private (mul-down (a uint) (b uint))
+	(/ (* a b) ONE_8))
+(define-private (mul-up (a uint) (b uint))
+	(let (
+			(product (* a b))
+		)
+		(if (is-eq product u0) u0 (+ u1 (/ (- product u1) ONE_8)))))
+(define-private (div-down (a uint) (b uint))
+	(if (is-eq a u0) u0 (/ (* a ONE_8) b)))
+(define-private (div-up (a uint) (b uint))
+	(if (is-eq a u0) u0 (+ u1 (/ (- (* a ONE_8) u1) b))))
+(define-private (pow-down (a uint) (b uint))
+	(let (
+			(raw (unwrap-panic (pow-fixed a b)))
+			(max-error (+ u1 (mul-up raw MAX_POW_RELATIVE_ERROR)))
+		)
+		(if (< raw max-error) u0 (- raw max-error))))
+(define-private (pow-up (a uint) (b uint))
+	(let (
+			(raw (unwrap-panic (pow-fixed a b)))
+			(max-error (+ u1 (mul-up raw MAX_POW_RELATIVE_ERROR)))
+		)
+		(+ raw max-error)))
+(define-constant UNSIGNED_ONE_8 (pow 10 8))
+(define-constant MAX_NATURAL_EXPONENT (* 69 UNSIGNED_ONE_8))
+(define-constant MIN_NATURAL_EXPONENT (* -18 UNSIGNED_ONE_8))
+(define-constant MILD_EXPONENT_BOUND (/ (pow u2 u126) (to-uint UNSIGNED_ONE_8)))
+(define-constant x_a_list_no_deci (list {x_pre: 6400000000, a_pre: 62351490808116168829, use_deci: false} ))
+(define-constant x_a_list (list
+	{x_pre: 3200000000, a_pre: 78962960182680695161, use_deci: true} ;; x2 = 2^5, a2 = e^(x2)
+	{x_pre: 1600000000, a_pre: 888611052050787, use_deci: true} ;; x3 = 2^4, a3 = e^(x3)
+	{x_pre: 800000000, a_pre: 298095798704, use_deci: true} ;; x4 = 2^3, a4 = e^(x4)
+	{x_pre: 400000000, a_pre: 5459815003, use_deci: true} ;; x5 = 2^2, a5 = e^(x5)
+	{x_pre: 200000000, a_pre: 738905610, use_deci: true} ;; x6 = 2^1, a6 = e^(x6)
+	{x_pre: 100000000, a_pre: 271828183, use_deci: true} ;; x7 = 2^0, a7 = e^(x7)
+	{x_pre: 50000000, a_pre: 164872127, use_deci: true} ;; x8 = 2^-1, a8 = e^(x8)
+	{x_pre: 25000000, a_pre: 128402542, use_deci: true} ;; x9 = 2^-2, a9 = e^(x9)
+	{x_pre: 12500000, a_pre: 113314845, use_deci: true} ;; x10 = 2^-3, a10 = e^(x10)
+	{x_pre: 6250000, a_pre: 106449446, use_deci: true} ;; x11 = 2^-4, a11 = e^x(11)
+))
+(define-constant ERR-X-OUT-OF-BOUNDS (err u5009))
+(define-constant ERR-Y-OUT-OF-BOUNDS (err u5010))
+(define-constant ERR-PRODUCT-OUT-OF-BOUNDS (err u5011))
+(define-constant ERR-INVALID-EXPONENT (err u5012))
+(define-constant ERR-OUT-OF-BOUNDS (err u5013))
+(define-private (ln-priv (a int))
+	(let (
+			(a_sum_no_deci (fold accumulate_division x_a_list_no_deci {a: a, sum: 0}))
+			(a_sum (fold accumulate_division x_a_list {a: (get a a_sum_no_deci), sum: (get sum a_sum_no_deci)}))
+			(out_a (get a a_sum))
+			(out_sum (get sum a_sum))
+			(z (/ (* (- out_a UNSIGNED_ONE_8) UNSIGNED_ONE_8) (+ out_a UNSIGNED_ONE_8)))
+			(z_squared (/ (* z z) UNSIGNED_ONE_8))
+			(div_list (list 3 5 7 9 11))
+			(num_sum_zsq (fold rolling_sum_div div_list {num: z, seriesSum: z, z_squared: z_squared}))
+			(seriesSum (get seriesSum num_sum_zsq))
+		)
+		(+ out_sum (* seriesSum 2))))
+(define-private (accumulate_division (x_a_pre (tuple (x_pre int) (a_pre int) (use_deci bool))) (rolling_a_sum (tuple (a int) (sum int))))
+	(let (
+			(a_pre (get a_pre x_a_pre))
+			(x_pre (get x_pre x_a_pre))
+			(use_deci (get use_deci x_a_pre))
+			(rolling_a (get a rolling_a_sum))
+			(rolling_sum (get sum rolling_a_sum))
+		)
+		(if (>= rolling_a (if use_deci a_pre (* a_pre UNSIGNED_ONE_8)))
+				{a: (/ (* rolling_a (if use_deci UNSIGNED_ONE_8 1)) a_pre), sum: (+ rolling_sum x_pre)}
+				{a: rolling_a, sum: rolling_sum})))
+(define-private (rolling_sum_div (n int) (rolling (tuple (num int) (seriesSum int) (z_squared int))))
+	(let (
+			(rolling_num (get num rolling))
+			(rolling_sum (get seriesSum rolling))
+			(z_squared (get z_squared rolling))
+			(next_num (/ (* rolling_num z_squared) UNSIGNED_ONE_8))
+			(next_sum (+ rolling_sum (/ next_num n)))
+		)
+		{num: next_num, seriesSum: next_sum, z_squared: z_squared}))
+(define-private (pow-priv (x uint) (y uint))
+    (let (
+            (x-int (to-int x))
+            (y-int (to-int y))
+            (lnx (ln-priv x-int))
+            (logx-times-y (/ (* lnx y-int) UNSIGNED_ONE_8)))
+        (asserts! (and (<= MIN_NATURAL_EXPONENT logx-times-y) (<= logx-times-y MAX_NATURAL_EXPONENT)) ERR-PRODUCT-OUT-OF-BOUNDS)
+        (ok (to-uint (try! (exp-fixed logx-times-y))))))
+(define-private (exp-pos (x int))
+	(begin
+		(asserts! (and (<= 0 x) (<= x MAX_NATURAL_EXPONENT)) ERR-INVALID-EXPONENT)
+		(let (
+				(x_product_no_deci (fold accumulate_product x_a_list_no_deci {x: x, product: 1}))
+				(x_adj (get x x_product_no_deci))
+				(firstAN (get product x_product_no_deci))
+				(x_product (fold accumulate_product x_a_list {x: x_adj, product: UNSIGNED_ONE_8}))
+				(product_out (get product x_product))
+				(x_out (get x x_product))
+				(seriesSum (+ UNSIGNED_ONE_8 x_out))
+				(div_list (list 2 3 4 5 6 7 8 9 10 11 12))
+				(term_sum_x (fold rolling_div_sum div_list {term: x_out, seriesSum: seriesSum, x: x_out}))
+				(sum (get seriesSum term_sum_x)))
+			(ok (* (/ (* product_out sum) UNSIGNED_ONE_8) firstAN)))))
+(define-private (accumulate_product (x_a_pre (tuple (x_pre int) (a_pre int) (use_deci bool))) (rolling_x_p (tuple (x int) (product int))))
+	(let (
+			(x_pre (get x_pre x_a_pre))
+			(a_pre (get a_pre x_a_pre))
+			(use_deci (get use_deci x_a_pre))
+			(rolling_x (get x rolling_x_p))
+			(rolling_product (get product rolling_x_p)))
+		(if (>= rolling_x x_pre)
+			{x: (- rolling_x x_pre), product: (/ (* rolling_product a_pre) (if use_deci UNSIGNED_ONE_8 1))}
+			{x: rolling_x, product: rolling_product})))
+(define-private (rolling_div_sum (n int) (rolling (tuple (term int) (seriesSum int) (x int))))
+	(let (
+			(rolling_term (get term rolling))
+			(rolling_sum (get seriesSum rolling))
+			(x (get x rolling))
+			(next_term (/ (/ (* rolling_term x) UNSIGNED_ONE_8) n))
+			(next_sum (+ rolling_sum next_term))
+		)
+		{term: next_term, seriesSum: next_sum, x: x}))
+(define-private (pow-fixed (x uint) (y uint))
+	(begin
+		(asserts! (< x (pow u2 u127)) ERR-X-OUT-OF-BOUNDS)
+		(asserts! (< y MILD_EXPONENT_BOUND) ERR-Y-OUT-OF-BOUNDS)
+		(if (is-eq y u0)
+			(ok (to-uint UNSIGNED_ONE_8))
+			(if (is-eq x u0) (ok u0) (pow-priv x y)))))
+(define-private (exp-fixed (x int))
+	(begin
+		(asserts! (and (<= MIN_NATURAL_EXPONENT x) (<= x MAX_NATURAL_EXPONENT)) ERR-INVALID-EXPONENT)
+		(if (< x 0) (ok (/ (* UNSIGNED_ONE_8 UNSIGNED_ONE_8) (try! (exp-pos (* -1 x))))) (exp-pos x))))
+(define-private (log-fixed (arg int) (base int))
+	(let (
+			(logBase (* (ln-priv base) UNSIGNED_ONE_8))
+			(logArg (* (ln-priv arg) UNSIGNED_ONE_8)))
+		(ok (/ (* logArg UNSIGNED_ONE_8) logBase))))
+(define-private (ln-fixed (a int))
+	(begin
+		(asserts! (> a 0) ERR-OUT-OF-BOUNDS)
+		(if (< a UNSIGNED_ONE_8) (ok (- 0 (ln-priv (/ (* UNSIGNED_ONE_8 UNSIGNED_ONE_8) a)))) (ok (ln-priv a)))))

+ 194 - 0
stacks/contracts/dependencies/amm-registry-v2-01.clar

@@ -0,0 +1,194 @@
+(use-trait ft-trait .trait-sip-010.sip-010-trait)
+(define-constant ERR-NOT-AUTHORIZED (err u1000))
+(define-constant ERR-INVALID-POOL (err u2001))
+(define-constant ERR-INVALID-LIQUIDITY (err u2003))
+(define-constant ERR-POOL-ALREADY-EXISTS (err u2000))
+(define-constant ERR-PERCENT-GREATER-THAN-ONE (err u5000))
+(define-constant ERR-EXCEEDS-MAX-SLIPPAGE (err u2020))
+(define-constant ERR-ORACLE-NOT-ENABLED (err u7002))
+(define-constant ERR-ORACLE-AVERAGE-BIGGER-THAN-ONE (err u7004))
+(define-constant ERR-PAUSED (err u1001))
+(define-constant ERR-SWITCH-THRESHOLD-BIGGER-THAN-ONE (err u7005))
+(define-constant ERR-NO-LIQUIDITY (err u2002))
+(define-constant ERR-MAX-IN-RATIO (err u4001))
+(define-constant ERR-MAX-OUT-RATIO (err u4002))
+(define-constant ONE_8 u100000000) ;; 8 decimal places
+(define-data-var pool-nonce uint u0)
+(define-data-var switch-threshold uint u80000000)
+(define-data-var max-ratio-limit uint ONE_8)
+(define-map blocklist principal bool)
+(define-map pools-id-map uint { token-x: principal, token-y: principal, factor: uint })
+(define-map pools-data-map
+  {
+    token-x: principal,
+    token-y: principal,
+    factor: uint
+  }
+  {
+    pool-id: uint,
+    total-supply: uint,
+    balance-x: uint,
+    balance-y: uint,
+    pool-owner: principal,    
+    fee-rate-x: uint,
+    fee-rate-y: uint,
+    fee-rebate: uint,
+    oracle-enabled: bool,
+    oracle-average: uint,
+    oracle-resilient: uint,
+    start-block: uint,
+    end-block: uint,
+    threshold-x: uint,
+    threshold-y: uint,
+    max-in-ratio: uint,
+    max-out-ratio: uint
+  }
+)
+(define-read-only (is-dao-or-extension)
+  (ok (asserts! (or (is-eq tx-sender .executor-dao) (contract-call? .executor-dao is-extension contract-caller)) ERR-NOT-AUTHORIZED)))
+(define-read-only (is-blocklisted-or-default (sender principal))
+	(default-to false (map-get? blocklist sender)))
+(define-read-only (get-switch-threshold)
+    (var-get switch-threshold))
+(define-read-only (get-max-ratio-limit)
+    (var-get max-ratio-limit))    
+(define-read-only (get-pool-details-by-id (pool-id uint))
+    (ok (unwrap! (map-get? pools-id-map pool-id) ERR-INVALID-POOL)))
+(define-read-only (get-pool-details (token-x principal) (token-y principal) (factor uint))
+    (ok (unwrap! (get-pool-exists token-x token-y factor) ERR-INVALID-POOL)))
+(define-read-only (get-pool-exists (token-x principal) (token-y principal) (factor uint))
+    (map-get? pools-data-map { token-x: token-x, token-y: token-y, factor: factor }) )
+(define-public (set-blocklist-many (blocked-many (list 1000 { sender: principal, blocked: bool })))
+	(begin 
+		(try! (is-dao-or-extension))
+		(ok (map set-blocklist blocked-many))))
+(define-public (set-switch-threshold (new-threshold uint))
+    (begin 
+        (try! (is-dao-or-extension))
+        (asserts! (<= new-threshold ONE_8) ERR-SWITCH-THRESHOLD-BIGGER-THAN-ONE)
+        (ok (var-set switch-threshold new-threshold))))
+(define-public (set-max-ratio-limit (new-limit uint))
+    (begin 
+        (try! (is-dao-or-extension))
+        (ok (var-set max-ratio-limit new-limit))))
+(define-public (create-pool (token-x-trait <ft-trait>) (token-y-trait <ft-trait>) (factor uint) (pool-owner principal)) 
+    (let (
+            (pool-id (+ (var-get pool-nonce) u1))
+            (token-x (contract-of token-x-trait))
+            (token-y (contract-of token-y-trait))
+            (pool-data {
+                pool-id: pool-id,
+                total-supply: u0,
+                balance-x: u0,
+                balance-y: u0,
+                pool-owner: pool-owner,
+                fee-rate-x: u0,
+                fee-rate-y: u0,
+                fee-rebate: u0,
+                oracle-enabled: false,
+                oracle-average: u0,
+                oracle-resilient: u0,
+                start-block: u340282366920938463463374607431768211455,
+                end-block: u340282366920938463463374607431768211455,
+                threshold-x: u0,
+                threshold-y: u0,
+                max-in-ratio: u0,
+                max-out-ratio: u0
+            }))
+        (try! (is-dao-or-extension))
+        (asserts! (and (is-none (map-get? pools-data-map { token-x: token-x, token-y: token-y, factor: factor })) (is-none (map-get? pools-data-map { token-x: token-y, token-y: token-x, factor: factor }))) ERR-POOL-ALREADY-EXISTS)             
+        (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } pool-data)
+        (map-set pools-id-map pool-id { token-x: token-x, token-y: token-y, factor: factor })
+        (var-set pool-nonce pool-id)
+        (print { object: "pool", action: "created", data: pool-data, token-x: token-x, token-y: token-y, factor: factor })
+        (ok true)))        
+(define-public (update-pool (token-x principal) (token-y principal) (factor uint)
+    (pool-data {
+        pool-id: uint,
+        total-supply: uint,
+        balance-x: uint,
+        balance-y: uint,
+        pool-owner: principal,    
+        fee-rate-x: uint,
+        fee-rate-y: uint,
+        fee-rebate: uint,
+        oracle-enabled: bool,
+        oracle-average: uint,
+        oracle-resilient: uint,
+        start-block: uint,
+        end-block: uint,
+        threshold-x: uint,
+        threshold-y: uint,
+        max-in-ratio: uint,
+        max-out-ratio: uint }))
+    (begin
+        (try! (is-dao-or-extension))
+        (ok (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } pool-data))))
+(define-public (set-fee-rebate (token-x principal) (token-y principal) (factor uint) (fee-rebate uint))
+    (let  (            
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (try! (is-dao-or-extension))
+        (ok (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { fee-rebate: fee-rebate })))))
+(define-public (set-pool-owner (token-x principal) (token-y principal) (factor uint) (pool-owner principal))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (try! (is-dao-or-extension))
+        (ok (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { pool-owner: pool-owner })))))
+(define-public (set-start-block (token-x principal) (token-y principal) (factor uint) (new-start-block uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (try! (is-dao-or-extension))
+        (ok (map-set  pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool {start-block: new-start-block})))))    
+(define-public (set-end-block (token-x principal) (token-y principal) (factor uint) (new-end-block uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (try! (is-dao-or-extension))
+        (ok (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool {end-block: new-end-block})))))
+(define-public (set-max-in-ratio (token-x principal) (token-y principal) (factor uint) (new-max-in-ratio uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (try! (is-dao-or-extension))
+        (asserts! (<= new-max-in-ratio (var-get max-ratio-limit)) ERR-MAX-IN-RATIO)
+        (ok (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool {max-in-ratio: new-max-in-ratio})))))
+(define-public (set-max-out-ratio (token-x principal) (token-y principal) (factor uint) (new-max-out-ratio uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (try! (is-dao-or-extension))
+        (asserts! (<= new-max-out-ratio (var-get max-ratio-limit)) ERR-MAX-OUT-RATIO)
+        (ok (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool {max-out-ratio: new-max-out-ratio})))))
+(define-public (set-oracle-enabled (token-x principal) (token-y principal) (factor uint) (enabled bool))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (try! (is-dao-or-extension))
+        (ok (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool {oracle-enabled: enabled})))))
+(define-public (set-oracle-average (token-x principal) (token-y principal) (factor uint) (new-oracle-average uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (try! (is-dao-or-extension))
+        (asserts! (get oracle-enabled pool) ERR-ORACLE-NOT-ENABLED)
+        (asserts! (< new-oracle-average ONE_8) ERR-ORACLE-AVERAGE-BIGGER-THAN-ONE)
+        (ok (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { oracle-average: new-oracle-average, oracle-resilient: u0 })))))
+(define-public (set-threshold-x (token-x principal) (token-y principal) (factor uint) (new-threshold uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (try! (is-dao-or-extension))
+        (ok (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { threshold-x: new-threshold })))))
+(define-public (set-threshold-y (token-x principal) (token-y principal) (factor uint) (new-threshold uint))
+    (let (
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (try! (is-dao-or-extension))
+        (ok (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { threshold-y: new-threshold })))))
+(define-public (set-fee-rate-x (token-x principal) (token-y principal) (factor uint) (fee-rate-x uint))
+    (let (        
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (try! (is-dao-or-extension))
+        (ok (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { fee-rate-x: fee-rate-x })))))
+(define-public (set-fee-rate-y (token-x principal) (token-y principal) (factor uint) (fee-rate-y uint))
+    (let (    
+            (pool (try! (get-pool-details token-x token-y factor))))
+        (try! (is-dao-or-extension))
+        (ok (map-set pools-data-map { token-x: token-x, token-y: token-y, factor: factor } (merge pool { fee-rate-y: fee-rate-y })))))
+(define-private (set-blocklist (blocked { sender: principal, blocked: bool }))
+	(begin
+		(print { object: "amm-registry", action: "set-blocklist", payload: blocked }) 
+		(ok (map-set blocklist (get sender blocked) (get blocked blocked)))))

+ 97 - 0
stacks/contracts/dependencies/amm-vault-v2-01.clar

@@ -0,0 +1,97 @@
+(use-trait ft-trait .trait-sip-010.sip-010-trait)
+(use-trait sft-trait .trait-semi-fungible.semi-fungible-trait)
+(use-trait flash-loan-trait .trait-flash-loan-user.flash-loan-user-trait)
+(define-constant ONE_8 u100000000) ;; 8 decimal places
+(define-constant ERR-NOT-AUTHORIZED (err u1000))
+(define-constant ERR-PAUSED (err u1001))
+(define-constant ERR-INVALID-BALANCE (err u1002))
+(define-constant ERR-INVALID-TOKEN (err u2026))
+(define-constant ERR-AMOUNT-EXCEED-RESERVE (err u2024))
+(define-map approved-tokens principal bool)
+(define-map approved-flash-loan-users principal bool)
+(define-map reserve principal uint)
+(define-data-var flash-loan-fee-rate uint u0)
+(define-data-var flash-loan-enabled bool false)
+(define-data-var paused bool false)
+(define-read-only (is-dao-or-extension)
+	(ok (asserts! (or (is-eq tx-sender .executor-dao) (contract-call? .executor-dao is-extension contract-caller)) ERR-NOT-AUTHORIZED)))
+(define-read-only (get-flash-loan-enabled)
+	(var-get flash-loan-enabled))
+(define-read-only (is-paused)
+	(var-get paused))
+(define-read-only (get-flash-loan-fee-rate)
+	(var-get flash-loan-fee-rate))
+(define-read-only (get-reserve (token-trait principal))
+	(default-to u0 (map-get? reserve token-trait)))
+(define-public (set-flash-loan-enabled (enabled bool))
+	(begin
+		(try! (is-dao-or-extension))
+		(ok (var-set flash-loan-enabled enabled))))
+(define-public (pause (new-paused bool))
+	(begin
+		(try! (is-dao-or-extension))
+		(ok (var-set paused new-paused))))
+(define-public (set-approved-flash-loan-user (flash-loan-user-trait principal) (approved bool))
+	(begin
+		(try! (is-dao-or-extension))
+		(ok (map-set approved-flash-loan-users flash-loan-user-trait approved))))
+(define-public (set-approved-token (token-trait principal) (approved bool))
+	(begin
+		(try! (is-dao-or-extension))
+		(ok (map-set approved-tokens token-trait approved))))
+(define-public (set-flash-loan-fee-rate (fee uint))
+	(begin
+		(try! (is-dao-or-extension))
+		(ok (var-set flash-loan-fee-rate fee))))
+(define-public (transfer-ft (token-trait <ft-trait>) (amount uint) (recipient principal))
+	(begin
+		(asserts! (not (is-paused)) ERR-PAUSED)
+		(asserts! (and (is-ok (is-dao-or-extension)) (is-ok (check-is-approved-token (contract-of token-trait)))) ERR-NOT-AUTHORIZED)
+		(as-contract (contract-call? token-trait transfer-fixed amount tx-sender recipient none))))
+(define-public (transfer-ft-two (token-x-trait <ft-trait>) (dx uint) (token-y-trait <ft-trait>) (dy uint) (recipient principal))
+	(begin
+		(try! (transfer-ft token-x-trait dx recipient))
+		(transfer-ft token-y-trait dy recipient)))
+(define-public (transfer-sft (token-trait <sft-trait>) (token-id uint) (amount uint) (recipient principal))
+	(begin
+		(asserts! (not (is-paused)) ERR-PAUSED)
+		(asserts! (and (is-ok (is-dao-or-extension)) (is-ok (check-is-approved-token (contract-of token-trait)))) ERR-NOT-AUTHORIZED)
+		(as-contract (contract-call? token-trait transfer-fixed token-id amount tx-sender recipient))))
+(define-public (flash-loan (flash-loan-user-trait <flash-loan-trait>) (token-trait <ft-trait>) (amount uint) (memo (optional (buff 16))))
+	(let (
+			(pre-bal (unwrap-panic (contract-call? token-trait get-balance-fixed (as-contract tx-sender))))
+			(fee-with-principal (+ ONE_8 (var-get flash-loan-fee-rate)))
+			(amount-with-fee (mul-up amount fee-with-principal))
+			(recipient tx-sender))
+		(asserts! (not (is-paused)) ERR-PAUSED)
+		(asserts! (and (is-ok (check-is-approved-flash-loan-user (contract-of flash-loan-user-trait))) (is-ok (check-is-approved-token (contract-of token-trait)))) ERR-NOT-AUTHORIZED)
+		;; make sure current balance > loan amount
+		(asserts! (> pre-bal amount) ERR-INVALID-BALANCE)
+		;; transfer loan to flash-loan-user
+		(as-contract (try! (contract-call? token-trait transfer-fixed amount tx-sender recipient none)))
+		;; flash-loan-user executes with loan received
+		(try! (contract-call? flash-loan-user-trait execute token-trait amount memo))
+		;; return the loan + fee
+		(try! (contract-call? token-trait transfer-fixed amount-with-fee recipient (as-contract tx-sender) none))
+		(ok amount-with-fee)))
+(define-public (add-to-reserve (token-trait principal) (amount uint))
+	(begin
+		(asserts! (not (is-paused)) ERR-PAUSED)
+		(try! (is-dao-or-extension))
+		(ok (map-set reserve token-trait (+ amount (get-reserve token-trait))))))
+(define-public (remove-from-reserve (token-trait principal) (amount uint))
+	(begin
+		(asserts! (not (is-paused)) ERR-PAUSED)
+		(try! (is-dao-or-extension))
+		(asserts! (<= amount (get-reserve token-trait)) ERR-AMOUNT-EXCEED-RESERVE)
+		(ok (map-set reserve token-trait (- (get-reserve token-trait) amount)))))
+(define-private (check-is-approved-flash-loan-user (flash-loan-user-trait principal))
+	(ok (asserts! (default-to false (map-get? approved-flash-loan-users flash-loan-user-trait)) ERR-NOT-AUTHORIZED)))
+(define-private (check-is-approved-token (flash-loan-token principal))
+	(ok (asserts! (default-to false (map-get? approved-tokens flash-loan-token)) ERR-NOT-AUTHORIZED)))
+(define-private (mul-down (a uint) (b uint))
+	(/ (* a b) ONE_8))
+(define-private (mul-up (a uint) (b uint))
+	(let  (
+			(product (* a b)))
+		(if (is-eq product u0) u0 (+ u1 (/ (- product u1) ONE_8)))))

+ 68 - 0
stacks/contracts/dependencies/clarity-stacks-helper.clar

@@ -0,0 +1,68 @@
+(define-constant err-invalid-length-version (err u1000))
+(define-constant err-invalid-length-chain-length (err u1001))
+(define-constant err-invalid-length-burn-spent (err u1002))
+(define-constant err-invalid-length-consensus-hash (err u1003))
+(define-constant err-invalid-length-parent-block-id (err u1004))
+(define-constant err-invalid-length-tx-merkle-root (err u1005))
+(define-constant err-invalid-length-state-index-root (err u1006))
+(define-constant err-invalid-length-timestamp (err u1007))
+(define-constant err-invalid-length-miner-signature (err u1008))
+(define-constant err-invalid-length-signer-bitvec (err u1009))
+(define-constant err-invalid-length-block-hash (err u1010))
+
+(define-read-only (valid-signer-bitvec (bitvec (buff 506)))
+	(let ((byte-length (buff-to-uint-be (unwrap-panic (as-max-len? (unwrap! (slice? bitvec u2 u6) false) u4)))))
+		(is-eq (len bitvec) (+ byte-length u6))
+	)
+)
+
+(define-read-only (block-header-hash
+	(version (buff 1))
+	(chain-length (buff 8))
+	(burn-spent (buff 8))
+	(consensus-hash (buff 20))
+	(parent-block-id (buff 32))
+	(tx-merkle-root (buff 32))
+	(state-index-root (buff 32))
+	(timestamp (buff 8))
+	(miner-signature (buff 65))
+	(signer-bitvec (buff 506))
+	)
+	(begin
+		(asserts! (is-eq (len version) u1) err-invalid-length-version)
+		(asserts! (is-eq (len chain-length) u8) err-invalid-length-chain-length)
+		(asserts! (is-eq (len burn-spent) u8) err-invalid-length-burn-spent)
+		(asserts! (is-eq (len consensus-hash) u20) err-invalid-length-consensus-hash)
+		(asserts! (is-eq (len parent-block-id) u32) err-invalid-length-parent-block-id)
+		(asserts! (is-eq (len tx-merkle-root) u32) err-invalid-length-tx-merkle-root)
+		(asserts! (is-eq (len state-index-root) u32) err-invalid-length-state-index-root)
+		(asserts! (is-eq (len timestamp) u8) err-invalid-length-timestamp)
+		(asserts! (is-eq (len miner-signature) u65) err-invalid-length-miner-signature)
+		(asserts! (valid-signer-bitvec signer-bitvec) err-invalid-length-signer-bitvec)
+		(ok
+			(sha512/256
+				(concat version
+				(concat chain-length
+				(concat burn-spent
+				(concat consensus-hash
+				(concat parent-block-id
+				(concat tx-merkle-root
+				(concat state-index-root
+				(concat timestamp
+				(concat miner-signature signer-bitvec)))))))))
+			)
+		)
+	)
+)
+
+(define-read-only (block-id-header-hash (block-hash (buff 32)) (consensus-hash (buff 20)))
+	(begin
+		(asserts! (is-eq (len block-hash) u32) err-invalid-length-block-hash)
+		(asserts! (is-eq (len consensus-hash) u20) err-invalid-length-consensus-hash)
+		(ok (sha512/256 (concat block-hash consensus-hash)))
+	)
+)
+
+(define-read-only (string-ascii-to-buffer (str (string-ascii 16000)))
+	(unwrap-panic (slice? (unwrap-panic (to-consensus-buff? str)) u5 (+ (len str) u5)))
+)

+ 81 - 0
stacks/contracts/dependencies/clarity-stacks.clar

@@ -0,0 +1,81 @@
+;; clarity-stacks
+;; Check if a Stacks transaction has been mined.
+;; Only works for Nakamoto blocks.
+
+(define-constant err-invalid-length-txid (err u2000))
+(define-constant err-proof-too-short (err u2001))
+(define-constant err-block-header-too-short (err u2005))
+(define-constant err-invalid-block-height (err u2002))
+(define-constant err-block-height-header-mismatch (err u2003))
+(define-constant err-merkle-proof-invalid (err u2004))
+
+(define-constant merkle-path-leaf-tag 0x00)
+(define-constant merkle-path-node-tag 0x01)
+
+(define-read-only (tagged-hash (tag (buff 1)) (data (buff 64)))
+	(sha512/256 (concat tag data))
+)
+
+(define-read-only (is-bit-set (val uint) (bit uint))
+	(> (bit-and val (bit-shift-left u1 bit)) u0)
+)
+
+(define-read-only (merkle-leaf-hash (data (buff 32)))
+	(tagged-hash merkle-path-leaf-tag data)
+)
+
+(define-private (inner-merkle-proof-verify (ctr uint) (state { path: uint, root-hash: (buff 32), proof-hashes: (list 14 (buff 32)), tree-depth: uint, cur-hash: (buff 32), verified: bool}))
+  (let ((path (get path state))
+        (is-left (is-bit-set path ctr))
+        (proof-hashes (get proof-hashes state))
+        (cur-hash (get cur-hash state))
+        (root-hash (get root-hash state))
+        (h1 (if is-left (unwrap-panic (element-at proof-hashes ctr)) cur-hash))
+        (h2 (if is-left cur-hash (unwrap-panic (element-at proof-hashes ctr))))
+        (next-hash (tagged-hash merkle-path-node-tag (concat h1 h2)))
+        (is-verified (and (is-eq (+ u1 ctr) (len proof-hashes)) (is-eq next-hash root-hash)))
+		)
+    	(merge state { cur-hash: next-hash, verified: is-verified})
+	)
+)
+
+;; Note that the hashes in the proof must be tagged hashes.
+;; Do not put TXIDs in the proof directly, they must first be
+;; hashed with (merkle-leaf-hash).
+;; Returns (ok true) if the proof is valid, or an error if not.
+(define-read-only (verify-merkle-proof (txid (buff 32)) (merkle-root (buff 32)) (proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint}))
+	(if (> (get tree-depth proof) (len (get hashes proof)))
+		err-proof-too-short
+		(ok (asserts! (get verified
+			(fold inner-merkle-proof-verify
+				(unwrap-panic (slice? (list u0 u1 u2 u3 u4 u5 u6 u7 u8 u9 u10 u11 u12 u13) u0 (get tree-depth proof)))
+				{
+					path: (+ (pow u2 (get tree-depth proof)) (get tx-index proof)),
+					root-hash: merkle-root, proof-hashes: (get hashes proof),
+					cur-hash: (tagged-hash merkle-path-leaf-tag txid),
+					tree-depth: (get tree-depth proof),
+					verified: false
+				}
+			)) err-merkle-proof-invalid)
+		)
+	)
+)
+
+(define-read-only (get-block-info-header-hash? (stx-height uint))
+	(get-stacks-block-info? header-hash stx-height)
+)
+
+;; Returns (ok true) if the transaction was mined.
+(define-read-only (was-tx-mined-compact (txid (buff 32)) (proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint}) (tx-block-height uint) (block-header-without-signer-signatures (buff 712)))
+	(let (
+		(target-header-hash (unwrap! (get-block-info-header-hash? tx-block-height) err-invalid-block-height))
+		(tx-merkle-root (unwrap-panic (as-max-len? (unwrap! (slice? block-header-without-signer-signatures u69 u101) err-block-header-too-short) u32)))
+		(header-hash (sha512/256 block-header-without-signer-signatures))
+		)
+		(asserts! (is-eq (len txid) u32) err-invalid-length-txid)
+		;; It is fine to compare header hash because the consensus hash is part
+		;; of the header in Nakamoto.
+		(asserts! (is-eq header-hash target-header-hash) err-block-height-header-mismatch)
+		(verify-merkle-proof txid tx-merkle-root proof)
+	)
+)

+ 89 - 0
stacks/contracts/dependencies/code-body-prover.clar

@@ -0,0 +1,89 @@
+;; code-body-prover
+;; Check if a Stacks contract with a specific code body has been deployed.
+;; Only works for Nakamoto blocks.
+;; Uses clarity-stacks.
+
+(define-constant chain-id-bytes (unwrap-panic (slice? (unwrap-panic (to-consensus-buff? chain-id)) u13 u17)))
+(define-constant tx-version (if is-in-mainnet 0x00 0x80))
+(define-constant auth-type-standard 0x04)
+(define-constant hash-mode-p2pkh 0x00)
+(define-constant key-encoding 0x00)
+(define-constant anchor-mode 0x03)
+(define-constant post-conditions-mode-allow 0x01)
+(define-constant versioned-smart-contract 0x06)
+(define-constant clarity-version 0x03)
+
+(define-constant err-invalid-length-nonce (err u2000))
+(define-constant err-invalid-length-fee (err u2001))
+(define-constant err-invalid-length-signature (err u2002))
+(define-constant err-invalid-principal-version (err u2003))
+(define-constant err-principal-not-contract (err u2004))
+
+(define-read-only (contract-name-length-byte (length uint))
+	(unwrap-panic (slice? (unwrap-panic (to-consensus-buff? length)) u16 u17))
+)
+
+(define-read-only (contract-code-length-length-bytes (length uint))
+	(unwrap-panic (slice? (unwrap-panic (to-consensus-buff? length)) u13 u17))
+)
+
+(define-read-only (string-to-buff (str (string-ascii 80)))
+	(unwrap-panic (slice? (unwrap-panic (to-consensus-buff? str)) u5 (+ (len str) u5)))
+)
+
+(define-read-only (calculate-txid
+	(nonce (buff 8))
+	(fee (buff 8))
+	(signature (buff 65))
+	(contract principal)
+	(code-body (buff 80000))
+	)
+	(let
+		(
+			(principal-data (unwrap! (principal-destruct? contract) err-invalid-principal-version))
+			(contract-name (unwrap! (get name principal-data) err-principal-not-contract))
+		)
+		(asserts! (is-eq (len nonce) u8) err-invalid-length-nonce)
+		(asserts! (is-eq (len fee) u8) err-invalid-length-fee)
+		(asserts! (is-eq (len signature) u65) err-invalid-length-signature)
+		(ok (sha512/256
+			(concat tx-version
+			(concat chain-id-bytes
+			(concat auth-type-standard
+			(concat hash-mode-p2pkh
+			(concat (get hash-bytes principal-data)
+			(concat nonce
+			(concat fee
+			(concat key-encoding
+			(concat signature
+			(concat anchor-mode
+			(concat post-conditions-mode-allow
+			(concat 0x00000000 ;; no post conditions
+			(concat versioned-smart-contract
+			(concat clarity-version
+			(concat (contract-name-length-byte (len contract-name))
+			(concat (string-to-buff contract-name)
+			(concat (contract-code-length-length-bytes (len code-body)) code-body
+			)))))))))))))))))
+		))
+	)
+)
+
+;; Returns (ok true) if the transaction was mined.
+(define-read-only (is-contract-deployed
+	(nonce (buff 8))
+	(fee (buff 8))
+	(signature (buff 65))
+	(contract principal)
+	(code-body (buff 80000))
+	(proof { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint})
+	(tx-block-height uint)
+	(block-header-without-signer-signatures (buff 712))
+	)
+	(contract-call? .clarity-stacks was-tx-mined-compact
+		(try! (calculate-txid nonce fee signature contract code-body))
+		proof
+		tx-block-height
+		block-header-without-signer-signatures
+	)
+)

+ 58 - 0
stacks/contracts/dependencies/executor-dao.clar

@@ -0,0 +1,58 @@
+(use-trait proposal-trait .proposal-trait.proposal-trait)
+(use-trait extension-trait .extension-trait.extension-trait)
+(define-constant err-unauthorised (err u1000))
+(define-constant err-already-executed (err u1001))
+(define-constant err-invalid-extension (err u1002))
+(define-data-var executive principal tx-sender)
+(define-map executed-proposals principal uint)
+(define-map extensions principal bool)
+(define-private (is-self-or-extension)
+	(ok (asserts! (or (is-eq tx-sender (as-contract tx-sender)) (is-extension contract-caller)) err-unauthorised))
+)
+(define-read-only (is-extension (extension principal))
+	(default-to false (map-get? extensions extension))
+)
+(define-public (set-extension (extension principal) (enabled bool))
+	(begin
+		(try! (is-self-or-extension))
+		(print {event: "extension", extension: extension, enabled: enabled})
+		(ok (map-set extensions extension enabled))
+	)
+)
+(define-private (set-extensions-iter (item {extension: principal, enabled: bool}))
+	(begin
+		(print {event: "extension", extension: (get extension item), enabled: (get enabled item)})
+		(map-set extensions (get extension item) (get enabled item))
+	)
+)
+(define-public (set-extensions (extension-list (list 200 {extension: principal, enabled: bool})))
+	(begin
+		(try! (is-self-or-extension))
+		(ok (map set-extensions-iter extension-list))
+	)
+)
+(define-read-only (executed-at (proposal <proposal-trait>))
+	(map-get? executed-proposals (contract-of proposal))
+)
+(define-public (execute (proposal <proposal-trait>) (sender principal))
+	(begin
+		(try! (is-self-or-extension))
+		(asserts! (map-insert executed-proposals (contract-of proposal) tenure-height) err-already-executed)
+		(print {event: "execute", proposal: proposal})
+		(as-contract (contract-call? proposal execute sender))
+	)
+)
+(define-public (construct (proposal <proposal-trait>))
+	(let ((sender tx-sender))
+		(asserts! (is-eq sender (var-get executive)) err-unauthorised)
+		(var-set executive (as-contract tx-sender))
+		(as-contract (execute proposal sender))
+	)
+)
+(define-public (request-extension-callback (extension <extension-trait>) (payload (buff 2048)))
+	(let ((sender tx-sender))
+		(asserts! (is-extension contract-caller) err-invalid-extension)
+		(asserts! (is-eq contract-caller (contract-of extension)) err-invalid-extension)
+		(as-contract (contract-call? extension callback sender payload))
+	)
+)

+ 5 - 0
stacks/contracts/dependencies/extension-trait.clar

@@ -0,0 +1,5 @@
+(define-trait extension-trait
+	(
+		(callback (principal (buff 2048)) (response bool uint))
+	)
+)

+ 133 - 0
stacks/contracts/dependencies/hk-cursor-v2.clar

@@ -0,0 +1,133 @@
+;; Title: hiro-kit-cursor
+;; Version: v1
+
+(define-read-only (read-buff-1 (cursor { bytes: (buff 8192), pos: uint }))
+    (ok { 
+        value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u1)) (err u1)) u1) (err u1)), 
+        next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u1) }
+    }))
+
+(define-read-only (read-buff-2 (cursor { bytes: (buff 8192), pos: uint }))
+    (ok { 
+        value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u2)) (err u1)) u2) (err u1)), 
+        next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u2) }
+    }))
+
+(define-read-only (read-buff-4 (cursor { bytes: (buff 8192), pos: uint }))
+    (ok { 
+        value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u4)) (err u1)) u4) (err u1)), 
+        next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u4) }
+    }))
+
+(define-read-only (read-buff-8 (cursor { bytes: (buff 8192), pos: uint }))
+    (ok { 
+        value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u8)) (err u1)) u8) (err u1)), 
+        next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u8) }
+    }))
+
+(define-read-only (read-buff-16 (cursor { bytes: (buff 8192), pos: uint }))
+    (ok { 
+        value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u16)) (err u1)) u16) (err u1)), 
+        next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u16) }
+    }))
+
+(define-read-only (read-buff-20 (cursor { bytes: (buff 8192), pos: uint }))
+    (ok { 
+        value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u20)) (err u1)) u20) (err u1)), 
+        next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u20) }
+    }))
+
+(define-read-only (read-buff-32 (cursor { bytes: (buff 8192), pos: uint }))
+    (ok { 
+        value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u32)) (err u1)) u32) (err u1)), 
+        next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u32) }
+    }))
+
+(define-read-only (read-buff-64 (cursor { bytes: (buff 8192), pos: uint }))
+    (ok { 
+        value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u64)) (err u1)) u64) (err u1)), 
+        next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u64) }
+    }))
+
+(define-read-only (read-buff-65 (cursor { bytes: (buff 8192), pos: uint }))
+    (ok { 
+        value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) (get pos cursor) (+ (get pos cursor) u65)) (err u1)) u65) (err u1)), 
+        next: { bytes: (get bytes cursor), pos: (+ (get pos cursor) u65) }
+    }))
+
+(define-read-only (read-buff-8192-max (cursor { bytes: (buff 8192), pos: uint }) (size (optional uint)))
+    (let ((min (get pos cursor))
+          (max (match size value 
+            (+ value (get pos cursor))
+            (len (get bytes cursor)))))
+      (ok { 
+          value: (unwrap! (as-max-len? (unwrap! (slice? (get bytes cursor) min max) (err u1)) u8192) (err u1)), 
+          next: { bytes: (get bytes cursor), pos: max }
+      })))
+
+(define-read-only (read-uint-8 (cursor { bytes: (buff 8192), pos: uint }))
+    (let ((cursor-bytes (try! (read-buff-1 cursor))))
+        (ok (merge cursor-bytes { value: (buff-to-uint-be (get value cursor-bytes)) }))))
+
+(define-read-only (read-uint-16 (cursor { bytes: (buff 8192), pos: uint }))
+    (let ((cursor-bytes (try! (read-buff-2 cursor))))
+        (ok (merge cursor-bytes { value: (buff-to-uint-be (get value cursor-bytes)) }))))
+
+(define-read-only (read-uint-32 (cursor { bytes: (buff 8192), pos: uint }))
+    (let ((cursor-bytes (try! (read-buff-4 cursor))))
+        (ok (merge cursor-bytes { value: (buff-to-uint-be (get value cursor-bytes)) }))))
+
+(define-read-only (read-uint-64 (cursor { bytes: (buff 8192), pos: uint }))
+    (let ((cursor-bytes (try! (read-buff-8 cursor))))
+        (ok (merge cursor-bytes { value: (buff-to-uint-be (get value cursor-bytes)) }))))
+
+(define-read-only (read-uint-128 (cursor { bytes: (buff 8192), pos: uint }))
+    (let ((cursor-bytes (try! (read-buff-16 cursor))))
+        (ok (merge cursor-bytes { value: (buff-to-uint-be (get value cursor-bytes)) }))))
+
+(define-read-only (read-int-8 (cursor { bytes: (buff 8192), pos: uint }))
+    (let ((cursor-bytes (try! (read-buff-1 cursor))))
+        (ok (merge 
+            cursor-bytes 
+            { value: (bit-shift-right (bit-shift-left (buff-to-int-be (get value cursor-bytes)) u120) u120) }))))
+
+(define-read-only (read-int-16 (cursor { bytes: (buff 8192), pos: uint }))
+    (let ((cursor-bytes (try! (read-buff-2 cursor))))
+        (ok (merge 
+            cursor-bytes 
+            { value: (bit-shift-right (bit-shift-left (buff-to-int-be (get value cursor-bytes)) u112) u112) }))))
+
+(define-read-only (read-int-32 (cursor { bytes: (buff 8192), pos: uint }))
+    (let ((cursor-bytes (try! (read-buff-4 cursor))))
+        (ok (merge 
+            cursor-bytes 
+            { value: (bit-shift-right (bit-shift-left (buff-to-int-be (get value cursor-bytes)) u96) u96) }))))
+
+(define-read-only (read-int-64 (cursor { bytes: (buff 8192), pos: uint }))
+    (let ((cursor-bytes (try! (read-buff-8 cursor))))
+        (ok (merge 
+            cursor-bytes 
+            { value: (bit-shift-right (bit-shift-left (buff-to-int-be (get value cursor-bytes)) u64) u64) }))))
+
+(define-read-only (read-int-128 (cursor { bytes: (buff 8192), pos: uint }))
+    (let ((cursor-bytes (try! (read-buff-16 cursor))))
+        (ok (merge 
+            cursor-bytes 
+            { value: (buff-to-int-be (get value cursor-bytes)) }))))
+
+(define-read-only (new (bytes (buff 8192)) (offset (optional uint)))
+    { 
+        value: none, 
+        next: { bytes: bytes, pos: (match offset value value u0) }
+    })
+
+(define-read-only (advance (cursor { bytes: (buff 8192), pos: uint }) (offset uint))
+     { bytes: (get bytes cursor), pos: (+ (get pos cursor) offset) })
+
+(define-read-only (slice (cursor { bytes: (buff 8192), pos: uint }) (size (optional uint)))
+    (match (slice? (get bytes cursor) 
+                   (get pos cursor) 
+                   (match size value 
+                   (+ (get pos cursor) value)    
+                      (len (get bytes cursor))))
+        bytes bytes 0x))

+ 11 - 0
stacks/contracts/dependencies/hk-ecc-v1.clar

@@ -0,0 +1,11 @@
+;; @desc Convert an uncompressed public key (64 bytes) into a compressed public key (33 bytes)
+(define-read-only (compute-compressed-public-key (uncompressed-public-key (buff 64)))
+  (let ((x-coordinate (unwrap-panic (slice? uncompressed-public-key u0 u32)))
+        (y-coordinate-parity (buff-to-uint-be (unwrap-panic (element-at? uncompressed-public-key u63)))))
+    (unwrap-panic (as-max-len? (concat (if (is-eq (mod y-coordinate-parity u2) u0) 0x02 0x03) x-coordinate) u33))))
+
+;; @desc Convert an uncompressed public key (64 bytes) into an eth address (20 bytes)
+(define-read-only (compute-eth-address (uncompressed-public-key (buff 64)))
+  (let ((x-coordinate (unwrap-panic (slice? uncompressed-public-key u0 u32)))
+        (y-coordinate-parity (buff-to-uint-be (unwrap-panic (element-at? uncompressed-public-key u63)))))
+    (unwrap-panic (as-max-len? (concat (if (is-eq (mod y-coordinate-parity u2) u0) 0x02 0x03) x-coordinate) u33))))

+ 27 - 0
stacks/contracts/dependencies/hk-merkle-tree-keccak160-v1.clar

@@ -0,0 +1,27 @@
+;; Title: hiro-merkle-tree-keccak160
+;; Version: v1
+
+(define-read-only (keccak160 (bytes (buff 1024)))
+    (unwrap-panic (as-max-len? (unwrap-panic (slice? (keccak256 bytes) u0 u20)) u20)))
+
+(define-read-only (buff-20-to-uint (bytes (buff 20)))
+    (buff-to-uint-be (unwrap-panic (as-max-len? (unwrap-panic (slice? bytes u0 u15)) u16))))
+
+(define-read-only (hash-leaf (bytes (buff 255)))
+    (keccak160 (concat 0x00 bytes)))
+
+(define-read-only (hash-nodes (node-1 (buff 20)) (node-2 (buff 20)))
+    (let ((uint-1 (buff-20-to-uint node-1))
+          (uint-2 (buff-20-to-uint node-2))
+          (sequence (if (< uint-2 uint-1) 
+            (concat (concat 0x01 node-2) node-1)
+            (concat (concat 0x01 node-1) node-2))))
+    (keccak160 sequence)))
+
+(define-read-only (check-proof (root-hash (buff 20)) (leaf (buff 255)) (path (list 255 (buff 20))))
+    (let ((hashed-leaf (hash-leaf leaf))
+          (computed-root-hash (fold hash-path path hashed-leaf)))
+        (is-eq root-hash computed-root-hash)))
+
+(define-private (hash-path (entry (buff 20)) (acc (buff 20)))
+    (hash-nodes entry acc))

+ 108 - 0
stacks/contracts/dependencies/liquidity-locker.clar

@@ -0,0 +1,108 @@
+;; SPDX-License-Identifier: BUSL-1.1
+
+(define-constant err-not-authorised (err u1000))
+(define-constant err-no-locked-liquidity (err u1100))
+(define-constant err-end-burn-block (err u1101))
+
+(define-constant MAX_UINT u340282366920938463463374607431768211455)
+
+(define-data-var lock-period uint u26280) ;; c. 6 months
+(define-map locked-liquidity { owner: principal, pool-id: uint } { amount: uint, end-burn-block: uint })
+(define-map locked-liquidity-owners uint (list 200 principal))
+(define-map burnt-liquidity uint uint)
+
+;; read-only calls
+
+(define-read-only (is-dao-or-extension)
+  (ok (asserts! (or (is-eq tx-sender 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.executor-dao) (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.executor-dao is-extension contract-caller)) err-not-authorised)))
+
+(define-read-only (get-lock-period)
+	(var-get lock-period))
+
+(define-read-only (get-locked-liquidity-or-default (owner principal) (pool-id uint))
+	(default-to { amount: u0, end-burn-block: MAX_UINT } (map-get? locked-liquidity { owner: owner, pool-id: pool-id })))
+
+(define-read-only (get-locked-liquidity-for-pool-or-default (pool-id uint))
+  (let (
+      (owners (default-to (list) (map-get? locked-liquidity-owners pool-id)))
+      (initial-state { pool-id: pool-id, result: (list) }))
+    (get result (fold accumulate-locked-liquidity owners initial-state))))
+
+(define-read-only (get-burnt-liquidity-or-default (pool-id uint))
+	(default-to u0 (map-get? burnt-liquidity pool-id)))
+
+;; public calls
+
+(define-public (lock-liquidity (amount uint) (pool-id uint))
+	(begin
+		(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-amm-pool-v2-01 transfer-fixed pool-id amount tx-sender (as-contract tx-sender)))
+		(map-set locked-liquidity { owner: tx-sender, pool-id: pool-id } { amount: (+ (get amount (get-locked-liquidity-or-default tx-sender pool-id)) amount), end-burn-block: (+ burn-block-height (get-lock-period)) })
+		(add-owner-to-locked-liquidity pool-id tx-sender)
+		(print { notification: "lock-liquidity", payload: { sender: tx-sender, amount: amount, pool-id: pool-id, end-burn-block: (+ burn-block-height (get-lock-period)) }})
+		(ok true)))
+
+(define-public (burn-liquidity (amount uint) (pool-id uint))
+ (let (
+		(sender tx-sender)
+		(current-burnt-amount (get-burnt-liquidity-or-default pool-id)))
+ 	(as-contract (try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-amm-pool-v2-01 burn-fixed pool-id amount sender)))
+  (map-set burnt-liquidity pool-id (+ current-burnt-amount amount))
+	(print { notification: "burn-liquidity", payload: { sender: sender, amount: amount, pool-id: pool-id, total-burnt: (+ current-burnt-amount amount) }})
+	(ok true)))
+
+(define-public (claim-liquidity (pool-id uint))
+	(let (
+			(sender tx-sender)
+			(liquidity-details (get-locked-liquidity-or-default sender pool-id)))
+		(asserts! (> (get amount liquidity-details) u0) err-no-locked-liquidity)
+		(asserts! (> burn-block-height (get end-burn-block liquidity-details)) err-end-burn-block)
+		(as-contract (try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.token-amm-pool-v2-01 transfer-fixed pool-id (get amount liquidity-details) tx-sender sender)))
+		(map-delete locked-liquidity { owner: sender, pool-id: pool-id })
+		(print { notification: "claim-liquidity", payload: liquidity-details, sender: sender, pool-id: pool-id })
+		(ok true)))
+
+;; governance calls
+
+(define-public (set-lock-period (new-lock-period uint))
+	(begin
+		(try! (is-dao-or-extension))
+		(ok (var-set lock-period new-lock-period))))
+
+(define-public (set-locked-liquidity (updates (list 200 { owner: principal, pool-id: uint, amount: uint, end-burn-block: uint })))
+	(begin
+		(try! (is-dao-or-extension))
+		(ok (fold update-locked-liquidity updates true))))
+
+(define-public (set-burnt-liquidity (updates (list 200 { pool-id: uint, burnt-liquidity: uint })))
+  (begin
+    (try! (is-dao-or-extension))
+    (ok (fold update-burnt-liquidity updates true))))
+
+;; private calls
+
+(define-private (update-locked-liquidity (entry { owner: principal, pool-id: uint, amount: uint, end-burn-block: uint }) (previous-result bool))
+  (begin
+    (map-set locked-liquidity { owner: (get owner entry), pool-id: (get pool-id entry) } { amount: (get amount entry), end-burn-block: (get end-burn-block entry) })
+    (add-owner-to-locked-liquidity (get pool-id entry) (get owner entry))
+		(print { notification: "manual-set-locked-liquidity", payload: { owner: (get owner entry), pool-id: (get pool-id entry), amount: (get amount entry), end-burn-block: (get end-burn-block entry) }})
+    true))
+
+(define-private (update-burnt-liquidity (entry { pool-id: uint, burnt-liquidity: uint }) (previous-result bool))
+  (begin
+      (map-set burnt-liquidity (get pool-id entry) (get burnt-liquidity entry))
+      (print { notification: "manual-set-burn-liquidity", payload: { amount: (get burnt-liquidity entry), pool-id: (get pool-id entry) }})
+      true))
+
+(define-private (add-owner-to-locked-liquidity (pool-id uint) (owner principal))
+	(let (
+			(current-owners (default-to (list) (map-get? locked-liquidity-owners pool-id))))
+		(and (is-none (index-of current-owners owner)) (map-set locked-liquidity-owners pool-id (unwrap-panic (as-max-len? (append current-owners owner) u200))))))
+
+(define-private (accumulate-locked-liquidity (owner principal) (state { pool-id: uint, result: (list 200 { owner: principal, amount: uint, end-burn-block: uint }) }))
+  (let (
+      (locked-info (get-locked-liquidity-or-default owner (get pool-id state)))
+      (amount (get amount locked-info))
+      (end-burn-block (get end-burn-block locked-info))
+      (new-entry { owner: owner, amount: amount, end-burn-block: end-burn-block }))
+    { pool-id: (get pool-id state), result: (unwrap-panic (as-max-len? (append (get result state) new-entry) u200)) }))
+

+ 5 - 0
stacks/contracts/dependencies/proposal-trait.clar

@@ -0,0 +1,5 @@
+(define-trait proposal-trait
+	(
+		(execute (principal) (response bool uint))
+	)
+)

+ 299 - 0
stacks/contracts/dependencies/self-listing-helper-v3.clar

@@ -0,0 +1,299 @@
+;; SPDX-License-Identifier: BUSL-1.1
+
+;; =================================
+;; Self Listing Helper V3
+;; =================================
+;;
+;; This contract enables permissionless creation of AMM pools through two main paths:
+;;
+;; 1. Standard Pool Creation (create):
+;;    - For tokens that are already approved in the system
+;;    - Requires token-x to be whitelisted and have sufficient balance
+;;    - Enforces standard pool parameters and security checks
+;;
+;; 2. Permissionless Pool Creation (create2):
+;;    - Allows creation of pools with new, unregistered tokens
+;;    - Uses a verification system to ensure token contract legitimacy:
+;;      a. Verifies the token contract deployment on Stacks blockchain
+;;      b. Checks contract code matches an approved template
+;;      c. Validates deployment proof using Stacks block headers
+;;    - The verify-deploy mechanism works by:
+;;      1. Taking deployment transaction proof from Stacks blockchain
+;;      2. Verifying the contract code matches a whitelisted template
+;;      3. Confirming the deployment transaction was properly mined
+;;      This ensures only legitimate token contracts can be used
+;;
+;; Additional Features:
+;; - Liquidity locking/burning mechanisms
+;; - Fee rebate management
+;; - Token approval governance
+;; - Pool parameter configuration
+;;
+;; The contract combines permissioned and permissionless approaches to enable
+;; safe, flexible AMM pool creation while maintaining system security.
+
+(use-trait ft-trait 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.trait-sip-010.sip-010-trait)
+
+(define-constant err-not-authorised (err u1000))
+(define-constant err-token-not-approved (err u1002))
+(define-constant err-insufficient-balance (err u1003))
+(define-constant err-pool-exists (err u1004))
+(define-constant err-invalid-lock-parameter (err u1005))
+
+(define-constant ONE_8 u100000000)
+(define-constant MAX_UINT u340282366920938463463374607431768211455)
+
+(define-constant NONE 0x00)
+(define-constant LOCK 0x01)
+(define-constant BURN 0x02)
+
+(define-constant tx-version (if is-in-mainnet 0x00 0x80))
+(define-constant curr-chain-id (if is-in-mainnet 0x00000001 0x80000000))
+(define-constant standard-auth-type 0x04)
+(define-constant p2pkh-hash-mode 0x00)
+(define-constant pub-key-encoding 0x00)
+(define-constant anchor-mode 0x03)
+(define-constant post-conditions-mode-allow 0x01)
+(define-constant post-conditions 0x00000000)
+(define-constant versioned-smart-contract 0x06)
+(define-constant clarity-version 0x03)
+
+(define-data-var wrapped-token-template (list 20 (string-ascii 5000)) (list))
+
+(define-map approved-token-x principal { approved: bool, min-x: uint })
+
+(define-data-var fee-rebate uint u50000000)
+
+(define-map wrap-token-map principal principal)
+
+;; read-only calls
+
+(define-read-only (is-dao-or-extension)
+  (ok (asserts! (or (is-eq tx-sender 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.executor-dao) (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.executor-dao is-extension contract-caller)) err-not-authorised)))
+
+(define-read-only (get-approved-token-x-or-default (token-x principal))
+    (default-to { approved: false, min-x: MAX_UINT } (map-get? approved-token-x token-x)))
+
+(define-read-only (get-fee-rebate)
+	(var-get fee-rebate))
+
+(define-read-only (get-lock-period)
+	(contract-call? .liquidity-locker get-lock-period))
+
+(define-read-only (get-locked-liquidity-or-default (owner principal) (pool-id uint))
+	(contract-call? .liquidity-locker get-locked-liquidity-or-default owner pool-id))
+
+(define-read-only (get-locked-liquidity-for-pool-or-default (pool-id uint))
+  (contract-call? .liquidity-locker get-locked-liquidity-for-pool-or-default pool-id))
+
+(define-read-only (get-burnt-liquidity-or-default (pool-id uint))
+	(contract-call? .liquidity-locker get-burnt-liquidity-or-default pool-id))
+
+(define-read-only (get-wrapped-token-contract-code (token principal))
+  (let ((token-str (unwrap-panic (as-max-len? (principal-to-string token) u100)))
+        (template (var-get wrapped-token-template)))
+    (get result (fold join-template-parts-iter 
+      template 
+      {token: token-str, result: ""}))))
+
+(define-read-only (principal-to-string (p principal))
+	(let (
+			(destructed (match (principal-destruct? p) ok-value ok-value err-value err-value))
+			(checksum (unwrap-panic (slice? (sha256 (sha256 (concat (get version destructed) (get hash-bytes destructed)))) u0 u4)))
+			(data (unwrap-panic (as-max-len? (concat (get hash-bytes destructed) checksum) u24)))
+			(result (concat (concat "S" (unwrap-panic (element-at? C32 (buff-to-uint-be (get version destructed))))) (append-leading-0 data (trim-leading-0 (hash-bytes-to-string data))))))
+		(match (get name destructed) n (concat (concat result ".") n) result)))
+
+(define-read-only (verify-deploy
+	(verify-params {
+		nonce: (buff 8),
+		fee-rate: (buff 8),
+		signature: (buff 65),
+		contract: principal,
+		token-y: principal,
+		proof: { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint},
+		tx-block-height: uint,
+		block-header-without-signer-signatures: (buff 712) }))
+	(contract-call? .code-body-prover is-contract-deployed 
+		(get nonce verify-params)
+		(get fee-rate verify-params)
+		(get signature verify-params)
+		(get contract verify-params)
+		(contract-call? .clarity-stacks-helper string-ascii-to-buffer (get-wrapped-token-contract-code (get token-y verify-params)))
+		(get proof verify-params)
+		(get tx-block-height verify-params)
+		(get block-header-without-signer-signatures verify-params)))
+
+;; public calls			
+
+(define-public (create
+  (request-details {
+		token-x-trait: <ft-trait>, token-y-trait: <ft-trait>,
+		factor: uint,
+		bal-x: uint, bal-y: uint,
+		fee-rate-x: uint, fee-rate-y: uint,
+		max-in-ratio: uint, max-out-ratio: uint,
+		threshold-x: uint, threshold-y: uint,
+		oracle-enabled: bool, oracle-average: uint,
+		start-block: uint,
+		lock: (buff 1) }))
+  (let (
+			(token-y-trait (get token-y-trait request-details)))
+		(try! (pre-check request-details))
+		(asserts! (< u0 (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-vault-v2-01 get-reserve (contract-of token-y-trait))) err-token-not-approved)
+		(print { type: "create", request: request-details })
+		(post-check request-details)))
+		
+(define-public (create2
+    (request-details {
+        token-x-trait: <ft-trait>, token-y-trait: <ft-trait>,
+				factor: uint,
+        bal-x: uint, bal-y: uint,
+        fee-rate-x: uint, fee-rate-y: uint,
+        max-in-ratio: uint, max-out-ratio: uint,
+        threshold-x: uint, threshold-y: uint,
+        oracle-enabled: bool, oracle-average: uint,
+        start-block: uint,
+				lock: (buff 1) })
+		(verify-params {
+			nonce: (buff 8),
+			fee-rate: (buff 8),
+			signature: (buff 65),
+			contract: principal,
+			token-y: principal,
+			proof: { tx-index: uint, hashes: (list 14 (buff 32)), tree-depth: uint},
+			tx-block-height: uint,
+			block-header-without-signer-signatures: (buff 712) }))
+  (let ((token-y-trait (get token-y-trait request-details)))
+		(asserts! (is-eq (contract-of token-y-trait) (get contract verify-params)) err-token-not-approved)
+		(try! (pre-check request-details))
+		(try! (verify-deploy verify-params))
+		(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-vault-v2-01 set-approved-token (contract-of token-y-trait) true))
+		(map-set wrap-token-map (get token-y verify-params) (get contract verify-params))
+		(print { type: "create2", request: request-details, verify: verify-params })
+		(post-check request-details)))
+
+(define-public (lock-liquidity (amount uint) (pool-id uint))
+	(contract-call? .liquidity-locker lock-liquidity amount pool-id))
+
+(define-public (burn-liquidity (amount uint) (pool-id uint))
+	(contract-call? .liquidity-locker burn-liquidity amount pool-id))
+
+(define-public (claim-liquidity (pool-id uint))
+	(contract-call? .liquidity-locker claim-liquidity pool-id))
+
+ ;; governance calls
+
+(define-public (approve-token-x (token principal) (approved bool) (min-x uint))
+    (begin
+        (try! (is-dao-or-extension))
+        (ok (map-set approved-token-x token { approved: approved, min-x: min-x }))))
+
+(define-public (set-fee-rebate (new-fee-rebate uint))
+	(begin 
+		(try! (is-dao-or-extension))
+		(ok (var-set fee-rebate new-fee-rebate))))
+
+(define-public (set-wrapped-token-template (new-template (list 20 (string-ascii 5000))))
+  (begin
+    (try! (is-dao-or-extension))
+    (ok (var-set wrapped-token-template new-template))))
+
+;; private calls
+
+(define-private (pre-check 
+	(request-details {
+		token-x-trait: <ft-trait>, token-y-trait: <ft-trait>,
+		factor: uint,
+		bal-x: uint, bal-y: uint,
+		fee-rate-x: uint, fee-rate-y: uint,
+		max-in-ratio: uint, max-out-ratio: uint,
+		threshold-x: uint, threshold-y: uint,
+		oracle-enabled: bool, oracle-average: uint,
+		start-block: uint,
+		lock: (buff 1) }))
+	(let (
+			(token-x-trait (get token-x-trait request-details))
+			(token-y-trait (get token-y-trait request-details))
+			(token-x-details (get-approved-token-x-or-default (contract-of token-x-trait))))
+		(asserts! (get approved token-x-details) err-token-not-approved)
+    (asserts! (>= (get bal-x request-details) (get min-x token-x-details)) err-insufficient-balance)
+    (asserts! (and 
+      (is-none (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 get-pool-exists (contract-of token-x-trait) (contract-of token-y-trait) (get factor request-details)))
+      (is-none (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 get-pool-exists (contract-of token-y-trait) (contract-of token-x-trait) (get factor request-details))))
+        err-pool-exists)
+    (asserts! (or (is-eq (get lock request-details) LOCK) (is-eq (get lock request-details) BURN) (is-eq (get lock request-details) NONE)) err-invalid-lock-parameter)
+		(ok true)))
+
+(define-private (post-check
+	(request-details {
+		token-x-trait: <ft-trait>, token-y-trait: <ft-trait>,
+		factor: uint,
+		bal-x: uint, bal-y: uint,
+		fee-rate-x: uint, fee-rate-y: uint,
+		max-in-ratio: uint, max-out-ratio: uint,
+		threshold-x: uint, threshold-y: uint,
+		oracle-enabled: bool, oracle-average: uint,
+		start-block: uint,
+		lock: (buff 1) }))
+	(let (
+			(token-x-trait (get token-x-trait request-details))
+			(token-y-trait (get token-y-trait request-details))
+			(token-x (contract-of token-x-trait))
+			(token-y (contract-of token-y-trait))
+			(factor (get factor request-details))
+			(supply (get supply (try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 create-pool token-x-trait token-y-trait factor tx-sender (get bal-x request-details) (get bal-y request-details)))))
+			(pool-id (get pool-id (try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 get-pool-details token-x token-y factor)))))						
+		(and (is-eq (get lock request-details) LOCK) (try! (lock-liquidity supply pool-id)))
+		(and (is-eq (get lock request-details) BURN) (try! (burn-liquidity supply pool-id)))
+		(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 set-fee-rate-x token-x token-y factor (get fee-rate-x request-details)))
+		(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 set-fee-rate-y token-x token-y factor (get fee-rate-y request-details)))
+		(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 set-max-in-ratio token-x token-y factor (get max-in-ratio request-details)))
+		(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 set-max-out-ratio token-x token-y factor (get max-out-ratio request-details)))
+		(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 set-threshold-x token-x token-y factor (get threshold-x request-details)))
+		(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 set-threshold-y token-x token-y factor (get threshold-y request-details)))
+		(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 set-oracle-enabled token-x token-y factor (get oracle-enabled request-details)))
+		(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 set-oracle-average token-x token-y factor (get oracle-average request-details)))
+		(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-pool-v2-01 set-start-block token-x token-y factor (get start-block request-details)))
+		(try! (contract-call? 'SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM.amm-registry-v2-01 set-fee-rebate token-x token-y factor (var-get fee-rebate)))
+    (ok pool-id)))	
+
+(define-constant C32 "0123456789ABCDEFGHJKMNPQRSTVWXYZ")
+(define-constant LIST_15 (list 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0))
+(define-constant LIST_24 (list 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0))
+(define-constant LIST_39 (concat LIST_24 LIST_15))
+
+(define-private (c32-to-string-iter (idx int) (it { s: (string-ascii 39), r: uint }))
+	{ s: (unwrap-panic (as-max-len? (concat (unwrap-panic (element-at? C32 (mod (get r it) u32))) (get s it)) u39)), r: (/ (get r it) u32) })
+
+(define-private (hash-bytes-to-string (data (buff 24)))
+	(let (
+			;; fixed-length: 8 * 15 / 5 = 24
+			(low-part (get s (fold c32-to-string-iter LIST_24 { s: "", r: (buff-to-uint-be (unwrap-panic (as-max-len? (unwrap-panic (slice? data u9 u24)) u16)))})))
+			;; fixed-length: ceil(8 * 9 / 5) = 15
+			(high-part (get s (fold c32-to-string-iter LIST_15 { s: "", r: (buff-to-uint-be (unwrap-panic (as-max-len? (unwrap-panic (slice? data u0 u9)) u16)))}))))
+		(unwrap-panic (as-max-len? (concat high-part low-part) u39))))
+
+(define-private (trim-leading-0-iter (idx int) (it (string-ascii 39)))
+	(if (is-eq (element-at? it u0) (some "0")) (unwrap-panic (slice? it u1 (len it))) it))
+
+(define-private (trim-leading-0 (s (string-ascii 39)))
+	(fold trim-leading-0-iter LIST_39 s))
+
+(define-private (append-leading-0-iter (idx int) (it { hash-bytes: (buff 24), address: (string-ascii 39)}))
+	(if (is-eq (element-at? (get hash-bytes it) u0) (some 0x00))
+		{ hash-bytes: (unwrap-panic (slice? (get hash-bytes it) u1 (len (get hash-bytes it)))), address: (unwrap-panic (as-max-len? (concat "0" (get address it)) u39)) }
+		it))
+
+(define-private (append-leading-0 (hash-bytes (buff 24)) (s (string-ascii 39)))
+	(get address (fold append-leading-0-iter LIST_24 { hash-bytes: hash-bytes, address: s })))
+
+(define-private (join-template-parts-iter (part (string-ascii 5000)) (state {token: (string-ascii 100), result: (string-ascii 10000)}))
+  {
+    token: (get token state),
+    result: (unwrap-panic (as-max-len? 
+      (concat 
+        (get result state) 
+        (if (is-eq (len (get result state)) u0) part (concat (get token state) part)))
+      u10000))
+  })

+ 182 - 0
stacks/contracts/dependencies/token-amm-pool-v2-01.clar

@@ -0,0 +1,182 @@
+(impl-trait .trait-semi-fungible.semi-fungible-trait)
+(define-constant ERR-NOT-AUTHORIZED (err u1000))
+(define-constant ERR-TOO-MANY-POOLS (err u2004))
+(define-constant ERR-INVALID-BALANCE (err u1001))
+(define-constant ERR-TRANSFER-FAILED (err u3000))
+(define-constant ONE_8 u100000000)
+(define-fungible-token amm-pool-v2-01-token)
+(define-map token-balances {token-id: uint, owner: principal} uint)
+(define-map token-supplies uint uint)
+(define-map token-owned principal (list 200 uint))
+(define-data-var token-name (string-ascii 32) "amm-pool-v2-01-token")
+(define-data-var token-symbol (string-ascii 32) "amm-pool-v2-01-token")
+(define-data-var token-uri (optional (string-utf8 256)) (some u"https://cdn.alexlab.co/metadata/token-amm-pool-v2-01.json"))
+(define-data-var token-decimals uint u8)
+(define-data-var transferrable bool true)
+(define-read-only (is-dao-or-extension)
+	(ok (asserts! (or (is-eq tx-sender .executor-dao) (contract-call? .executor-dao is-extension contract-caller)) ERR-NOT-AUTHORIZED)))
+(define-read-only (get-transferrable)
+	(ok (var-get transferrable)))
+(define-read-only (get-token-owned (owner principal))
+    (default-to (list) (map-get? token-owned owner)))
+(define-read-only (get-balance (token-id uint) (who principal))
+	(ok (get-balance-or-default token-id who)))
+(define-read-only (get-overall-balance (who principal))
+	(ok (ft-get-balance amm-pool-v2-01-token who)))
+(define-read-only (get-total-supply (token-id uint))
+	(ok (default-to u0 (map-get? token-supplies token-id))))
+(define-read-only (get-overall-supply)
+	(ok (ft-get-supply amm-pool-v2-01-token)))
+(define-read-only (get-decimals (token-id uint))
+	(ok (var-get token-decimals)))
+(define-read-only (get-token-uri (token-id uint))
+	(ok (var-get token-uri)))
+(define-read-only (get-name (token-id uint))
+	(ok (var-get token-name)))
+(define-read-only (get-symbol (token-id uint))
+	(ok (var-get token-symbol)))
+(define-read-only (get-total-supply-fixed (token-id uint))
+  	(ok (decimals-to-fixed (default-to u0 (map-get? token-supplies token-id)))))
+(define-read-only (get-balance-fixed (token-id uint) (who principal))
+  	(ok (decimals-to-fixed (get-balance-or-default token-id who))))
+(define-read-only (get-overall-supply-fixed)
+	(ok (decimals-to-fixed (ft-get-supply amm-pool-v2-01-token))))
+(define-read-only (get-overall-balance-fixed (who principal))
+	(ok (decimals-to-fixed (ft-get-balance amm-pool-v2-01-token who))))
+(define-read-only (get-token-balance-owned-in-fixed (owner principal))
+	(begin 
+		(match (map-get? token-owned owner)
+			token-ids
+			(map 
+				create-tuple-token-balance 
+				token-ids 
+				(map 
+					get-balance-or-default
+					token-ids
+					(list 
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+						owner	owner	owner	owner	owner	owner	owner	owner	owner	owner
+					)
+				)
+			)
+			(list)
+		)
+	)
+)
+(define-public (set-transferrable (new-transferrable bool))
+	(begin 
+		(try! (is-dao-or-extension))
+		(ok (var-set transferrable new-transferrable))))
+(define-public (set-decimals (new-decimals uint))
+	(begin
+		(try! (is-dao-or-extension))
+		(ok (var-set token-decimals new-decimals))))
+(define-public (set-token-uri (new-uri (optional (string-utf8 256))))
+	(begin
+		(try! (is-dao-or-extension))
+		(ok (var-set token-uri new-uri))))
+(define-public (set-name (new-name (string-ascii 32)))
+	(begin
+		(try! (is-dao-or-extension))
+		(ok (var-set token-name new-name))))
+(define-public (set-symbol (new-symbol (string-ascii 10)))
+	(begin
+		(try! (is-dao-or-extension))
+		(ok (var-set token-symbol new-symbol))))
+(define-public (mint (token-id uint) (amount uint) (recipient principal))
+	(begin
+		(try! (is-dao-or-extension))
+		(try! (ft-mint? amm-pool-v2-01-token amount recipient))
+		(try! (set-balance token-id (+ (get-balance-or-default token-id recipient) amount) recipient))
+		(map-set token-supplies token-id (+ (unwrap-panic (get-total-supply token-id)) amount))
+		(print {type: "sft_mint", token-id: token-id, amount: amount, recipient: recipient})
+		(ok true)))
+(define-public (burn (token-id uint) (amount uint) (sender principal))
+	(begin
+		(try! (is-dao-or-extension))
+		(try! (ft-burn? amm-pool-v2-01-token amount sender))
+		(try! (set-balance token-id (- (get-balance-or-default token-id sender) amount) sender))
+		(map-set token-supplies token-id (- (unwrap-panic (get-total-supply token-id)) amount))
+		(print {type: "sft_burn", token-id: token-id, amount: amount, sender: sender})
+		(ok true)))
+(define-public (mint-fixed (token-id uint) (amount uint) (recipient principal))
+  	(mint token-id (fixed-to-decimals amount) recipient))
+(define-public (burn-fixed (token-id uint) (amount uint) (sender principal))
+  	(burn token-id (fixed-to-decimals amount) sender))
+(define-public (transfer (token-id uint) (amount uint) (sender principal) (recipient principal))
+	(let (
+			(sender-balance (get-balance-or-default token-id sender)))
+		(asserts! (var-get transferrable) ERR-TRANSFER-FAILED)
+		(asserts! (is-eq tx-sender sender) ERR-NOT-AUTHORIZED)
+		(asserts! (<= amount sender-balance) ERR-INVALID-BALANCE)
+		(try! (ft-transfer? amm-pool-v2-01-token amount sender recipient))
+		(try! (set-balance token-id (- sender-balance amount) sender))
+		(try! (set-balance token-id (+ (get-balance-or-default token-id recipient) amount) recipient))
+		(print {type: "sft_transfer", token-id: token-id, amount: amount, sender: sender, recipient: recipient})
+		(ok true)))
+(define-public (transfer-memo (token-id uint) (amount uint) (sender principal) (recipient principal) (memo (buff 34)))
+	(let (
+			(sender-balance (get-balance-or-default token-id sender)))
+		(asserts! (var-get transferrable) ERR-TRANSFER-FAILED)
+		(asserts! (is-eq tx-sender sender) ERR-NOT-AUTHORIZED)
+		(asserts! (<= amount sender-balance) ERR-INVALID-BALANCE)
+		(try! (ft-transfer? amm-pool-v2-01-token amount sender recipient))
+		(try! (set-balance token-id (- sender-balance amount) sender))
+		(try! (set-balance token-id (+ (get-balance-or-default token-id recipient) amount) recipient))
+		(print {type: "sft_transfer", token-id: token-id, amount: amount, sender: sender, recipient: recipient, memo: memo})
+		(ok true)))
+(define-public (transfer-fixed (token-id uint) (amount uint) (sender principal) (recipient principal))
+  	(transfer token-id (fixed-to-decimals amount) sender recipient))
+(define-public (transfer-memo-fixed (token-id uint) (amount uint) (sender principal) (recipient principal) (memo (buff 34)))
+  	(transfer-memo token-id (fixed-to-decimals amount) sender recipient memo))
+(define-public (transfer-many (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal})))
+	(fold transfer-many-iter transfers (ok true)))
+(define-public (transfer-many-memo (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)})))
+	(fold transfer-many-memo-iter transfers (ok true)))
+(define-public (transfer-many-fixed (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal})))
+	(fold transfer-many-fixed-iter transfers (ok true)))
+(define-public (transfer-many-memo-fixed (transfers (list 200 {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)})))
+	(fold transfer-many-memo-fixed-iter transfers (ok true)))
+(define-private (pow-decimals)
+  	(pow u10 (unwrap-panic (get-decimals u0))))
+(define-private (fixed-to-decimals (amount uint))
+  	(/ (* amount (pow-decimals)) ONE_8))
+(define-private (decimals-to-fixed (amount uint))
+  	(/ (* amount ONE_8) (pow-decimals)))
+(define-private (transfer-many-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal}) (previous-response (response bool uint)))
+	(match previous-response prev-ok (transfer (get token-id item) (get amount item) (get sender item) (get recipient item)) prev-err previous-response))
+(define-private (transfer-many-memo-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)}) (previous-response (response bool uint)))
+	(match previous-response prev-ok (transfer-memo (get token-id item) (get amount item) (get sender item) (get recipient item) (get memo item)) prev-err previous-response))
+(define-private (transfer-many-fixed-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal}) (previous-response (response bool uint)))
+	(match previous-response prev-ok (transfer-fixed (get token-id item) (get amount item) (get sender item) (get recipient item)) prev-err previous-response))
+(define-private (transfer-many-memo-fixed-iter (item {token-id: uint, amount: uint, sender: principal, recipient: principal, memo: (buff 34)}) (previous-response (response bool uint)))
+	(match previous-response prev-ok (transfer-memo-fixed (get token-id item) (get amount item) (get sender item) (get recipient item) (get memo item)) prev-err previous-response))
+(define-private (create-tuple-token-balance (token-id uint) (balance uint))
+	{ token-id: token-id, balance: (decimals-to-fixed balance) })
+(define-private (set-balance (token-id uint) (balance uint) (owner principal))
+    (begin
+		(and 
+			(is-none (index-of (get-token-owned owner) token-id))
+			(map-set token-owned owner (unwrap! (as-max-len? (append (get-token-owned owner) token-id) u200) ERR-TOO-MANY-POOLS)))	
+	    (map-set token-balances {token-id: token-id, owner: owner} balance)
+        (ok true)))
+(define-private (get-balance-or-default (token-id uint) (who principal))
+	(default-to u0 (map-get? token-balances {token-id: token-id, owner: who})))

+ 6 - 0
stacks/contracts/dependencies/trait-flash-loan-user.clar

@@ -0,0 +1,6 @@
+(use-trait ft-trait .trait-sip-010.sip-010-trait)
+(define-trait flash-loan-user-trait
+  (
+    (execute (<ft-trait> uint (optional (buff 16))) (response bool uint))
+  )
+)

+ 31 - 0
stacks/contracts/dependencies/trait-semi-fungible.clar

@@ -0,0 +1,31 @@
+(define-trait semi-fungible-trait
+	(
+		;; Get a token type balance of the passed principal.
+		(get-balance (uint principal) (response uint uint))
+		;; Get the total SFT balance of the passed principal.
+		(get-overall-balance (principal) (response uint uint))
+		;; Get the current total supply of a token type.
+		(get-total-supply (uint) (response uint uint))
+		;; Get the overall SFT supply.
+		(get-overall-supply () (response uint uint))
+		;; Get the number of decimal places of a token type.
+		(get-decimals (uint) (response uint uint))
+		;; Get an optional token URI that represents metadata for a specific token.
+		(get-token-uri (uint) (response (optional (string-utf8 256)) uint))
+		;; Transfer from one principal to another.
+		(transfer (uint uint principal principal) (response bool uint))
+		;; Transfer from one principal to another with a memo.
+		(transfer-memo (uint uint principal principal (buff 34)) (response bool uint))
+		;; helper functions for fixed notation
+		(transfer-fixed (uint uint principal principal) (response bool uint))
+		(transfer-memo-fixed (uint uint principal principal (buff 34)) (response bool uint))
+    	(get-balance-fixed (uint principal) (response uint uint))
+		(get-overall-balance-fixed (principal) (response uint uint))				
+    	(get-total-supply-fixed (uint) (response uint uint))		
+		(get-overall-supply-fixed () (response uint uint))	
+        (mint (uint uint principal) (response bool uint))
+        (burn (uint uint principal) (response bool uint))  
+        (mint-fixed (uint uint principal) (response bool uint))
+		(burn-fixed (uint uint principal) (response bool uint))  					
+	)
+)

+ 26 - 0
stacks/contracts/dependencies/trait-sip-010.clar

@@ -0,0 +1,26 @@
+(define-trait sip-010-trait
+  (
+    ;; Transfer from the caller to a new principal
+    (transfer (uint principal principal (optional (buff 34))) (response bool uint))
+    ;; the human readable name of the token
+    (get-name () (response (string-ascii 32) uint))
+    ;; the ticker symbol, or empty if none
+    (get-symbol () (response (string-ascii 32) uint))
+    ;; the number of decimals used, e.g. 6 would mean 1_000_000 represents 1 token
+    (get-decimals () (response uint uint))
+    ;; the balance of the passed principal
+    (get-balance (principal) (response uint uint))
+    ;; the current total supply (which does not need to be a constant)
+    (get-total-supply () (response uint uint))
+    ;; an optional URI that represents metadata of this token
+    (get-token-uri () (response (optional (string-utf8 256)) uint))
+    ;; helper functions for 8-digit fixed notation
+    (transfer-fixed (uint principal principal (optional (buff 34))) (response bool uint))
+    (get-balance-fixed (principal) (response uint uint))
+    (get-total-supply-fixed () (response uint uint))    
+    (mint (uint principal) (response bool uint))
+    (burn (uint principal) (response bool uint))  
+    (mint-fixed (uint principal) (response bool uint))
+		(burn-fixed (uint principal) (response bool uint))      
+ )
+)

+ 93 - 0
stacks/contracts/wormhole-core-proxy-v2.clar

@@ -0,0 +1,93 @@
+;; title: wormhole-core-proxy-v2
+;; version: v2
+;; summary: Checks that `core` trait passed is valid and is active core contract
+;; description:
+;;
+;; =============================
+;; Proxy Summary
+;; =============================
+;;
+;; We can't do a proper proxy in Clarity, as dynamic dispatch is limited in order to preserve decidability
+;; The best we can do is have users of a third-party app query the state contract for the current active contract,
+;; and pass it through this contract to validate it.
+;;
+;; This puts a burden on third-party developers to query the state contract and supply a value to users,
+;; but allows them to avoid updating and re-deploying their contracts every time `wormhole-core` updates
+;;
+;; =============================
+;; Proxy Dataflow
+;; =============================
+;;
+;; -----------------------------
+;; 1. Transaction Initiation
+;; -----------------------------
+;;   When a transaction is initiated, it must include the principal of the current active `wormhole-core` contract as an argument to the function call.
+;;   This can be queried from the `wormhole-core-state` by the application and supplied to the user
+;;
+;; -----------------------------
+;; 2. Third-party contract using Wormhole
+;; -----------------------------
+;;   The third-party contract must accept the trait as a function argument and pass it through to this contract (`wormhole-core-proxy-v2`)
+;;   It is not necessary to check the trait argument here
+;;
+;; -----------------------------
+;; 3. Trait Checker (THIS CONTRACT)
+;; -----------------------------
+;;   This will take the trait argument and check it against the currently active `wormhole-core` contract in `wormhole-core-state`.
+;;   If it matches, it will call the the corresponding function in `wormhole-core`.
+;;   If not, an error is returned
+;;
+;; -----------------------------
+;; 4. Wormhole Core Contract
+;; -----------------------------
+;;   The last step is to call the currently active version of `wormhole-core` implementing `core-trait`
+
+;;;; traits
+
+(use-trait core-trait .wormhole-trait-core-v2.core-trait)
+
+;;;; constants
+
+;; No active wormhole-core contract found
+(define-constant ERR_TRAIT_CHECK_NO_ACTIVE_CONTRACT (err u20001))
+;; Trait does not match active contract
+(define-constant ERR_TRAIT_CHECK_CONTRACT_MISMATCH (err u20002))
+
+;;;; Public functions: Proxy for `wormhole-core`
+
+(define-public (get-chain-id (core-contract <core-trait>))
+  (begin
+    (try! (check-active-wormhole-core-contract core-contract))
+    (contract-call? core-contract get-chain-id)))
+
+(define-public (get-message-fee (core-contract <core-trait>))
+  (begin
+    (try! (check-active-wormhole-core-contract core-contract))
+    (contract-call? core-contract get-message-fee)))
+
+(define-public (get-governance-contract (core-contract <core-trait>))
+  (begin
+    (try! (check-active-wormhole-core-contract core-contract))
+    (contract-call? core-contract get-governance-contract)))
+
+(define-public (parse-and-verify-vaa (core-contract <core-trait>) (vaa-bytes (buff 8192)))
+  (begin
+    (try! (check-active-wormhole-core-contract core-contract))
+    (contract-call? core-contract parse-and-verify-vaa vaa-bytes)))
+
+(define-public (post-message (core-contract <core-trait>) (payload (buff 8192)) (nonce uint) (consistency-level-opt (optional uint)))
+  (begin
+    (try! (check-active-wormhole-core-contract core-contract))
+    (contract-call? core-contract post-message-via-proxy payload nonce consistency-level-opt contract-caller)))
+
+(define-public (get-wormhole-address (core-contract <core-trait>) (p principal))
+  (begin
+    (try! (check-active-wormhole-core-contract core-contract))
+    (contract-call? core-contract get-wormhole-address p)))
+
+;;;; Read-only functions
+
+(define-read-only (check-active-wormhole-core-contract (expected-core-contract <core-trait>))
+  (let ((active-core-contract (unwrap! (contract-call? .wormhole-core-state get-active-wormhole-core-contract) ERR_TRAIT_CHECK_NO_ACTIVE_CONTRACT)))
+    (asserts! (is-eq (contract-of expected-core-contract) active-core-contract) ERR_TRAIT_CHECK_CONTRACT_MISMATCH)
+    (ok expected-core-contract)))

+ 288 - 0
stacks/contracts/wormhole-core-state.clar

@@ -0,0 +1,288 @@
+;; Title: wormhole-core-state-01
+;; Version: final
+;; Check for latest version: https://github.com/hirosystems/stacks-wormhole-core#latest-version
+;; Report an issue: https://github.com/hirosystems/stacks-wormhole-core/issues
+
+;; Store the state for `wormhole-core`
+;;
+;; THIS CONTRACT CANNOT BE UPDATED, so it should contain as little logic as possible
+;;
+;; This contract is *specifically* meant for state that can grow unbounded, and eventually too big to export
+;; State with a finite size is stored in `wormhole-core`, and transfered via import/export functions during an update
+;;
+;; This contract does not have a version in its name because there cannot be different versions of it
+;; If you need additional state in the future, use `kv-store` or deploy a `wormhole-core-state-2` contract
+
+;;;; Constants
+
+;; State contract not initialized (no active core contract set)
+(define-constant ERR_STATE_UNINITIALIZED (err u10001))
+;; Caller is not active core contract
+(define-constant ERR_STATE_UNAUTHORIZED (err u10002))
+;; Attempted to initialize already initialized contract
+(define-constant ERR_STATE_ALREADY_INITIALIZED (err u10003))
+;; Value not allowed
+(define-constant ERR_STATE_INVALID_VALUE (err u10004))
+;; VAA has already been used
+(define-constant ERR_STATE_VAA_REPLAYED (err u10005))
+;; No state transfer in progress
+(define-constant ERR_STATE_XFER_NOT_IN_PROGRESS (err u10006))
+;; Sequence number for emitter is too large
+(define-constant ERR_STATE_OVERFLOW_SEQUENCE (err u10007))
+
+;; Max value for unsigned 64-bit integer
+(define-constant MAX_VALUE_U64 u18446744073709551615)
+
+;;;; Data vars
+
+;; Only the owner of this contract can make changes to its state
+;; This defines what the currently active `wormhole-core` contract is
+;; Should never be `none` once contract is initialized
+(define-data-var owner (optional principal) none)
+
+;; New owner we are transferring this contract to during `wormhole-core` update
+;; Should always be `none` except during update process
+(define-data-var transferring-to (optional principal) none)
+
+;;;; Data maps
+
+;; Map to track "Wormhole Address"
+;; In Wormhole protocol, addresses are limited to 32 bytes in size
+;; Since a Stacks Contract principal can be much longer, we use `keccak256(address)` in Wormhole messages
+;; This allows us to use the protocol unmodified
+(define-map wormhole-to-stacks
+  (buff 32)  ;; keccak256(principal)
+  principal  ;; Contract emitting message
+)
+
+;; Inverse of `wormhole-to-stacks`
+;; Each entry in that map must have corresponding entry here
+(define-map stacks-to-wormhole
+  principal  ;; Contract emitting message
+  (buff 32)  ;; keccak256(principal)
+)
+
+;; Map tracking sequence numbers for all contracts which have sent a Wormhole message
+(define-map emitter-sequence
+  principal  ;; Contract emitting message
+  uint       ;; Sequence number of *next* message
+)
+
+;; Map tracking consumed governance VAA hashes to prevent replay attacks
+(define-map consumed-governance-vaa-hashes (buff 32) bool)
+
+;; Map tracking guardian sets
+(define-map guardian-sets
+  uint         ;; Guardian set ID
+  (list 30 {   ;; Max Guardian set size is 30
+    compressed-public-key: (buff 33),
+    uncompressed-public-key: (buff 64)
+  }))
+
+;; Since this contract can't be updated, provide a dynamically-typed key/value store,
+;; which can be used if additional state is required by future core contract updates.
+;;
+;; If additional state is necessary, it may be preferable to deploy another state contract instead of using this map,
+;; depending on the size, complexity, and the performance needs of the additional state
+;;
+;; This option is here to provide options and flexibility for future core contract updates
+(define-map kv-store (string-ascii 32) (buff 4096))
+
+;;;; Public functions: Update process
+
+;; @desc Initialize newly deployed contract with owner (active core contract)
+;;
+;; Returns:
+;;   - (ok true): If state contract initialized
+;;   - (err ...): If already initialized or invalid argument
+(define-public (initialize (core-contract principal))
+  (let ((contract-parts (unwrap! (principal-destruct? core-contract) ERR_STATE_INVALID_VALUE)))
+    ;; Can't call this function more than once
+    (asserts! (is-none (get-owner)) ERR_STATE_ALREADY_INITIALIZED)
+    ;; Check we have a contract principal and not a standard principal
+    (asserts! (is-some (get name contract-parts)) ERR_STATE_INVALID_VALUE)
+    ;; Checks passed! Contract is initialized
+    (var-set owner (some core-contract))
+    (ok true)))
+
+;; @desc: Start ownership transfer: Set successor contract which will be allowed to claim ownership of this contract
+;;        The 2-step process prevents transferring ownership to a broken deployment and permanently losing access to the state contract
+(define-public (start-ownership-transfer (new-owner principal))
+  (let ((current-owner (try! (check-caller-is-owner)))
+        (new-owner-parts (unwrap! (principal-destruct? new-owner) ERR_STATE_INVALID_VALUE)))
+    ;; Can't transfer to current owner
+    (asserts! (not (is-eq new-owner current-owner)) ERR_STATE_INVALID_VALUE)
+    ;; Check we have a contract principal and not a standard principal
+    (asserts! (is-some (get name new-owner-parts)) ERR_STATE_INVALID_VALUE)
+    ;; Checks passed! Ownership transfer in progress
+    (var-set transferring-to (some new-owner))
+    (ok true)))
+
+;; @desc: Finalize ownership transfer: Set successor contract as owner
+;;        Called by the contract we are transferring ownership to, NOT the current owner
+;;        This is the only public function that does NOT call `check-caller-is-owner`
+(define-public (finalize-ownership-transfer)
+  (let ((new-owner (unwrap! (var-get transferring-to) ERR_STATE_XFER_NOT_IN_PROGRESS)))
+    ;; Check that the contract caller is who we are transferring to
+    (asserts! (is-eq contract-caller new-owner) ERR_STATE_UNAUTHORIZED)
+    ;; Checks passed! Ownership transfer complete
+    (var-set owner (some new-owner))
+    (var-set transferring-to none)
+    (ok true)))
+
+;;;; Public functions: Owned by `wormhole-core`
+
+;; These functions "belong" to `wormhole-core` and cannot be called by anyone else
+;; ALL FUNCTIONS HERE MUST CALL `check-caller-is-owner`
+
+;; @desc: Post a message to watchers
+;;        This needs to be in the state contract so the source address of these messages never changes
+(define-public (post-message (payload (buff 8192)) (nonce uint) (consistency-level uint) (emitter principal))
+  (let ((check-caller (try! (check-caller-is-owner)))
+        (result (try! (consume-emitter-sequence emitter)))
+        (message {
+            emitter-principal: emitter,
+            emitter: (get wormhole-address result),
+            nonce: nonce,
+            sequence: (get sequence result),
+            consistency-level: consistency-level,
+            payload: payload
+          }))
+      ;; Message has passed checks, and we got a sequence number, so emit to watchers
+      (print {
+        event: "post-message",
+        data: message
+      })
+      (ok message)))
+
+;; @desc Track hashes of processed governance VAAs so we don't replay them
+;;       Returns `(ok true)` if the VAA is marked as "consumed"
+;;       On failure, returns `(err ...)` does not consume the VAA
+;;
+;; @param hash: Governance VAA hash, computed by `(keccak256 (keccak256 vaa-body))`
+(define-public (consume-governance-vaa (hash (buff 32)))
+  (begin
+    (try! (check-caller-is-owner))
+    (asserts! (map-insert consumed-governance-vaa-hashes hash true) ERR_STATE_VAA_REPLAYED)
+    (ok true)))
+
+;; @desc Set raw buffer in key/value store using `map-insert` (fails if entry exists)
+;;       Caller is responsible for serializing data with `to-consensus-buff?`
+;;       If no error, returns `(ok bool)` with the result of `map-insert`
+(define-public (kv-store-insert (key (string-ascii 32)) (value (buff 4096)))
+  (begin
+    (try! (check-caller-is-owner))
+    (ok (map-insert kv-store key value))))
+
+;; @desc Set raw buffer in key/value store using `map-set` (overwrites existing entries)
+;;       Caller is responsible for serializing data with `to-consensus-buff?`
+;;       If no error, returns `(ok bool)` with the result of `map-set`
+(define-public (kv-store-set (key (string-ascii 32)) (value (buff 4096)))
+  (begin
+    (try! (check-caller-is-owner))
+    (ok (map-set kv-store key value))))
+
+;; @desc Set guardian set
+;;       Does not check if index already exists, that is responsibility of core contract
+;;       If no error, returns `(ok bool)` with the result of `map-set`
+;; @param set-id: Guardian set ID
+;; @param guardian-set: List of 19 Guardian pubkeys
+(define-public (guardian-sets-set (set-id uint) (guardian-set (list 30 { compressed-public-key: (buff 33), uncompressed-public-key: (buff 64) })))
+  (begin
+    (try! (check-caller-is-owner))
+    (ok (map-set guardian-sets set-id guardian-set))))
+
+;; @desc Map "Wormhole address" to `principal` and vice versa
+;;       On success, returns tuple with the following fields:
+;;        - `created`: `true` if address was generated and added to cache
+;;        - `wormhole-address`: 32-byte Wormhole address
+(define-public (get-wormhole-address (p principal))
+  (begin
+    (try! (check-caller-is-owner))
+    (asserts! (is-standard p) ERR_STATE_INVALID_VALUE)
+    (ok (inner-get-wormhole-address p))))
+
+;;;; Private functions
+
+;; @desc Get next sequence # for emitter and mark it as used
+;;       On success, returns `(ok {wormhole-address, created, sequence})` and increments emitter's sequence
+;;       On failure, returns `(err uint)` does not increment sequence
+(define-private (consume-emitter-sequence (emitter principal))
+  (let ((wormhole-address (inner-get-wormhole-address emitter))
+        (sequence (default-to u0 (emitter-sequence-get emitter))))
+    ;; If `sequence` has reached its limit we cannot continue
+    (asserts! (<= sequence MAX_VALUE_U64) ERR_STATE_OVERFLOW_SEQUENCE)
+    (map-set emitter-sequence emitter (+ sequence u1))
+    (ok (merge
+      wormhole-address
+      {
+        sequence: sequence
+      }))))
+
+;; @desc Unchecked version of `register-wormhole-address`
+(define-private (inner-get-wormhole-address (p principal))
+  (match (stacks-to-wormhole-get p)
+    ;; Wormhole address already in map
+    addr {
+      created: false,
+      wormhole-address: addr
+    }
+    ;; Not in map, compute address
+    (let ((p-as-string (contract-call? 'SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC.self-listing-helper-v3 principal-to-string p))
+          (addr (keccak256 (string-ascii-to-buff p-as-string))))
+      (map-set wormhole-to-stacks addr p)
+      (map-set stacks-to-wormhole p addr)
+      {
+        created: true,
+        wormhole-address: addr
+      })))
+
+;;;; Read-only functions
+
+;; These functions do not modify state and can be called by anyone
+
+;; @desc Check that the calling contract is the owner (currently active `wormhole-core` contract)
+;;       This must be called in any function that modifies state
+(define-read-only (check-caller-is-owner)
+  (let ((current-owner (unwrap! (get-owner) ERR_STATE_UNINITIALIZED)))
+    (asserts! (is-eq contract-caller current-owner) ERR_STATE_UNAUTHORIZED)
+    (ok current-owner)))
+
+;; @desc Returns contract owner, which is allowed to modify state
+(define-read-only (get-owner)
+  (var-get owner))
+
+;; @desc Returns currently active wormhole-core contract (defined by `owner`)
+(define-read-only (get-active-wormhole-core-contract)
+  (get-owner))
+
+;; @desc Helper function so that we can hash a string
+(define-read-only (string-ascii-to-buff (s (string-ascii 256)))
+  (let ((cb (unwrap-panic (to-consensus-buff? s))))
+    ;; Consensus buff format for string:
+    ;;   bytes[0]:     Consensus Buff Type
+    ;;   bytes[1..4]:  String length
+    ;;   bytes[5..]:   String data
+    (unwrap-panic (slice? cb u5 (len cb)))))
+
+;;;; Read-only functions: <map_name>-get
+
+;; These functions simply call `map-get?` on the given map
+
+(define-read-only (stacks-to-wormhole-get (p principal))
+  (map-get? stacks-to-wormhole p))
+
+(define-read-only (wormhole-to-stacks-get (hash (buff 32)))
+  (map-get? wormhole-to-stacks hash))
+
+(define-read-only (emitter-sequence-get (p principal))
+  (map-get? emitter-sequence p))
+
+(define-read-only (kv-store-get (key (string-ascii 32)))
+  (map-get? kv-store key))
+
+(define-read-only (guardian-sets-get (set-id uint))
+  (map-get? guardian-sets set-id))
+
+(define-read-only (consumed-governance-vaa-hashes-get (hash (buff 32)))
+  (map-get? consumed-governance-vaa-hashes hash))

+ 1120 - 0
stacks/contracts/wormhole-core-v4.clar

@@ -0,0 +1,1120 @@
+;; Title: wormhole-core
+;; Version: v4
+;; Check for latest version: https://github.com/hirosystems/stacks-wormhole-core#latest-version
+;; Report an issue: https://github.com/hirosystems/stacks-wormhole-core/issues
+
+;; Contracts using the Wormhole protocol can interact directly with this contract, but it
+;; is recommended instead to interact with it via proxy (`wormhole-core-proxy`)
+;; Using the proxy allows you to use the latest version of `wormhole-core` without updating your contract code
+
+;;;; Contract Principals
+
+;; WARNING: THESE MAY NEED TO BE CHANGED WHEN UPDATING THE CORE CONTRACT!
+
+;; The contract that is allowed to act as a proxy and specify `emitter` to `post-message`
+;; This can be a constant because it will not change as long as the contract is active
+(define-constant PRINCIPAL_PROXY_CONTRACT .wormhole-core-proxy-v2)
+
+;;;; Traits
+
+;; Implements trait specified in wormhole-core-trait contract
+(impl-trait .wormhole-trait-core-v2.core-trait)
+(impl-trait .wormhole-trait-governance-v1.governance-trait)
+(impl-trait .wormhole-trait-export-v1.export-trait)
+
+;; Export trait used previous contract that we are importing from
+;; May not match the version of `export-trait` this contract implements
+(use-trait previous-export-trait .wormhole-trait-export-v1.export-trait)
+
+;;;; Constants
+
+;; VAA version not supported
+(define-constant ERR_VAA_PARSING_VERSION (err u1001))
+;; Unable to extract the guardian set-id from the VAA
+(define-constant ERR_VAA_PARSING_GUARDIAN_SET_ID (err u1002))
+;; Unable to extract the number of signatures from the VAA
+(define-constant ERR_VAA_PARSING_SIGNATURES_LEN (err u1003))
+;; Unable to extract the signatures from the VAA
+(define-constant ERR_VAA_PARSING_SIGNATURES (err u1004))
+;; Unable to extract the timestamp from the VAA
+(define-constant ERR_VAA_PARSING_TIMESTAMP (err u1005))
+;; Unable to extract the nonce from the VAA
+(define-constant ERR_VAA_PARSING_NONCE (err u1006))
+;; Unable to extract the emitter chain from the VAA
+(define-constant ERR_VAA_PARSING_EMITTER_CHAIN (err u1007))
+;; Unable to extract the emitter address from the VAA
+(define-constant ERR_VAA_PARSING_EMITTER_ADDRESS (err u1008))
+;; Unable to extract the sequence from the VAA
+(define-constant ERR_VAA_PARSING_SEQUENCE (err u1009))
+;; Unable to extract the consistency level from the VAA
+(define-constant ERR_VAA_PARSING_CONSISTENCY_LEVEL (err u1010))
+;; Unable to extract the payload from the VAA
+(define-constant ERR_VAA_PARSING_PAYLOAD (err u1011))
+;; Unable to extract the hash the payload from the VAA
+(define-constant ERR_VAA_HASHING_BODY (err u1012))
+
+;; Unknown `version` number
+(define-constant ERR_VAA_CHECKS_VERSION_UNSUPPORTED (err u1101))
+;; Number of valid signatures insufficient (min: 2/3 * num_guardians + 1)
+(define-constant ERR_VAA_CHECKS_THRESHOLD_SIGNATURE (err u1102))
+;; Guardian signature not comprised in guardian set specified
+(define-constant ERR_VAA_CHECKS_GUARDIAN_SET_CONSISTENCY (err u1103))
+
+;; Guardian Set Upgrade: error parsing `index`
+(define-constant ERR_GSU_PARSING_INDEX (err u1201))
+;; Guardian Set Upgrade: error parsing `length`
+(define-constant ERR_GSU_PARSING_GUARDIAN_LEN (err u1202))
+;; Guardian Set Upgrade: guardians payload is malformed
+(define-constant ERR_GSU_PARSING_GUARDIANS_BYTES (err u1203))
+;; Guardian Set Upgrade: error parsing pubkeys
+(define-constant ERR_GSU_UNCOMPRESSED_PUBLIC_KEYS (err u1204))
+
+;; Guardian Set Upgrade: new index invalid
+(define-constant ERR_GSU_CHECK_INDEX (err u1301))
+;; Guardian Set Upgrade: caller invalid
+(define-constant ERR_GSU_CHECK_CALLER (err u1302))
+;; Overlay present in vaa bytes
+(define-constant ERR_GSU_CHECK_OVERLAY (err u1303))
+;; Empty guardian set
+(define-constant ERR_GSU_EMPTY_GUARDIAN_SET (err u1304))
+;; Guardian Set Upgrade: emission payload unauthorized
+(define-constant ERR_GSU_DUPLICATED_GUARDIAN_ADDRESSES (err u1305))
+
+;; Post Message: Consistency level is too large
+(define-constant ERR_POST_OVERFLOW_CONSISTENCY_LEVEL (err u1401))
+;; Post Message: Nonce is too large
+(define-constant ERR_POST_OVERFLOW_NONCE (err u1402))
+
+;; Wormhole Governance: error parsing `module`
+(define-constant ERR_GOV_PARSING_MODULE (err u1501))
+;; Wormhole Governance: error parsing `action`
+(define-constant ERR_GOV_PARSING_ACTION (err u1502))
+;; Wormhole Governance: error parsing `chain`
+(define-constant ERR_GOV_PARSING_CHAIN (err u1503))
+;; Wormhole Governance: error parsing governance VAA payload
+(define-constant ERR_GOV_PARSING_PAYLOAD (err u1504))
+;; Wormhole Governance: `module` is not expected value
+(define-constant ERR_GOV_CHECK_MODULE (err u1505))
+;; Wormhole Governance: `action` is not expected value for this message type
+(define-constant ERR_GOV_CHECK_ACTION (err u1506))
+;; Wormhole Governance: `chain` does not match this blockchain
+(define-constant ERR_GOV_CHECK_CHAIN (err u1507))
+;; Wormhole Governance: Message not from authorized chain/emitter
+(define-constant ERR_GOV_CHECK_EMITTER (err u1508))
+;; Wormhole Governance: Tried to use more than max number of guardians allowed
+(define-constant ERR_GOV_MAX_GUARDIANS_EXCEEDED (err u1509))
+;; Governance VAA not signed by the most recent Guardian Set on VAA
+(define-constant ERR_GOV_VAA_OLD_GUARDIAN_SET (err u1510))
+
+;; Set Message Fee: error parsing first 128 bits of `fee`
+(define-constant ERR_SMF_PARSING_FEE_1 (err u1601))
+;; Set Message Fee: error parsing second 128 bits of `fee`
+(define-constant ERR_SMF_PARSING_FEE_2 (err u1602))
+;; Set Message Fee: overlay present in vaa bytes
+(define-constant ERR_SMF_CHECK_OVERLAY (err u1603))
+;; Set Message Fee: `fee` value too high
+(define-constant ERR_SMF_CHECK_FEE (err u1604))
+
+;; Transfer Fees: error parsing first 128 bits of `fee`
+(define-constant ERR_TXF_PARSING_AMOUNT_1 (err u1701))
+;; Transfer Fees: error parsing second 128 bits of `fee`
+(define-constant ERR_TXF_PARSING_AMOUNT_2 (err u1702))
+;; Transfer Fees: error parsing `recipient`
+(define-constant ERR_TXF_PARSING_RECIPIENT (err u1703))
+;; Transfer Fees: overlay present in vaa bytes
+(define-constant ERR_TXF_CHECK_OVERLAY (err u1704))
+;; Transfer Fees: `amount` value too high
+(define-constant ERR_TXF_CHECK_AMOUNT (err u1705))
+;; Transfer Fees: `recipient` hash is not registered with state contract
+(define-constant ERR_TXF_LOOKUP_RECIPIENT_ADDRESS (err u1706))
+;; Transfer Fees: `recipient` is not a valid address on this network
+(define-constant ERR_TXF_CHECK_RECIPIENT_ADDRESS (err u1707))
+
+;; Contract Upgrade: error parsing `contract`
+(define-constant ERR_UPG_PARSING_CONTRACT (err u1801))
+;; Contract Upgrade: overlay present in vaa bytes
+(define-constant ERR_UPG_CHECK_OVERLAY (err u1802))
+;; Contract Upgrade: `contract` is not valid contract address on this network
+(define-constant ERR_UPG_CHECK_CONTRACT_ADDRESS (err u1803))
+;; Contract Upgrade: Unauthorized successor contract
+(define-constant ERR_UPG_UNAUTHORIZED (err u1804))
+;; Contract Upgrade: Previous contract invalid
+(define-constant ERR_UPG_PREV_CONTRACT_INVALID (err u1805))
+
+;; Deployment State: This deployment is not the active core contract
+(define-constant ERR_DEPLOYMENT_STATE_NOT_ACTIVE (err u1901))
+;; Deployment State: Tried to initialize new contract more than once
+(define-constant ERR_DEPLOYMENT_STATE_ALREADY_INITIALIZED (err u1902))
+
+;; Misc. errors
+;; Call not from allowed proxy contract
+(define-constant ERR_PROXY_UNAUTHORIZED (err u2001))
+;; Unable to get stacks timestamp
+(define-constant ERR_STACKS_TIMESTAMP (err u2002))
+;; Guardian set has not been initialized
+(define-constant ERR_NO_GUARDIAN_SET (err u2003))
+;; No guardians found for guardian set
+(define-constant ERR_NO_GUARDIANS (err u2004))
+
+;; Wormhole Governance: emitting chain
+(define-constant GOV_EMITTING_CHAIN u1)
+;; Wormhole Governance: emitting address
+(define-constant GOV_EMITTING_ADDRESS 0x0000000000000000000000000000000000000000000000000000000000000004)
+;; Wormhole Governance: Message intended for all chains
+(define-constant GOV_BROADCAST_CHAIN_ID 0x0000)
+;; Wormhole Governance: ContractUpgrade action
+(define-constant GOV_ACTION_CONTRACT_UPGRADE u1)
+;; Wormhole Governance: GuardianSetUpgrade action
+(define-constant GOV_ACTION_GUARDIAN_SET_UPDATE u2)
+;; Wormhole Governance: SetMessageFee action
+(define-constant GOV_ACTION_SET_MESSAGE_FEE u3)
+;; Wormhole Governance: TransferFees action
+(define-constant GOV_ACTION_TRANSFER_FEES u4)
+;; Wormhole Governance: Maximum number of Wormhole Guardians possible
+(define-constant GOV_MAX_GUARDIANS u30)
+;; Stacks chain ID in Wormhole protocol
+(define-constant WORMHOLE_STACKS_CHAIN_ID 0x003c)
+;; Core string module
+(define-constant CORE_STRING_MODULE 0x00000000000000000000000000000000000000000000000000000000436f7265)
+;; Guardian eth address size
+(define-constant GUARDIAN_ETH_ADDRESS_SIZE u20)
+;; 24 hours in seconds
+(define-constant TWENTY_FOUR_HOURS u86400)
+;; Default consistency level for emitted messages
+(define-constant DEFAULT_CONSISTENCY_LEVEL u0)
+;; Max value for unsigned 8-bit integer
+(define-constant MAX_VALUE_U8 u255)
+;; Max value for unsigned 32-bit integer
+(define-constant MAX_VALUE_U32 u4294967295)
+;; List of `u0` with length of GOV_MAX_GUARDIANS
+(define-constant EMPTY_LIST_MAX_GUARDIANS (list u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0 u0))
+(asserts! (is-eq (len EMPTY_LIST_MAX_GUARDIANS) GOV_MAX_GUARDIANS) ERR_GOV_MAX_GUARDIANS_EXCEEDED)
+;; Empty 16-byte buffer
+(define-constant EMPTY_BUFFER_16 0x00000000000000000000000000000000)
+;; Contract deployer
+(define-constant DEPLOYER tx-sender)
+
+;;;; Data vars
+
+;; `deployment-state` can have one of the following values:
+(define-constant DEPLOYMENT_STATE_UNITIALIZED u0) ;; This deployment has not been initialized and is not ready for use
+(define-constant DEPLOYMENT_STATE_ACTIVE      u1) ;; This deployment has been initialized and is the active wormhole core contract
+(define-constant DEPLOYMENT_STATE_DEPRECATED  u2) ;; This deployment has been upgraded and is no longer active
+;; The state of this particular deployment of the core contract
+(define-data-var deployment-state uint DEPLOYMENT_STATE_UNITIALIZED)
+;; If we have recieved a ContractUpgrade VAA, this is the contract we are upgrading to
+;; We receive the hash of the address in the VAA. Store it this way so we don't have to register the sucessor contract manually with `get-wormhole-address`
+(define-data-var successor-contract (optional { wormhole-address: (buff 32), set-at-burn-block: uint }) none)
+
+;; ----- DATA VARS BELOW THIS LINE MUST BE EXPORTED IN `get-exported-vars`! -----
+
+;; Keep track of the active guardian set-id
+(define-data-var active-guardian-set-id (optional uint) none)
+;; Keep track of exiting guardian set
+(define-data-var previous-guardian-set (optional {set-id: uint, expires-at: uint}) none)
+;; Fee to post message to Wormhole guardians
+(define-constant MINIMUM_MESSAGE_FEE u1)
+(define-data-var message-fee uint MINIMUM_MESSAGE_FEE)
+
+;;;; Data maps
+
+;; WARNING: It's not a good idea to store maps in this contract!
+;; There is no limit to how big they can get and this can make import/export during a ContractUpgrade operation impossible
+;; Please use a separate state contract like `wormhole-core-state` for maps!
+
+;;;; Public functions: Getters
+
+(define-read-only (get-chain-id) (ok WORMHOLE_STACKS_CHAIN_ID))
+(define-read-only (get-successor-contract) (var-get successor-contract))
+(define-read-only (get-deployment-state) (var-get deployment-state))
+(define-read-only (get-message-fee) (ok (var-get message-fee)))
+(define-read-only (get-governance-contract) (ok { chain-id: GOV_EMITTING_CHAIN, address: GOV_EMITTING_ADDRESS }))
+;;;; Public functions
+
+;; @desc Initialize this contract and maybe other Wormhole contracts
+;;       Must be called before any other public functions (not necessary for read-only)
+;;
+;; @param previous-contract: If this is not the first deployment of wormhole-core, the currently active contract is required
+(define-public (initialize (previous-contract (optional <previous-export-trait>)))
+  (match previous-contract
+    ;; Transfer state from previous contract
+    contract (initialize-from-previous-contract contract)
+    ;; This is the first deployment, initialize all Wormhole state
+    (initialize-wormhole)))
+
+;; @desc Returns true if this is the currently active core contract deployment (can only be one at any time)
+(define-read-only (is-active-deployment)
+  (is-eq (get-deployment-state) DEPLOYMENT_STATE_ACTIVE))
+
+;; @desc Like `is-active-deployment` but returns error instead of bool
+(define-read-only (check-active-deployment)
+  (begin
+    (asserts! (is-active-deployment) ERR_DEPLOYMENT_STATE_NOT_ACTIVE)
+    (ok true)))
+
+;; @desc Get state stored in *this* contract that can be exported during contract upgrade.
+;;       THIS DOES NOT EXPORT STATE FROM `wormhole-core-state` CONTRACT!
+(define-read-only (get-exported-vars) {
+  active-guardian-set-id: (var-get active-guardian-set-id),
+  previous-guardian-set: (var-get previous-guardian-set),
+  message-fee: (var-get message-fee)})
+
+;; @desc Deactivate this contract, transfer state contract ownership, and return this contract's state
+;;       Can only be called by successor contract
+;;
+;; Steps to update `wormhole-core` contract:
+;;   1. Make some changes to `wormhole-core` and deploy new contract
+;;   2. Governance contract publishes VAA with address of updated `wormhole-core` contract
+;;   3. Someone (anyone) calls `contract-upgrade` with the VAA
+;;   4. `contract-upgrade` will validate the VAA and record the address hash of the new contract
+;;   5. Someone (anyone) calls new contract's `import-state` with the old contract's address to initialize it
+;;   6. New contract's `import-state` calls old contract's `export-state`, which does the following:
+;;     a. Checks that `contract-caller` is the one set by the ContractUpgrade VAA
+;;     b. Initialize ownership transfer of `wormhole-state-core` contract
+;;     c. Sets it's internal state as "deprecated"
+;;     d. Transfer all STX owned by this contract to new contract
+;;     e. Returns old contract's exportable state variables
+;;   7. New contract's `import-state` then does the following:
+;;     a. Finalize ownership transfer of `wormhole-state-core` contract
+;;     b. Initializes it's state variables to those returned by `export-state`
+;;     c. Sets it's internal state to "active"
+;;
+;; NOTE: In future versions of this contract, `export-state` may require newer version of `export-trait` than `import-state`
+(define-public (export-state)
+  (let ((active (try! (check-active-deployment)))
+        (contract-principal (get-contract-principal))
+        (stx-balance (stx-get-balance contract-principal))
+        (caller contract-caller)
+        (caller-parts (unwrap! (principal-destruct? caller) ERR_UPG_CHECK_CONTRACT_ADDRESS))
+        (wormhole-address (get wormhole-address (try! (contract-call? .wormhole-core-state get-wormhole-address caller))))
+        (new-wormhole-address (get wormhole-address (unwrap! (get-successor-contract) ERR_UPG_UNAUTHORIZED))))
+    ;; Check we have a contract principal and not a standard principal
+    (asserts! (is-some (get name caller-parts)) ERR_UPG_CHECK_CONTRACT_ADDRESS)
+    ;; Only the contract set by the ContractUpgrade VAA is allowed to call this function
+    (asserts! (is-eq wormhole-address new-wormhole-address) ERR_UPG_UNAUTHORIZED)
+    ;; Transfer ownership of state contract
+    (try! (contract-call? .wormhole-core-state start-ownership-transfer caller))
+    ;; If we have an STX balance, transfer to new contract
+    (if (> stx-balance u0)
+      (try! (as-contract (stx-transfer? stx-balance tx-sender caller)))
+      true)
+    (var-set deployment-state DEPLOYMENT_STATE_DEPRECATED)
+    (ok (get-exported-vars))))
+
+;; @desc Parse and check the validity of a Verified Action Approval (VAA)
+;; @param vaa-bytes: VAA as raw bytes
+(define-read-only (parse-and-verify-vaa (vaa-bytes (buff 8192)))
+    (let ((message (try! (parse-vaa vaa-bytes)))
+          (guardian-set-id (get guardian-set-id (get vaa message))))
+      ;; Ensure that the guardian-set-id is the active one or unexpired previous one
+      (asserts! (try! (is-valid-guardian-set guardian-set-id)) ERR_VAA_CHECKS_GUARDIAN_SET_CONSISTENCY)
+      (let ((active-guardians (unwrap! (contract-call? .wormhole-core-state guardian-sets-get guardian-set-id) ERR_VAA_CHECKS_GUARDIAN_SET_CONSISTENCY))
+            (signatures-from-active-guardians (fold batch-check-active-public-keys (get recovered-public-keys message)
+              {
+                  active-guardians: active-guardians,
+                  result: (list)
+              })))
+        ;; Ensure that version is supported (v1 only)
+        (asserts! (is-eq (get version (get vaa message)) u1)
+          ERR_VAA_CHECKS_VERSION_UNSUPPORTED)
+        ;; Ensure that the count of valid signatures is >= 13
+        (asserts! (>= (len (get result signatures-from-active-guardians)) (get-quorum (len active-guardians)))
+          ERR_VAA_CHECKS_THRESHOLD_SIGNATURE)
+        ;; Good to go!
+        (ok {
+          vaa: (get vaa message),
+          vaa-body-hash: (get vaa-body-hash message)
+        }))))
+
+;; @desc Update the active set of guardians
+;; @param guardian-set-vaa: VAA embedding the Guardian Set Update information
+;; @param uncompressed-public-keys: uncompressed public keys, used for recomputing
+;; the addresses embedded in the VAA. `secp256k1-verify` returns a compressed
+;; public key, and uncompressing the key in clarity would be inefficient and expensive.
+(define-public (guardian-set-upgrade (guardian-set-vaa (buff 8192)) (uncompressed-public-keys (list 30 (buff 64))))
+  (let ((active-set-id (var-get active-guardian-set-id))
+        (message (match active-set-id
+          ;; We have a guardian set, so check VAA signatures
+          set-id (let ((verified-message (try! (parse-and-verify-vaa guardian-set-vaa))))
+            ;; If it's not the first guardian set, then the upgrade must come from the current Guardian Set.
+            (asserts! (is-eq set-id (get guardian-set-id (get vaa verified-message))) ERR_GOV_VAA_OLD_GUARDIAN_SET)
+            verified-message)
+          ;; This is initial guardian set, so we can't check signatures
+          (begin
+            (asserts! (is-eq contract-caller DEPLOYER) ERR_GSU_CHECK_CALLER)
+            (try! (parse-vaa guardian-set-vaa)))))
+        (vaa (get vaa message))
+        (hash (get vaa-body-hash message))
+        (governance-message (try! (parse-and-verify-guardian-set-upgrade (get payload vaa))))
+        (guardians-message (get payload governance-message))
+        (new-set-id (get new-index guardians-message))
+        (eth-addresses (get guardians-eth-addresses guardians-message))
+        (consolidated-public-keys (fold check-and-consolidate-public-keys
+          uncompressed-public-keys
+          { cursor: u0, eth-addresses: eth-addresses, result: (list) }))
+        (result (get result consolidated-public-keys)))
+
+    ;; Ensure that enough uncompressed-public-keys were provided
+    (try! (fold is-valid-guardian-entry result (ok true)))
+    (asserts! (is-eq (len uncompressed-public-keys) (len eth-addresses))
+      ERR_GSU_UNCOMPRESSED_PUBLIC_KEYS)
+    ;; Check emitting address and chain
+    (try! (check-emitter (get emitter-address vaa) (get emitter-chain vaa)))
+    ;; ensure guardian set has atleast one member
+    (asserts! (>= (len result) u1) ERR_GSU_EMPTY_GUARDIAN_SET)
+    ;; Replay protection for Governance VAAs.
+    ;; Theoretically, the increasing Guardian Set index should be sufficient for protection. 
+    (try! (contract-call? .wormhole-core-state consume-governance-vaa hash))
+
+    ;; Update storage
+    (match active-set-id
+      ;; Check that id is incremented by exactly 1
+      id (asserts! (is-eq new-set-id (+ u1 id)) ERR_GSU_CHECK_INDEX)
+      ;; Initial guardian set, allow any value
+      true)
+    (try! (contract-call? .wormhole-core-state guardian-sets-set new-set-id result))
+    (try! (set-new-guardian-set-id new-set-id))
+    ;; Emit Event
+    (print {
+      event: "governance-action",
+      action: "GuardianSetUpgrade",
+      hash: hash,
+      data: {
+        id: new-set-id,
+        guardians: {
+          eth-addresses: eth-addresses,
+          public-keys: uncompressed-public-keys,
+        }
+      }
+    })
+    (ok {
+      vaa: vaa,
+      result: {
+        guardians-eth-addresses: eth-addresses,
+        guardians-public-keys: uncompressed-public-keys
+      }
+    })))
+
+(define-read-only (get-active-guardian-set)
+  (let ((set-id (unwrap! (var-get active-guardian-set-id) ERR_NO_GUARDIAN_SET))
+        (guardians (unwrap! (contract-call? .wormhole-core-state guardian-sets-get set-id) ERR_NO_GUARDIANS)))
+    (ok {
+      set-id: set-id,
+      guardians: guardians
+    })))
+
+;; @desc Post message for Wormhole Guardians from our proxy contract
+;;       Allows caller to set `emitter`, so ONLY allow calls from trusted proxy contract
+;;
+;; @param payload: Raw data to send, specific to the contract sending the message
+;; @param nonce: 32-bit nonce set by emitter
+;; @param consistency-level-opt: Optional, use custom level instead of default
+;; @param emitter: Principal that proxy captured using `contract-caller`
+(define-public (post-message-via-proxy (payload (buff 8192)) (nonce uint) (consistency-level-opt (optional uint)) (emitter principal))
+  (begin
+    (asserts! (is-eq contract-caller PRINCIPAL_PROXY_CONTRACT) ERR_PROXY_UNAUTHORIZED)
+    (inner-post-message payload nonce consistency-level-opt emitter)))
+
+;; @desc Post message for Wormhole Guardians
+;;
+;; @param payload: Raw data to send, specific to the contract sending the message
+;; @param nonce: 32-bit nonce set by emitter
+;; @param consistency-level-opt: Optional, use custom level instead of default
+(define-public (post-message (payload (buff 8192)) (nonce uint) (consistency-level-opt (optional uint)))
+  (inner-post-message payload nonce consistency-level-opt contract-caller))
+
+;; @desc Set message fee for `post-message` using VAA
+;;       Returns `ok(fee)` if successful
+;;
+;; @param vaa-bytes: VAA as raw bytes
+(define-public (set-message-fee (vaa-bytes (buff 8192)))
+  (let ((message (try! (parse-and-verify-set-message-fee vaa-bytes)))
+        (vaa (get vaa message))
+        (hash (get vaa-body-hash message))
+        (gov-payload (get payload (get payload vaa)))
+        (fee (get fee gov-payload)))
+
+    ;; --- Message has been validated, check if we can apply update ---
+    ;; Check we are not re-processing an old message
+    (try! (contract-call? .wormhole-core-state consume-governance-vaa hash))
+
+    ;; --- Commit State ---
+    (var-set message-fee fee)
+
+    ;; Emit event
+    (print {
+      event: "governance-action",
+      action: "SetMessageFee",
+      hash: hash,
+      data: {
+        fee: fee
+      }
+    })
+    (ok fee)))
+
+;; @desc Parse and validate VAA for updating `post-message` fee
+;;       Returns parsed VAA as tuple if successful
+;;
+;; @param vaa-bytes: VAA as raw bytes
+(define-read-only (parse-and-verify-set-message-fee (vaa-bytes (buff 8192)))
+  (let ((message (try! (parse-and-verify-vaa vaa-bytes)))
+        (vaa (get vaa message)))
+
+    ;; Check emitting address and chain
+    (try! (check-emitter (get emitter-address vaa) (get emitter-chain vaa)))
+
+    ;; Governance actions can only be performed by the most recent Guardian Set
+    (try! (check-governance-vaa-guardian-set-id (get guardian-set-id (get vaa message))))
+
+    ;; Replace payload raw bytes with parsed payload
+    (ok {
+      vaa: (merge vaa {
+        payload: (try! (parse-set-message-fee-payload (get payload vaa)))
+      }),
+      vaa-body-hash: (get vaa-body-hash message)
+    })))
+
+;; @desc Transfer message fees accumulated from `post-message` using VAA
+;;       Returns `ok({amount, recipient})` if successful
+;;
+;; @param vaa-bytes: VAA as raw bytes
+(define-public (transfer-fees (vaa-bytes (buff 8192)))
+  (let ((message (try! (parse-and-verify-transfer-fees vaa-bytes)))
+        (vaa (get vaa message))
+        (hash (get vaa-body-hash message))
+        (gov-payload (get payload (get payload vaa)))
+        (amount (get amount gov-payload))
+        (recipient (get recipient gov-payload)))
+
+    ;; --- Message has been validated, check if we can apply update ---
+    ;; Check we are not re-processing an old message
+    (try! (contract-call? .wormhole-core-state consume-governance-vaa hash))
+
+    ;; --- Execute Action ---
+    (try! (as-contract (stx-transfer? amount tx-sender recipient)))
+
+    ;; Emit event
+    (print {
+      event: "governance-action",
+      action: "TransferFees",
+      hash: hash,
+      data: {
+        recipient: recipient,
+        amount: amount
+      }
+    })
+    (ok {
+      recipient: recipient,
+      amount: amount
+    })))
+
+;; @desc Parse and validate VAA for transferring `post-message` fees
+;;       Returns parsed VAA as tuple if successful
+;;
+;; @param vaa-bytes: VAA as raw bytes
+(define-read-only (parse-and-verify-transfer-fees (vaa-bytes (buff 8192)))
+  (let ((message (try! (parse-and-verify-vaa vaa-bytes)))
+        (vaa (get vaa message)))
+
+    ;; Check emitting address and chain
+    (try! (check-emitter (get emitter-address vaa) (get emitter-chain vaa)))
+
+    ;; Governance actions can only be performed by the most recent Guardian Set
+    (try! (check-governance-vaa-guardian-set-id (get guardian-set-id (get vaa message))))
+
+    ;; Replace payload raw bytes with parsed payload
+    (ok {
+      vaa: (merge vaa {
+        payload: (try! (parse-transfer-fees-payload (get payload vaa)))
+      }),
+      vaa-body-hash: (get vaa-body-hash message)
+    })))
+
+;; @desc Upgrade wormhole-core contract to new deployment
+;;       Returns `(ok contract)` if successful
+;;
+;; @param vaa-bytes: VAA as raw bytes
+(define-public (contract-upgrade (vaa-bytes (buff 8192)))
+  (let ((message (try! (parse-and-verify-contract-upgrade vaa-bytes)))
+        (vaa (get vaa message))
+        (hash (get vaa-body-hash message))
+        (gov-payload (get payload (get payload vaa)))
+        (contract (get contract gov-payload)))
+
+    ;; --- Message has been validated, check if we can apply update ---
+    ;; Check we are not re-processing an old message
+    (try! (contract-call? .wormhole-core-state consume-governance-vaa hash))
+
+    ;; --- Commit State ---
+    ;; Save address and keep this contract active for now.
+    ;; When successor contract calls this contract's `export-state` function, this contract will transfer state ownership and then deactivate
+    ;; This verifies new contract actually exists before this one becomes unusable
+    ;; Also keep track of when this was set
+    (var-set successor-contract (some {
+      wormhole-address: contract,
+      set-at-burn-block: burn-block-height
+    }))
+
+    ;; Emit event
+    (print {
+      event: "governance-action",
+      action: "ContractUpgrade",
+      hash: hash,
+      data: {
+        contract: contract
+      }
+    })
+    (ok contract)))
+
+;; @desc Parse and validate VAA for updating `wormhole-core` contract
+;;       Returns parsed VAA as tuple if successful
+;;
+;; @param vaa-bytes: VAA as raw bytes
+(define-read-only (parse-and-verify-contract-upgrade (vaa-bytes (buff 8192)))
+  (let ((message (try! (parse-and-verify-vaa vaa-bytes)))
+        (vaa (get vaa message)))
+
+    ;; Check emitting address and chain
+    (try! (check-emitter (get emitter-address vaa) (get emitter-chain vaa)))
+
+    ;; Governance actions can only be performed by the most recent Guardian Set
+    (try! (check-governance-vaa-guardian-set-id (get guardian-set-id (get vaa message))))
+
+    ;; Replace payload raw bytes with parsed payload
+    (ok {
+      vaa: (merge vaa {
+        payload: (try! (parse-contract-upgrade-payload (get payload vaa)))
+      }),
+      vaa-body-hash: (get vaa-body-hash message)
+    })))
+
+;; @desc Get or generate new "Wormhole address" for a Stacks `prrincipal` that can be used in Wormhole messages
+;;       Addresses in the Wormhole protocol are limited to 32 bytes, but a Stacks `principal` can be longer than this
+(define-public (get-wormhole-address (p principal))
+  (contract-call? .wormhole-core-state get-wormhole-address p))
+
+;;;; Private functions
+
+;; @desc Parse a Verified Action Approval (VAA)
+;;
+;; VAA Header
+;; byte        version             (VAA Version)
+;; u32         guardian_set_index  (Indicates which guardian set is signing)
+;; u8          len_signatures      (Number of signatures stored)
+;; [][66]byte  signatures          (Collection of ecdsa signatures)
+;;
+;; VAA Body
+;; u32         timestamp           (Timestamp of the block where the source transaction occurred)
+;; u32         nonce               (A grouping number)
+;; u16         emitter_chain       (Wormhole ChainId of emitter contract)
+;; [32]byte    emitter_address     (Emitter contract address, in Wormhole format)
+;; u64         sequence            (Strictly increasing sequence, tied to emitter address & chain)
+;; u8          consistency_level   (What finality level was reached before emitting this message)
+;; []byte      payload             (VAA message content)
+;;
+;; @param vaa-bytes: VAA as raw bytes
+(define-private (parse-vaa (vaa-bytes (buff 8192)))
+  (let ((active (try! (check-active-deployment)))
+        (version (unwrap! (read-uint-8 vaa-bytes u0)
+          ERR_VAA_PARSING_VERSION))
+        (guardian-set-id (unwrap! (read-uint-32 vaa-bytes u1)
+          ERR_VAA_PARSING_GUARDIAN_SET_ID))
+        (signatures-len (unwrap! (read-uint-8 vaa-bytes u5)
+          ERR_VAA_PARSING_SIGNATURES_LEN))
+        (check-signatures-len (asserts! (<= signatures-len GOV_MAX_GUARDIANS)
+          ERR_GOV_MAX_GUARDIANS_EXCEEDED))
+        (result-signatures (fold batch-read-signatures
+          EMPTY_LIST_MAX_GUARDIANS
+          {
+              bytes: (unwrap! (slice? vaa-bytes u6 (len vaa-bytes)) ERR_VAA_PARSING_SIGNATURES),
+              value: (list),
+              iter: signatures-len
+          }))
+        (signatures (get value result-signatures))
+        (end-signatures (get bytes result-signatures))
+        (vaa-body-hash (keccak256 (keccak256 (unwrap! (read-buff-8192-max end-signatures u0 none)
+          ERR_VAA_HASHING_BODY))))
+        (timestamp (unwrap! (read-uint-32 end-signatures u0)
+          ERR_VAA_PARSING_TIMESTAMP))
+        (nonce (unwrap! (read-uint-32 end-signatures u4)
+          ERR_VAA_PARSING_NONCE))
+        (emitter-chain (unwrap! (read-uint-16 end-signatures u8)
+          ERR_VAA_PARSING_EMITTER_CHAIN))
+        (emitter-address (unwrap! (read-buff-32 end-signatures u10)
+          ERR_VAA_PARSING_EMITTER_ADDRESS))
+        (sequence (unwrap! (read-uint-64 end-signatures u42)
+          ERR_VAA_PARSING_SEQUENCE))
+        (consistency-level (unwrap! (read-uint-8 end-signatures u50)
+          ERR_VAA_PARSING_CONSISTENCY_LEVEL))
+        (payload (unwrap! (read-buff-8192-max end-signatures u51 none)
+          ERR_VAA_PARSING_PAYLOAD))
+        (public-keys-results (fold batch-recover-public-keys signatures {
+          message-hash: vaa-body-hash,
+          value: (list)
+        })))
+    (ok {
+        vaa: {
+          version: version,
+          guardian-set-id: guardian-set-id,
+          signatures-len: signatures-len,
+          signatures: signatures,
+          timestamp: timestamp,
+          nonce: nonce,
+          emitter-chain: emitter-chain,
+          emitter-address: emitter-address,
+          sequence: sequence,
+          consistency-level: consistency-level,
+          payload: payload,
+        },
+        recovered-public-keys: (get value public-keys-results),
+        vaa-body-hash: vaa-body-hash,
+    })))
+
+;; @desc Parse and check the header of a VAA containing a governance payload
+;;       Returns parsed payload as tuple if successful
+;;
+;; Expected message format for VAA governance payload header:
+;;   [32]byte    module     Should be "Core"
+;;   u8          action     Should be `3` for this message type
+;;   u16         chain      Chain this action is intended for
+;;
+;; @param vaa-payload: VAA `payload` as raw bytes
+(define-private (parse-governance-payload-header (gov-action uint) (vaa-payload (buff 8192)))
+  (let ((module (unwrap! (read-buff-32 vaa-payload u0)
+          ERR_GOV_PARSING_MODULE))
+        (action (unwrap! (read-uint-8 vaa-payload u32)
+          ERR_GOV_PARSING_ACTION))
+        (chain (unwrap! (read-buff-2 vaa-payload u33)
+          ERR_GOV_PARSING_CHAIN))
+        (payload (unwrap! (read-buff-8192-max vaa-payload u35 none)
+          ERR_GOV_PARSING_PAYLOAD)))
+
+    ;; Ensure that this message was emitted from authorized module
+    (asserts! (is-eq module CORE_STRING_MODULE)
+      ERR_GOV_CHECK_MODULE)
+    ;; Ensure that this message is matching the expected action
+    (asserts! (is-eq action gov-action)
+      ERR_GOV_CHECK_ACTION)
+    ;; Ensure that this message is addressed to this chain
+    (if (is-eq gov-action GOV_ACTION_GUARDIAN_SET_UPDATE)
+      ;; Only allow broadcast for GuardianSetUpgrade
+      (asserts! (or (is-eq chain WORMHOLE_STACKS_CHAIN_ID) (is-eq chain GOV_BROADCAST_CHAIN_ID)) ERR_GOV_CHECK_CHAIN)
+      (asserts! (is-eq chain WORMHOLE_STACKS_CHAIN_ID) ERR_GOV_CHECK_CHAIN))
+
+    ;; --- Checks Passed, Return Parsed Payload ---
+    (ok {
+      module: module,
+      action: action,
+      chain: (buff-to-uint-be chain),
+      payload: payload,
+    })))
+
+;; @desc Parse and check VAA payload for `set-message-fee`
+;;       Returns parsed payload as tuple if successful
+;;
+;; Expected message format for SetMessageFee payload:
+;;   [35]byte    header     Governance payload header
+;;   u256        fee        New fee to emit message (in uSTX)
+;;
+;; @param vaa-payload: VAA `payload` as raw bytes
+(define-private (parse-set-message-fee-payload (vaa-payload (buff 8192)))
+  (let ((gov-message (try! (parse-governance-payload-header GOV_ACTION_SET_MESSAGE_FEE vaa-payload)))
+        (gov-payload (get payload gov-message))
+        (fee-first-128-bits (unwrap! (read-buff-16 gov-payload u0)
+          ERR_SMF_PARSING_FEE_1))
+        (fee (unwrap! (read-uint-128 gov-payload u16)
+          ERR_SMF_PARSING_FEE_2)))
+
+    ;; --- Validate Message Data ---
+    ;; Check no extra bytes in buffer
+    (asserts! (is-eq (len gov-payload) u32)
+      ERR_SMF_CHECK_OVERLAY)
+    ;; Check fee fits into `u128`, because that's the max value clarity can represent
+    (asserts! (is-eq fee-first-128-bits EMPTY_BUFFER_16)
+      ERR_SMF_CHECK_FEE)
+    ;; Check fee isn't below minimum
+    (asserts! (>= fee MINIMUM_MESSAGE_FEE)
+      ERR_SMF_CHECK_FEE)
+
+    ;; --- Checks Passed, Return Parsed Payload ---
+    (ok (merge gov-message {
+      payload: {
+        fee: fee
+      }
+    }))))
+
+;; @desc Parse and check VAA payload for `transfer-fees`
+;;       Returns parsed payload as tuple if successful
+;;
+;; Expected message format for TransferFees payload:
+;;   [35]byte   header     Governance payload header
+;;   u256       amount     Amount to transfer (in uSTX)
+;;   [32]byte   recipient  Hash of recipient address (MUST HAVE BEEN REGISTERED WITH `get-wormhole-address`!!)
+;;
+;; @param vaa-payload: VAA `payload` as raw bytes
+(define-private (parse-transfer-fees-payload (vaa-payload (buff 8192)))
+  (let ((gov-message (try! (parse-governance-payload-header GOV_ACTION_TRANSFER_FEES vaa-payload)))
+        (gov-payload (get payload gov-message))
+        (amount-first-128-bits (unwrap! (read-buff-16 gov-payload u0)
+          ERR_TXF_PARSING_AMOUNT_1))
+        (amount (unwrap! (read-uint-128 gov-payload u16)
+          ERR_TXF_PARSING_AMOUNT_2))
+        (recipient-hash (unwrap! (read-buff-32 gov-payload u32)
+          ERR_TXF_PARSING_RECIPIENT))
+        (recipient (unwrap! (contract-call? .wormhole-core-state wormhole-to-stacks-get recipient-hash)
+          ERR_TXF_LOOKUP_RECIPIENT_ADDRESS)))
+
+    ;; --- Validate Message Data ---
+    ;; Check no extra bytes in buffer
+    (asserts! (is-eq (len gov-payload) u64)
+      ERR_TXF_CHECK_OVERLAY)
+    ;; Check amount fits into `u128`, because that's the max value clarity can represent
+    (asserts! (is-eq amount-first-128-bits EMPTY_BUFFER_16)
+      ERR_TXF_CHECK_AMOUNT)
+    ;; Check recipient address is valid on this network
+    (asserts! (is-standard recipient)
+      ERR_TXF_CHECK_RECIPIENT_ADDRESS)
+
+    ;; --- Checks Passed, Return Parsed Payload ---
+    (ok (merge gov-message {
+      payload: {
+        amount: amount,
+        recipient: recipient,
+      }
+    }))))
+
+;; @desc Parse and check VAA payload for `contract-upgrade`
+;;       Returns parsed payload as tuple if successful
+;;
+;; Expected message format for ContractUpgrade VAA payload:
+;;   [35]byte      header     Governance payload header
+;;   [32]byte      successor  Hash of sucessor contract principal
+;;
+;; @param vaa-payload: VAA `payload` as raw bytes
+(define-private (parse-contract-upgrade-payload (vaa-payload (buff 8192)))
+  (let ((gov-message (try! (parse-governance-payload-header GOV_ACTION_CONTRACT_UPGRADE vaa-payload)))
+        (gov-payload (get payload gov-message))
+        (contract (unwrap! (read-buff-32 gov-payload u0)
+          ERR_UPG_PARSING_CONTRACT)))
+
+    ;; --- Validate Message Data ---
+    ;; Check no extra bytes in buffer
+    (asserts! (is-eq (len gov-payload) u32)
+      ERR_UPG_CHECK_OVERLAY)
+
+    ;; --- Checks Passed, Return Parsed Payload ---
+    (ok (merge gov-message {
+      payload: {
+        contract: contract
+      }
+    }))))
+
+;; @desc Foldable function admitting an uncompressed 64 bytes public key as an input, producing a record { uncompressed-public-key, compressed-public-key }
+(define-private (check-and-consolidate-public-keys
+      (uncompressed-public-key (buff 64))
+      (acc {
+        cursor: uint,
+        eth-addresses: (list 30 (buff 20)),
+        result: (list 30 { compressed-public-key: (buff 33), uncompressed-public-key: (buff 64)})
+      }))
+  (let ((eth-address (unwrap-panic (element-at? (get eth-addresses acc) (get cursor acc))))
+        (compressed-public-key (compress-public-key uncompressed-public-key))
+        (entry (if (is-eth-address-matching-public-key uncompressed-public-key eth-address)
+            { compressed-public-key: compressed-public-key, uncompressed-public-key: uncompressed-public-key }
+            { compressed-public-key: 0x, uncompressed-public-key: 0x })))
+    {
+      cursor: (+ u1 (get cursor acc)),
+      eth-addresses: (get eth-addresses acc),
+      result: (unwrap-panic (as-max-len? (append (get result acc) entry) u30)),
+    }))
+
+;; @desc Foldable function admitting an guardian input and their signature as an input, producing a record { recovered-compressed-public-key }
+(define-private (batch-recover-public-keys
+      (entry { guardian-id: uint, signature: (buff 65) })
+      (acc { message-hash: (buff 32), value: (list 30 { recovered-compressed-public-key: (buff 33), guardian-id: uint }) }))
+  (let ((recovered-compressed-public-key (secp256k1-recover? (get message-hash acc) (get signature entry)))
+        (updated-public-keys (match recovered-compressed-public-key
+            public-key (append (get value acc) { recovered-compressed-public-key: public-key, guardian-id: (get guardian-id entry) } )
+            error (get value acc))))
+    {
+      message-hash: (get message-hash acc),
+      value: (unwrap-panic (as-max-len? updated-public-keys u30))
+    }))
+
+;; @desc Foldable function evaluating signatures from a list of { guardian-id: u8, signature: (buff 65) }, returning a list of recovered public-keys
+(define-private (batch-check-active-public-keys
+      (entry { recovered-compressed-public-key: (buff 33), guardian-id: uint })
+      (acc {
+        active-guardians: (list 30 { compressed-public-key: (buff 33), uncompressed-public-key: (buff 64) }),
+        result: (list 30 (buff 33))
+      }))
+   (let ((compressed-public-key (get compressed-public-key (unwrap-panic (element-at? (get active-guardians acc) (get guardian-id entry))))))
+     (if (and
+            (is-eq (get recovered-compressed-public-key entry) compressed-public-key)
+            (is-none (index-of? (get result acc) (get recovered-compressed-public-key entry))))
+          {
+            result: (unwrap-panic (as-max-len? (append (get result acc) (get recovered-compressed-public-key entry)) u30)),
+            active-guardians: (get active-guardians acc)
+          }
+          acc)))
+
+;; @desc Foldable function parsing a sequence of bytes into a list of { guardian-id: u8, signature: (buff 65) }
+(define-private (batch-read-signatures
+      (entry uint)
+      (acc { bytes: (buff 8192), iter: uint, value: (list 30 { guardian-id: uint, signature: (buff 65) })}))
+  (if (is-eq (get iter acc) u0)
+    acc
+    (let ((bytes (get bytes acc))
+          (guardian-id (unwrap-panic (read-uint-8 bytes u0)))
+          (signature (unwrap-panic (read-buff-65 bytes u1))))
+      {
+        iter: (- (get iter acc) u1),
+        bytes: (unwrap-panic (slice? bytes u66 (len bytes))),
+        value:
+          (unwrap-panic (as-max-len? (append (get value acc) { guardian-id: guardian-id, signature: signature }) u30))
+      })))
+
+;; @desc Convert an uncompressed public key (64 bytes) into a compressed public key (33 bytes)
+(define-private (compress-public-key (uncompressed-public-key (buff 64)))
+  (if (is-eq 0x uncompressed-public-key)
+    0x
+    (let ((x-coordinate (unwrap-panic (slice? uncompressed-public-key u0 u32)))
+          (y-coordinate-parity (buff-to-uint-be (unwrap-panic (element-at? uncompressed-public-key u63)))))
+      (unwrap-panic (as-max-len? (concat (if (is-eq (mod y-coordinate-parity u2) u0) 0x02 0x03) x-coordinate) u33)))))
+
+(define-private (is-eth-address-matching-public-key (uncompressed-public-key (buff 64)) (eth-address (buff 20)))
+  (is-eq (unwrap-panic (slice? (keccak256 uncompressed-public-key) u12 u32)) eth-address))
+
+(define-private (parse-guardian (cue-position uint) (acc { bytes: (buff 8192), result: (list 30 (buff 20))}))
+  (let (
+    (address-bytes (unwrap-panic (read-buff-20 (get bytes acc) cue-position )))
+  )
+  (if (is-none (index-of? (get result acc) address-bytes))
+    {
+      bytes: (get bytes acc),
+      result: (unwrap-panic (as-max-len? (append (get result acc) address-bytes) u30))
+    }
+    acc
+  )))
+
+;; @desc Parse and verify payload's VAA
+(define-private (parse-and-verify-guardian-set-upgrade (vaa-payload (buff 8192)))
+  (let ((gov-message (try! (parse-governance-payload-header GOV_ACTION_GUARDIAN_SET_UPDATE vaa-payload)))
+        (gov-payload (get payload gov-message))
+        (new-index (unwrap! (read-uint-32 gov-payload u0)
+          ERR_GSU_PARSING_INDEX))
+        (guardians-count (unwrap! (read-uint-8 gov-payload u4)
+          ERR_GSU_PARSING_GUARDIAN_LEN))
+        (check-guardians-count (asserts! (<= guardians-count GOV_MAX_GUARDIANS)
+          ERR_GOV_MAX_GUARDIANS_EXCEEDED))
+        (guardians-len (* guardians-count GUARDIAN_ETH_ADDRESS_SIZE))
+        (guardians-bytes (unwrap! (read-buff-8192-max gov-payload u5 (some guardians-len))
+          ERR_GSU_PARSING_GUARDIANS_BYTES))
+        (guardians-cues (get result (fold is-guardian-cue guardians-bytes { cursor: u0, result: (list) })))
+        (eth-addresses (get result (fold parse-guardian guardians-cues { bytes: guardians-bytes, result: (list) }))))
+    (asserts! (is-eq (len gov-payload) (+ u5 guardians-len)) ERR_GSU_CHECK_OVERLAY)
+    ;; Ensure there are no duplicated addresses
+    (asserts! (is-eq (len eth-addresses) guardians-count) ERR_GSU_DUPLICATED_GUARDIAN_ADDRESSES)
+
+    ;; Good to go!
+    (ok (merge gov-message {
+      payload: {
+        guardians-eth-addresses: eth-addresses,
+        new-index: new-index
+      }
+    }))))
+
+(define-private (get-quorum (guardian-set-size uint))
+  (+ (/ (* guardian-set-size u2) u3) u1))
+
+(define-private (is-guardian-cue (byte (buff 1)) (acc { cursor: uint, result: (list 30 uint) }))
+  (if (is-eq u0 (mod (get cursor acc) GUARDIAN_ETH_ADDRESS_SIZE))
+    {
+      cursor: (+ u1 (get cursor acc)),
+      result: (unwrap-panic (as-max-len? (append (get result acc) (get cursor acc)) u30)),
+    }
+    {
+      cursor: (+ u1 (get cursor acc)),
+      result: (get result acc),
+    }))
+
+(define-private (is-valid-guardian-entry (entry { compressed-public-key: (buff 33), uncompressed-public-key: (buff 64)}) (prev-res (response bool uint)))
+  (begin
+    (try! prev-res)
+    (let (
+      (compressed (get compressed-public-key entry))
+      (uncompressed (get uncompressed-public-key entry)))
+      (if (or (is-eq 0x compressed) (is-eq 0x uncompressed))
+        ERR_GSU_PARSING_GUARDIAN_LEN
+        (ok true)
+      )
+    )
+  )
+)
+
+(define-private (get-latest-stacks-timestamp)
+  (ok (unwrap! (get-stacks-block-info? time (- stacks-block-height u1)) ERR_STACKS_TIMESTAMP)))
+
+(define-private (set-new-guardian-set-id (new-set-id uint))
+  (begin
+    (match (var-get active-guardian-set-id)
+      set-id (var-set previous-guardian-set (some {
+        set-id: set-id,
+        expires-at: (+ TWENTY_FOUR_HOURS (try! (get-latest-stacks-timestamp)))
+      }))
+      true)
+    (ok (var-set active-guardian-set-id (some new-set-id)))))
+
+(define-private (is-valid-guardian-set (set-id uint))
+  (let ((active-set-id (unwrap! (var-get active-guardian-set-id) (ok false))))
+    (if (is-eq active-set-id set-id)
+      (ok true)
+      (let ((prev-set (unwrap! (var-get previous-guardian-set) (ok false)))
+            (prev-set-valid (>= (get expires-at prev-set) (try! (get-latest-stacks-timestamp)))))
+        (ok (and prev-set-valid (is-eq set-id (get set-id prev-set))))))))
+
+;; @desc Check if `set-id` is valid for a governance VAA
+;;       Governance actions can only be performed by the most recent Guardian Set
+(define-private (check-governance-vaa-guardian-set-id (set-id uint))
+  (let ((active-set-id (unwrap! (var-get active-guardian-set-id) ERR_NO_GUARDIAN_SET)))
+    (asserts! (is-eq set-id active-set-id) ERR_GOV_VAA_OLD_GUARDIAN_SET)
+    (ok true)))
+
+;; Get this contract's principal
+(define-private (get-contract-principal)
+  (as-contract tx-sender))
+
+;; @desc Initialize all Wormhole contracts (this one and the state contract)
+;;       Only needs to be called once, when Wormhole contracts are first deployed
+;;
+;; Returns
+;;  - (ok true):  State was initialized
+;;  - (err ...):  Already initialized or permissions error
+(define-private (initialize-wormhole)
+  (begin
+    (try! (contract-call? .wormhole-core-state initialize (get-contract-principal)))
+    ;; This is the initial deployment of `wormhole-core`
+    (var-set deployment-state DEPLOYMENT_STATE_ACTIVE)
+
+    ;; Emit event
+    (print {
+      event: "initialize",
+      data: {
+        previous-contract: none
+      }
+    })
+
+    (ok true)))
+
+;; @desc Call previous contract's `export-state` to initialize this contract
+;;
+;; NOTE: This uses the previous contract's version of `export-trait`
+(define-private (initialize-from-previous-contract (previous-contract <previous-export-trait>))
+  (let ((active-contract (unwrap! (contract-call? .wormhole-core-state get-active-wormhole-core-contract) ERR_UPG_PREV_CONTRACT_INVALID)))
+    (asserts! (is-eq (contract-of previous-contract) active-contract) ERR_UPG_PREV_CONTRACT_INVALID)
+    ;; Only allow importing state once
+    (asserts! (is-eq (get-deployment-state) DEPLOYMENT_STATE_UNITIALIZED) ERR_DEPLOYMENT_STATE_ALREADY_INITIALIZED)
+    (let ((previous-state (try! (contract-call? previous-contract export-state))))
+      ;; Finalize ownership transfer of state contract
+      (try! (contract-call? .wormhole-core-state finalize-ownership-transfer))
+      ;; Import state from previous contract
+      (var-set active-guardian-set-id (get active-guardian-set-id previous-state))
+      (var-set previous-guardian-set (get previous-guardian-set previous-state))
+      (var-set message-fee (get message-fee previous-state))
+      ;; Activate this contract
+      (var-set deployment-state DEPLOYMENT_STATE_ACTIVE)
+
+      ;; Emit event
+      (print {
+        event: "initialize",
+        data: {
+          previous-contract: (some {
+            address: previous-contract,
+            state: previous-state
+          })
+        }
+      })
+
+      (ok true))))
+
+;; @desc Charge sender fee set by `set-message-fee`
+(define-private (charge-message-fee)
+  (let ((fee (var-get message-fee)))
+    ;; If a fee has been set, collect it
+    (if (> fee u0)
+      (try! (stx-transfer? fee tx-sender (get-contract-principal)))
+      true
+    )
+    (ok true)))
+
+;; @desc This function does the work for both `post-message` and `post-message-by-proxy`
+;;       See those functions for further details
+;;       Those functions will set and/or check `emitter`, this one assumes `emiiter` is valid
+(define-private (inner-post-message (payload (buff 8192)) (nonce uint) (consistency-level-opt (optional uint)) (emitter principal))
+  (let ((active (try! (check-active-deployment)))
+        (consistency-level (default-to DEFAULT_CONSISTENCY_LEVEL consistency-level-opt))
+        (fee (var-get message-fee)))
+    ;; `consistency-level` must fit into `u8` type
+    (asserts! (<= consistency-level MAX_VALUE_U8) ERR_POST_OVERFLOW_CONSISTENCY_LEVEL)
+    ;; `nonce` must fit into `u32` type
+    (asserts! (<= nonce MAX_VALUE_U32) ERR_POST_OVERFLOW_NONCE)
+    ;; If a fee has been set, collect it
+    (try! (charge-message-fee))
+    (contract-call? .wormhole-core-state post-message payload nonce consistency-level emitter)))
+
+
+(define-private (check-emitter (address (buff 32)) (chain uint))
+  (begin
+    ;; Check emitting address
+    (asserts! (is-eq address GOV_EMITTING_ADDRESS) ERR_GOV_CHECK_EMITTER)
+    ;; Check emitting chain
+    (asserts! (is-eq chain GOV_EMITTING_CHAIN) ERR_GOV_CHECK_EMITTER)
+    (ok true)))
+
+;; Based on functions from `SP2J933XB2CP2JQ1A4FGN8JA968BBG3NK3EKZ7Q9F.hk-cursor-v2`
+;; Modified for better performance
+
+(define-private (read-buff-1 (bytes (buff 8192)) (pos uint))
+  (ok (unwrap-panic (as-max-len? (unwrap! (slice? bytes pos (+ pos u1)) (err u1)) u1))))
+
+(define-private (read-buff-2 (bytes (buff 8192)) (pos uint))
+  (ok (unwrap-panic (as-max-len? (unwrap! (slice? bytes pos (+ pos u2)) (err u1)) u2))))
+
+(define-private (read-buff-4 (bytes (buff 8192)) (pos uint))
+  (ok (unwrap-panic (as-max-len? (unwrap! (slice? bytes pos (+ pos u4)) (err u1)) u4))))
+
+(define-private (read-buff-8 (bytes (buff 8192)) (pos uint))
+  (ok (unwrap-panic (as-max-len? (unwrap! (slice? bytes pos (+ pos u8)) (err u1)) u8))))
+
+(define-private (read-buff-16 (bytes (buff 8192)) (pos uint))
+  (ok (unwrap-panic (as-max-len? (unwrap! (slice? bytes pos (+ pos u16)) (err u1)) u16))))
+
+(define-private (read-buff-20 (bytes (buff 8192)) (pos uint))
+  (ok (unwrap-panic (as-max-len? (unwrap! (slice? bytes pos (+ pos u20)) (err u1)) u20))))
+
+(define-private (read-buff-32 (bytes (buff 8192)) (pos uint))
+  (ok (unwrap-panic (as-max-len? (unwrap! (slice? bytes pos (+ pos u32)) (err u1)) u32))))
+
+(define-private (read-buff-65 (bytes (buff 8192)) (pos uint))
+  (ok (unwrap-panic (as-max-len? (unwrap! (slice? bytes pos (+ pos u65)) (err u1)) u65))))
+
+(define-private (read-buff-8192-max (bytes (buff 8192)) (pos uint) (size (optional uint)))
+    (let ((max (match size
+            value (+ value pos)
+            (len bytes))))
+      (ok (unwrap! (slice? bytes pos max) (err u1)))))
+
+(define-private (read-uint-8 (bytes (buff 8192)) (pos uint))
+  (ok (buff-to-uint-be (unwrap-panic (as-max-len? (unwrap! (slice? bytes pos (+ pos u1)) (err u1)) u1)))))
+
+(define-private (read-uint-16 (bytes (buff 8192)) (pos uint))
+  (ok (buff-to-uint-be (unwrap-panic (as-max-len? (unwrap! (slice? bytes pos (+ pos u2)) (err u1)) u2)))))
+
+(define-private (read-uint-32 (bytes (buff 8192)) (pos uint))
+  (ok (buff-to-uint-be (unwrap-panic (as-max-len? (unwrap! (slice? bytes pos (+ pos u4)) (err u1)) u4)))))
+
+(define-private (read-uint-64 (bytes (buff 8192)) (pos uint))
+  (ok (buff-to-uint-be (unwrap-panic (as-max-len? (unwrap! (slice? bytes pos (+ pos u8)) (err u1)) u8)))))
+
+(define-private (read-uint-128 (bytes (buff 8192)) (pos uint))
+  (ok (buff-to-uint-be (unwrap-panic (as-max-len? (unwrap! (slice? bytes pos (+ pos u16)) (err u1)) u16)))))

+ 61 - 0
stacks/contracts/wormhole-trait-core-v2.clar

@@ -0,0 +1,61 @@
+;; These are the functions required by third-party contracts using the Wormhold protocol
+;; If we implement a proxy contract in front of wormhole-core (to allow for easy updates),
+;; we will need to use this trait to proxy these functions
+(define-trait core-trait
+  (
+    ;; Getters
+    ;; Ideally these would not return `result` types, but currently trait-defined functions must do this 
+    (get-chain-id () (response (buff 2) uint))
+    (get-message-fee () (response uint uint))
+    (get-governance-contract () (response {
+      chain-id: uint,
+      address: (buff 32),
+    } uint))
+    ;; Parse and Verify cryptographic validity of a VAA
+    (parse-and-verify-vaa ((buff 8192)) (response {
+      vaa: {
+        version: uint,
+        guardian-set-id: uint,
+        signatures-len: uint ,
+        signatures: (list 30 { guardian-id: uint, signature: (buff 65) }),
+        timestamp: uint,
+        nonce: uint,
+        emitter-chain: uint,
+        emitter-address: (buff 32),
+        sequence: uint,
+        consistency-level: uint,
+        payload: (buff 8192),
+      },
+      vaa-body-hash: (buff 32)
+    } uint))
+    ;; Emit message for Guardians to observe
+    ;; Provided in this trait in case user wants to implement their own proxy logic
+    ;; NOTE: May charge sender a fee to send message
+    (post-message ((buff 8192) uint (optional uint)) (response {
+      emitter-principal: principal,
+      emitter: (buff 32),
+      nonce: uint,                ;; Must fit into `u32`
+      sequence: uint,             ;; Must fit into `u64`
+      consistency-level: uint,    ;; Must fit into `u8`
+      payload: (buff 8192)
+    } uint))
+    ;; Emit message for Guardians to observe
+    ;; Can only called by *OUR* proxy contract!
+    ;; NOTE: May charge sender a fee to send message
+    (post-message-via-proxy ((buff 8192) uint (optional uint) principal) (response {
+      emitter-principal: principal,
+      emitter: (buff 32),
+      nonce: uint,                ;; Must fit into `u32`
+      sequence: uint,             ;; Must fit into `u64`
+      consistency-level: uint,    ;; Must fit into `u8`
+      payload: (buff 8192)
+    } uint))
+    ;; Get or generate new "Wormhole address" for a Stacks `prrincipal` that can be used in Wormhole messages
+    ;; Addresses in the Wormhole protocol are limited to 32 bytes, but a Stacks `principal` can be longer than this
+    ;; NOTE: May charge sender a fee to generate new address
+    (get-wormhole-address (principal) (response {
+      created: bool,
+      wormhole-address: (buff 32)
+    } uint))
+  )
+)

+ 16 - 0
stacks/contracts/wormhole-trait-export-v1.clar

@@ -0,0 +1,16 @@
+;; This is for transferring the state of `wormhole-core` during updates
+;; This does not cover `wormhole-core-state`, which cannot be updated
+(define-trait export-trait
+  (
+    ;; Returns current state of `wormhole-core` and deactivates contract
+    ;; Fails if caller is not successor contract
+    (export-state () (response {
+      active-guardian-set-id: (optional uint),
+      previous-guardian-set: (optional {
+        set-id: uint,
+        expires-at: uint
+      }),
+      message-fee: uint,
+    } uint))
+  )
+)

+ 36 - 0
stacks/contracts/wormhole-trait-governance-v1.clar

@@ -0,0 +1,36 @@
+;; Interface for handling VAAs from governance contract
+;; We may not need to use this trait for abstraction or proxying,
+;; but it's still useful to define the governance interface
+(define-trait governance-trait
+  (
+    ;; Handle governance action #1: ContractUpgrade
+    (contract-upgrade ((buff 8192)) (response (buff 32) uint))
+    ;; Handle governance action #2: GuardianSetUpgrade
+    (guardian-set-upgrade ((buff 8192) (list 30 (buff 64))) (response {
+      vaa: {
+        version: uint,
+        guardian-set-id: uint,
+        signatures-len: uint ,
+        signatures: (list 30 { guardian-id: uint, signature: (buff 65) }),
+        timestamp: uint,
+        nonce: uint,
+        emitter-chain: uint,
+        emitter-address: (buff 32),
+        sequence: uint,
+        consistency-level: uint,
+        payload: (buff 8192),
+      },
+      result: {
+        guardians-eth-addresses: (list 30 (buff 64)),
+        guardians-public-keys: (list 30 (buff 64))
+      }
+    } uint))
+    ;; Handle governance action #3: SetMessageFee
+    (set-message-fee ((buff 8192)) (response uint uint))
+    ;; Handle governance action #4: TransferFees
+    (transfer-fees ((buff 8192)) (response {
+      recipient: principal,
+      amount: uint
+    } uint))
+  )
+)

+ 11 - 0
stacks/stacker/Dockerfile

@@ -0,0 +1,11 @@
+FROM node@sha256:101246ec9f5f803f58d6c1eb1d40477c7a99049718a5849ae78229cc6f9198b2 AS builder
+
+RUN apt-get update && apt-get install -y curl gettext-base jq
+
+WORKDIR /root
+COPY package.json package-lock.json /root/
+RUN npm i
+
+COPY stacking.ts common.ts /root/
+
+CMD ["npx", "tsx", "/root/stacking.ts"]

+ 135 - 0
stacks/stacker/common.ts

@@ -0,0 +1,135 @@
+import { StackingClient } from '@stacks/stacking';
+import { StacksTestnet } from '@stacks/network';
+import {
+  getAddressFromPrivateKey,
+  TransactionVersion,
+  createStacksPrivateKey,
+  StacksPrivateKey,
+} from '@stacks/transactions';
+import { getPublicKeyFromPrivate, publicKeyToBtcAddress } from '@stacks/encryption';
+import {
+  InfoApi,
+  Configuration,
+  BlocksApi,
+  TransactionsApi,
+  SmartContractsApi,
+  AccountsApi,
+} from '@stacks/blockchain-api-client';
+import pino, { Logger } from 'pino';
+
+const serviceName = process.env.SERVICE_NAME || 'JS';
+export let logger: Logger;
+if (process.env.STACKS_LOG_JSON === '1') {
+  logger = pino({
+    level: process.env.LOG_LEVEL || 'info',
+    name: serviceName,
+  });
+} else {
+  logger = pino({
+    name: serviceName,
+    level: process.env.LOG_LEVEL || 'info',
+    transport: {
+      target: 'pino-pretty',
+    },
+    // @ts-ignore
+    options: {
+      colorize: true,
+    },
+  });
+}
+
+export const nodeUrl = `http://${process.env.STACKS_CORE_RPC_HOST}:${process.env.STACKS_CORE_RPC_PORT}`;
+export const network = new StacksTestnet({ url: nodeUrl });
+const apiConfig = new Configuration({
+  basePath: nodeUrl,
+});
+export const infoApi = new InfoApi(apiConfig);
+export const blocksApi = new BlocksApi(apiConfig);
+export const txApi = new TransactionsApi(apiConfig);
+export const contractsApi = new SmartContractsApi(apiConfig);
+export const accountsApi = new AccountsApi(apiConfig);
+
+export const EPOCH_30_START = parseEnvInt('STACKS_30_HEIGHT', true);
+export const EPOCH_25_START = parseEnvInt('STACKS_25_HEIGHT', true);
+export const POX_PREPARE_LENGTH = parseEnvInt('POX_PREPARE_LENGTH', true);
+export const POX_REWARD_LENGTH = parseEnvInt('POX_REWARD_LENGTH', true);
+
+export type Account = {
+  privKey: string;
+  pubKey: string;
+  stxAddress: string;
+  btcAddr: string;
+  signerPrivKey: StacksPrivateKey;
+  signerPubKey: string;
+  targetSlots: number;
+  index: number;
+  client: StackingClient;
+  logger: Logger;
+};
+
+export const getAccounts = (stackingKeys: string[], stackingSlotDistribution: number[]) =>
+  stackingKeys.map((privKey, index) => {
+    const pubKey = getPublicKeyFromPrivate(privKey);
+    const stxAddress = getAddressFromPrivateKey(privKey, TransactionVersion.Testnet);
+    const signerPrivKey = createStacksPrivateKey(privKey);
+    const signerPubKey = getPublicKeyFromPrivate(signerPrivKey.data);
+    return {
+      privKey,
+      pubKey,
+      stxAddress,
+      btcAddr: publicKeyToBtcAddress(pubKey),
+      signerPrivKey: signerPrivKey,
+      signerPubKey: signerPubKey,
+      targetSlots: stackingSlotDistribution[index]!,
+      index,
+      client: new StackingClient(stxAddress, network),
+      logger: logger.child({
+        account: stxAddress,
+        index: index,
+      }),
+    };
+  });
+
+export const MAX_U128 = 2n ** 128n - 1n;
+export const maxAmount = MAX_U128;
+
+export async function waitForSetup(stackingKeys: string[], stackingSlotDistribution: number[]) {
+  try {
+    await getAccounts(stackingKeys, stackingSlotDistribution)[0].client.getPoxInfo();
+  } catch (error) {
+    if (/(ECONNREFUSED|ENOTFOUND|SyntaxError)/.test(error.cause?.message)) {
+      console.log(`Stacks node not ready, waiting...`);
+    }
+    await new Promise(resolve => setTimeout(resolve, 3000));
+    return waitForSetup(stackingKeys, stackingSlotDistribution);
+  }
+}
+
+export function parseEnvInt<T extends boolean = false>(
+  envKey: string,
+  required?: T
+): T extends true ? number : number | undefined {
+  let value = process.env[envKey];
+  if (typeof value === 'undefined') {
+    if (required) {
+      throw new Error(`Missing required env var: ${envKey}`);
+    }
+    return undefined as T extends true ? number : number | undefined;
+  }
+  return parseInt(value, 10);
+}
+
+export function burnBlockToRewardCycle(burnBlock: number) {
+  const cycleLength = BigInt(POX_REWARD_LENGTH);
+  return Number(BigInt(burnBlock) / cycleLength) + 1;
+}
+
+export const EPOCH_30_START_CYCLE = burnBlockToRewardCycle(EPOCH_30_START);
+
+export function isPreparePhase(burnBlock: number) {
+  return POX_REWARD_LENGTH - (burnBlock % POX_REWARD_LENGTH) < POX_PREPARE_LENGTH;
+}
+
+export function didCrossPreparePhase(lastBurnHeight: number, newBurnHeight: number) {
+  return isPreparePhase(newBurnHeight) && !isPreparePhase(lastBurnHeight);
+}

+ 1366 - 0
stacks/stacker/package-lock.json

@@ -0,0 +1,1366 @@
+{
+  "name": "stacker",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "stacker",
+      "version": "1.0.0",
+      "license": "MIT",
+      "dependencies": {
+        "@stacks/blockchain-api-client": "7.8.2",
+        "@stacks/encryption": "6.11.4-pr.36558cf.0",
+        "@stacks/network": "6.11.4-pr.36558cf.0",
+        "@stacks/stacking": "6.11.4-pr.36558cf.0",
+        "@stacks/transactions": "6.11.4-pr.36558cf.0",
+        "pino": "^8.19.0",
+        "pino-pretty": "^10.3.1"
+      },
+      "devDependencies": {
+        "@stacks/prettier-config": "^0.0.10",
+        "tsx": "4.7.1"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
+      "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
+      "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
+      "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
+      "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
+      "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
+      "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
+      "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
+      "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
+      "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
+      "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
+      "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
+      "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
+      "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
+      "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
+      "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
+      "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
+      "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
+      "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
+      "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
+      "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
+      "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
+      "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
+      "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@noble/hashes": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.5.tgz",
+      "integrity": "sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/@noble/secp256k1": {
+      "version": "1.7.1",
+      "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz",
+      "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/@scure/base": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz",
+      "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@scure/bip39": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz",
+      "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "~1.1.1",
+        "@scure/base": "~1.1.0"
+      }
+    },
+    "node_modules/@socket.io/component-emitter": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+      "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
+      "license": "MIT"
+    },
+    "node_modules/@stacks/blockchain-api-client": {
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/@stacks/blockchain-api-client/-/blockchain-api-client-7.8.2.tgz",
+      "integrity": "sha512-yGPFJTnuZ89S5tOCx9Dook6A4ghzR7RDTE3zpcfgae2wAenN2LPgQYGGNONKeLuxKl1giPiiT0ywv/MYR60YnA==",
+      "license": "GPL-3.0",
+      "dependencies": {
+        "@stacks/stacks-blockchain-api-types": "*",
+        "@types/ws": "7.4.7",
+        "cross-fetch": "3.1.5",
+        "eventemitter3": "4.0.7",
+        "jsonrpc-lite": "2.2.0",
+        "socket.io-client": "4.7.3",
+        "ws": "8.16.0"
+      }
+    },
+    "node_modules/@stacks/common": {
+      "version": "6.16.0",
+      "resolved": "https://registry.npmjs.org/@stacks/common/-/common-6.16.0.tgz",
+      "integrity": "sha512-PnzvhrdGRMVZvxTulitlYafSK4l02gPCBBoI9QEoTqgSnv62oaOXhYAUUkTMFKxdHW1seVEwZsrahuXiZPIAwg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/bn.js": "^5.1.0",
+        "@types/node": "^18.0.4"
+      }
+    },
+    "node_modules/@stacks/encryption": {
+      "version": "6.11.4-pr.36558cf.0",
+      "resolved": "https://registry.npmjs.org/@stacks/encryption/-/encryption-6.11.4-pr.36558cf.0.tgz",
+      "integrity": "sha512-rBeLtfu0HxqZpSb29iLXfa0MJQM0FX4wP9c11iJCNt+Wkld53cDhcmxA5PMngGQYKws0fTFT3ZDsHtFpTdi4Xg==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "1.1.5",
+        "@noble/secp256k1": "1.7.1",
+        "@scure/bip39": "1.1.0",
+        "@stacks/common": "^6.11.4-pr.36558cf.0",
+        "@types/node": "^18.0.4",
+        "base64-js": "^1.5.1",
+        "bs58": "^5.0.0",
+        "ripemd160-min": "^0.0.6",
+        "varuint-bitcoin": "^1.1.2"
+      }
+    },
+    "node_modules/@stacks/network": {
+      "version": "6.11.4-pr.36558cf.0",
+      "resolved": "https://registry.npmjs.org/@stacks/network/-/network-6.11.4-pr.36558cf.0.tgz",
+      "integrity": "sha512-liLhuUlqx0EqydAEgf7trWt90QiDwRsZQHqgaJPzCDWiN3I4yoN6gpT0jQ2CUNL49NRDZWK1X3XUYDKx2w/0yw==",
+      "license": "MIT",
+      "dependencies": {
+        "@stacks/common": "^6.11.4-pr.36558cf.0",
+        "cross-fetch": "^3.1.5"
+      }
+    },
+    "node_modules/@stacks/prettier-config": {
+      "version": "0.0.10",
+      "resolved": "https://registry.npmjs.org/@stacks/prettier-config/-/prettier-config-0.0.10.tgz",
+      "integrity": "sha512-MrYWGEgO/mYR8TOZIKknQEHbFQZ5VyAD/s8eF2Yxr6Lgalt2alVEh+6ODehVP2uepkyXPmJzLbaQYs8/L4E78Q==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "prettier": "2.5"
+      }
+    },
+    "node_modules/@stacks/stacking": {
+      "version": "6.11.4-pr.36558cf.0",
+      "resolved": "https://registry.npmjs.org/@stacks/stacking/-/stacking-6.11.4-pr.36558cf.0.tgz",
+      "integrity": "sha512-BK3da72woKdqwrSi5KrQrp7pfEBSrJKDE/gn/yt5RRkYcX38jbUoNauJqbNCHaPgKdPlYVzYnmtbe4f5YUQ+Ng==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "1.1.5",
+        "@scure/base": "1.1.1",
+        "@stacks/common": "^6.11.4-pr.36558cf.0",
+        "@stacks/encryption": "^6.11.4-pr.36558cf.0",
+        "@stacks/network": "^6.11.4-pr.36558cf.0",
+        "@stacks/stacks-blockchain-api-types": "^0.61.0",
+        "@stacks/transactions": "^6.11.4-pr.36558cf.0",
+        "bs58": "^5.0.0"
+      }
+    },
+    "node_modules/@stacks/stacking/node_modules/@scure/base": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
+      "integrity": "sha512-ZxOhsSyxYwLJj3pLZCefNitxsj093tb2vq90mp2txoYeBqbcjDjqFhyM8eUjq/uFm6zJ+mUuqxlS2FkuSY1MTA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/@stacks/stacking/node_modules/@stacks/stacks-blockchain-api-types": {
+      "version": "0.61.0",
+      "resolved": "https://registry.npmjs.org/@stacks/stacks-blockchain-api-types/-/stacks-blockchain-api-types-0.61.0.tgz",
+      "integrity": "sha512-yPOfTUboo5eA9BZL/hqMcM71GstrFs9YWzOrJFPeP4cOO1wgYvAcckgBRbgiE3NqeX0A7SLZLDAXLZbATuRq9w==",
+      "license": "ISC"
+    },
+    "node_modules/@stacks/stacks-blockchain-api-types": {
+      "version": "7.14.1",
+      "resolved": "https://registry.npmjs.org/@stacks/stacks-blockchain-api-types/-/stacks-blockchain-api-types-7.14.1.tgz",
+      "integrity": "sha512-65hvhXxC+EUqHJAQsqlBCqXB+zwfxZICSKYJugdg6BCp9I9qniyfz5XyQeC4RMVo0tgEoRdS/b5ZCFo5kLWmxA==",
+      "license": "ISC"
+    },
+    "node_modules/@stacks/transactions": {
+      "version": "6.11.4-pr.36558cf.0",
+      "resolved": "https://registry.npmjs.org/@stacks/transactions/-/transactions-6.11.4-pr.36558cf.0.tgz",
+      "integrity": "sha512-l9Yoiks/+P5TGgXZELuCebS1YG3jBaStrbZydhb15nstCtX3OvU/GTfq6XVuy/K4TlMbeb6vrlV+t6gSMxcDSA==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "1.1.5",
+        "@noble/secp256k1": "1.7.1",
+        "@stacks/common": "^6.11.4-pr.36558cf.0",
+        "@stacks/network": "^6.11.4-pr.36558cf.0",
+        "c32check": "^2.0.0",
+        "lodash.clonedeep": "^4.5.0"
+      }
+    },
+    "node_modules/@types/bn.js": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz",
+      "integrity": "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/node": {
+      "version": "18.19.130",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz",
+      "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~5.26.4"
+      }
+    },
+    "node_modules/@types/ws": {
+      "version": "7.4.7",
+      "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz",
+      "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/abort-controller": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+      "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+      "license": "MIT",
+      "dependencies": {
+        "event-target-shim": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=6.5"
+      }
+    },
+    "node_modules/atomic-sleep": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
+      "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/base-x": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.1.tgz",
+      "integrity": "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==",
+      "license": "MIT"
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "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/bs58": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz",
+      "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==",
+      "license": "MIT",
+      "dependencies": {
+        "base-x": "^4.0.0"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "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",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
+    "node_modules/c32check": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/c32check/-/c32check-2.0.0.tgz",
+      "integrity": "sha512-rpwfAcS/CMqo0oCqDf3r9eeLgScRE3l/xHDCXhM3UyrfvIn7PrLq63uHh7yYbv8NzaZn5MVsVhIRpQ+5GZ5HyA==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "^1.1.2",
+        "base-x": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/colorette": {
+      "version": "2.0.20",
+      "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+      "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+      "license": "MIT"
+    },
+    "node_modules/cross-fetch": {
+      "version": "3.1.5",
+      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.5.tgz",
+      "integrity": "sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==",
+      "license": "MIT",
+      "dependencies": {
+        "node-fetch": "2.6.7"
+      }
+    },
+    "node_modules/dateformat": {
+      "version": "4.6.3",
+      "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz",
+      "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/debug": {
+      "version": "4.3.7",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+      "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/end-of-stream": {
+      "version": "1.4.5",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+      "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+      "license": "MIT",
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/engine.io-client": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz",
+      "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.1",
+        "engine.io-parser": "~5.2.1",
+        "ws": "~8.17.1",
+        "xmlhttprequest-ssl": "~2.0.0"
+      }
+    },
+    "node_modules/engine.io-client/node_modules/ws": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+      "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/engine.io-parser": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+      "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.19.12",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
+      "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.19.12",
+        "@esbuild/android-arm": "0.19.12",
+        "@esbuild/android-arm64": "0.19.12",
+        "@esbuild/android-x64": "0.19.12",
+        "@esbuild/darwin-arm64": "0.19.12",
+        "@esbuild/darwin-x64": "0.19.12",
+        "@esbuild/freebsd-arm64": "0.19.12",
+        "@esbuild/freebsd-x64": "0.19.12",
+        "@esbuild/linux-arm": "0.19.12",
+        "@esbuild/linux-arm64": "0.19.12",
+        "@esbuild/linux-ia32": "0.19.12",
+        "@esbuild/linux-loong64": "0.19.12",
+        "@esbuild/linux-mips64el": "0.19.12",
+        "@esbuild/linux-ppc64": "0.19.12",
+        "@esbuild/linux-riscv64": "0.19.12",
+        "@esbuild/linux-s390x": "0.19.12",
+        "@esbuild/linux-x64": "0.19.12",
+        "@esbuild/netbsd-x64": "0.19.12",
+        "@esbuild/openbsd-x64": "0.19.12",
+        "@esbuild/sunos-x64": "0.19.12",
+        "@esbuild/win32-arm64": "0.19.12",
+        "@esbuild/win32-ia32": "0.19.12",
+        "@esbuild/win32-x64": "0.19.12"
+      }
+    },
+    "node_modules/event-target-shim": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+      "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/eventemitter3": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+      "license": "MIT"
+    },
+    "node_modules/events": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+      "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.8.x"
+      }
+    },
+    "node_modules/fast-copy": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz",
+      "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==",
+      "license": "MIT"
+    },
+    "node_modules/fast-redact": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz",
+      "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/fast-safe-stringify": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
+      "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==",
+      "license": "MIT"
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/get-tsconfig": {
+      "version": "4.13.0",
+      "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
+      "integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "resolve-pkg-maps": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
+      }
+    },
+    "node_modules/help-me": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz",
+      "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==",
+      "license": "MIT"
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "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": "BSD-3-Clause"
+    },
+    "node_modules/joycon": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
+      "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/jsonrpc-lite": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/jsonrpc-lite/-/jsonrpc-lite-2.2.0.tgz",
+      "integrity": "sha512-/cbbSxtZWs1O7R4tWqabrCM/t3N8qKUZMAg9IUqpPvUs6UyRvm6pCNYkskyKN/XU0UgffW+NY2ZRr8t0AknX7g==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.clonedeep": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+      "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
+      "license": "MIT"
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/node-fetch": {
+      "version": "2.6.7",
+      "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
+      "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/on-exit-leak-free": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
+      "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "license": "ISC",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/pino": {
+      "version": "8.21.0",
+      "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz",
+      "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==",
+      "license": "MIT",
+      "dependencies": {
+        "atomic-sleep": "^1.0.0",
+        "fast-redact": "^3.1.1",
+        "on-exit-leak-free": "^2.1.0",
+        "pino-abstract-transport": "^1.2.0",
+        "pino-std-serializers": "^6.0.0",
+        "process-warning": "^3.0.0",
+        "quick-format-unescaped": "^4.0.3",
+        "real-require": "^0.2.0",
+        "safe-stable-stringify": "^2.3.1",
+        "sonic-boom": "^3.7.0",
+        "thread-stream": "^2.6.0"
+      },
+      "bin": {
+        "pino": "bin.js"
+      }
+    },
+    "node_modules/pino-abstract-transport": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz",
+      "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==",
+      "license": "MIT",
+      "dependencies": {
+        "readable-stream": "^4.0.0",
+        "split2": "^4.0.0"
+      }
+    },
+    "node_modules/pino-pretty": {
+      "version": "10.3.1",
+      "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-10.3.1.tgz",
+      "integrity": "sha512-az8JbIYeN/1iLj2t0jR9DV48/LQ3RC6hZPpapKPkb84Q+yTidMCpgWxIT3N0flnBDilyBQ1luWNpOeJptjdp/g==",
+      "license": "MIT",
+      "dependencies": {
+        "colorette": "^2.0.7",
+        "dateformat": "^4.6.3",
+        "fast-copy": "^3.0.0",
+        "fast-safe-stringify": "^2.1.1",
+        "help-me": "^5.0.0",
+        "joycon": "^3.1.1",
+        "minimist": "^1.2.6",
+        "on-exit-leak-free": "^2.1.0",
+        "pino-abstract-transport": "^1.0.0",
+        "pump": "^3.0.0",
+        "readable-stream": "^4.0.0",
+        "secure-json-parse": "^2.4.0",
+        "sonic-boom": "^3.0.0",
+        "strip-json-comments": "^3.1.1"
+      },
+      "bin": {
+        "pino-pretty": "bin.js"
+      }
+    },
+    "node_modules/pino-std-serializers": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz",
+      "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==",
+      "license": "MIT"
+    },
+    "node_modules/prettier": {
+      "version": "2.5.1",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz",
+      "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "prettier": "bin-prettier.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
+    "node_modules/process-warning": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz",
+      "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==",
+      "license": "MIT"
+    },
+    "node_modules/pump": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz",
+      "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==",
+      "license": "MIT",
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "node_modules/quick-format-unescaped": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
+      "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
+      "license": "MIT"
+    },
+    "node_modules/readable-stream": {
+      "version": "4.7.0",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+      "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+      "license": "MIT",
+      "dependencies": {
+        "abort-controller": "^3.0.0",
+        "buffer": "^6.0.3",
+        "events": "^3.3.0",
+        "process": "^0.11.10",
+        "string_decoder": "^1.3.0"
+      },
+      "engines": {
+        "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+      }
+    },
+    "node_modules/real-require": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
+      "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 12.13.0"
+      }
+    },
+    "node_modules/resolve-pkg-maps": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
+      "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
+      "dev": true,
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
+      }
+    },
+    "node_modules/ripemd160-min": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz",
+      "integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "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/safe-stable-stringify": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+      "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/secure-json-parse": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz",
+      "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/socket.io-client": {
+      "version": "4.7.3",
+      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.3.tgz",
+      "integrity": "sha512-nU+ywttCyBitXIl9Xe0RSEfek4LneYkJxCeNnKCuhwoH4jGXO1ipIUw/VA/+Vvv2G1MTym11fzFC0SxkrcfXDw==",
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.2",
+        "engine.io-client": "~6.5.2",
+        "socket.io-parser": "~4.2.4"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/socket.io-parser": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
+      "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+      "license": "MIT",
+      "dependencies": {
+        "@socket.io/component-emitter": "~3.1.0",
+        "debug": "~4.3.1"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/sonic-boom": {
+      "version": "3.8.1",
+      "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz",
+      "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==",
+      "license": "MIT",
+      "dependencies": {
+        "atomic-sleep": "^1.0.0"
+      }
+    },
+    "node_modules/split2": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+      "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+      "license": "ISC",
+      "engines": {
+        "node": ">= 10.x"
+      }
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/thread-stream": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz",
+      "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==",
+      "license": "MIT",
+      "dependencies": {
+        "real-require": "^0.2.0"
+      }
+    },
+    "node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+      "license": "MIT"
+    },
+    "node_modules/tsx": {
+      "version": "4.7.1",
+      "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.7.1.tgz",
+      "integrity": "sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "~0.19.10",
+        "get-tsconfig": "^4.7.2"
+      },
+      "bin": {
+        "tsx": "dist/cli.mjs"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      }
+    },
+    "node_modules/undici-types": {
+      "version": "5.26.5",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+      "license": "MIT"
+    },
+    "node_modules/varuint-bitcoin": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/varuint-bitcoin/-/varuint-bitcoin-1.1.2.tgz",
+      "integrity": "sha512-4EVb+w4rx+YfVM32HQX42AbbT7/1f5zwAYhIujKXKk8NQK+JfRVl3pqT3hjNn/L+RstigmGGKVwHA/P0wgITZw==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "^5.1.1"
+      }
+    },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "license": "ISC"
+    },
+    "node_modules/ws": {
+      "version": "8.16.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
+      "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/xmlhttprequest-ssl": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz",
+      "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    }
+  }
+}

+ 24 - 0
stacks/stacker/package.json

@@ -0,0 +1,24 @@
+{
+  "name": "@wormhole-foundation/stacks-stacker",
+  "private": true,
+  "version": "1.0.0",
+  "description": "",
+  "type": "module",
+  "keywords": [],
+  "author": "",
+  "license": "MIT",
+  "dependencies": {
+    "@stacks/blockchain-api-client": "7.8.2",
+    "@stacks/encryption": "6.11.4-pr.36558cf.0",
+    "@stacks/network": "6.11.4-pr.36558cf.0",
+    "@stacks/stacking": "6.11.4-pr.36558cf.0",
+    "@stacks/transactions": "6.11.4-pr.36558cf.0",
+    "pino-pretty": "^10.3.1",
+    "pino": "^8.19.0"
+  },
+  "devDependencies": {
+    "@stacks/prettier-config": "^0.0.10",
+    "tsx": "4.7.1"
+  },
+  "prettier": "@stacks/prettier-config"
+}

+ 321 - 0
stacks/stacker/stacking.ts

@@ -0,0 +1,321 @@
+import { PoxInfo, Pox4SignatureTopic } from '@stacks/stacking';
+import crypto from 'crypto';
+import {
+  Account,
+  getAccounts,
+  maxAmount,
+  parseEnvInt,
+  waitForSetup,
+  logger,
+  burnBlockToRewardCycle,
+} from './common';
+
+const randInt = () => crypto.randomInt(0, 0xffffffffffff);
+const stackingInterval = parseEnvInt('STACKING_INTERVAL', true);
+const postTxWait = parseEnvInt('POST_TX_WAIT', true);
+const stackingCycles = parseEnvInt('STACKING_CYCLES', true);
+
+const SLOT_MULTIPLIER = 2.5;
+const DEFAULT_NUM_SLOTS = 2;
+
+let startTxFee = 1000;
+const getNextTxFee = () => startTxFee++;
+
+type RewardCycleId = number;
+type AmountToStack = bigint;
+
+// Map to store fixed stacking amounts per reward cycle to ensure consistent
+// proportional weights based on target slots. Maps target reward cycle ID to
+// fixed amount per slot for that cycle.
+//
+// This prevents dynamic threshold changes from causing unexpected weight
+// distribution as stackers join throughout the cycle and affect the
+// participation-based threshold.
+const cycleStackingAmounts = new Map<RewardCycleId, AmountToStack>();
+
+/**
+ * Compute and store the fixed stacking amount for a given target reward cycle.
+ * This ensures all stackers have expected weights regardless of the stacking
+ * order within the cycle.
+ *
+ * @param targetRewardCycle The reward cycle ID for which the fixed amount is
+ *                          computed
+ * @param currentThreshold The current minimum threshold for the cycle
+ * @param multiplier Optional multiplier for the starting threshold
+ *                   (default: SLOT_MULTIPLIER)
+ * @returns The fixed stacking amount for this cycle
+ */
+function getFixedStackingAmount(
+  targetRewardCycle: number,
+  currentThreshold: number,
+  multiplier: number = SLOT_MULTIPLIER
+): AmountToStack {
+  if (cycleStackingAmounts.has(targetRewardCycle)) {
+    return cycleStackingAmounts.get(targetRewardCycle)!;
+  }
+
+  // Use the threshold at the time this target cycle was first encountered.
+  // Bump by multiplier to avoid getting stuck if threshold increases slightly
+  // over time.
+  const fixedAmount = BigInt(Math.floor(currentThreshold * multiplier));
+  cycleStackingAmounts.set(targetRewardCycle, fixedAmount);
+
+  logger.info(
+    {
+      targetRewardCycle: targetRewardCycle,
+      currentThreshold,
+      fixedAmount: fixedAmount.toString(),
+      multiplier,
+    },
+    `Set fixed stacking amount for target reward cycle ${targetRewardCycle}`
+  );
+
+  return fixedAmount;
+}
+
+async function run(stackingKeys: string[], stackingSlotDistribution: number[]) {
+  const accounts = getAccounts(stackingKeys, stackingSlotDistribution);
+  const poxInfo = await accounts[0].client.getPoxInfo();
+  if (!poxInfo.contract_id.endsWith('.pox-4')) {
+    logger.info(
+      {
+        poxContract: poxInfo.contract_id,
+      },
+      `Pox contract is not .pox-4, skipping stacking (contract=${poxInfo.contract_id})`
+    );
+    return;
+  }
+
+  const runLog = logger.child({
+    burnHeight: poxInfo.current_burnchain_block_height,
+  });
+
+  const accountInfos = await Promise.all(
+    accounts.map(async a => {
+      const info = await a.client.getAccountStatus();
+      const unlockHeight = Number(info.unlock_height);
+      const lockedAmount = BigInt(info.locked);
+      const balance = BigInt(info.balance);
+      return { ...a, info, unlockHeight, lockedAmount, balance };
+    })
+  );
+
+  let txSubmitted = false;
+
+  // Bump min threshold by SLOT_MULTIPLIER to avoid getting stuck if threshold
+  // increases.
+  const minStx = Math.floor(poxInfo.next_cycle.min_threshold_ustx * SLOT_MULTIPLIER);
+  const nextCycleStx = poxInfo.next_cycle.stacked_ustx;
+  if (nextCycleStx < minStx) {
+    runLog.info(`Next cycle has less than min threshold.. stacking should be performed soon`);
+  }
+
+  await Promise.all(
+    accountInfos.map(async account => {
+      if (account.lockedAmount === 0n) {
+        runLog.info(
+          {
+            burnHeight: poxInfo.current_burnchain_block_height,
+            unlockHeight: account.unlockHeight,
+            account: account.index,
+          },
+          `Account ${account.index} is unlocked, stack-stx required`
+        );
+        await stackStx(poxInfo, account, account.balance);
+        txSubmitted = true;
+        return;
+      }
+      const unlockHeightCycle = burnBlockToRewardCycle(account.unlockHeight);
+      const nowCycle = burnBlockToRewardCycle(poxInfo.current_burnchain_block_height ?? 0);
+      if (unlockHeightCycle === nowCycle + 1) {
+        runLog.info(
+          {
+            burnHeight: poxInfo.current_burnchain_block_height,
+            unlockHeight: account.unlockHeight,
+            account: account.index,
+            nowCycle,
+            unlockCycle: unlockHeightCycle,
+          },
+          `Account ${account.index} unlocks before next cycle ${account.unlockHeight} vs ${poxInfo.current_burnchain_block_height}, stack-extend required`
+        );
+        await stackExtend(poxInfo, account);
+        txSubmitted = true;
+        return;
+      }
+      runLog.info(
+        {
+          burnHeight: poxInfo.current_burnchain_block_height,
+          unlockHeight: account.unlockHeight,
+          account: account.index,
+          nowCycle,
+          unlockCycle: unlockHeightCycle,
+        },
+        `Account ${account.index} is locked for next cycle, skipping stacking`
+      );
+    })
+  );
+
+  if (txSubmitted) {
+    await new Promise(resolve => setTimeout(resolve, postTxWait * 1000));
+  }
+}
+
+async function stackStx(poxInfo: PoxInfo, account: Account, balance: bigint) {
+  // Determine the fixed stacking amount per slot for the target reward cycle.
+  // This ensures the stacked amount per slot is constant for the entire cycle,
+  // regardless of potential increases in the minimum threshold.
+  const baseStackingAmount = getFixedStackingAmount(
+    poxInfo.next_cycle.id,
+    poxInfo.next_cycle.min_threshold_ustx
+  );
+
+  // Calculate total amount needed based on target slots and fixed base amount.
+  const amountToStack = baseStackingAmount * BigInt(account.targetSlots);
+
+  // Compare with current threshold.
+  const currentThreshold = poxInfo.next_cycle.min_threshold_ustx;
+  const adjustedThreshold = Math.floor(currentThreshold * SLOT_MULTIPLIER);
+
+  if (balance < baseStackingAmount) {
+    throw new Error(
+      `Insufficient balance to stack minimum amount (required=${baseStackingAmount}, balance=${balance})`
+    );
+  }
+
+  if (balance < amountToStack) {
+    throw new Error(
+      `Insufficient balance to stack (required=${amountToStack}, balance=${balance}), this can lead to unexpected weight distribution.`
+    );
+  }
+  const authId = randInt();
+  const sigArgs = {
+    topic: Pox4SignatureTopic.StackStx,
+    rewardCycle: poxInfo.reward_cycle_id,
+    poxAddress: account.btcAddr,
+    period: stackingCycles,
+    signerPrivateKey: account.signerPrivKey,
+    authId,
+    maxAmount,
+  } as const;
+  const signerSignature = account.client.signPoxSignature(sigArgs);
+  const stackingArgs = {
+    poxAddress: account.btcAddr,
+    privateKey: account.privKey,
+    amountMicroStx: amountToStack,
+    burnBlockHeight: poxInfo.current_burnchain_block_height,
+    cycles: stackingCycles,
+    fee: getNextTxFee(),
+    signerKey: account.signerPubKey,
+    signerSignature,
+    authId,
+    maxAmount,
+  };
+  account.logger.debug(
+    {
+      ...stackingArgs,
+      ...sigArgs,
+      // The total amount to stack.
+      stackedAmount: amountToStack.toString(),
+      // The fixed amount per slot for the target reward cycle.
+      baseStackingAmount: baseStackingAmount.toString(),
+      // How many slots the account is targeting to stack. Will stack this
+      // amount multiplied by a constant multiplier to avoid getting locked out
+      // if the threshold increases.
+      targetSlots: account.targetSlots,
+      // The current minimum threshold for the cycle.
+      currentThreshold,
+      // The threshold after applying the multiplier.
+      adjustedThreshold,
+    },
+    `Stack-stx with args:`
+  );
+  const stackResult = await account.client.stack(stackingArgs);
+  account.logger.info(
+    {
+      ...stackResult,
+    },
+    `Stack-stx tx result`
+  );
+}
+
+async function stackExtend(
+  poxInfo: PoxInfo,
+  account: Account & { lockedAmount: bigint; balance: bigint }
+) {
+  const authId = randInt();
+  const sigArgs = {
+    topic: Pox4SignatureTopic.StackExtend,
+    rewardCycle: poxInfo.reward_cycle_id,
+    poxAddress: account.btcAddr,
+    period: stackingCycles,
+    signerPrivateKey: account.signerPrivKey,
+    authId,
+    maxAmount,
+  } as const;
+  const signerSignature = account.client.signPoxSignature(sigArgs);
+  const stackingArgs = {
+    poxAddress: account.btcAddr,
+    privateKey: account.privKey,
+    extendCycles: stackingCycles,
+    fee: getNextTxFee(),
+    signerKey: account.signerPubKey,
+    signerSignature,
+    authId,
+    maxAmount,
+  };
+  account.logger.debug(
+    {
+      stxAddress: account.stxAddress,
+      account: account.index,
+      ...stackingArgs,
+      ...sigArgs,
+    },
+    `Stack-extend with args:`
+  );
+  const stackResult = await account.client.stackExtend(stackingArgs);
+  account.logger.info(
+    {
+      stxAddress: account.stxAddress,
+      account: account.index,
+      ...stackResult,
+    },
+    `Stack-extend tx result`
+  );
+}
+
+async function loop() {
+  const stackingKeys = process.env.STACKING_KEYS?.split(',') || [];
+
+  if (stackingKeys.length === 0) {
+    throw new Error('No stacking keys provided using STACKING_KEYS.');
+  }
+
+  const envStackingSlotDistribution =
+    process.env.STACKING_SLOT_DISTRO?.split(',').map(Number) || [];
+  const stackingSlotDistribution: number[] = Array(stackingKeys.length)
+    .fill(DEFAULT_NUM_SLOTS)
+    .map((defaultValue, index) => envStackingSlotDistribution[index] ?? defaultValue);
+
+  logger.info(
+    {
+      stackingKeys: stackingKeys.length,
+      stackingSlotDistribution,
+      stackingInterval,
+      postTxWait,
+      stackingCycles,
+    },
+    `Starting stacker with configuration:`
+  );
+
+  await waitForSetup(stackingKeys, stackingSlotDistribution);
+
+  while (true) {
+    try {
+      await run(stackingKeys, stackingSlotDistribution);
+    } catch (e) {
+      console.error('Error running stacking:', e);
+    }
+    await new Promise(resolve => setTimeout(resolve, stackingInterval * 1000));
+  }
+}
+loop();

+ 23 - 0
stacks/stacks-test.yaml

@@ -0,0 +1,23 @@
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: stacks-test
+  labels:
+    app: stacks-test
+spec:
+  selector:
+    matchLabels:
+      app: stacks-test
+  template:
+    metadata:
+      labels:
+        app: stacks-test
+    spec:
+      containers:
+        - name: stacks-test
+          image: stacks-test
+          command:
+            - /bin/sh
+            - -c
+            - npm run test:integration

+ 7 - 0
stacks/test/Dockerfile

@@ -0,0 +1,7 @@
+FROM node@sha256:101246ec9f5f803f58d6c1eb1d40477c7a99049718a5849ae78229cc6f9198b2
+
+WORKDIR /app
+COPY . .
+
+WORKDIR /app/test
+RUN npm ci

+ 543 - 0
stacks/test/integration.test.ts

@@ -0,0 +1,543 @@
+import {
+  broadcastTransaction,
+  Cl,
+  fetchCallReadOnlyFunction,
+  fetchContractMapEntry,
+  fetchNonce,
+  makeContractCall,
+  makeContractDeploy,
+  Pc,
+  privateKeyToAddress,
+  TupleCV,
+  UIntCV,
+} from "@stacks/transactions";
+import fs from "fs";
+import path from "path";
+import { describe, expect, it } from "vitest";
+import { STACKS_API_URL, STACKS_PRIVATE_KEY } from "./lib/constants";
+import {
+  expectNoStacksVAA,
+  expectVAA,
+  waitForTransactionSuccess,
+  wormhole,
+} from "./lib/helpers";
+
+const root = path.resolve(process.cwd(), "../");
+
+describe("Stacks Wormhole Integration Tests", () => {
+  it("should deploy stacks contracts", async () => {
+    const ADDRESS = privateKeyToAddress(STACKS_PRIVATE_KEY, "devnet");
+
+    const contractPath = path.resolve(root, "contracts");
+    const dependencyPath = path.resolve(root, "contracts/dependencies");
+
+    const rewriteClarity = (code: string) => {
+      return code
+        .replaceAll("SP2J933XB2CP2JQ1A4FGN8JA968BBG3NK3EKZ7Q9F", ADDRESS)
+        .replaceAll("SP1E0XBN9T4B10E9QMR7XMFJPMA19D77WY3KP2QKC", ADDRESS)
+        .replaceAll("SP102V8P0F7JX67ARQ77WEA3D3CFB5XW39REDT0AM", ADDRESS);
+    };
+
+    const dependencyFiles = [
+      "trait-sip-010.clar",
+      "proposal-trait.clar",
+      "extension-trait.clar",
+      "executor-dao.clar",
+      "trait-semi-fungible.clar",
+      "token-amm-pool-v2-01.clar",
+      "liquidity-locker.clar",
+      "clarity-stacks.clar",
+      "trait-flash-loan-user.clar",
+      "amm-vault-v2-01.clar",
+      "amm-registry-v2-01.clar",
+      "amm-pool-v2-01.clar",
+      "code-body-prover.clar",
+      "clarity-stacks-helper.clar",
+      "self-listing-helper-v3.clar",
+      "hk-ecc-v1.clar",
+      "hk-cursor-v2.clar",
+      "hk-merkle-tree-keccak160-v1.clar",
+    ].map((file) => path.join(dependencyPath, file));
+
+    const contractFiles = [
+      "wormhole-core-state.clar",
+      "wormhole-trait-core-v2.clar",
+      "wormhole-core-proxy-v2.clar",
+      "wormhole-trait-export-v1.clar",
+      "wormhole-trait-governance-v1.clar",
+      "wormhole-core-v4.clar",
+    ].map((filename) => path.join(contractPath, filename));
+
+    const versionMap = {
+      "executor-dao.clar": 3,
+    } as Record<string, number>;
+
+    const contracts = [...dependencyFiles, ...contractFiles].map(
+      (filePath) => ({
+        name: path.basename(filePath).replace(".clar", ""),
+        filename: path.basename(filePath),
+        code: rewriteClarity(fs.readFileSync(filePath, "utf8")),
+      })
+    );
+
+    let nonce = await fetchNonce({
+      address: ADDRESS,
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    const results = {
+      totalContracts: contracts.length,
+      successfulDeployments: 0,
+      contracts: [] as string[],
+      deployedTxIds: [] as string[],
+      startingNonce: nonce,
+    };
+
+    console.log(
+      `Deploying ${contracts.length} contracts starting with nonce ${nonce}`
+    );
+
+    for (const contract of contracts) {
+      const transaction = await makeContractDeploy({
+        contractName: contract.name,
+        codeBody: contract.code,
+        clarityVersion: versionMap?.[contract.filename] ?? 3,
+        senderKey: STACKS_PRIVATE_KEY,
+        nonce,
+        network: "devnet",
+        client: { baseUrl: STACKS_API_URL },
+      });
+
+      const response = await broadcastTransaction({
+        transaction,
+        network: "devnet",
+        client: { baseUrl: STACKS_API_URL },
+      });
+
+      if (
+        "error" in response &&
+        response.reason === "ContractAlreadyExists"
+        // Allow pre existing contracts only for local testing
+      ) {
+        console.log(
+          `Contract ${contract.name} already exists, skipping deployment`
+        );
+        results.successfulDeployments++;
+        results.contracts.push(contract.name);
+        continue;
+      } else if ("error" in response) {
+        throw new Error(
+          `Deploy failed for ${contract.name}: ${response.error} ${response.reason}`
+        );
+      }
+
+      expect(response.txid).toBeDefined();
+      expect(response.txid.length).toBe(64);
+
+      console.log(`Deployed ${contract.name}: ${response.txid}`);
+
+      // Wait for transaction to be successful before proceeding
+      await waitForTransactionSuccess(response.txid);
+
+      results.successfulDeployments++;
+      results.contracts.push(contract.name);
+      results.deployedTxIds.push(response.txid);
+      nonce += 1n;
+    }
+
+    expect(results.totalContracts).toBeGreaterThan(0);
+    expect(results.successfulDeployments).toBe(results.totalContracts);
+    expect(results.contracts).toContain("wormhole-core-state");
+    expect(results.contracts).toContain("wormhole-core-v4");
+    expect(results.contracts).toContain("wormhole-core-proxy-v2");
+  });
+
+  it("should initialize stacks core contract", async () => {
+    const ADDRESS = privateKeyToAddress(STACKS_PRIVATE_KEY, "devnet");
+
+    const nonce = await fetchNonce({
+      address: ADDRESS,
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    const transaction = await makeContractCall({
+      contractAddress: ADDRESS,
+      contractName: "wormhole-core-v4",
+      functionName: "initialize",
+      functionArgs: [Cl.none()],
+      senderKey: STACKS_PRIVATE_KEY,
+      fee: 50_000,
+      nonce,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    const response = await broadcastTransaction({
+      transaction,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    if ("txid" in response && response.txid) {
+      console.log(`Initialized core: ${response.txid}`);
+
+      try {
+        await waitForTransactionSuccess(response.txid);
+      } catch (error) {
+        // Check if it's the "already initialized" error (u10003)
+        if (
+          // Allow already initialized only for local testing
+          error instanceof Error &&
+          error.message.includes("(err u10003)")
+        ) {
+          console.log(`Core already initialized, continuing...`);
+        } else throw error;
+      }
+
+      const owner = await fetchCallReadOnlyFunction({
+        contractAddress: ADDRESS,
+        contractName: "wormhole-core-state",
+        functionName: "get-active-wormhole-core-contract",
+        functionArgs: [],
+        network: "devnet",
+        client: { baseUrl: STACKS_API_URL },
+        senderAddress: ADDRESS,
+      });
+
+      expect(owner).toBeDefined();
+      console.log(`Core initialization verified`);
+    } else {
+      console.error(`Failed to initialize core:`, response);
+      throw new Error(`Core initialization failed`);
+    }
+  });
+
+  it("should upgrade guardian set", async () => {
+    const ADDRESS = privateKeyToAddress(STACKS_PRIVATE_KEY, "devnet");
+
+    const exportedVars = (await fetchCallReadOnlyFunction({
+      contractAddress: ADDRESS,
+      contractName: "wormhole-core-v4",
+      functionName: "get-exported-vars",
+      functionArgs: [],
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+      senderAddress: ADDRESS,
+    })) as TupleCV<{ "active-guardian-set-id": UIntCV }>;
+
+    const activeGuardianSetId = Number(
+      exportedVars.value["active-guardian-set-id"].value
+    );
+
+    const keychain = wormhole.generateGuardianSetKeychain(19);
+    const guardianSetUpgrade = wormhole.generateGuardianSetUpdateVaa(
+      keychain,
+      activeGuardianSetId + 1
+    );
+
+    const nonce = await fetchNonce({
+      address: ADDRESS,
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    const transaction = await makeContractCall({
+      contractAddress: ADDRESS,
+      contractName: "wormhole-core-v4",
+      functionName: "guardian-set-upgrade",
+      functionArgs: [
+        Cl.buffer(guardianSetUpgrade.vaa),
+        Cl.list(guardianSetUpgrade.uncompressedPublicKeys),
+      ],
+      senderKey: STACKS_PRIVATE_KEY,
+      fee: 100_000,
+      nonce,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    const response = await broadcastTransaction({
+      transaction,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    if ("txid" in response && response.txid) {
+      console.log(`Guardian set upgrade: ${response.txid}`);
+
+      try {
+        await waitForTransactionSuccess(response.txid);
+
+        const guardianSet = await fetchContractMapEntry({
+          contractAddress: ADDRESS,
+          contractName: "wormhole-core-state",
+          mapName: "guardian-sets",
+          mapKey: Cl.uint(1),
+          network: "devnet",
+          client: { baseUrl: STACKS_API_URL },
+        });
+
+        expect(guardianSet).toBeDefined();
+        console.log(`Guardian set upgrade verified`);
+      } catch (error) {
+        if (
+          // Allow existing guardian set only for local testing
+          error instanceof Error &&
+          error.message.includes("(err u1102)")
+        ) {
+          console.log(`Guardian set upgrade failed, continuing...`);
+        } else {
+          throw error;
+        }
+      }
+    } else {
+      console.error(`Failed to upgrade guardian set:`, response);
+      throw new Error(`Guardian set upgrade failed`);
+    }
+  });
+
+  it("should post and spy onmessage", async () => {
+    const ADDRESS = privateKeyToAddress(STACKS_PRIVATE_KEY, "devnet");
+
+    const payload = Buffer.from("test-payload-success-case");
+    const messageNonce = Math.floor(Math.random() * 0xffffffff);
+
+    const spyPromise = expectVAA(payload);
+
+    const nonce = await fetchNonce({
+      address: ADDRESS,
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    const transaction = await makeContractCall({
+      contractAddress: ADDRESS,
+      contractName: "wormhole-core-v4",
+      functionName: "post-message",
+      functionArgs: [Cl.buffer(payload), Cl.uint(messageNonce), Cl.none()],
+      postConditionMode: "allow",
+      senderKey: STACKS_PRIVATE_KEY,
+      fee: 100_000,
+      nonce,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    const response = await broadcastTransaction({
+      transaction,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    if ("error" in response) throw new Error(response.error);
+
+    console.log(`Posted message: ${response.txid}`);
+    await waitForTransactionSuccess(response.txid);
+
+    expect(response.txid).toBeDefined();
+    expect(response.txid.length).toBe(64);
+
+    await expect(spyPromise).resolves.toBeUndefined();
+  });
+
+  it("should spy but not find VAA for faulty transaction (abort_by_post_condition)", async () => {
+    const ADDRESS = privateKeyToAddress(STACKS_PRIVATE_KEY, "devnet");
+
+    const payload = Buffer.from("test-payload-abort-by-post-condition");
+    const messageNonce = Math.floor(Math.random() * 0xffffffff);
+
+    const spyPromise = expectNoStacksVAA();
+
+    const nonce = await fetchNonce({
+      address: ADDRESS,
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    const transaction = await makeContractCall({
+      contractAddress: ADDRESS,
+      contractName: "wormhole-core-v4",
+      functionName: "post-message",
+      functionArgs: [Cl.buffer(payload), Cl.uint(messageNonce), Cl.none()],
+      postConditionMode: "allow",
+      postConditions: [Pc.origin().willSendEq(66).ustx()],
+      senderKey: STACKS_PRIVATE_KEY,
+      fee: 100_000,
+      nonce,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    const response = await broadcastTransaction({
+      transaction,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    if ("error" in response) throw new Error(response.error);
+
+    console.log(
+      `Posted faulty message (abort_by_post_condition): ${response.txid}`
+    );
+    await waitForTransactionSuccess(response.txid);
+
+    expect(response.txid).toBeDefined();
+    expect(response.txid.length).toBe(64);
+
+    await expect(spyPromise).resolves.toBeUndefined();
+  });
+
+  it("should spy but not find VAA for faulty transaction (abort_by_response) #1", async () => {
+    const ADDRESS = privateKeyToAddress(STACKS_PRIVATE_KEY, "devnet");
+
+    const payload = Buffer.from("test-payload-abort-by-response-1");
+
+    const spyPromise = expectNoStacksVAA();
+
+    const nonce = await fetchNonce({
+      address: ADDRESS,
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    // Deploy a contract that calls post-message on deploy
+    // The contract-call succeeds but transaction fails because response isn't handled
+    const clarityCode = `(begin
+                            (contract-call? '${ADDRESS}.wormhole-core-v4 post-message
+                                0x${payload.toString("hex")}
+                                u42
+                                none)
+                            (err u1))`;
+
+    const transaction = await makeContractDeploy({
+      contractName: `test-post-message-abort-${nonce}`,
+      codeBody: clarityCode,
+      clarityVersion: 3,
+      senderKey: STACKS_PRIVATE_KEY,
+      fee: 100_000,
+      nonce,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    const response = await broadcastTransaction({
+      transaction,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    if ("error" in response) {
+      console.log(`Deploy failed: ${JSON.stringify(response, null, 2)}`);
+      throw new Error(response.error);
+    }
+
+    console.log(`Posted faulty message (abort_by_response): ${response.txid}`);
+    try {
+      await waitForTransactionSuccess(response.txid);
+    } catch (error) {
+      if (error instanceof Error && error.message.includes("(err none)")) {
+        console.log(`Transaction failed as expected.`);
+      }
+    }
+
+    expect(response.txid).toBeDefined();
+    expect(response.txid.length).toBe(64);
+
+    await expect(spyPromise).resolves.toBeUndefined();
+  });
+
+  it("should spy but not find VAA for faulty transaction (abort_by_response) #2", async () => {
+    const ADDRESS = privateKeyToAddress(STACKS_PRIVATE_KEY, "devnet");
+
+    const payload = Buffer.from("test-payload-abort-by-response-2");
+
+    const spyPromise = expectNoStacksVAA();
+
+    let nonce = await fetchNonce({
+      address: ADDRESS,
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    // STEP 1: Deploy a contract with a public function that calls post-message and returns error
+    const clarityCode = `(define-public (post-and-fail)
+  (begin
+    (try! (contract-call? '${ADDRESS}.wormhole-core-v4 post-message
+      0x${payload.toString("hex")}
+      u43
+      none))
+    (err u1)))`;
+
+    const deployTransaction = await makeContractDeploy({
+      contractName: `test-post-message-two-step-${nonce}`,
+      codeBody: clarityCode,
+      clarityVersion: 3,
+      senderKey: STACKS_PRIVATE_KEY,
+      fee: 100_000,
+      nonce,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    const deployResponse = await broadcastTransaction({
+      transaction: deployTransaction,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    if ("error" in deployResponse) {
+      console.log(`Deploy failed: ${JSON.stringify(deployResponse, null, 2)}`);
+      throw new Error(deployResponse.error);
+    }
+
+    console.log(`Deployed two-step test contract: ${deployResponse.txid}`);
+    try {
+      await waitForTransactionSuccess(deployResponse.txid);
+    } catch (error) {
+      if (error instanceof Error && error.message.includes("(err")) {
+        console.log(`Transaction failed as expected.`);
+      }
+    }
+
+    expect(deployResponse.txid).toBeDefined();
+    expect(deployResponse.txid.length).toBe(64);
+
+    nonce += 1n;
+
+    // STEP 2: Call the public function that will post-message and return error
+    const callTransaction = await makeContractCall({
+      contractAddress: ADDRESS,
+      contractName: `test-post-message-two-step-${nonce - 1n}`,
+      functionName: "post-and-fail",
+      functionArgs: [],
+      postConditionMode: "allow",
+      senderKey: STACKS_PRIVATE_KEY,
+      fee: 100_000,
+      nonce,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    const callResponse = await broadcastTransaction({
+      transaction: callTransaction,
+      network: "devnet",
+      client: { baseUrl: STACKS_API_URL },
+    });
+
+    if ("error" in callResponse) {
+      console.log(`Call failed: ${JSON.stringify(callResponse, null, 2)}`);
+      throw new Error(callResponse.error);
+    }
+
+    console.log(
+      `Posted faulty message (abort_by_response_two_step): ${callResponse.txid}`
+    );
+    try {
+      await waitForTransactionSuccess(callResponse.txid);
+    } catch (error) {
+      if (error instanceof Error && error.message.includes("(err")) {
+        console.log(`Transaction failed as expected.`);
+      }
+    }
+
+    expect(callResponse.txid).toBeDefined();
+    expect(callResponse.txid.length).toBe(64);
+
+    await expect(spyPromise).resolves.toBeUndefined();
+  });
+});

+ 55 - 0
stacks/test/lib/constants.ts

@@ -0,0 +1,55 @@
+// Constants for Stacks ↔ Ethereum integration testing
+// Based on existing devnet configuration
+
+// Stacks Configuration (from existing test files)
+export const STACKS_API_URL = "http://stacks-node:20443";
+export const STACKS_PRIVATE_KEY =
+  "714a5bf161a680ebb2670c5ea6e8bcd75f299eae234412af0cf12d21e11ae09901";
+
+// Chain IDs (Wormhole numbering system)
+export const CHAIN_ID_STACKS = 60;
+export const CHAIN_ID_ETHEREUM = 2;
+
+// Guardian Spy Service Host
+export const SPY_SERVICE_HOST = "spy:7072";
+
+// Ethereum Devnet Contract Addresses (from research)
+export const ETHEREUM_CONTRACTS = {
+  WORMHOLE_CORE: "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550",
+  TOKEN_BRIDGE: "0x0290FB167208Af455bB137780163b7B7a9a10C16",
+  TEST_TOKEN: "0x2D8BE6BF0baA74e0A907016679CaE9190e80dD0A",
+} as const;
+
+// Ethereum Configuration
+export const ETH_NODE_URL = "http://localhost:8545";
+
+// Ethereum Private Keys (from existing SDK test configuration)
+export const ETH_PRIVATE_KEY =
+  "0x4f3edf983ac636a65a842ce7c78d9aa706d3b113bce9c46f30d7d21715b23b1d"; // account 0
+export const ETH_PRIVATE_KEY_STACKS =
+  "0x6370fd033278c143179d81c5526140625662b8daa446c22ee2d73db3707e620c"; // account 1 - for Stacks tests
+
+// Stacks Contract Names (based on available contracts)
+export const STACKS_CONTRACTS = {
+  CORE_STATE: "wormhole-core-state",
+  CORE_V4: "wormhole-core-v4",
+  CORE_PROXY: "wormhole-core-proxy-v2",
+} as const;
+
+// Ethereum NTT Contract Addresses (deployed by 05-deploy-ntt-ethereum.ts)
+export const ETHEREUM_NTT_CONTRACTS = {
+  DUMMY_TOKEN: "0xfE82e8f24A51E670133f4268cDfc164c49FC3b37",
+  TRANSCEIVER_STRUCTS_LIBRARY: "0xb4fFe5983B0B748124577Af4d16953bd096b6897",
+  NTT_MANAGER: "0x6f84742680311CEF5ba42bc10A71a4708b4561d1",
+  WORMHOLE_TRANSCEIVER: "0x25AF99b922857C37282f578F428CB7f34335B379",
+} as const;
+
+// NTT Configuration Constants
+export const NTT_CONFIG = {
+  MODE: 0, // LOCKING mode - preserve STX supply on Stacks
+  RATE_LIMIT_DURATION: 86400, // 24 hours in seconds
+  CONSISTENCY_LEVEL: 202, // Finalized
+  GAS_LIMIT: 500000,
+  OUTBOUND_LIMIT: "1000000000000000000000000", // 1M tokens (scaled by 18 decimals)
+  THRESHOLD: 1, // Single transceiver setup
+} as const;

+ 925 - 0
stacks/test/lib/helpers.ts

@@ -0,0 +1,925 @@
+import { spy } from "@certusone/wormhole-sdk-proto-node";
+import * as grpc from "@grpc/grpc-js";
+import { fc } from "@fast-check/vitest";
+import { keccak_256 } from "@noble/hashes/sha3";
+import * as secp from "@noble/secp256k1";
+import { Cl, ClarityValue } from "@stacks/transactions";
+import { toBigIntBE, toBufferBE } from "bigint-buffer";
+import { webcrypto } from "node:crypto";
+
+// @ts-ignore
+if (!globalThis.crypto) globalThis.crypto = webcrypto;
+
+import { hmac } from "@noble/hashes/hmac";
+import { sha256 } from "@noble/hashes/sha256";
+import { hexToBytes } from "@noble/hashes/utils";
+import { createClient } from "@stacks/blockchain-api-client";
+import { promisify } from "util";
+import { CHAIN_ID_STACKS, SPY_SERVICE_HOST, STACKS_API_URL } from "./constants";
+
+const sleep = promisify(setTimeout);
+
+export type ReadinessCheck = () => Promise<boolean>;
+
+// STACKS
+
+export async function waitForTransactionSuccess(
+  txid: string,
+  timeoutMs = 30_000
+): Promise<void> {
+  const api = createClient({ baseUrl: STACKS_API_URL });
+  const startTime = Date.now();
+
+  while (Date.now() - startTime < timeoutMs) {
+    try {
+      const tx = (await api.GET("/v3/transaction/{tx_id}" as any, {
+        params: { path: { tx_id: txid } },
+      })) as unknown as {
+        data?: { index_block_hash: string; tx: string; result: string };
+        response: Response;
+      };
+
+      if (tx.data?.result.startsWith("(ok ")) {
+        console.log(`Transaction ${txid}: ${tx.data.result}`);
+        return;
+      }
+
+      if (tx.data?.result.startsWith("(err ")) {
+        throw new Error(`Transaction ${txid} failed: ${tx.data.result}`);
+      }
+    } catch (error) {
+      // Re-throw errors that we explicitly threw
+      if (error instanceof Error && error.message.includes("failed")) {
+        throw error;
+      }
+
+      console.warn(`Error waiting for tx success`, error?.message);
+    }
+
+    await sleep(1000);
+  }
+
+  throw new Error(
+    `Timeout waiting for transaction ${txid} to succeed after ${timeoutMs}ms`
+  );
+}
+
+// WORMHOLE
+
+const initialGovernanceDataSource = {
+  chain: 1,
+  address: hexToBytes(
+    "5635979a221c34931e32620b9293a463065555ea71fe97cd6237ade875b12e9e"
+  ),
+};
+
+secp.etc.hmacSha256Sync = (k, ...m) =>
+  hmac(sha256, k, secp.etc.concatBytes(...m));
+
+export namespace wormhole {
+  export interface Guardian {
+    guardianId: number;
+    secretKey: Uint8Array;
+    compressedPublicKey: Uint8Array;
+    uncompressedPublicKey: Uint8Array;
+    ethereumAddress: Uint8Array;
+  }
+
+  export const privateKeyToGuardian = (
+    privateKey: Uint8Array,
+    guardianId: number = 0
+  ): Guardian => {
+    const uncompressedPublicKey = secp
+      .getPublicKey(privateKey, false)
+      .slice(1, 65);
+    return {
+      guardianId,
+      secretKey: privateKey,
+      compressedPublicKey: secp.getPublicKey(privateKey, true),
+      uncompressedPublicKey,
+      ethereumAddress: keccak_256(uncompressedPublicKey).slice(12, 32),
+    };
+  };
+
+  export const generateGuardianSetKeychain = (count = 19): Guardian[] => {
+    let keychain: Guardian[] = [];
+    for (let i = 0; i < count; i++) {
+      let secretKey = secp.utils.randomPrivateKey();
+      let uncompressedPublicKey = secp
+        .getPublicKey(secretKey, false)
+        .slice(1, 65);
+      let ethereumAddress = keccak_256(uncompressedPublicKey).slice(12, 32);
+      keychain.push({
+        guardianId: i,
+        secretKey,
+        uncompressedPublicKey,
+        ethereumAddress,
+        compressedPublicKey: secp.getPublicKey(secretKey, true),
+      });
+    }
+    return keychain;
+  };
+
+  export interface NewEmitter {
+    chain: number;
+    sequence: bigint;
+    address: Uint8Array;
+  }
+
+  export interface Emitter {
+    chain: number;
+    address: Uint8Array;
+  }
+
+  export interface VaaHeader {
+    version: number;
+    guardianSetId: number;
+    signatures: Uint8Array[];
+  }
+
+  export interface VaaBody {
+    timestamp: number;
+    emitterChain: number;
+    nonce: number;
+    emitterAddress: Uint8Array;
+    sequence: bigint;
+    consistencyLevel: number;
+    payload: Uint8Array;
+  }
+
+  export interface VaaHeaderBuildOptions {
+    version?: number;
+    guardianSetId?: number;
+    signatures?: Uint8Array[];
+  }
+
+  export interface VaaBodyBuildOptions {
+    timestamp?: number;
+    emitterChain?: number;
+    nonce?: number;
+    emitterAddress?: Uint8Array;
+    sequence?: bigint;
+    consistencyLevel?: number;
+    payload?: Uint8Array;
+  }
+
+  export const GovernanceUpdateEmitter = {
+    chain: 1,
+    sequence: 0,
+    address: hexToBytes(
+      "0000000000000000000000000000000000000000000000000000000000000004"
+    ),
+  };
+
+  export namespace fc_ext {
+    // Helper for generating a VAA Body;
+    // Wire format reminder:
+    // ===========================
+    // VAA Body
+    // u32         timestamp           (Timestamp of the block where the source transaction occurred)
+    // u32         nonce               (A grouping number)
+    // u16         emitter_chain       (Wormhole ChainId of emitter contract)
+    // [32]byte    emitter_address     (Emitter contract address, in Wormhole format)
+    // u64         sequence            (Strictly increasing sequence, tied to emitter address & chain)
+    // u8          consistency_level   (What finality level was reached before emitting this message)
+    // []byte      payload             (VAA message content)
+    export const vaaBody = (opts?: VaaBodyBuildOptions) => {
+      // Timestamp
+      const timestamp =
+        opts && opts.timestamp
+          ? fc.constant(opts.timestamp)
+          : fc.nat(4294967295);
+
+      // Nonce
+      const nonce =
+        opts && opts.nonce ? fc.constant(opts.nonce) : fc.nat(4294967295);
+
+      // Emitter chain
+      const emitterChain =
+        opts && opts.emitterChain
+          ? fc.constant(opts.emitterChain)
+          : fc.nat(65535);
+
+      // Emitter address
+      const emitterAddress =
+        opts && opts.emitterAddress
+          ? fc.constant(opts.emitterAddress)
+          : fc.uint8Array({ minLength: 32, maxLength: 32 });
+
+      // Sequence
+      const sequence =
+        opts && opts.sequence ? fc.constant(opts.sequence) : fc.bigInt();
+
+      // Consistency level
+      const consistencyLevel =
+        opts && opts.consistencyLevel
+          ? fc.constant(opts.consistencyLevel)
+          : fc.nat(255);
+
+      // Payload
+      const payload =
+        opts && opts.payload
+          ? fc.constant(opts.payload)
+          : fc.uint8Array({ minLength: 20, maxLength: 2048 });
+
+      return fc.tuple(
+        timestamp,
+        nonce,
+        emitterChain,
+        emitterAddress,
+        sequence,
+        consistencyLevel,
+        payload
+      );
+    };
+
+    // Helper for generating a VAA Header;
+    // Wire format reminder:
+    // ===========================
+    // VAA Header
+    // byte        version             (VAA Version)
+    // u32         guardian_set_index  (Indicates which guardian set is signing)
+    // u8          len_signatures      (Number of signatures stored)
+    // [][66]byte  signatures          (Collection of ecdsa signatures)
+    export const vaaHeader = (
+      opts?: VaaHeaderBuildOptions,
+      numberOfSignatures = 19
+    ) => {
+      // Version
+      const version =
+        opts && opts.version ? fc.constant(opts.version) : fc.nat(255);
+
+      // Guardian set id
+      const guardianSetId =
+        opts && opts.guardianSetId
+          ? fc.constant(opts.guardianSetId)
+          : fc.nat(255);
+
+      // Specified signatures
+      const specifiedSignatures =
+        opts && opts.signatures
+          ? fc.constant(opts.signatures)
+          : fc.array(fc.uint8Array({ minLength: 66, maxLength: 66 }), {
+              minLength: 0,
+              maxLength: 0,
+            });
+
+      const specifiedSignaturesLen =
+        opts && opts.signatures ? opts.signatures.length : 0;
+
+      // Generated signatures
+      let generatedSignatures = fc.array(
+        fc.uint8Array({ minLength: 66, maxLength: 66 }),
+        {
+          minLength: 0,
+          maxLength: numberOfSignatures - specifiedSignaturesLen,
+        }
+      );
+
+      return fc.tuple(
+        version,
+        guardianSetId,
+        specifiedSignatures,
+        generatedSignatures
+      );
+    };
+  }
+
+  export const buildValidVaaHeaderSpecs = (
+    keychain: Guardian[],
+    body: VaaBody,
+    opts?: VaaHeaderBuildOptions
+  ): VaaHeaderBuildOptions => {
+    let signatures: Buffer[] = [];
+    const messageHash = hashVaaBody(body);
+
+    for (let guardian of keychain) {
+      const signature = secp.sign(messageHash, guardian.secretKey);
+
+      const id = Buffer.alloc(1);
+      id.writeUint8(guardian.guardianId, 0);
+
+      // v.writeUint8(signature.addRecoveryBit, 1);
+      if (signature.recovery) {
+        const rec = Buffer.alloc(1);
+        rec.writeUint8(signature.recovery, 0);
+        signatures.push(
+          Buffer.concat([
+            Uint8Array.from(id),
+            signature.toCompactRawBytes(),
+            Uint8Array.from(rec),
+          ])
+        );
+      } else {
+        const rec = Buffer.alloc(1);
+        rec.writeUint8(0, 0);
+        signatures.push(
+          Buffer.concat([
+            Uint8Array.from(id),
+            signature.toCompactRawBytes(),
+            Uint8Array.from(rec),
+          ])
+        );
+      }
+    }
+    return {
+      version: opts?.version,
+      guardianSetId: opts?.guardianSetId,
+      signatures: signatures.map((x) => Uint8Array.from(x)),
+    };
+  };
+
+  export const buildValidVaaHeader = (
+    keychain: Guardian[],
+    body: VaaBody,
+    opts: VaaHeaderBuildOptions
+  ): VaaHeader => {
+    let specs = buildValidVaaHeaderSpecs(keychain, body, opts);
+    return {
+      version: specs.version!,
+      guardianSetId: specs.guardianSetId!,
+      signatures: specs.signatures!,
+    };
+  };
+
+  export const serializeVaaToClarityValue = (
+    header: VaaHeader,
+    body: VaaBody,
+    keychain: Guardian[]
+  ): [ClarityValue, any[]] => {
+    let guardiansPublicKeys: ClarityValue[] = [];
+    let guardiansSignatures: ClarityValue[] = [];
+    for (let i = 0; i < header.signatures.length; i++) {
+      let guardianId = header.signatures[i]?.[0];
+      if (keychain.length > i && guardianId !== undefined) {
+        let guardian = keychain[i];
+        if (guardian) {
+          guardiansPublicKeys.push(
+            Cl.tuple({
+              "guardian-id": Cl.uint(guardianId),
+              "recovered-compressed-public-key": Cl.buffer(
+                guardian.compressedPublicKey
+              ),
+            })
+          );
+        }
+      }
+      guardiansSignatures.push(
+        Cl.tuple({
+          "guardian-id": Cl.uint(header.signatures[i]?.slice(0, 1) || 0),
+          signature: Cl.buffer(
+            header.signatures[i]?.slice(1, 66) || Buffer.alloc(65)
+          ),
+        })
+      );
+    }
+    let value = Cl.tuple({
+      "consistency-level": Cl.uint(body.consistencyLevel),
+      version: Cl.uint(header.version),
+      "guardian-set-id": Cl.uint(header.guardianSetId),
+      "signatures-len": Cl.uint(header.signatures.length),
+      signatures: Cl.list(guardiansSignatures),
+      "emitter-chain": Cl.uint(body.emitterChain),
+      "emitter-address": Cl.buffer(body.emitterAddress),
+      sequence: Cl.uint(body.sequence),
+      timestamp: Cl.uint(body.timestamp),
+      nonce: Cl.uint(body.nonce),
+      payload: Cl.buffer(body.payload),
+    });
+    return [value, guardiansPublicKeys];
+  };
+
+  export interface VaaBodySpec {
+    values: VaaBody;
+    specs: (
+      | fc.Arbitrary<number>
+      | fc.Arbitrary<Uint8Array>
+      | fc.Arbitrary<Uint8Array[]>
+      | fc.Arbitrary<bigint>
+    )[];
+  }
+
+  export const buildValidVaaBodySpecs = (opts?: {
+    payload?: Uint8Array;
+    emitter?: Emitter;
+    sequence?: bigint;
+  }): VaaBody => {
+    const date = Math.floor(Date.now() / 1000);
+    const timestamp = date >>> 0;
+    const payload =
+      (opts && opts.payload && opts.payload) || new Uint8Array(32);
+    let emitter =
+      opts && opts.emitter ? opts.emitter : initialGovernanceDataSource;
+    let values = {
+      timestamp: timestamp,
+      nonce: 0,
+      emitterChain: emitter.chain,
+      emitterAddress: emitter.address,
+      sequence: opts && opts.sequence ? opts.sequence : 1n,
+      consistencyLevel: 0,
+      payload: payload,
+    };
+    return values;
+  };
+
+  export const assembleVaaBody = (
+    timestamp: number | bigint | Uint8Array,
+    nonce: number | bigint | Uint8Array,
+    emitterChain: number | bigint | Uint8Array,
+    emitterAddress: number | bigint | Uint8Array,
+    sequence: number | bigint | Uint8Array,
+    consistencyLevel: number | bigint | Uint8Array,
+    payload: number | bigint | Uint8Array
+  ): VaaBody => {
+    return {
+      timestamp: timestamp as number,
+      nonce: nonce as number,
+      emitterChain: emitterChain as number,
+      emitterAddress: emitterAddress as Uint8Array,
+      sequence: sequence as bigint,
+      consistencyLevel: consistencyLevel as number,
+      payload: payload as Uint8Array,
+    };
+  };
+
+  export const assembleVaaHeader = (
+    version: number | bigint | Uint8Array,
+    guardianSetId: number | bigint | Uint8Array,
+    signatures: number | bigint | Uint8Array[]
+  ): VaaHeader => {
+    return {
+      version: version as number,
+      guardianSetId: guardianSetId as number,
+      signatures: signatures as Uint8Array[],
+    };
+  };
+
+  export const hashVaaBody = (body: VaaBody): Uint8Array => {
+    return keccak_256(
+      keccak_256(Uint8Array.from(serializeVaaBodyToBuffer(body)))
+    );
+  };
+
+  export const serializeVaaToBuffer = (
+    vaaHeader: VaaHeader,
+    vaaBody: VaaBody
+  ) => {
+    return Buffer.concat(
+      [
+        serializeVaaHeaderToBuffer(vaaHeader),
+        serializeVaaBodyToBuffer(vaaBody),
+      ].map((x) => Uint8Array.from(x))
+    );
+  };
+
+  export const serializeVaaHeaderToBuffer = (vaaHeader: VaaHeader) => {
+    const components: Buffer[] = [];
+    var v = Buffer.alloc(1);
+    v.writeUint8(vaaHeader.version, 0);
+    components.push(v);
+
+    v = Buffer.alloc(4);
+    v.writeUInt32BE(vaaHeader.guardianSetId, 0);
+    components.push(v);
+
+    v = Buffer.alloc(1);
+    v.writeUint8(vaaHeader.signatures.length, 0);
+    components.push(v);
+
+    components.push(Buffer.concat(vaaHeader.signatures));
+    return Buffer.concat(components.map((x) => Uint8Array.from(x)));
+  };
+
+  export const serializeVaaBodyToBuffer = (vaaBody: VaaBody) => {
+    const components: (Buffer | Uint8Array)[] = [];
+    let v = Buffer.alloc(4);
+    v.writeUInt32BE(vaaBody.timestamp, 0);
+    components.push(v);
+
+    v = Buffer.alloc(4);
+    v.writeUInt32BE(vaaBody.nonce, 0);
+    components.push(v);
+
+    v = Buffer.alloc(2);
+    v.writeUInt16BE(vaaBody.emitterChain, 0);
+    components.push(v);
+
+    components.push(vaaBody.emitterAddress);
+
+    components.push(bigintToBuffer(vaaBody.sequence, 8));
+
+    v = Buffer.alloc(1);
+    v.writeUint8(vaaBody.consistencyLevel, 0);
+    components.push(v);
+
+    components.push(vaaBody.payload);
+
+    return Buffer.concat(components.map((x) => Uint8Array.from(x)));
+  };
+
+  // This hex string represents the ASCII string "Core"
+  export const coreModule = Buffer.from(
+    "00000000000000000000000000000000000000000000000000000000436f7265",
+    "hex"
+  );
+
+  export const validContractUpgradeModule = coreModule;
+  export const validGuardianRotationModule = coreModule;
+  export const validSetMessageFeeModule = coreModule;
+  export const validTransferFeesModule = coreModule;
+
+  export const serializeGuardianUpdateVaaPayloadToBuffer = (
+    keyChain: Guardian[],
+    action: number,
+    chain: number,
+    setId: number,
+    module = validGuardianRotationModule
+  ) => {
+    const components: (Buffer | Uint8Array)[] = [];
+    components.push(module);
+
+    let v = Buffer.alloc(1);
+    v.writeUint8(action, 0);
+    components.push(v);
+
+    v = Buffer.alloc(2);
+    v.writeUInt16BE(chain, 0);
+    components.push(v);
+
+    v = Buffer.alloc(4);
+    v.writeUInt32BE(setId, 0);
+    components.push(v);
+
+    v = Buffer.alloc(1);
+    v.writeUint8(keyChain.length, 0);
+    components.push(v);
+
+    for (let guardian of keyChain) {
+      components.push(guardian.ethereumAddress);
+    }
+
+    return Buffer.concat(components.map((x) => Uint8Array.from(x)));
+  };
+
+  // Serialize payload for a ContractUpgrade VAA
+  //
+  // Wire format reminder:
+  // ===========================
+  // [32]byte    module     (Module, should be "Core")
+  // u8          action     (Action type, `4` for TransferFees)
+  // u16         chain      (Blockchain ID which this message is intended for)
+  // principal   contract   (Successor contract)
+  //
+  // See also: https://github.com/wormhole-foundation/wormhole/blob/main/whitepapers/0004_message_publishing.md
+  export const serializeContractUpgradeVaaPayloadToBuffer = (
+    action: number,
+    chain: number,
+    contract: string,
+    module = validSetMessageFeeModule
+  ) => {
+    const components: (Buffer | Uint8Array)[] = [];
+    components.push(module);
+
+    let v = Buffer.alloc(1);
+    v.writeUint8(action, 0);
+    components.push(v);
+
+    v = Buffer.alloc(2);
+    v.writeUInt16BE(chain, 0);
+    components.push(v);
+
+    // Encode `contract` as Stacks wire format
+    v = Buffer.from(Cl.serialize(Cl.principal(contract)));
+    components.push(v);
+
+    return Buffer.concat(components.map((x) => Uint8Array.from(x)));
+  };
+
+  // Serialize payload for a SetMessageFee VAA
+  //
+  // Wire format reminder:
+  // ===========================
+  // [32]byte    module     (Module, should be "Core")
+  // u8          action     (Action type, `3` for SetMessageFee)
+  // u16         chain      (Blockchain ID which this message is intended for)
+  // u256        fee        (New fee (in uSTX) to emit a message to Wormhole Guardians)
+  //
+  // See also: https://github.com/wormhole-foundation/wormhole/blob/main/whitepapers/0004_message_publishing.md
+  export const serializeSetMessageFeeVaaPayloadToBuffer = (
+    action: number,
+    chain: number,
+    fee: number | bigint,
+    module = validSetMessageFeeModule
+  ) => {
+    const components: Buffer[] = [];
+    components.push(module);
+
+    const v1 = Buffer.alloc(1);
+    v1.writeUint8(action, 0);
+    components.push(v1);
+
+    const v2 = Buffer.alloc(2);
+    v2.writeUInt16BE(chain, 0);
+    components.push(v2);
+
+    components.push(bigintToBuffer(fee, 32));
+
+    return Buffer.concat(components.map((x) => Uint8Array.from(x)));
+  };
+
+  // Serialize payload for a TransferFees VAA
+  //
+  // Wire format reminder:
+  // ===========================
+  // [32]byte    module     (Module, should be "Core")
+  // u8          action     (Action type, `4` for TransferFees)
+  // u16         chain      (Blockchain ID which this message is intended for)
+  // u256        amount     (Amount to transfer from accumulated message fees)
+  // principal   recipient  (Recipient of message fees)
+  //
+  // See also: https://github.com/wormhole-foundation/wormhole/blob/main/whitepapers/0004_message_publishing.md
+  export const serializeTransferFeesVaaPayloadToBuffer = (
+    action: number,
+    chain: number,
+    amount: number | bigint,
+    recipient: string,
+    module = validSetMessageFeeModule
+  ) => {
+    const components: Buffer[] = [];
+    components.push(module);
+
+    const v1 = Buffer.alloc(1);
+    v1.writeUint8(action, 0);
+    components.push(v1);
+
+    const v2 = Buffer.alloc(2);
+    v2.writeUInt16BE(chain, 0);
+    components.push(v2);
+
+    components.push(bigintToBuffer(amount, 32));
+
+    // Encode `recipient` as Stacks wire format
+    components.push(Buffer.from(Cl.serialize(Cl.principal(recipient))));
+
+    return Buffer.concat(components.map((x) => Uint8Array.from(x)));
+  };
+
+  export function generateGuardianSetUpdateVaa(
+    keychain: wormhole.Guardian[],
+    guardianSetId: number
+  ) {
+    let guardianRotationPayload =
+      wormhole.serializeGuardianUpdateVaaPayloadToBuffer(
+        keychain,
+        2,
+        0,
+        guardianSetId,
+        wormhole.validGuardianRotationModule
+      );
+    let vaaBody = wormhole.buildValidVaaBodySpecs({
+      payload: Uint8Array.from(guardianRotationPayload),
+      emitter: wormhole.GovernanceUpdateEmitter,
+    });
+    let vaaHeader = wormhole.buildValidVaaHeader(keychain, vaaBody, {
+      version: 1,
+      guardianSetId: guardianSetId - 1,
+    });
+    let vaa = wormhole.serializeVaaToBuffer(vaaHeader, vaaBody);
+    let uncompressedPublicKey: ClarityValue[] = [];
+    for (let guardian of keychain) {
+      uncompressedPublicKey.push(Cl.buffer(guardian.uncompressedPublicKey));
+    }
+    return {
+      vaa: Uint8Array.from(vaa),
+      uncompressedPublicKeys: uncompressedPublicKey,
+      header: vaaHeader,
+      body: vaaBody,
+    };
+  }
+
+  export function generateMessageFeeVaa(
+    keychain: wormhole.Guardian[],
+    guardianSetId: number,
+    fee: number | bigint,
+    chain: number
+  ) {
+    const payload = wormhole.serializeSetMessageFeeVaaPayloadToBuffer(
+      3,
+      chain,
+      fee
+    );
+    const body = wormhole.buildValidVaaBodySpecs({
+      payload: Uint8Array.from(payload),
+      emitter: wormhole.GovernanceUpdateEmitter,
+    });
+    const header = wormhole.buildValidVaaHeader(keychain, body, {
+      version: 1,
+      guardianSetId,
+    });
+    const vaa = wormhole.serializeVaaToBuffer(header, body);
+
+    return {
+      vaa: Uint8Array.from(vaa),
+      header,
+      body,
+    };
+  }
+
+  // This links `wormhole-core`
+}
+
+// SPY
+
+export interface VAAData {
+  version: number;
+  signatureCount: number;
+  bodyOffset: number;
+  emitterChain: number;
+  emitterAddress: string;
+  sequence: bigint;
+  payload: Buffer;
+}
+
+export const parseVAABuffer = (buf: Buffer): VAAData => {
+  const version = buf.readUInt8(0);
+  const signatureCount = buf.readUInt8(5);
+  const bodyOffset = 6 + signatureCount * 66;
+  const emitterChain = buf.readUInt16BE(bodyOffset + 8);
+  const emitterAddress = buf
+    .subarray(bodyOffset + 10, bodyOffset + 42)
+    .toString("hex");
+  const sequence = buf.readBigUInt64BE(bodyOffset + 42);
+  const payload = buf.subarray(bodyOffset + 51);
+
+  return {
+    version,
+    signatureCount,
+    bodyOffset,
+    emitterChain,
+    emitterAddress,
+    sequence,
+    payload,
+  };
+};
+
+export const expectVAA = (
+  expectedPayload: Buffer,
+  timeoutMs = 120_000
+): Promise<void> => {
+  const { SpyRPCServiceClient, SubscribeSignedVAARequest } = spy;
+  const client = new SpyRPCServiceClient(
+    SPY_SERVICE_HOST,
+    grpc.credentials.createInsecure()
+  );
+  const stream = client.subscribeSignedVAA(
+    SubscribeSignedVAARequest.fromPartial({ filters: [] })
+  );
+
+  return new Promise<void>((resolve, reject) => {
+    let vaaReceived = false;
+
+    const cleanup = () => {
+      stream.destroy();
+      client.close();
+    };
+
+    const timeout = setTimeout(() => {
+      if (!vaaReceived) {
+        cleanup();
+        reject("VAA monitoring timed out");
+      }
+    }, timeoutMs);
+
+    stream.on("data", (vaa) => {
+      if (vaaReceived) return;
+
+      if (vaa.vaaBytes && vaa.vaaBytes.length > 0) {
+        const vaaBuffer = Buffer.from(vaa.vaaBytes);
+
+        if (vaaBuffer.length >= 51) {
+          const signatureCount = vaaBuffer.readUInt8(5);
+          const bodyOffset = 6 + signatureCount * 66;
+
+          if (vaaBuffer.length >= bodyOffset + 51) {
+            const parsedVAA = parseVAABuffer(vaaBuffer);
+
+            // Only process VAAs from Stacks chain
+            if (parsedVAA.emitterChain !== CHAIN_ID_STACKS) return;
+
+            // Check if this VAA contains our expected payload
+            if (!parsedVAA.payload.equals(expectedPayload)) return;
+
+            console.log(`VAA received, emitter: ${parsedVAA.emitterAddress}`);
+
+            vaaReceived = true;
+            clearTimeout(timeout);
+            cleanup();
+            resolve();
+          }
+        }
+      }
+    });
+
+    stream.on("error", (error: Error) => {
+      clearTimeout(timeout);
+      cleanup();
+      reject(`VAA monitoring failed: ${error.message}`);
+    });
+
+    stream.on("end", () => {
+      clearTimeout(timeout);
+      cleanup();
+      if (!vaaReceived) resolve();
+    });
+  });
+};
+
+export const expectNoStacksVAA = (timeoutMs = 60_000): Promise<void> => {
+  const { SpyRPCServiceClient, SubscribeSignedVAARequest } = spy;
+  const client = new SpyRPCServiceClient(
+    SPY_SERVICE_HOST,
+    grpc.credentials.createInsecure()
+  );
+  const stream = client.subscribeSignedVAA(
+    SubscribeSignedVAARequest.fromPartial({ filters: [] })
+  );
+
+  return new Promise<void>((resolve, reject) => {
+    let vaaReceived = false;
+
+    const cleanup = () => {
+      stream.destroy();
+      client.close();
+    };
+
+    const timeout = setTimeout(() => {
+      if (!vaaReceived) {
+        cleanup();
+        console.log("No VAA received as expected (timeout reached)");
+        resolve();
+      }
+    }, timeoutMs);
+
+    stream.on("data", (vaa) => {
+      if (vaaReceived) return;
+
+      if (vaa.vaaBytes && vaa.vaaBytes.length > 0) {
+        const vaaBuffer = Buffer.from(vaa.vaaBytes);
+
+        if (vaaBuffer.length >= 51) {
+          const signatureCount = vaaBuffer.readUInt8(5);
+          const bodyOffset = 6 + signatureCount * 66;
+
+          if (vaaBuffer.length >= bodyOffset + 51) {
+            const emitterChain = vaaBuffer.readUInt16BE(bodyOffset + 8);
+
+            if (emitterChain === CHAIN_ID_STACKS) {
+              vaaReceived = true;
+              clearTimeout(timeout);
+              cleanup();
+              reject(
+                new Error("Unexpected VAA received for faulty transaction")
+              );
+              return;
+            }
+          }
+        }
+      }
+    });
+
+    stream.on("error", (error: Error) => {
+      clearTimeout(timeout);
+      cleanup();
+      reject(`VAA monitoring failed: ${error.message}`);
+    });
+
+    stream.on("end", () => {
+      clearTimeout(timeout);
+      cleanup();
+      if (!vaaReceived) resolve();
+    });
+  });
+};
+
+export function bigintToBuffer(
+  value: bigint | number | string,
+  byteLength: number
+): Buffer {
+  switch (typeof value) {
+    case "number":
+    case "string":
+      return toBufferBE(BigInt(value), byteLength);
+    case "bigint":
+      return toBufferBE(value, byteLength);
+  }
+}
+
+export function bufferToHexString(bytes: Uint8Array) {
+  return Array.from(bytes, function (byte) {
+    return ("0" + (byte & 0xff).toString(16)).slice(-2);
+  }).join("");
+}
+
+export function bufferToBigint(bytes: Buffer): BigInt {
+  return toBigIntBE(bytes);
+}

+ 10196 - 0
stacks/test/package-lock.json

@@ -0,0 +1,10196 @@
+{
+  "name": "wormhole-stacks-tools",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "wormhole-stacks-tools",
+      "version": "1.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "@certusone/wormhole-sdk-proto-node": "^0.0.7",
+        "@fast-check/vitest": "^0.2.1",
+        "@grpc/grpc-js": "^1.13.4",
+        "@noble/curves": "^1.9.2",
+        "@noble/hashes": "^1.8.0",
+        "@noble/secp256k1": "^2.3.0",
+        "@scure/base": "^1.2.6",
+        "@stacks/transactions": "^7.2.0",
+        "@wormhole-foundation/wormhole-cli": "^0.0.4",
+        "bigint-buffer": "^1.1.5",
+        "ethers": "^5",
+        "micro-packed": "^0.7.3"
+      },
+      "devDependencies": {
+        "@types/node": "^20.17.25",
+        "elliptic": "^6.6.1",
+        "ts-node": "^10.9.2",
+        "typescript": "^5.9.3",
+        "vitest": "^2.1.6"
+      }
+    },
+    "node_modules/@adraffy/ens-normalize": {
+      "version": "1.10.1",
+      "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz",
+      "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==",
+      "license": "MIT"
+    },
+    "node_modules/@apollo/client": {
+      "version": "3.13.5",
+      "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.13.5.tgz",
+      "integrity": "sha512-ceHa1lApLAiGmUur4V+G/CrjwVwHYujfB7U5HM++poCgHpfGn6eet8YGM93fgeWjYX85SaqwdZbQk18IVwhRHg==",
+      "license": "MIT",
+      "dependencies": {
+        "@graphql-typed-document-node/core": "^3.1.1",
+        "@wry/caches": "^1.0.0",
+        "@wry/equality": "^0.5.6",
+        "@wry/trie": "^0.5.0",
+        "graphql-tag": "^2.12.6",
+        "hoist-non-react-statics": "^3.3.2",
+        "optimism": "^0.18.0",
+        "prop-types": "^15.7.2",
+        "rehackt": "^0.1.0",
+        "symbol-observable": "^4.0.0",
+        "ts-invariant": "^0.10.3",
+        "tslib": "^2.3.0",
+        "zen-observable-ts": "^1.2.5"
+      },
+      "peerDependencies": {
+        "graphql": "^15.0.0 || ^16.0.0",
+        "graphql-ws": "^5.5.5 || ^6.0.3",
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc",
+        "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc",
+        "subscriptions-transport-ws": "^0.9.0 || ^0.11.0"
+      },
+      "peerDependenciesMeta": {
+        "graphql-ws": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        },
+        "react-dom": {
+          "optional": true
+        },
+        "subscriptions-transport-ws": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@aptos-labs/aptos-client": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/@aptos-labs/aptos-client/-/aptos-client-0.1.1.tgz",
+      "integrity": "sha512-kJsoy4fAPTOhzVr7Vwq8s/AUg6BQiJDa7WOqRzev4zsuIS3+JCuIZ6vUd7UBsjnxtmguJJulMRs9qWCzVBt2XA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "axios": "1.7.4",
+        "got": "^11.8.6"
+      },
+      "engines": {
+        "node": ">=15.10.0"
+      }
+    },
+    "node_modules/@aptos-labs/aptos-client/node_modules/axios": {
+      "version": "1.7.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz",
+      "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/@babel/runtime": {
+      "version": "7.26.10",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz",
+      "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==",
+      "license": "MIT",
+      "dependencies": {
+        "regenerator-runtime": "^0.14.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk": {
+      "version": "0.10.18",
+      "resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.10.18.tgz",
+      "integrity": "sha512-VuN4AGB018ELkzTT/jN+yWgE6TWqXsHilxxCVWqGctzow2hKSFd8ADUhxhHigies436rS0vPvrgXi6m0J1+Ecw==",
+      "deprecated": "Please use the new Wormhole TypeScript SDK instead: @wormhole-foundation/sdk",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@certusone/wormhole-sdk-proto-web": "0.0.7",
+        "@certusone/wormhole-sdk-wasm": "^0.0.1",
+        "@coral-xyz/borsh": "0.2.6",
+        "@mysten/sui.js": "0.32.2",
+        "@project-serum/anchor": "^0.25.0",
+        "@solana/spl-token": "^0.3.5",
+        "@solana/web3.js": "^1.66.2",
+        "@terra-money/terra.js": "3.1.9",
+        "@xpla/xpla.js": "^0.2.1",
+        "algosdk": "^2.4.0",
+        "aptos": "1.5.0",
+        "axios": "^0.24.0",
+        "bech32": "^2.0.0",
+        "binary-parser": "^2.2.1",
+        "bs58": "^4.0.1",
+        "elliptic": "^6.5.4",
+        "js-base64": "^3.6.1",
+        "near-api-js": "^1.0.0"
+      },
+      "optionalDependencies": {
+        "@injectivelabs/networks": "1.10.12",
+        "@injectivelabs/sdk-ts": "1.10.72",
+        "@injectivelabs/utils": "1.10.12"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk-proto-node": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk-proto-node/-/wormhole-sdk-proto-node-0.0.7.tgz",
+      "integrity": "sha512-NUjPuGu0Y28gAgsiolWFznd+W8C/RmlZHM3DFjY6eS1vFenDsV2RgORLeFXItsNLwjuiOwwr6eqIr8Do7kzhZA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@grpc/grpc-js": "^1.6.7",
+        "protobufjs": "^7.0.0",
+        "rxjs": "^7.5.6"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk-proto-web": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk-proto-web/-/wormhole-sdk-proto-web-0.0.7.tgz",
+      "integrity": "sha512-GCe1/bcqMS0Mt+hsWp4SE4NLL59pWmK0lhQXO0oqAKl0G9AuuTdudySMDF/sLc7z5H2w34bSuSrIEKvPuuSC+w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@improbable-eng/grpc-web": "^0.15.0",
+        "protobufjs": "^7.0.0",
+        "rxjs": "^7.5.6"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk-wasm": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk-wasm/-/wormhole-sdk-wasm-0.0.1.tgz",
+      "integrity": "sha512-LdIwLhOyr4pPs2jqYubqC7d4UkqYBX0EG/ppspQlW3qlVE0LZRMrH6oVzzLMyHtV0Rw7O9sIKzORW/T3mrJv2w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/long": "^4.0.2",
+        "@types/node": "^18.0.3"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk-wasm/node_modules/@types/node": {
+      "version": "18.19.81",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.81.tgz",
+      "integrity": "sha512-7KO9oZ2//ivtSsryp0LQUqq79zyGXzwq1WqfywpC9ucjY7YyltMMmxWgtRFRKCxwa7VPxVBVy4kHf5UC1E8Lug==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~5.26.4"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk-wasm/node_modules/undici-types": {
+      "version": "5.26.5",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+      "license": "MIT"
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/amino": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.30.1.tgz",
+      "integrity": "sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@cosmjs/crypto": "^0.30.1",
+        "@cosmjs/encoding": "^0.30.1",
+        "@cosmjs/math": "^0.30.1",
+        "@cosmjs/utils": "^0.30.1"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/crypto": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.30.1.tgz",
+      "integrity": "sha512-rAljUlake3MSXs9xAm87mu34GfBLN0h/1uPPV6jEwClWjNkAMotzjC0ab9MARy5FFAvYHL3lWb57bhkbt2GtzQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@cosmjs/encoding": "^0.30.1",
+        "@cosmjs/math": "^0.30.1",
+        "@cosmjs/utils": "^0.30.1",
+        "@noble/hashes": "^1",
+        "bn.js": "^5.2.0",
+        "elliptic": "^6.5.4",
+        "libsodium-wrappers": "^0.7.6"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/encoding": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.30.1.tgz",
+      "integrity": "sha512-rXmrTbgqwihORwJ3xYhIgQFfMSrwLu1s43RIK9I8EBudPx3KmnmyAKzMOVsRDo9edLFNuZ9GIvysUCwQfq3WlQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "bech32": "^1.1.4",
+        "readonly-date": "^1.0.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/encoding/node_modules/bech32": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+      "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/json-rpc": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.30.1.tgz",
+      "integrity": "sha512-pitfC/2YN9t+kXZCbNuyrZ6M8abnCC2n62m+JtU9vQUfaEtVsgy+1Fk4TRQ175+pIWSdBMFi2wT8FWVEE4RhxQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@cosmjs/stream": "^0.30.1",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/math": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.30.1.tgz",
+      "integrity": "sha512-yaoeI23pin9ZiPHIisa6qqLngfnBR/25tSaWpkTm8Cy10MX70UF5oN4+/t1heLaM6SSmRrhk3psRkV4+7mH51Q==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "bn.js": "^5.2.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/proto-signing": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.30.1.tgz",
+      "integrity": "sha512-tXh8pPYXV4aiJVhTKHGyeZekjj+K9s2KKojMB93Gcob2DxUjfKapFYBMJSgfKPuWUPEmyr8Q9km2hplI38ILgQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@cosmjs/amino": "^0.30.1",
+        "@cosmjs/crypto": "^0.30.1",
+        "@cosmjs/encoding": "^0.30.1",
+        "@cosmjs/math": "^0.30.1",
+        "@cosmjs/utils": "^0.30.1",
+        "cosmjs-types": "^0.7.1",
+        "long": "^4.0.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/socket": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.30.1.tgz",
+      "integrity": "sha512-r6MpDL+9N+qOS/D5VaxnPaMJ3flwQ36G+vPvYJsXArj93BjgyFB7BwWwXCQDzZ+23cfChPUfhbINOenr8N2Kow==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@cosmjs/stream": "^0.30.1",
+        "isomorphic-ws": "^4.0.1",
+        "ws": "^7",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/stargate": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.30.1.tgz",
+      "integrity": "sha512-RdbYKZCGOH8gWebO7r6WvNnQMxHrNXInY/gPHPzMjbQF6UatA6fNM2G2tdgS5j5u7FTqlCI10stNXrknaNdzog==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@confio/ics23": "^0.6.8",
+        "@cosmjs/amino": "^0.30.1",
+        "@cosmjs/encoding": "^0.30.1",
+        "@cosmjs/math": "^0.30.1",
+        "@cosmjs/proto-signing": "^0.30.1",
+        "@cosmjs/stream": "^0.30.1",
+        "@cosmjs/tendermint-rpc": "^0.30.1",
+        "@cosmjs/utils": "^0.30.1",
+        "cosmjs-types": "^0.7.1",
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.3",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/stargate/node_modules/protobufjs": {
+      "version": "6.11.4",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+      "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "optional": true,
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.1",
+        "@types/node": ">=13.7.0",
+        "long": "^4.0.0"
+      },
+      "bin": {
+        "pbjs": "bin/pbjs",
+        "pbts": "bin/pbts"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/stream": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.30.1.tgz",
+      "integrity": "sha512-Fg0pWz1zXQdoxQZpdHRMGvUH5RqS6tPv+j9Eh7Q953UjMlrwZVo0YFLC8OTf/HKVf10E4i0u6aM8D69Q6cNkgQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/tendermint-rpc": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.30.1.tgz",
+      "integrity": "sha512-Z3nCwhXSbPZJ++v85zHObeUggrEHVfm1u18ZRwXxFE9ZMl5mXTybnwYhczuYOl7KRskgwlB+rID0WYACxj4wdQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@cosmjs/crypto": "^0.30.1",
+        "@cosmjs/encoding": "^0.30.1",
+        "@cosmjs/json-rpc": "^0.30.1",
+        "@cosmjs/math": "^0.30.1",
+        "@cosmjs/socket": "^0.30.1",
+        "@cosmjs/stream": "^0.30.1",
+        "@cosmjs/utils": "^0.30.1",
+        "axios": "^0.21.2",
+        "readonly-date": "^1.0.0",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/tendermint-rpc/node_modules/axios": {
+      "version": "0.21.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+      "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "follow-redirects": "^1.14.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@cosmjs/utils": {
+      "version": "0.30.1",
+      "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.30.1.tgz",
+      "integrity": "sha512-KvvX58MGMWh7xA+N+deCfunkA/ZNDvFLw4YbOmX3f/XBIkqrVY7qlotfy2aNb1kgp6h4B6Yc8YawJPDTfvWX7g==",
+      "license": "Apache-2.0",
+      "optional": true
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@injectivelabs/core-proto-ts": {
+      "version": "0.0.14",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/core-proto-ts/-/core-proto-ts-0.0.14.tgz",
+      "integrity": "sha512-NZWlgBzgVrXow9IknFQHvcYKX4QkUD25taRigoNYQK8PDn4+VXd9xM5WFUDRhzm2smTCguyl/+MghpEp4oTPWw==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@injectivelabs/grpc-web": "^0.0.1",
+        "google-protobuf": "^3.14.0",
+        "protobufjs": "^7.0.0",
+        "rxjs": "^7.4.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@injectivelabs/indexer-proto-ts": {
+      "version": "1.10.8-rc.4",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/indexer-proto-ts/-/indexer-proto-ts-1.10.8-rc.4.tgz",
+      "integrity": "sha512-IwbepTfsHHAv3Z36As6yH/+HIplOEpUu6SFHBCVgdSIaQ8GuvTib4HETiVnV4mjYqoyVgWs+zLSAfih46rdMJQ==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@injectivelabs/grpc-web": "^0.0.1",
+        "google-protobuf": "^3.14.0",
+        "protobufjs": "^7.0.0",
+        "rxjs": "^7.4.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@injectivelabs/mito-proto-ts": {
+      "version": "1.0.9",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/mito-proto-ts/-/mito-proto-ts-1.0.9.tgz",
+      "integrity": "sha512-+TZMvJ4SHwcn6SFPdqaiQFZdNhjH7hyRFozY15nOTC2utdGij9jEsjz1NsyOejfYDA0s1z5Wm1SgrMYKaVpAmQ==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@injectivelabs/grpc-web": "^0.0.1",
+        "google-protobuf": "^3.14.0",
+        "protobufjs": "^7.0.0",
+        "rxjs": "^7.4.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@injectivelabs/networks": {
+      "version": "1.10.12",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/networks/-/networks-1.10.12.tgz",
+      "integrity": "sha512-tTHyLls1Nik5QTs/S03qqG2y/ITvNwI8CJOQbMmmsr1CL2CdjJBtzRYn9Dyx2p8XgzRFf9hmlybpe20tq9O3SA==",
+      "hasInstallScript": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@injectivelabs/exceptions": "^1.10.12",
+        "@injectivelabs/ts-types": "^1.10.12",
+        "@injectivelabs/utils": "^1.10.12",
+        "link-module-alias": "^1.2.0",
+        "shx": "^0.3.2"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@injectivelabs/sdk-ts": {
+      "version": "1.10.72",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/sdk-ts/-/sdk-ts-1.10.72.tgz",
+      "integrity": "sha512-A5mHNNBgO4fI1c/7CZ0bGfVXliy8laP+VaYZ++aWh1YyudoZw4CTCEmLetZRy7AUU3XcfbHa8sAImRi7db+v6Q==",
+      "hasInstallScript": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@apollo/client": "^3.5.8",
+        "@cosmjs/amino": "^0.30.1",
+        "@cosmjs/proto-signing": "^0.30.1",
+        "@cosmjs/stargate": "^0.30.1",
+        "@ethersproject/bytes": "^5.7.0",
+        "@injectivelabs/core-proto-ts": "^0.0.14",
+        "@injectivelabs/exceptions": "^1.10.12",
+        "@injectivelabs/grpc-web": "^0.0.1",
+        "@injectivelabs/grpc-web-node-http-transport": "^0.0.2",
+        "@injectivelabs/grpc-web-react-native-transport": "^0.0.2",
+        "@injectivelabs/indexer-proto-ts": "1.10.8-rc.4",
+        "@injectivelabs/mito-proto-ts": "1.0.9",
+        "@injectivelabs/networks": "^1.10.12",
+        "@injectivelabs/test-utils": "^1.10.12",
+        "@injectivelabs/token-metadata": "^1.10.42",
+        "@injectivelabs/ts-types": "^1.10.12",
+        "@injectivelabs/utils": "^1.10.12",
+        "@metamask/eth-sig-util": "^4.0.0",
+        "axios": "^0.27.2",
+        "bech32": "^2.0.0",
+        "bip39": "^3.0.4",
+        "cosmjs-types": "^0.7.1",
+        "eth-crypto": "^2.6.0",
+        "ethereumjs-util": "^7.1.4",
+        "ethers": "^5.7.2",
+        "google-protobuf": "^3.21.0",
+        "graphql": "^16.3.0",
+        "http-status-codes": "^2.2.0",
+        "js-sha3": "^0.8.0",
+        "jscrypto": "^1.0.3",
+        "keccak256": "^1.0.6",
+        "link-module-alias": "^1.2.0",
+        "rxjs": "^7.8.0",
+        "secp256k1": "^4.0.3",
+        "shx": "^0.3.2",
+        "snakecase-keys": "^5.4.1"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@injectivelabs/sdk-ts/node_modules/axios": {
+      "version": "0.27.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "follow-redirects": "^1.14.9",
+        "form-data": "^4.0.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@injectivelabs/utils": {
+      "version": "1.10.12",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/utils/-/utils-1.10.12.tgz",
+      "integrity": "sha512-c8al79nxIJgV1cBAdW2TPDGldj/8gm5k0h5TIN/AJs8/AeIjpTwwVGfLY3QvPOpRsxuQ9CjBkTXrAcSL1wwkcw==",
+      "hasInstallScript": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@injectivelabs/exceptions": "^1.10.12",
+        "@injectivelabs/ts-types": "^1.10.12",
+        "axios": "^0.21.1",
+        "bignumber.js": "^9.0.1",
+        "http-status-codes": "^2.2.0",
+        "link-module-alias": "^1.2.0",
+        "shx": "^0.3.2",
+        "snakecase-keys": "^5.1.2",
+        "store2": "^2.12.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@injectivelabs/utils/node_modules/axios": {
+      "version": "0.21.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+      "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "follow-redirects": "^1.14.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@noble/hashes": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.3.tgz",
+      "integrity": "sha512-CE0FCR57H2acVI5UOzIGSSIYxZ6v/HOhDR0Ro9VLyhnzLwx0o8W1mmgaqlEUx4049qJDlIBRztv5k+MM8vbO3A==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@scure/base": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz",
+      "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@scure/bip39": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.0.tgz",
+      "integrity": "sha512-pwrPOS16VeTKg98dYXQyIjJEcWfz7/1YJIwxUEPFfQPtc86Ym/1sVgQ2RLoD43AazMk2l/unK4ITySSpW2+82w==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "~1.1.1",
+        "@scure/base": "~1.1.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@terra-money/terra.js": {
+      "version": "3.1.9",
+      "resolved": "https://registry.npmjs.org/@terra-money/terra.js/-/terra.js-3.1.9.tgz",
+      "integrity": "sha512-JulSvOHLM56fL7s+cIjIbZeWPBluq883X1soWxA4TG5rKkDythT/DHeLXr3jP5Ld/26VENPSg6lNvK7cEYKpiw==",
+      "license": "MIT",
+      "dependencies": {
+        "@classic-terra/terra.proto": "^1.1.0",
+        "@terra-money/terra.proto": "^2.1.0",
+        "axios": "^0.27.2",
+        "bech32": "^2.0.0",
+        "bip32": "^2.0.6",
+        "bip39": "^3.0.3",
+        "bufferutil": "^4.0.3",
+        "decimal.js": "^10.2.1",
+        "jscrypto": "^1.0.1",
+        "readable-stream": "^3.6.0",
+        "secp256k1": "^4.0.2",
+        "tmp": "^0.2.1",
+        "utf-8-validate": "^5.0.5",
+        "ws": "^7.5.9"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/@terra-money/terra.js/node_modules/axios": {
+      "version": "0.27.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.14.9",
+        "form-data": "^4.0.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/aptos": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/aptos/-/aptos-1.5.0.tgz",
+      "integrity": "sha512-N7OuRtU7IYHkDkNx+4QS3g/QQGCp+36KzYn3oXPmT7Kttfuv+UKliQVdjy3cLmwd/DCQSh9ObTovwdxnHjUn0g==",
+      "deprecated": "Package aptos is no longer supported, please migrate to https://www.npmjs.com/package/@aptos-labs/ts-sdk",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@noble/hashes": "1.1.3",
+        "@scure/bip39": "1.1.0",
+        "axios": "0.27.2",
+        "form-data": "4.0.0",
+        "tweetnacl": "1.0.3"
+      },
+      "engines": {
+        "node": ">=11.0.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/aptos/node_modules/axios": {
+      "version": "0.27.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.14.9",
+        "form-data": "^4.0.0"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/cosmjs-types": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.7.2.tgz",
+      "integrity": "sha512-vf2uLyktjr/XVAgEq0DjMxeAWh1yYREe7AMHDKd7EiHVqxBPCaBS+qEEQUkXbR9ndnckqr1sUG8BQhazh4X5lA==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.2"
+      }
+    },
+    "node_modules/@certusone/wormhole-sdk/node_modules/cosmjs-types/node_modules/protobufjs": {
+      "version": "6.11.4",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+      "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "optional": true,
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.1",
+        "@types/node": ">=13.7.0",
+        "long": "^4.0.0"
+      },
+      "bin": {
+        "pbjs": "bin/pbjs",
+        "pbts": "bin/pbts"
+      }
+    },
+    "node_modules/@classic-terra/terra.proto": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@classic-terra/terra.proto/-/terra.proto-1.1.0.tgz",
+      "integrity": "sha512-bYhQG5LUaGF0KPRY9hYT/HEcd1QExZPQd6zLV/rQkCe/eDxfwFRLzZHpaaAdfWoAAZjsRWqJbUCqCg7gXBbJpw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@improbable-eng/grpc-web": "^0.14.1",
+        "google-protobuf": "^3.17.3",
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.2"
+      }
+    },
+    "node_modules/@classic-terra/terra.proto/node_modules/@improbable-eng/grpc-web": {
+      "version": "0.14.1",
+      "resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.14.1.tgz",
+      "integrity": "sha512-XaIYuunepPxoiGVLLHmlnVminUGzBTnXr8Wv7khzmLWbNw4TCwJKX09GSMJlKhu/TRk6gms0ySFxewaETSBqgw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "browser-headers": "^0.4.1"
+      },
+      "peerDependencies": {
+        "google-protobuf": "^3.14.0"
+      }
+    },
+    "node_modules/@classic-terra/terra.proto/node_modules/protobufjs": {
+      "version": "6.11.4",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+      "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.1",
+        "@types/node": ">=13.7.0",
+        "long": "^4.0.0"
+      },
+      "bin": {
+        "pbjs": "bin/pbjs",
+        "pbts": "bin/pbts"
+      }
+    },
+    "node_modules/@confio/ics23": {
+      "version": "0.6.8",
+      "resolved": "https://registry.npmjs.org/@confio/ics23/-/ics23-0.6.8.tgz",
+      "integrity": "sha512-wB6uo+3A50m0sW/EWcU64xpV/8wShZ6bMTa7pF8eYsTrSkQA7oLUIJcs/wb8g4y2Oyq701BaGiO6n/ak5WXO1w==",
+      "deprecated": "Unmaintained. The codebase for this package was moved to https://github.com/cosmos/ics23 but then the JS implementation was removed in https://github.com/cosmos/ics23/pull/353. Please consult the maintainers of https://github.com/cosmos for further assistance.",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@noble/hashes": "^1.0.0",
+        "protobufjs": "^6.8.8"
+      }
+    },
+    "node_modules/@confio/ics23/node_modules/protobufjs": {
+      "version": "6.11.4",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+      "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.1",
+        "@types/node": ">=13.7.0",
+        "long": "^4.0.0"
+      },
+      "bin": {
+        "pbjs": "bin/pbjs",
+        "pbts": "bin/pbts"
+      }
+    },
+    "node_modules/@coral-xyz/borsh": {
+      "version": "0.2.6",
+      "resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.2.6.tgz",
+      "integrity": "sha512-y6nmHw1bFcJib7sMHsQPpC8r47xhqDZVvhUdna7NUPzpSbOZG6f46N21+aXsQ2w/tG8Ggls488J/ZmwbgVmyjg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bn.js": "^5.1.2",
+        "buffer-layout": "^1.2.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "peerDependencies": {
+        "@solana/web3.js": "^1.2.0"
+      }
+    },
+    "node_modules/@cosmjs/amino": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.32.4.tgz",
+      "integrity": "sha512-zKYOt6hPy8obIFtLie/xtygCkH9ZROiQ12UHfKsOkWaZfPQUvVbtgmu6R4Kn1tFLI/SRkw7eqhaogmW/3NYu/Q==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/crypto": "^0.32.4",
+        "@cosmjs/encoding": "^0.32.4",
+        "@cosmjs/math": "^0.32.4",
+        "@cosmjs/utils": "^0.32.4"
+      }
+    },
+    "node_modules/@cosmjs/amino/node_modules/@cosmjs/encoding": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.32.4.tgz",
+      "integrity": "sha512-tjvaEy6ZGxJchiizzTn7HVRiyTg1i4CObRRaTRPknm5EalE13SV+TCHq38gIDfyUeden4fCuaBVEdBR5+ti7Hw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "bech32": "^1.1.4",
+        "readonly-date": "^1.0.0"
+      }
+    },
+    "node_modules/@cosmjs/amino/node_modules/bech32": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+      "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+      "license": "MIT"
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/cosmwasm-stargate/-/cosmwasm-stargate-0.29.5.tgz",
+      "integrity": "sha512-TNdSvm2tEE3XMCuxHxquzls56t40hC8qnLeYJWHsY2ECZmRK3KrnpRReEr7N7bLtODToK7X/riYrV0JaYxjrYA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/amino": "^0.29.5",
+        "@cosmjs/crypto": "^0.29.5",
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/proto-signing": "^0.29.5",
+        "@cosmjs/stargate": "^0.29.5",
+        "@cosmjs/tendermint-rpc": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5",
+        "cosmjs-types": "^0.5.2",
+        "long": "^4.0.0",
+        "pako": "^2.0.2"
+      }
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/@cosmjs/amino": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.29.5.tgz",
+      "integrity": "sha512-Qo8jpC0BiziTSUqpkNatBcwtKNhCovUnFul9SlT/74JUCdLYaeG5hxr3q1cssQt++l4LvlcpF+OUXL48XjNjLw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/crypto": "^0.29.5",
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5"
+      }
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/@cosmjs/crypto": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.29.5.tgz",
+      "integrity": "sha512-2bKkaLGictaNL0UipQCL6C1afaisv6k8Wr/GCLx9FqiyFkh9ZgRHDyetD64ZsjnWV/N/D44s/esI+k6oPREaiQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5",
+        "@noble/hashes": "^1",
+        "bn.js": "^5.2.0",
+        "elliptic": "^6.5.4",
+        "libsodium-wrappers": "^0.7.6"
+      }
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/@cosmjs/encoding": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.29.5.tgz",
+      "integrity": "sha512-G4rGl/Jg4dMCw5u6PEZHZcoHnUBlukZODHbm/wcL4Uu91fkn5jVo5cXXZcvs4VCkArVGrEj/52eUgTZCmOBGWQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "bech32": "^1.1.4",
+        "readonly-date": "^1.0.0"
+      }
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/@cosmjs/json-rpc": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.29.5.tgz",
+      "integrity": "sha512-C78+X06l+r9xwdM1yFWIpGl03LhB9NdM1xvZpQHwgCOl0Ir/WV8pw48y3Ez2awAoUBRfTeejPe4KvrE6NoIi/w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/stream": "^0.29.5",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/@cosmjs/math": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.29.5.tgz",
+      "integrity": "sha512-2GjKcv+A9f86MAWYLUkjhw1/WpRl2R1BTb3m9qPG7lzMA7ioYff9jY5SPCfafKdxM4TIQGxXQlYGewQL16O68Q==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bn.js": "^5.2.0"
+      }
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/@cosmjs/proto-signing": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.29.5.tgz",
+      "integrity": "sha512-QRrS7CiKaoETdgIqvi/7JC2qCwCR7lnWaUsTzh/XfRy3McLkEd+cXbKAW3cygykv7IN0VAEIhZd2lyIfT8KwNA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/amino": "^0.29.5",
+        "@cosmjs/crypto": "^0.29.5",
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5",
+        "cosmjs-types": "^0.5.2",
+        "long": "^4.0.0"
+      }
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/@cosmjs/socket": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.29.5.tgz",
+      "integrity": "sha512-5VYDupIWbIXq3ftPV1LkS5Ya/T7Ol/AzWVhNxZ79hPe/mBfv1bGau/LqIYOm2zxGlgm9hBHOTmWGqNYDwr9LNQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/stream": "^0.29.5",
+        "isomorphic-ws": "^4.0.1",
+        "ws": "^7",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/@cosmjs/stargate": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.29.5.tgz",
+      "integrity": "sha512-hjEv8UUlJruLrYGJcUZXM/CziaINOKwfVm2BoSdUnNTMxGvY/jC1ABHKeZUYt9oXHxEJ1n9+pDqzbKc8pT0nBw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@confio/ics23": "^0.6.8",
+        "@cosmjs/amino": "^0.29.5",
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/proto-signing": "^0.29.5",
+        "@cosmjs/stream": "^0.29.5",
+        "@cosmjs/tendermint-rpc": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5",
+        "cosmjs-types": "^0.5.2",
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.3",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/@cosmjs/stream": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.29.5.tgz",
+      "integrity": "sha512-TToTDWyH1p05GBtF0Y8jFw2C+4783ueDCmDyxOMM6EU82IqpmIbfwcdMOCAm0JhnyMh+ocdebbFvnX/sGKzRAA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/@cosmjs/tendermint-rpc": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.29.5.tgz",
+      "integrity": "sha512-ar80twieuAxsy0x2za/aO3kBr2DFPAXDmk2ikDbmkda+qqfXgl35l9CVAAjKRqd9d+cRvbQyb5M4wy6XQpEV6w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/crypto": "^0.29.5",
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/json-rpc": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/socket": "^0.29.5",
+        "@cosmjs/stream": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5",
+        "axios": "^0.21.2",
+        "readonly-date": "^1.0.0",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/@cosmjs/utils": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.29.5.tgz",
+      "integrity": "sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/axios": {
+      "version": "0.21.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+      "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.14.0"
+      }
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/bech32": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+      "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+      "license": "MIT"
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/cosmjs-types": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.5.2.tgz",
+      "integrity": "sha512-zxCtIJj8v3Di7s39uN4LNcN3HIE1z0B9Z0SPE8ZNQR0oSzsuSe1ACgxoFkvhkS7WBasCAFcglS11G2hyfd5tPg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.2"
+      }
+    },
+    "node_modules/@cosmjs/cosmwasm-stargate/node_modules/protobufjs": {
+      "version": "6.11.4",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+      "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.1",
+        "@types/node": ">=13.7.0",
+        "long": "^4.0.0"
+      },
+      "bin": {
+        "pbjs": "bin/pbjs",
+        "pbts": "bin/pbts"
+      }
+    },
+    "node_modules/@cosmjs/crypto": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.32.4.tgz",
+      "integrity": "sha512-zicjGU051LF1V9v7bp8p7ovq+VyC91xlaHdsFOTo2oVry3KQikp8L/81RkXmUIT8FxMwdx1T7DmFwVQikcSDIw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/encoding": "^0.32.4",
+        "@cosmjs/math": "^0.32.4",
+        "@cosmjs/utils": "^0.32.4",
+        "@noble/hashes": "^1",
+        "bn.js": "^5.2.0",
+        "elliptic": "^6.5.4",
+        "libsodium-wrappers-sumo": "^0.7.11"
+      }
+    },
+    "node_modules/@cosmjs/crypto/node_modules/@cosmjs/encoding": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.32.4.tgz",
+      "integrity": "sha512-tjvaEy6ZGxJchiizzTn7HVRiyTg1i4CObRRaTRPknm5EalE13SV+TCHq38gIDfyUeden4fCuaBVEdBR5+ti7Hw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "bech32": "^1.1.4",
+        "readonly-date": "^1.0.0"
+      }
+    },
+    "node_modules/@cosmjs/crypto/node_modules/bech32": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+      "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+      "license": "MIT"
+    },
+    "node_modules/@cosmjs/encoding": {
+      "version": "0.26.8",
+      "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.26.8.tgz",
+      "integrity": "sha512-rsqdeCHPmSXSjwE6pzbsx/drxIZKPiIxza4hYsGCaVsxrFZmgOFNmvWgtuIOIEXPS/ZoyST9XU9aMpkaYzLEVQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "bech32": "^1.1.4",
+        "readonly-date": "^1.0.0"
+      }
+    },
+    "node_modules/@cosmjs/encoding/node_modules/bech32": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+      "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+      "license": "MIT"
+    },
+    "node_modules/@cosmjs/json-rpc": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.32.4.tgz",
+      "integrity": "sha512-/jt4mBl7nYzfJ2J/VJ+r19c92mUKF0Lt0JxM3MXEJl7wlwW5haHAWtzRujHkyYMXOwIR+gBqT2S0vntXVBRyhQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/stream": "^0.32.4",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@cosmjs/math": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.32.4.tgz",
+      "integrity": "sha512-++dqq2TJkoB8zsPVYCvrt88oJWsy1vMOuSOKcdlnXuOA/ASheTJuYy4+oZlTQ3Fr8eALDLGGPhJI02W2HyAQaw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bn.js": "^5.2.0"
+      }
+    },
+    "node_modules/@cosmjs/proto-signing": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.32.4.tgz",
+      "integrity": "sha512-QdyQDbezvdRI4xxSlyM1rSVBO2st5sqtbEIl3IX03uJ7YiZIQHyv6vaHVf1V4mapusCqguiHJzm4N4gsFdLBbQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/amino": "^0.32.4",
+        "@cosmjs/crypto": "^0.32.4",
+        "@cosmjs/encoding": "^0.32.4",
+        "@cosmjs/math": "^0.32.4",
+        "@cosmjs/utils": "^0.32.4",
+        "cosmjs-types": "^0.9.0"
+      }
+    },
+    "node_modules/@cosmjs/proto-signing/node_modules/@cosmjs/encoding": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.32.4.tgz",
+      "integrity": "sha512-tjvaEy6ZGxJchiizzTn7HVRiyTg1i4CObRRaTRPknm5EalE13SV+TCHq38gIDfyUeden4fCuaBVEdBR5+ti7Hw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "bech32": "^1.1.4",
+        "readonly-date": "^1.0.0"
+      }
+    },
+    "node_modules/@cosmjs/proto-signing/node_modules/bech32": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+      "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+      "license": "MIT"
+    },
+    "node_modules/@cosmjs/socket": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.32.4.tgz",
+      "integrity": "sha512-davcyYziBhkzfXQTu1l5NrpDYv0K9GekZCC9apBRvL1dvMc9F/ygM7iemHjUA+z8tJkxKxrt/YPjJ6XNHzLrkw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/stream": "^0.32.4",
+        "isomorphic-ws": "^4.0.1",
+        "ws": "^7",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@cosmjs/stargate": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.32.4.tgz",
+      "integrity": "sha512-usj08LxBSsPRq9sbpCeVdyLx2guEcOHfJS9mHGCLCXpdAPEIEQEtWLDpEUc0LEhWOx6+k/ChXTc5NpFkdrtGUQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@confio/ics23": "^0.6.8",
+        "@cosmjs/amino": "^0.32.4",
+        "@cosmjs/encoding": "^0.32.4",
+        "@cosmjs/math": "^0.32.4",
+        "@cosmjs/proto-signing": "^0.32.4",
+        "@cosmjs/stream": "^0.32.4",
+        "@cosmjs/tendermint-rpc": "^0.32.4",
+        "@cosmjs/utils": "^0.32.4",
+        "cosmjs-types": "^0.9.0",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@cosmjs/stargate/node_modules/@cosmjs/encoding": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.32.4.tgz",
+      "integrity": "sha512-tjvaEy6ZGxJchiizzTn7HVRiyTg1i4CObRRaTRPknm5EalE13SV+TCHq38gIDfyUeden4fCuaBVEdBR5+ti7Hw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "bech32": "^1.1.4",
+        "readonly-date": "^1.0.0"
+      }
+    },
+    "node_modules/@cosmjs/stargate/node_modules/bech32": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+      "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+      "license": "MIT"
+    },
+    "node_modules/@cosmjs/stream": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.32.4.tgz",
+      "integrity": "sha512-Gih++NYHEiP+oyD4jNEUxU9antoC0pFSg+33Hpp0JlHwH0wXhtD3OOKnzSfDB7OIoEbrzLJUpEjOgpCp5Z+W3A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@cosmjs/tendermint-rpc": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.32.4.tgz",
+      "integrity": "sha512-MWvUUno+4bCb/LmlMIErLypXxy7ckUuzEmpufYYYd9wgbdCXaTaO08SZzyFM5PI8UJ/0S2AmUrgWhldlbxO8mw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/crypto": "^0.32.4",
+        "@cosmjs/encoding": "^0.32.4",
+        "@cosmjs/json-rpc": "^0.32.4",
+        "@cosmjs/math": "^0.32.4",
+        "@cosmjs/socket": "^0.32.4",
+        "@cosmjs/stream": "^0.32.4",
+        "@cosmjs/utils": "^0.32.4",
+        "axios": "^1.6.0",
+        "readonly-date": "^1.0.0",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@cosmjs/tendermint-rpc/node_modules/@cosmjs/encoding": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.32.4.tgz",
+      "integrity": "sha512-tjvaEy6ZGxJchiizzTn7HVRiyTg1i4CObRRaTRPknm5EalE13SV+TCHq38gIDfyUeden4fCuaBVEdBR5+ti7Hw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "bech32": "^1.1.4",
+        "readonly-date": "^1.0.0"
+      }
+    },
+    "node_modules/@cosmjs/tendermint-rpc/node_modules/axios": {
+      "version": "1.8.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
+      "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/@cosmjs/tendermint-rpc/node_modules/bech32": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+      "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+      "license": "MIT"
+    },
+    "node_modules/@cosmjs/utils": {
+      "version": "0.32.4",
+      "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.32.4.tgz",
+      "integrity": "sha512-D1Yc+Zy8oL/hkUkFUL/bwxvuDBzRGpc4cF7/SkdhxX4iHpSLgdOuTt1mhCh9+kl6NQREy9t7SYZ6xeW5gFe60w==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@cspotcode/source-map-support": {
+      "version": "0.8.1",
+      "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+      "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/trace-mapping": "0.3.9"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/aix-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "aix"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz",
+      "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz",
+      "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz",
+      "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz",
+      "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz",
+      "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz",
+      "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz",
+      "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz",
+      "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz",
+      "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz",
+      "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz",
+      "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==",
+      "cpu": [
+        "loong64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz",
+      "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==",
+      "cpu": [
+        "mips64el"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz",
+      "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==",
+      "cpu": [
+        "ppc64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz",
+      "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz",
+      "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==",
+      "cpu": [
+        "s390x"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz",
+      "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz",
+      "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz",
+      "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz",
+      "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz",
+      "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz",
+      "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==",
+      "cpu": [
+        "ia32"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz",
+      "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@ethereumjs/common": {
+      "version": "2.6.5",
+      "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-2.6.5.tgz",
+      "integrity": "sha512-lRyVQOeCDaIVtgfbowla32pzeDv2Obr8oR8Put5RdUBNRGr1VGPGQNGP6elWIpgK3YdpzqTOh4GyUGOureVeeA==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "crc-32": "^1.2.0",
+        "ethereumjs-util": "^7.1.5"
+      }
+    },
+    "node_modules/@ethereumjs/tx": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-3.5.2.tgz",
+      "integrity": "sha512-gQDNJWKrSDGu2w7w0PzVXVBNMzb7wwdDOmOqczmhNjqFxFuIbhVJDwiGEnxFNC2/b8ifcZzY7MLcluizohRzNw==",
+      "license": "MPL-2.0",
+      "optional": true,
+      "dependencies": {
+        "@ethereumjs/common": "^2.6.4",
+        "ethereumjs-util": "^7.1.5"
+      }
+    },
+    "node_modules/@ethersproject/abi": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.8.0.tgz",
+      "integrity": "sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/address": "^5.8.0",
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/constants": "^5.8.0",
+        "@ethersproject/hash": "^5.8.0",
+        "@ethersproject/keccak256": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/strings": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/abstract-provider": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz",
+      "integrity": "sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/networks": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/transactions": "^5.8.0",
+        "@ethersproject/web": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/abstract-signer": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz",
+      "integrity": "sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/abstract-provider": "^5.8.0",
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/address": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.8.0.tgz",
+      "integrity": "sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/keccak256": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/rlp": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/base64": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.8.0.tgz",
+      "integrity": "sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/basex": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.8.0.tgz",
+      "integrity": "sha512-PIgTszMlDRmNwW9nhS6iqtVfdTAKosA7llYXNmGPw4YAI1PUyMv28988wAb41/gHF/WqGdoLv0erHaRcHRKW2Q==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/bignumber": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.8.0.tgz",
+      "integrity": "sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "bn.js": "^5.2.1"
+      }
+    },
+    "node_modules/@ethersproject/bytes": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz",
+      "integrity": "sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/logger": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/constants": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.8.0.tgz",
+      "integrity": "sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/contracts": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.8.0.tgz",
+      "integrity": "sha512-0eFjGz9GtuAi6MZwhb4uvUM216F38xiuR0yYCjKJpNfSEy4HUM8hvqqBj9Jmm0IUz8l0xKEhWwLIhPgxNY0yvQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/abi": "^5.8.0",
+        "@ethersproject/abstract-provider": "^5.8.0",
+        "@ethersproject/abstract-signer": "^5.8.0",
+        "@ethersproject/address": "^5.8.0",
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/constants": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/transactions": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/hash": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.8.0.tgz",
+      "integrity": "sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/abstract-signer": "^5.8.0",
+        "@ethersproject/address": "^5.8.0",
+        "@ethersproject/base64": "^5.8.0",
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/keccak256": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/strings": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/hdnode": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.8.0.tgz",
+      "integrity": "sha512-4bK1VF6E83/3/Im0ERnnUeWOY3P1BZml4ZD3wcH8Ys0/d1h1xaFt6Zc+Dh9zXf9TapGro0T4wvO71UTCp3/uoA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/abstract-signer": "^5.8.0",
+        "@ethersproject/basex": "^5.8.0",
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/pbkdf2": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/sha2": "^5.8.0",
+        "@ethersproject/signing-key": "^5.8.0",
+        "@ethersproject/strings": "^5.8.0",
+        "@ethersproject/transactions": "^5.8.0",
+        "@ethersproject/wordlists": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/json-wallets": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.8.0.tgz",
+      "integrity": "sha512-HxblNck8FVUtNxS3VTEYJAcwiKYsBIF77W15HufqlBF9gGfhmYOJtYZp8fSDZtn9y5EaXTE87zDwzxRoTFk11w==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/abstract-signer": "^5.8.0",
+        "@ethersproject/address": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/hdnode": "^5.8.0",
+        "@ethersproject/keccak256": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/pbkdf2": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/random": "^5.8.0",
+        "@ethersproject/strings": "^5.8.0",
+        "@ethersproject/transactions": "^5.8.0",
+        "aes-js": "3.0.0",
+        "scrypt-js": "3.0.1"
+      }
+    },
+    "node_modules/@ethersproject/keccak256": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.8.0.tgz",
+      "integrity": "sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "js-sha3": "0.8.0"
+      }
+    },
+    "node_modules/@ethersproject/logger": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz",
+      "integrity": "sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/@ethersproject/networks": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.8.0.tgz",
+      "integrity": "sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/logger": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/pbkdf2": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.8.0.tgz",
+      "integrity": "sha512-wuHiv97BrzCmfEaPbUFpMjlVg/IDkZThp9Ri88BpjRleg4iePJaj2SW8AIyE8cXn5V1tuAaMj6lzvsGJkGWskg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/sha2": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/properties": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz",
+      "integrity": "sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/logger": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/providers": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.8.0.tgz",
+      "integrity": "sha512-3Il3oTzEx3o6kzcg9ZzbE+oCZYyY+3Zh83sKkn4s1DZfTUjIegHnN2Cm0kbn9YFy45FDVcuCLLONhU7ny0SsCw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/abstract-provider": "^5.8.0",
+        "@ethersproject/abstract-signer": "^5.8.0",
+        "@ethersproject/address": "^5.8.0",
+        "@ethersproject/base64": "^5.8.0",
+        "@ethersproject/basex": "^5.8.0",
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/constants": "^5.8.0",
+        "@ethersproject/hash": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/networks": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/random": "^5.8.0",
+        "@ethersproject/rlp": "^5.8.0",
+        "@ethersproject/sha2": "^5.8.0",
+        "@ethersproject/strings": "^5.8.0",
+        "@ethersproject/transactions": "^5.8.0",
+        "@ethersproject/web": "^5.8.0",
+        "bech32": "1.1.4",
+        "ws": "8.18.0"
+      }
+    },
+    "node_modules/@ethersproject/providers/node_modules/bech32": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+      "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+      "license": "MIT"
+    },
+    "node_modules/@ethersproject/providers/node_modules/ws": {
+      "version": "8.18.0",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+      "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@ethersproject/random": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.8.0.tgz",
+      "integrity": "sha512-E4I5TDl7SVqyg4/kkA/qTfuLWAQGXmSOgYyO01So8hLfwgKvYK5snIlzxJMk72IFdG/7oh8yuSqY2KX7MMwg+A==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/rlp": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.8.0.tgz",
+      "integrity": "sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/sha2": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.8.0.tgz",
+      "integrity": "sha512-dDOUrXr9wF/YFltgTBYS0tKslPEKr6AekjqDW2dbn1L1xmjGR+9GiKu4ajxovnrDbwxAKdHjW8jNcwfz8PAz4A==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "hash.js": "1.1.7"
+      }
+    },
+    "node_modules/@ethersproject/signing-key": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz",
+      "integrity": "sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "bn.js": "^5.2.1",
+        "elliptic": "6.6.1",
+        "hash.js": "1.1.7"
+      }
+    },
+    "node_modules/@ethersproject/solidity": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.8.0.tgz",
+      "integrity": "sha512-4CxFeCgmIWamOHwYN9d+QWGxye9qQLilpgTU0XhYs1OahkclF+ewO+3V1U0mvpiuQxm5EHHmv8f7ClVII8EHsA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/keccak256": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/sha2": "^5.8.0",
+        "@ethersproject/strings": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/strings": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.8.0.tgz",
+      "integrity": "sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/constants": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/transactions": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.8.0.tgz",
+      "integrity": "sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/address": "^5.8.0",
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/constants": "^5.8.0",
+        "@ethersproject/keccak256": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/rlp": "^5.8.0",
+        "@ethersproject/signing-key": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/units": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.8.0.tgz",
+      "integrity": "sha512-lxq0CAnc5kMGIiWW4Mr041VT8IhNM+Pn5T3haO74XZWFulk7wH1Gv64HqE96hT4a7iiNMdOCFEBgaxWuk8ETKQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/constants": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/wallet": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.8.0.tgz",
+      "integrity": "sha512-G+jnzmgg6UxurVKRKvw27h0kvG75YKXZKdlLYmAHeF32TGUzHkOFd7Zn6QHOTYRFWnfjtSSFjBowKo7vfrXzPA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/abstract-provider": "^5.8.0",
+        "@ethersproject/abstract-signer": "^5.8.0",
+        "@ethersproject/address": "^5.8.0",
+        "@ethersproject/bignumber": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/hash": "^5.8.0",
+        "@ethersproject/hdnode": "^5.8.0",
+        "@ethersproject/json-wallets": "^5.8.0",
+        "@ethersproject/keccak256": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/random": "^5.8.0",
+        "@ethersproject/signing-key": "^5.8.0",
+        "@ethersproject/transactions": "^5.8.0",
+        "@ethersproject/wordlists": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/web": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.8.0.tgz",
+      "integrity": "sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/base64": "^5.8.0",
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/strings": "^5.8.0"
+      }
+    },
+    "node_modules/@ethersproject/wordlists": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.8.0.tgz",
+      "integrity": "sha512-2df9bbXicZws2Sb5S6ET493uJ0Z84Fjr3pC4tu/qlnZERibZCeUVuqdtt+7Tv9xxhUxHoIekIA7avrKUWHrezg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bytes": "^5.8.0",
+        "@ethersproject/hash": "^5.8.0",
+        "@ethersproject/logger": "^5.8.0",
+        "@ethersproject/properties": "^5.8.0",
+        "@ethersproject/strings": "^5.8.0"
+      }
+    },
+    "node_modules/@fast-check/vitest": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/@fast-check/vitest/-/vitest-0.2.1.tgz",
+      "integrity": "sha512-qbmGd1PC38iGvDjAY3WCpfQbWl60Bh0S5A/P60bFTVobyXzhYwq/3AtqVJWMRLnKHSfCDc2CSAApRUqrhVfcZQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/dubzzz"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fast-check"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "fast-check": "^3.0.0 || ^4.0.0"
+      },
+      "peerDependencies": {
+        "vitest": "^1 || ^2 || ^3"
+      }
+    },
+    "node_modules/@graphql-typed-document-node/core": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
+      "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==",
+      "license": "MIT",
+      "peerDependencies": {
+        "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
+      }
+    },
+    "node_modules/@grpc/grpc-js": {
+      "version": "1.13.4",
+      "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz",
+      "integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@grpc/proto-loader": "^0.7.13",
+        "@js-sdsl/ordered-map": "^4.4.2"
+      },
+      "engines": {
+        "node": ">=12.10.0"
+      }
+    },
+    "node_modules/@grpc/proto-loader": {
+      "version": "0.7.15",
+      "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz",
+      "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "lodash.camelcase": "^4.3.0",
+        "long": "^5.0.0",
+        "protobufjs": "^7.2.5",
+        "yargs": "^17.7.2"
+      },
+      "bin": {
+        "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/@grpc/proto-loader/node_modules/long": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
+      "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@improbable-eng/grpc-web": {
+      "version": "0.15.0",
+      "resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.15.0.tgz",
+      "integrity": "sha512-ERft9/0/8CmYalqOVnJnpdDry28q+j+nAlFFARdjyxXDJ+Mhgv9+F600QC8BR9ygOfrXRlAk6CvST2j+JCpQPg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "browser-headers": "^0.4.1"
+      },
+      "peerDependencies": {
+        "google-protobuf": "^3.14.0"
+      }
+    },
+    "node_modules/@improbable-eng/grpc-web-node-http-transport": {
+      "version": "0.15.0",
+      "resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web-node-http-transport/-/grpc-web-node-http-transport-0.15.0.tgz",
+      "integrity": "sha512-HLgJfVolGGpjc9DWPhmMmXJx8YGzkek7jcCFO1YYkSOoO81MWRZentPOd/JiKiZuU08wtc4BG+WNuGzsQB5jZA==",
+      "license": "Apache-2.0",
+      "peerDependencies": {
+        "@improbable-eng/grpc-web": ">=0.13.0"
+      }
+    },
+    "node_modules/@injectivelabs/abacus-proto-ts": {
+      "version": "1.13.3",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/abacus-proto-ts/-/abacus-proto-ts-1.13.3.tgz",
+      "integrity": "sha512-GUxYtBRcskg8LkX2g0oK+D9zlbahq2QTBzmLAQoG5zCeXsjoWxkSkmO1Ez146gODL9ytofnwZsCr2hCE7wgH0w==",
+      "license": "MIT",
+      "dependencies": {
+        "@injectivelabs/grpc-web": "^0.0.1",
+        "google-protobuf": "^3.14.0",
+        "protobufjs": "^7.0.0",
+        "rxjs": "^7.4.0"
+      }
+    },
+    "node_modules/@injectivelabs/core-proto-ts": {
+      "version": "1.13.6",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/core-proto-ts/-/core-proto-ts-1.13.6.tgz",
+      "integrity": "sha512-dN8UTUyWc4uPHIUBfeauT68xLFuCQpUqwhVHTsxAgOOVpfZeTc7aKw6vGbbdj6uLG85V4ooFoKJcChOSV3A5JA==",
+      "license": "MIT",
+      "dependencies": {
+        "@injectivelabs/grpc-web": "^0.0.1",
+        "google-protobuf": "^3.14.0",
+        "protobufjs": "^7.0.0",
+        "rxjs": "^7.4.0"
+      }
+    },
+    "node_modules/@injectivelabs/exceptions": {
+      "version": "1.14.41",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/exceptions/-/exceptions-1.14.41.tgz",
+      "integrity": "sha512-tY2s6ivb2qgQO4lrsLd3aT7q7Hrn8uPVeN8Vc25FAWwlBmwm7okkLWLGI1jnO8v3oe99OO3be+rvTa/TexN5+g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@injectivelabs/grpc-web": "^0.0.1",
+        "@injectivelabs/ts-types": "^1.14.41",
+        "http-status-codes": "^2.2.0",
+        "shx": "^0.3.2"
+      }
+    },
+    "node_modules/@injectivelabs/grpc-web": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/grpc-web/-/grpc-web-0.0.1.tgz",
+      "integrity": "sha512-Pu5YgaZp+OvR5UWfqbrPdHer3+gDf+b5fQoY+t2VZx1IAVHX8bzbN9EreYTvTYtFeDpYRWM8P7app2u4EX5wTw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "browser-headers": "^0.4.1"
+      },
+      "peerDependencies": {
+        "google-protobuf": "^3.14.0"
+      }
+    },
+    "node_modules/@injectivelabs/grpc-web-node-http-transport": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/grpc-web-node-http-transport/-/grpc-web-node-http-transport-0.0.2.tgz",
+      "integrity": "sha512-rpyhXLiGY/UMs6v6YmgWHJHiO9l0AgDyVNv+jcutNVt4tQrmNvnpvz2wCAGOFtq5LuX/E9ChtTVpk3gWGqXcGA==",
+      "license": "Apache-2.0",
+      "peerDependencies": {
+        "@injectivelabs/grpc-web": ">=0.0.1"
+      }
+    },
+    "node_modules/@injectivelabs/grpc-web-react-native-transport": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/grpc-web-react-native-transport/-/grpc-web-react-native-transport-0.0.2.tgz",
+      "integrity": "sha512-mk+aukQXnYNgPsPnu3KBi+FD0ZHQpazIlaBZ2jNZG7QAVmxTWtv3R66Zoq99Wx2dnE946NsZBYAoa0K5oSjnow==",
+      "license": "Apache-2.0",
+      "peerDependencies": {
+        "@injectivelabs/grpc-web": ">=0.0.1"
+      }
+    },
+    "node_modules/@injectivelabs/indexer-proto-ts": {
+      "version": "1.13.6",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/indexer-proto-ts/-/indexer-proto-ts-1.13.6.tgz",
+      "integrity": "sha512-G0E/lpqumpcFA1qn++eZqcGPTQ58P5mAXT3hvegqVd7k/qzWIGtrT8mXv7KeaK48LjZIrjjpt9ks39O0pUgJUw==",
+      "license": "MIT",
+      "dependencies": {
+        "@injectivelabs/grpc-web": "^0.0.1",
+        "google-protobuf": "^3.14.0",
+        "protobufjs": "^7.0.0",
+        "rxjs": "^7.4.0"
+      }
+    },
+    "node_modules/@injectivelabs/mito-proto-ts": {
+      "version": "1.13.2",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/mito-proto-ts/-/mito-proto-ts-1.13.2.tgz",
+      "integrity": "sha512-D4qEDB4OgaV1LoYNg6FB+weVcLMu5ea0x/W/p+euIVly3qia44GmAicIbQhrkqTs2o2c+1mbK1c4eOzFxQcwhg==",
+      "license": "MIT",
+      "dependencies": {
+        "@injectivelabs/grpc-web": "^0.0.1",
+        "google-protobuf": "^3.14.0",
+        "protobufjs": "^7.0.0",
+        "rxjs": "^7.4.0"
+      }
+    },
+    "node_modules/@injectivelabs/networks": {
+      "version": "1.14.41",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/networks/-/networks-1.14.41.tgz",
+      "integrity": "sha512-UVkzBLpfD1zrnkBNmrtuFxsIgYZSfNJVSyaX979OdGorLt76PKjwgURfg1uatqEgeyU+G/2alRjPlg81bWQ6Ug==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@injectivelabs/exceptions": "^1.14.41",
+        "@injectivelabs/ts-types": "^1.14.41",
+        "@injectivelabs/utils": "^1.14.41",
+        "shx": "^0.3.2"
+      }
+    },
+    "node_modules/@injectivelabs/olp-proto-ts": {
+      "version": "1.13.3",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/olp-proto-ts/-/olp-proto-ts-1.13.3.tgz",
+      "integrity": "sha512-z2AQOXVYItMlM+qYraPbAzC6uhhJty7qpiI3KVGLCqFfTtICefOh4sa0gzMR/xP5zhS9SoRq0Wu4zuqaDy9UrQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@injectivelabs/grpc-web": "^0.0.1",
+        "google-protobuf": "^3.14.0",
+        "protobufjs": "^7.0.0",
+        "rxjs": "^7.4.0"
+      }
+    },
+    "node_modules/@injectivelabs/sdk-ts": {
+      "version": "1.14.41",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/sdk-ts/-/sdk-ts-1.14.41.tgz",
+      "integrity": "sha512-iZbDJnv9X5G500/GHrX8XR5/89Bp3v5jZz0c8IABWvZ5hhQmXeyk7msoJiv77rx+u6eg3JecVrwbEuxt0aabpA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@apollo/client": "^3.11.10",
+        "@cosmjs/amino": "^0.32.3",
+        "@cosmjs/proto-signing": "^0.32.3",
+        "@cosmjs/stargate": "^0.32.3",
+        "@ethersproject/bytes": "^5.7.0",
+        "@injectivelabs/abacus-proto-ts": "1.13.3",
+        "@injectivelabs/core-proto-ts": "1.13.6",
+        "@injectivelabs/exceptions": "^1.14.41",
+        "@injectivelabs/grpc-web": "^0.0.1",
+        "@injectivelabs/grpc-web-node-http-transport": "^0.0.2",
+        "@injectivelabs/grpc-web-react-native-transport": "^0.0.2",
+        "@injectivelabs/indexer-proto-ts": "1.13.6",
+        "@injectivelabs/mito-proto-ts": "1.13.2",
+        "@injectivelabs/networks": "^1.14.41",
+        "@injectivelabs/olp-proto-ts": "1.13.3",
+        "@injectivelabs/test-utils": "^1.14.41",
+        "@injectivelabs/ts-types": "^1.14.41",
+        "@injectivelabs/utils": "^1.14.41",
+        "@metamask/eth-sig-util": "^4.0.0",
+        "@noble/curves": "^1.4.0",
+        "axios": "^1.6.4",
+        "bech32": "^2.0.0",
+        "bip39": "^3.0.4",
+        "cosmjs-types": "^0.9.0",
+        "crypto-js": "^4.2.0",
+        "ethereumjs-util": "^7.1.4",
+        "ethers": "^6.5.1",
+        "google-protobuf": "^3.21.0",
+        "graphql": "^16.3.0",
+        "http-status-codes": "^2.2.0",
+        "keccak256": "^1.0.6",
+        "secp256k1": "^4.0.3",
+        "shx": "^0.3.2",
+        "snakecase-keys": "^5.4.1"
+      }
+    },
+    "node_modules/@injectivelabs/sdk-ts/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==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@injectivelabs/sdk-ts/node_modules/@types/node": {
+      "version": "22.7.5",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz",
+      "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~6.19.2"
+      }
+    },
+    "node_modules/@injectivelabs/sdk-ts/node_modules/aes-js": {
+      "version": "4.0.0-beta.5",
+      "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz",
+      "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==",
+      "license": "MIT"
+    },
+    "node_modules/@injectivelabs/sdk-ts/node_modules/axios": {
+      "version": "1.8.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
+      "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/@injectivelabs/sdk-ts/node_modules/ethers": {
+      "version": "6.14.4",
+      "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.14.4.tgz",
+      "integrity": "sha512-Jm/dzRs2Z9iBrT6e9TvGxyb5YVKAPLlpna7hjxH7KH/++DSh2T/JVmQUv7iHI5E55hDbp/gEVvstWYXVxXFzsA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/ethers-io/"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@adraffy/ens-normalize": "1.10.1",
+        "@noble/curves": "1.2.0",
+        "@noble/hashes": "1.3.2",
+        "@types/node": "22.7.5",
+        "aes-js": "4.0.0-beta.5",
+        "tslib": "2.7.0",
+        "ws": "8.17.1"
+      },
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@injectivelabs/sdk-ts/node_modules/ethers/node_modules/@noble/curves": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz",
+      "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "1.3.2"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@injectivelabs/sdk-ts/node_modules/tslib": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
+      "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
+      "license": "0BSD"
+    },
+    "node_modules/@injectivelabs/sdk-ts/node_modules/ws": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+      "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@injectivelabs/test-utils": {
+      "version": "1.14.41",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/test-utils/-/test-utils-1.14.41.tgz",
+      "integrity": "sha512-++xeSobV62Yd67cIK6xCepZIU+YxQx/I9I5UT1pMOfostiCxQusKWsJhAKNjwkWbKNvFZifEw6TWwU3UYhneBQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@injectivelabs/exceptions": "^1.14.41",
+        "@injectivelabs/networks": "^1.14.41",
+        "@injectivelabs/ts-types": "^1.14.41",
+        "@injectivelabs/utils": "^1.14.41",
+        "axios": "^1.6.4",
+        "bignumber.js": "^9.0.1",
+        "shx": "^0.3.2",
+        "snakecase-keys": "^5.1.2",
+        "store2": "^2.12.0"
+      }
+    },
+    "node_modules/@injectivelabs/test-utils/node_modules/axios": {
+      "version": "1.8.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
+      "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/@injectivelabs/token-metadata": {
+      "version": "1.14.11",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/token-metadata/-/token-metadata-1.14.11.tgz",
+      "integrity": "sha512-WKJlvjKiTRHxpOeez4kYrzIwgWmpspD1IMxWclkTysAcwGltUfUsvUhu1cKuACleIjFFWmiZv/HoGRFdvEAZ8w==",
+      "hasInstallScript": true,
+      "license": "Apache-2.0",
+      "optional": true,
+      "dependencies": {
+        "@injectivelabs/exceptions": "^1.14.11",
+        "@injectivelabs/networks": "^1.14.11",
+        "@injectivelabs/ts-types": "^1.14.11",
+        "@injectivelabs/utils": "^1.14.11",
+        "@types/lodash.values": "^4.3.6",
+        "copyfiles": "^2.4.1",
+        "jsonschema": "^1.4.0",
+        "link-module-alias": "^1.2.0",
+        "lodash": "^4.17.21",
+        "lodash.values": "^4.3.0",
+        "shx": "^0.3.2"
+      }
+    },
+    "node_modules/@injectivelabs/ts-types": {
+      "version": "1.14.41",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/ts-types/-/ts-types-1.14.41.tgz",
+      "integrity": "sha512-W1Es6NmlKKIkSOyIhCfpHzndatzalGIbjHf6jfukUvsb+DVQ9SkZrOEYYSCXZskdJ1OpUoGD0u3UNP99fM6G6g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "shx": "^0.3.2"
+      }
+    },
+    "node_modules/@injectivelabs/utils": {
+      "version": "1.14.41",
+      "resolved": "https://registry.npmjs.org/@injectivelabs/utils/-/utils-1.14.41.tgz",
+      "integrity": "sha512-ZReylpIqknVQ0Dzh0NrReTERDv8hiMaP+RcxKGtb2TIvzl1zve/9otEE4tkuHKPNPD/0F5hKmSTzhP4DdGiMtA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@injectivelabs/exceptions": "^1.14.41",
+        "@injectivelabs/ts-types": "^1.14.41",
+        "axios": "^1.6.4",
+        "bignumber.js": "^9.0.1",
+        "http-status-codes": "^2.2.0",
+        "shx": "^0.3.2",
+        "snakecase-keys": "^5.1.2",
+        "store2": "^2.12.0"
+      }
+    },
+    "node_modules/@injectivelabs/utils/node_modules/axios": {
+      "version": "1.8.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
+      "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+      "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+      "license": "MIT"
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.9",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+      "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/resolve-uri": "^3.0.3",
+        "@jridgewell/sourcemap-codec": "^1.4.10"
+      }
+    },
+    "node_modules/@js-sdsl/ordered-map": {
+      "version": "4.4.2",
+      "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
+      "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==",
+      "license": "MIT",
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/js-sdsl"
+      }
+    },
+    "node_modules/@keplr-wallet/types": {
+      "version": "0.11.64",
+      "resolved": "https://registry.npmjs.org/@keplr-wallet/types/-/types-0.11.64.tgz",
+      "integrity": "sha512-GgzeLDHHfZFyne3O7UIfFHj/uYqVbxAZI31RbBwt460OBbvwQzjrlZwvJW3vieWRAgxKSITjzEDBl2WneFTQdQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "axios": "^0.27.2",
+        "long": "^4.0.0"
+      }
+    },
+    "node_modules/@keplr-wallet/types/node_modules/axios": {
+      "version": "0.27.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.14.9",
+        "form-data": "^4.0.0"
+      }
+    },
+    "node_modules/@metamask/eth-sig-util": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz",
+      "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==",
+      "license": "ISC",
+      "dependencies": {
+        "ethereumjs-abi": "^0.6.8",
+        "ethereumjs-util": "^6.2.1",
+        "ethjs-util": "^0.1.6",
+        "tweetnacl": "^1.0.3",
+        "tweetnacl-util": "^0.15.1"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/@metamask/eth-sig-util/node_modules/@types/bn.js": {
+      "version": "4.11.6",
+      "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz",
+      "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@metamask/eth-sig-util/node_modules/bn.js": {
+      "version": "4.12.1",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
+      "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
+      "license": "MIT"
+    },
+    "node_modules/@metamask/eth-sig-util/node_modules/ethereumjs-util": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz",
+      "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==",
+      "license": "MPL-2.0",
+      "dependencies": {
+        "@types/bn.js": "^4.11.3",
+        "bn.js": "^4.11.0",
+        "create-hash": "^1.1.2",
+        "elliptic": "^6.5.2",
+        "ethereum-cryptography": "^0.1.3",
+        "ethjs-util": "0.1.6",
+        "rlp": "^2.2.3"
+      }
+    },
+    "node_modules/@mysten/bcs": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/@mysten/bcs/-/bcs-0.7.1.tgz",
+      "integrity": "sha512-wFPb8bkhwrbiStfZMV5rFM7J+umpke59/dNjDp+UYJKykNlW23LCk2ePyEUvGdb62HGJM1jyOJ8g4egE3OmdKA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bs58": "^5.0.0"
+      }
+    },
+    "node_modules/@mysten/bcs/node_modules/bs58": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/bs58/-/bs58-5.0.0.tgz",
+      "integrity": "sha512-r+ihvQJvahgYT50JD05dyJNKlmmSlMoOGwn1lCcEzanPglg7TxYjioQUYehQ9mAR/+hOSd2jRc/Z2y5UxBymvQ==",
+      "license": "MIT",
+      "dependencies": {
+        "base-x": "^4.0.0"
+      }
+    },
+    "node_modules/@mysten/sui.js": {
+      "version": "0.32.2",
+      "resolved": "https://registry.npmjs.org/@mysten/sui.js/-/sui.js-0.32.2.tgz",
+      "integrity": "sha512-/Hm4xkGolJhqj8FvQr7QSHDTlxIvL52mtbOao9f75YjrBh7y1Uh9kbJSY7xiTF1NY9sv6p5hUVlYRJuM0Hvn9A==",
+      "deprecated": "This package has been renamed to @mysten/sui, please update to use the renamed package.",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@mysten/bcs": "0.7.1",
+        "@noble/curves": "^1.0.0",
+        "@noble/hashes": "^1.3.0",
+        "@scure/bip32": "^1.3.0",
+        "@scure/bip39": "^1.2.0",
+        "@suchipi/femver": "^1.0.0",
+        "jayson": "^4.0.0",
+        "rpc-websockets": "^7.5.1",
+        "superstruct": "^1.0.3",
+        "tweetnacl": "^1.0.3"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/@noble/curves": {
+      "version": "1.9.2",
+      "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.2.tgz",
+      "integrity": "sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "1.8.0"
+      },
+      "engines": {
+        "node": "^14.21.3 || >=16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@noble/hashes": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
+      "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
+      "license": "MIT",
+      "engines": {
+        "node": "^14.21.3 || >=16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@noble/secp256k1": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-2.3.0.tgz",
+      "integrity": "sha512-0TQed2gcBbIrh7Ccyw+y/uZQvbJwm7Ao4scBUxqpBCcsOlZG0O4KGfjtNAy/li4W8n1xt3dxrwJ0beZ2h2G6Kw==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@osmonauts/helpers": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/@osmonauts/helpers/-/helpers-0.6.0.tgz",
+      "integrity": "sha512-l62tWR/0W4R+5wRvMeRK0zlaJ8WZhULKsQAZ7kNzggL0pbndIAV+0BJ/jEBbNletoeGtuV8rpi6Wo+w+RmtZGw==",
+      "license": "SEE LICENSE IN LICENSE",
+      "dependencies": {
+        "@babel/runtime": "^7.18.9",
+        "@cosmjs/amino": "0.28.13",
+        "@cosmjs/crypto": "0.28.13",
+        "@cosmjs/proto-signing": "0.28.13",
+        "@cosmjs/stargate": "0.28.13",
+        "cosmjs-types": "0.5.1",
+        "long": "^5.2.0",
+        "protobufjs": "^6.11.3"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/amino": {
+      "version": "0.28.13",
+      "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.28.13.tgz",
+      "integrity": "sha512-IHnH2zGwaY69qT4mVAavr/pfzx6YE+ud1NHJbvVePlbGiz68CXTi5LHR+K0lrKB5mQ7E+ZErWz2mw5U/x+V1wQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/crypto": "0.28.13",
+        "@cosmjs/encoding": "0.28.13",
+        "@cosmjs/math": "0.28.13",
+        "@cosmjs/utils": "0.28.13"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/crypto": {
+      "version": "0.28.13",
+      "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.28.13.tgz",
+      "integrity": "sha512-ynKfM0q/tMBQMHJby6ad8lR3gkgBKaelQhIsCZTjClsnuC7oYT9y3ThSZCUWr7Pa9h0J8ahU2YV2oFWFVWJQzQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/encoding": "0.28.13",
+        "@cosmjs/math": "0.28.13",
+        "@cosmjs/utils": "0.28.13",
+        "@noble/hashes": "^1",
+        "bn.js": "^5.2.0",
+        "elliptic": "^6.5.3",
+        "libsodium-wrappers": "^0.7.6"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/encoding": {
+      "version": "0.28.13",
+      "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.28.13.tgz",
+      "integrity": "sha512-jtXbAYtV77rLHxoIrjGFsvgGjeTKttuHRv6cvuy3toCZzY7JzTclKH5O2g36IIE4lXwD9xwuhGJ2aa6A3dhNkA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "bech32": "^1.1.4",
+        "readonly-date": "^1.0.0"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/json-rpc": {
+      "version": "0.28.13",
+      "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.28.13.tgz",
+      "integrity": "sha512-fInSvg7x9P6p+GWqet+TMhrMTM3OWWdLJOGS5w2ryubMjgpR1rLiAx77MdTNkArW+/6sUwku0sN4veM4ENQu6A==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/stream": "0.28.13",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/math": {
+      "version": "0.28.13",
+      "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.28.13.tgz",
+      "integrity": "sha512-PDpL8W/kbyeWi0mQ2OruyqE8ZUAdxPs1xCbDX3WXJwy2oU+X2UTbkuweJHVpS9CIqmZulBoWQAmlf6t6zr1N/g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bn.js": "^5.2.0"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/proto-signing": {
+      "version": "0.28.13",
+      "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.28.13.tgz",
+      "integrity": "sha512-nSl/2ZLsUJYz3Ad0RY3ihZUgRHIow2OnYqKsESMu+3RA/jTi9bDYhiBu8mNMHI0xrEJry918B2CyI56pOUHdPQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/amino": "0.28.13",
+        "@cosmjs/crypto": "0.28.13",
+        "@cosmjs/encoding": "0.28.13",
+        "@cosmjs/math": "0.28.13",
+        "@cosmjs/utils": "0.28.13",
+        "cosmjs-types": "^0.4.0",
+        "long": "^4.0.0"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/proto-signing/node_modules/cosmjs-types": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.4.1.tgz",
+      "integrity": "sha512-I7E/cHkIgoJzMNQdFF0YVqPlaTqrqKHrskuSTIqlEyxfB5Lf3WKCajSXVK2yHOfOFfSux/RxEdpMzw/eO4DIog==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.2"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/proto-signing/node_modules/long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/socket": {
+      "version": "0.28.13",
+      "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.28.13.tgz",
+      "integrity": "sha512-lavwGxQ5VdeltyhpFtwCRVfxeWjH5D5mmN7jgx9nuCf3XSFbTcOYxrk2pQ4usenu1Q1KZdL4Yl5RCNrJuHD9Ug==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/stream": "0.28.13",
+        "isomorphic-ws": "^4.0.1",
+        "ws": "^7",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/stargate": {
+      "version": "0.28.13",
+      "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.28.13.tgz",
+      "integrity": "sha512-dVBMazDz8/eActHsRcZjDHHptOBMqvibj5CFgEtZBp22gP6ASzoAUXTlkSVk5FBf4sfuUHoff6st134/+PGMAg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@confio/ics23": "^0.6.8",
+        "@cosmjs/amino": "0.28.13",
+        "@cosmjs/encoding": "0.28.13",
+        "@cosmjs/math": "0.28.13",
+        "@cosmjs/proto-signing": "0.28.13",
+        "@cosmjs/stream": "0.28.13",
+        "@cosmjs/tendermint-rpc": "0.28.13",
+        "@cosmjs/utils": "0.28.13",
+        "cosmjs-types": "^0.4.0",
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.3",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/stargate/node_modules/cosmjs-types": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.4.1.tgz",
+      "integrity": "sha512-I7E/cHkIgoJzMNQdFF0YVqPlaTqrqKHrskuSTIqlEyxfB5Lf3WKCajSXVK2yHOfOFfSux/RxEdpMzw/eO4DIog==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.2"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/stargate/node_modules/long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/stream": {
+      "version": "0.28.13",
+      "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.28.13.tgz",
+      "integrity": "sha512-AnjtfwT8NwPPkd3lhZhjOlOzT0Kn9bgEu2IPOZjQ1nmG2bplsr6TJmnwn0dJxHT7UGtex17h6whKB5N4wU37Wg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/tendermint-rpc": {
+      "version": "0.28.13",
+      "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.28.13.tgz",
+      "integrity": "sha512-GB+ZmfuJIGQm0hsRtLYjeR3lOxF7Z6XyCBR0cX5AAYOZzSEBJjevPgUHD6tLn8zIhvzxaW3/VKnMB+WmlxdH4w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/crypto": "0.28.13",
+        "@cosmjs/encoding": "0.28.13",
+        "@cosmjs/json-rpc": "0.28.13",
+        "@cosmjs/math": "0.28.13",
+        "@cosmjs/socket": "0.28.13",
+        "@cosmjs/stream": "0.28.13",
+        "@cosmjs/utils": "0.28.13",
+        "axios": "^0.21.2",
+        "readonly-date": "^1.0.0",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/@cosmjs/utils": {
+      "version": "0.28.13",
+      "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.28.13.tgz",
+      "integrity": "sha512-dVeMBiyg+46x7XBZEfJK8yTihphbCFpjVYmLJVqmTsHfJwymQ65cpyW/C+V/LgWARGK8hWQ/aX9HM5Ao8QmMSg==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@osmonauts/helpers/node_modules/axios": {
+      "version": "0.21.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+      "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.14.0"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/bech32": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+      "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+      "license": "MIT"
+    },
+    "node_modules/@osmonauts/helpers/node_modules/cosmjs-types": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.5.1.tgz",
+      "integrity": "sha512-NcC58xUIVLlKdIimWWQAmSlmCjiMrJnuHf4i3LiD8PCextfHR0fT3V5/WlXZZreyMgdmh6ML1zPUfGTbbo3Z5g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.2"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/cosmjs-types/node_modules/long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@osmonauts/helpers/node_modules/long": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz",
+      "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@osmonauts/helpers/node_modules/protobufjs": {
+      "version": "6.11.4",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+      "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.1",
+        "@types/node": ">=13.7.0",
+        "long": "^4.0.0"
+      },
+      "bin": {
+        "pbjs": "bin/pbjs",
+        "pbts": "bin/pbts"
+      }
+    },
+    "node_modules/@osmonauts/helpers/node_modules/protobufjs/node_modules/long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@osmonauts/lcd": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/@osmonauts/lcd/-/lcd-0.8.0.tgz",
+      "integrity": "sha512-k7m2gAVnXc0H4m/eTq4z/8A6hFrr3MPS9wnLV4Xu9/K/WYltCnp2PpiObZm+feZUPK/svES6hxIQeO1bODLx8g==",
+      "license": "SEE LICENSE IN LICENSE",
+      "dependencies": {
+        "@babel/runtime": "^7.19.0",
+        "axios": "0.27.2"
+      }
+    },
+    "node_modules/@osmonauts/lcd/node_modules/axios": {
+      "version": "0.27.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.14.9",
+        "form-data": "^4.0.0"
+      }
+    },
+    "node_modules/@project-serum/anchor": {
+      "version": "0.25.0",
+      "resolved": "https://registry.npmjs.org/@project-serum/anchor/-/anchor-0.25.0.tgz",
+      "integrity": "sha512-E6A5Y/ijqpfMJ5psJvbw0kVTzLZFUcOFgs6eSM2M2iWE1lVRF18T6hWZVNl6zqZsoz98jgnNHtVGJMs+ds9A7A==",
+      "license": "(MIT OR Apache-2.0)",
+      "dependencies": {
+        "@project-serum/borsh": "^0.2.5",
+        "@solana/web3.js": "^1.36.0",
+        "base64-js": "^1.5.1",
+        "bn.js": "^5.1.2",
+        "bs58": "^4.0.1",
+        "buffer-layout": "^1.2.2",
+        "camelcase": "^5.3.1",
+        "cross-fetch": "^3.1.5",
+        "crypto-hash": "^1.3.0",
+        "eventemitter3": "^4.0.7",
+        "js-sha256": "^0.9.0",
+        "pako": "^2.0.3",
+        "snake-case": "^3.0.4",
+        "superstruct": "^0.15.4",
+        "toml": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=11"
+      }
+    },
+    "node_modules/@project-serum/anchor/node_modules/superstruct": {
+      "version": "0.15.5",
+      "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.15.5.tgz",
+      "integrity": "sha512-4AOeU+P5UuE/4nOUkmcQdW5y7i9ndt1cQd/3iUe+LTz3RxESf/W/5lg4B74HbDMMv8PHnPnGCQFH45kBcrQYoQ==",
+      "license": "MIT"
+    },
+    "node_modules/@project-serum/borsh": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/@project-serum/borsh/-/borsh-0.2.5.tgz",
+      "integrity": "sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bn.js": "^5.1.2",
+        "buffer-layout": "^1.2.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "peerDependencies": {
+        "@solana/web3.js": "^1.2.0"
+      }
+    },
+    "node_modules/@protobufjs/aspromise": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+      "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/base64": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+      "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/codegen": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+      "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/eventemitter": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+      "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/fetch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+      "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.1",
+        "@protobufjs/inquire": "^1.1.0"
+      }
+    },
+    "node_modules/@protobufjs/float": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+      "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/inquire": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+      "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/path": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+      "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/pool": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+      "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/utf8": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+      "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@rollup/rollup-android-arm-eabi": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz",
+      "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-android-arm64": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz",
+      "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-arm64": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz",
+      "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-darwin-x64": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz",
+      "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-arm64": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz",
+      "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-freebsd-x64": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz",
+      "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz",
+      "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz",
+      "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-gnu": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz",
+      "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-arm64-musl": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz",
+      "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz",
+      "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==",
+      "cpu": [
+        "loong64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz",
+      "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==",
+      "cpu": [
+        "ppc64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz",
+      "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==",
+      "cpu": [
+        "riscv64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-riscv64-musl": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz",
+      "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==",
+      "cpu": [
+        "riscv64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-s390x-gnu": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz",
+      "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==",
+      "cpu": [
+        "s390x"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-gnu": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz",
+      "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-linux-x64-musl": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz",
+      "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-arm64-msvc": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz",
+      "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-ia32-msvc": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz",
+      "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==",
+      "cpu": [
+        "ia32"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@rollup/rollup-win32-x64-msvc": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz",
+      "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@scure/base": {
+      "version": "1.2.6",
+      "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz",
+      "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@scure/bip32": {
+      "version": "1.6.2",
+      "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.6.2.tgz",
+      "integrity": "sha512-t96EPDMbtGgtb7onKKqxRLfE5g05k7uHnHRM2xdE6BP/ZmxaLtPek4J4KfVn/90IQNrU1IOAqMgiDtUdtbe3nw==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/curves": "~1.8.1",
+        "@noble/hashes": "~1.7.1",
+        "@scure/base": "~1.2.2"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@scure/bip32/node_modules/@noble/curves": {
+      "version": "1.8.2",
+      "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.8.2.tgz",
+      "integrity": "sha512-vnI7V6lFNe0tLAuJMu+2sX+FcL14TaCWy1qiczg1VwRmPrpQCdq5ESXQMqUc2tluRNf6irBXrWbl1mGN8uaU/g==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "1.7.2"
+      },
+      "engines": {
+        "node": "^14.21.3 || >=16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@scure/bip32/node_modules/@noble/hashes": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz",
+      "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==",
+      "license": "MIT",
+      "engines": {
+        "node": "^14.21.3 || >=16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@scure/bip39": {
+      "version": "1.5.4",
+      "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.5.4.tgz",
+      "integrity": "sha512-TFM4ni0vKvCfBpohoh+/lY05i9gRbSwXWngAsF4CABQxoaOHijxuaZ2R6cStDQ5CHtHO9aGJTr4ksVJASRRyMA==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "~1.7.1",
+        "@scure/base": "~1.2.4"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@scure/bip39/node_modules/@noble/hashes": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.2.tgz",
+      "integrity": "sha512-biZ0NUSxyjLLqo6KxEJ1b+C2NAx0wtDoFvCaXHGgUkeHzf3Xc1xKumFKREuT7f7DARNZ/slvYUwFG6B0f2b6hQ==",
+      "license": "MIT",
+      "engines": {
+        "node": "^14.21.3 || >=16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/@sei-js/core": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/@sei-js/core/-/core-1.5.0.tgz",
+      "integrity": "sha512-5Ojk6YvCfe5Fj2hwZ6nShHfNemwf9CFUylpLJmsxkQg0UYXhe6w5TFtZ+mnE+sOFO086vD0MYMUEYXXMqn0E9g==",
+      "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
+      "license": "MIT",
+      "dependencies": {
+        "@cosmjs/amino": "0.29.5",
+        "@cosmjs/cosmwasm-stargate": "0.29.5",
+        "@cosmjs/crypto": "0.29.5",
+        "@cosmjs/encoding": "0.29.5",
+        "@cosmjs/json-rpc": "0.29.5",
+        "@cosmjs/math": "0.29.5",
+        "@cosmjs/proto-signing": "0.29.5",
+        "@cosmjs/stargate": "0.29.5",
+        "@cosmjs/tendermint-rpc": "0.29.5",
+        "@cosmjs/utils": "0.29.5",
+        "@ethersproject/keccak256": "^5.7.0",
+        "@keplr-wallet/types": "^0.11.41",
+        "@sei-js/proto": "1.3.0",
+        "crypto-js": "^4.1.1",
+        "readonly-date": "^1.0.0",
+        "sha.js": "^2.4.11",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@sei-js/core/node_modules/@cosmjs/amino": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.29.5.tgz",
+      "integrity": "sha512-Qo8jpC0BiziTSUqpkNatBcwtKNhCovUnFul9SlT/74JUCdLYaeG5hxr3q1cssQt++l4LvlcpF+OUXL48XjNjLw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/crypto": "^0.29.5",
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5"
+      }
+    },
+    "node_modules/@sei-js/core/node_modules/@cosmjs/crypto": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.29.5.tgz",
+      "integrity": "sha512-2bKkaLGictaNL0UipQCL6C1afaisv6k8Wr/GCLx9FqiyFkh9ZgRHDyetD64ZsjnWV/N/D44s/esI+k6oPREaiQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5",
+        "@noble/hashes": "^1",
+        "bn.js": "^5.2.0",
+        "elliptic": "^6.5.4",
+        "libsodium-wrappers": "^0.7.6"
+      }
+    },
+    "node_modules/@sei-js/core/node_modules/@cosmjs/encoding": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.29.5.tgz",
+      "integrity": "sha512-G4rGl/Jg4dMCw5u6PEZHZcoHnUBlukZODHbm/wcL4Uu91fkn5jVo5cXXZcvs4VCkArVGrEj/52eUgTZCmOBGWQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "bech32": "^1.1.4",
+        "readonly-date": "^1.0.0"
+      }
+    },
+    "node_modules/@sei-js/core/node_modules/@cosmjs/json-rpc": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.29.5.tgz",
+      "integrity": "sha512-C78+X06l+r9xwdM1yFWIpGl03LhB9NdM1xvZpQHwgCOl0Ir/WV8pw48y3Ez2awAoUBRfTeejPe4KvrE6NoIi/w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/stream": "^0.29.5",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@sei-js/core/node_modules/@cosmjs/math": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.29.5.tgz",
+      "integrity": "sha512-2GjKcv+A9f86MAWYLUkjhw1/WpRl2R1BTb3m9qPG7lzMA7ioYff9jY5SPCfafKdxM4TIQGxXQlYGewQL16O68Q==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bn.js": "^5.2.0"
+      }
+    },
+    "node_modules/@sei-js/core/node_modules/@cosmjs/proto-signing": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.29.5.tgz",
+      "integrity": "sha512-QRrS7CiKaoETdgIqvi/7JC2qCwCR7lnWaUsTzh/XfRy3McLkEd+cXbKAW3cygykv7IN0VAEIhZd2lyIfT8KwNA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/amino": "^0.29.5",
+        "@cosmjs/crypto": "^0.29.5",
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5",
+        "cosmjs-types": "^0.5.2",
+        "long": "^4.0.0"
+      }
+    },
+    "node_modules/@sei-js/core/node_modules/@cosmjs/socket": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.29.5.tgz",
+      "integrity": "sha512-5VYDupIWbIXq3ftPV1LkS5Ya/T7Ol/AzWVhNxZ79hPe/mBfv1bGau/LqIYOm2zxGlgm9hBHOTmWGqNYDwr9LNQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/stream": "^0.29.5",
+        "isomorphic-ws": "^4.0.1",
+        "ws": "^7",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@sei-js/core/node_modules/@cosmjs/stargate": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.29.5.tgz",
+      "integrity": "sha512-hjEv8UUlJruLrYGJcUZXM/CziaINOKwfVm2BoSdUnNTMxGvY/jC1ABHKeZUYt9oXHxEJ1n9+pDqzbKc8pT0nBw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@confio/ics23": "^0.6.8",
+        "@cosmjs/amino": "^0.29.5",
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/proto-signing": "^0.29.5",
+        "@cosmjs/stream": "^0.29.5",
+        "@cosmjs/tendermint-rpc": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5",
+        "cosmjs-types": "^0.5.2",
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.3",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@sei-js/core/node_modules/@cosmjs/stream": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.29.5.tgz",
+      "integrity": "sha512-TToTDWyH1p05GBtF0Y8jFw2C+4783ueDCmDyxOMM6EU82IqpmIbfwcdMOCAm0JhnyMh+ocdebbFvnX/sGKzRAA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@sei-js/core/node_modules/@cosmjs/tendermint-rpc": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.29.5.tgz",
+      "integrity": "sha512-ar80twieuAxsy0x2za/aO3kBr2DFPAXDmk2ikDbmkda+qqfXgl35l9CVAAjKRqd9d+cRvbQyb5M4wy6XQpEV6w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/crypto": "^0.29.5",
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/json-rpc": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/socket": "^0.29.5",
+        "@cosmjs/stream": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5",
+        "axios": "^0.21.2",
+        "readonly-date": "^1.0.0",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@sei-js/core/node_modules/@cosmjs/utils": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.29.5.tgz",
+      "integrity": "sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@sei-js/core/node_modules/axios": {
+      "version": "0.21.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+      "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.14.0"
+      }
+    },
+    "node_modules/@sei-js/core/node_modules/bech32": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+      "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+      "license": "MIT"
+    },
+    "node_modules/@sei-js/core/node_modules/cosmjs-types": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.5.2.tgz",
+      "integrity": "sha512-zxCtIJj8v3Di7s39uN4LNcN3HIE1z0B9Z0SPE8ZNQR0oSzsuSe1ACgxoFkvhkS7WBasCAFcglS11G2hyfd5tPg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.2"
+      }
+    },
+    "node_modules/@sei-js/core/node_modules/protobufjs": {
+      "version": "6.11.4",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+      "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.1",
+        "@types/node": ">=13.7.0",
+        "long": "^4.0.0"
+      },
+      "bin": {
+        "pbjs": "bin/pbjs",
+        "pbts": "bin/pbts"
+      }
+    },
+    "node_modules/@sei-js/proto": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/@sei-js/proto/-/proto-1.3.0.tgz",
+      "integrity": "sha512-p/nn07Kc7h6nvTk4PJ+OqQFHmfZ4YWZ0i2Q2pnKKqq/o1sKCpoS3atP63mB6vC5r5tJfNbBjFWiCyQI2foGiiQ==",
+      "license": "SEE LICENSE IN LICENSE",
+      "dependencies": {
+        "@babel/runtime": "^7.18.9",
+        "@cosmjs/amino": "0.29.5",
+        "@cosmjs/encoding": "0.29.5",
+        "@cosmjs/proto-signing": "0.29.5",
+        "@cosmjs/stargate": "0.29.5",
+        "@cosmjs/tendermint-rpc": "0.29.5",
+        "@osmonauts/helpers": "^0.6.0",
+        "@osmonauts/lcd": "^0.8.0",
+        "protobufjs": "^6.11.2"
+      }
+    },
+    "node_modules/@sei-js/proto/node_modules/@cosmjs/amino": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.29.5.tgz",
+      "integrity": "sha512-Qo8jpC0BiziTSUqpkNatBcwtKNhCovUnFul9SlT/74JUCdLYaeG5hxr3q1cssQt++l4LvlcpF+OUXL48XjNjLw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/crypto": "^0.29.5",
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5"
+      }
+    },
+    "node_modules/@sei-js/proto/node_modules/@cosmjs/crypto": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/crypto/-/crypto-0.29.5.tgz",
+      "integrity": "sha512-2bKkaLGictaNL0UipQCL6C1afaisv6k8Wr/GCLx9FqiyFkh9ZgRHDyetD64ZsjnWV/N/D44s/esI+k6oPREaiQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5",
+        "@noble/hashes": "^1",
+        "bn.js": "^5.2.0",
+        "elliptic": "^6.5.4",
+        "libsodium-wrappers": "^0.7.6"
+      }
+    },
+    "node_modules/@sei-js/proto/node_modules/@cosmjs/encoding": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/encoding/-/encoding-0.29.5.tgz",
+      "integrity": "sha512-G4rGl/Jg4dMCw5u6PEZHZcoHnUBlukZODHbm/wcL4Uu91fkn5jVo5cXXZcvs4VCkArVGrEj/52eUgTZCmOBGWQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "base64-js": "^1.3.0",
+        "bech32": "^1.1.4",
+        "readonly-date": "^1.0.0"
+      }
+    },
+    "node_modules/@sei-js/proto/node_modules/@cosmjs/json-rpc": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/json-rpc/-/json-rpc-0.29.5.tgz",
+      "integrity": "sha512-C78+X06l+r9xwdM1yFWIpGl03LhB9NdM1xvZpQHwgCOl0Ir/WV8pw48y3Ez2awAoUBRfTeejPe4KvrE6NoIi/w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/stream": "^0.29.5",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@sei-js/proto/node_modules/@cosmjs/math": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/math/-/math-0.29.5.tgz",
+      "integrity": "sha512-2GjKcv+A9f86MAWYLUkjhw1/WpRl2R1BTb3m9qPG7lzMA7ioYff9jY5SPCfafKdxM4TIQGxXQlYGewQL16O68Q==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bn.js": "^5.2.0"
+      }
+    },
+    "node_modules/@sei-js/proto/node_modules/@cosmjs/proto-signing": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/proto-signing/-/proto-signing-0.29.5.tgz",
+      "integrity": "sha512-QRrS7CiKaoETdgIqvi/7JC2qCwCR7lnWaUsTzh/XfRy3McLkEd+cXbKAW3cygykv7IN0VAEIhZd2lyIfT8KwNA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/amino": "^0.29.5",
+        "@cosmjs/crypto": "^0.29.5",
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5",
+        "cosmjs-types": "^0.5.2",
+        "long": "^4.0.0"
+      }
+    },
+    "node_modules/@sei-js/proto/node_modules/@cosmjs/socket": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/socket/-/socket-0.29.5.tgz",
+      "integrity": "sha512-5VYDupIWbIXq3ftPV1LkS5Ya/T7Ol/AzWVhNxZ79hPe/mBfv1bGau/LqIYOm2zxGlgm9hBHOTmWGqNYDwr9LNQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/stream": "^0.29.5",
+        "isomorphic-ws": "^4.0.1",
+        "ws": "^7",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@sei-js/proto/node_modules/@cosmjs/stargate": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/stargate/-/stargate-0.29.5.tgz",
+      "integrity": "sha512-hjEv8UUlJruLrYGJcUZXM/CziaINOKwfVm2BoSdUnNTMxGvY/jC1ABHKeZUYt9oXHxEJ1n9+pDqzbKc8pT0nBw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@confio/ics23": "^0.6.8",
+        "@cosmjs/amino": "^0.29.5",
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/proto-signing": "^0.29.5",
+        "@cosmjs/stream": "^0.29.5",
+        "@cosmjs/tendermint-rpc": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5",
+        "cosmjs-types": "^0.5.2",
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.3",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@sei-js/proto/node_modules/@cosmjs/stream": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/stream/-/stream-0.29.5.tgz",
+      "integrity": "sha512-TToTDWyH1p05GBtF0Y8jFw2C+4783ueDCmDyxOMM6EU82IqpmIbfwcdMOCAm0JhnyMh+ocdebbFvnX/sGKzRAA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@sei-js/proto/node_modules/@cosmjs/tendermint-rpc": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/tendermint-rpc/-/tendermint-rpc-0.29.5.tgz",
+      "integrity": "sha512-ar80twieuAxsy0x2za/aO3kBr2DFPAXDmk2ikDbmkda+qqfXgl35l9CVAAjKRqd9d+cRvbQyb5M4wy6XQpEV6w==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@cosmjs/crypto": "^0.29.5",
+        "@cosmjs/encoding": "^0.29.5",
+        "@cosmjs/json-rpc": "^0.29.5",
+        "@cosmjs/math": "^0.29.5",
+        "@cosmjs/socket": "^0.29.5",
+        "@cosmjs/stream": "^0.29.5",
+        "@cosmjs/utils": "^0.29.5",
+        "axios": "^0.21.2",
+        "readonly-date": "^1.0.0",
+        "xstream": "^11.14.0"
+      }
+    },
+    "node_modules/@sei-js/proto/node_modules/@cosmjs/utils": {
+      "version": "0.29.5",
+      "resolved": "https://registry.npmjs.org/@cosmjs/utils/-/utils-0.29.5.tgz",
+      "integrity": "sha512-m7h+RXDUxOzEOGt4P+3OVPX7PuakZT3GBmaM/Y2u+abN3xZkziykD/NvedYFvvCCdQo714XcGl33bwifS9FZPQ==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/@sei-js/proto/node_modules/axios": {
+      "version": "0.21.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
+      "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.14.0"
+      }
+    },
+    "node_modules/@sei-js/proto/node_modules/bech32": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+      "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+      "license": "MIT"
+    },
+    "node_modules/@sei-js/proto/node_modules/cosmjs-types": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.5.2.tgz",
+      "integrity": "sha512-zxCtIJj8v3Di7s39uN4LNcN3HIE1z0B9Z0SPE8ZNQR0oSzsuSe1ACgxoFkvhkS7WBasCAFcglS11G2hyfd5tPg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.2"
+      }
+    },
+    "node_modules/@sei-js/proto/node_modules/protobufjs": {
+      "version": "6.11.4",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+      "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.1",
+        "@types/node": ">=13.7.0",
+        "long": "^4.0.0"
+      },
+      "bin": {
+        "pbjs": "bin/pbjs",
+        "pbts": "bin/pbts"
+      }
+    },
+    "node_modules/@sindresorhus/is": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+      "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/is?sponsor=1"
+      }
+    },
+    "node_modules/@solana/buffer-layout": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz",
+      "integrity": "sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==",
+      "license": "MIT",
+      "dependencies": {
+        "buffer": "~6.0.3"
+      },
+      "engines": {
+        "node": ">=5.10"
+      }
+    },
+    "node_modules/@solana/buffer-layout-utils": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz",
+      "integrity": "sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@solana/buffer-layout": "^4.0.0",
+        "@solana/web3.js": "^1.32.0",
+        "bigint-buffer": "^1.1.5",
+        "bignumber.js": "^9.0.1"
+      },
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@solana/codecs": {
+      "version": "2.0.0-rc.1",
+      "resolved": "https://registry.npmjs.org/@solana/codecs/-/codecs-2.0.0-rc.1.tgz",
+      "integrity": "sha512-qxoR7VybNJixV51L0G1RD2boZTcxmwUWnKCaJJExQ5qNKwbpSyDdWfFJfM5JhGyKe9DnPVOZB+JHWXnpbZBqrQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@solana/codecs-core": "2.0.0-rc.1",
+        "@solana/codecs-data-structures": "2.0.0-rc.1",
+        "@solana/codecs-numbers": "2.0.0-rc.1",
+        "@solana/codecs-strings": "2.0.0-rc.1",
+        "@solana/options": "2.0.0-rc.1"
+      },
+      "peerDependencies": {
+        "typescript": ">=5"
+      }
+    },
+    "node_modules/@solana/codecs-core": {
+      "version": "2.0.0-rc.1",
+      "resolved": "https://registry.npmjs.org/@solana/codecs-core/-/codecs-core-2.0.0-rc.1.tgz",
+      "integrity": "sha512-bauxqMfSs8EHD0JKESaNmNuNvkvHSuN3bbWAF5RjOfDu2PugxHrvRebmYauvSumZ3cTfQ4HJJX6PG5rN852qyQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@solana/errors": "2.0.0-rc.1"
+      },
+      "peerDependencies": {
+        "typescript": ">=5"
+      }
+    },
+    "node_modules/@solana/codecs-data-structures": {
+      "version": "2.0.0-rc.1",
+      "resolved": "https://registry.npmjs.org/@solana/codecs-data-structures/-/codecs-data-structures-2.0.0-rc.1.tgz",
+      "integrity": "sha512-rinCv0RrAVJ9rE/rmaibWJQxMwC5lSaORSZuwjopSUE6T0nb/MVg6Z1siNCXhh/HFTOg0l8bNvZHgBcN/yvXog==",
+      "license": "MIT",
+      "dependencies": {
+        "@solana/codecs-core": "2.0.0-rc.1",
+        "@solana/codecs-numbers": "2.0.0-rc.1",
+        "@solana/errors": "2.0.0-rc.1"
+      },
+      "peerDependencies": {
+        "typescript": ">=5"
+      }
+    },
+    "node_modules/@solana/codecs-numbers": {
+      "version": "2.0.0-rc.1",
+      "resolved": "https://registry.npmjs.org/@solana/codecs-numbers/-/codecs-numbers-2.0.0-rc.1.tgz",
+      "integrity": "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@solana/codecs-core": "2.0.0-rc.1",
+        "@solana/errors": "2.0.0-rc.1"
+      },
+      "peerDependencies": {
+        "typescript": ">=5"
+      }
+    },
+    "node_modules/@solana/codecs-strings": {
+      "version": "2.0.0-rc.1",
+      "resolved": "https://registry.npmjs.org/@solana/codecs-strings/-/codecs-strings-2.0.0-rc.1.tgz",
+      "integrity": "sha512-9/wPhw8TbGRTt6mHC4Zz1RqOnuPTqq1Nb4EyuvpZ39GW6O2t2Q7Q0XxiB3+BdoEjwA2XgPw6e2iRfvYgqty44g==",
+      "license": "MIT",
+      "dependencies": {
+        "@solana/codecs-core": "2.0.0-rc.1",
+        "@solana/codecs-numbers": "2.0.0-rc.1",
+        "@solana/errors": "2.0.0-rc.1"
+      },
+      "peerDependencies": {
+        "fastestsmallesttextencoderdecoder": "^1.0.22",
+        "typescript": ">=5"
+      }
+    },
+    "node_modules/@solana/errors": {
+      "version": "2.0.0-rc.1",
+      "resolved": "https://registry.npmjs.org/@solana/errors/-/errors-2.0.0-rc.1.tgz",
+      "integrity": "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ==",
+      "license": "MIT",
+      "dependencies": {
+        "chalk": "^5.3.0",
+        "commander": "^12.1.0"
+      },
+      "bin": {
+        "errors": "bin/cli.mjs"
+      },
+      "peerDependencies": {
+        "typescript": ">=5"
+      }
+    },
+    "node_modules/@solana/options": {
+      "version": "2.0.0-rc.1",
+      "resolved": "https://registry.npmjs.org/@solana/options/-/options-2.0.0-rc.1.tgz",
+      "integrity": "sha512-mLUcR9mZ3qfHlmMnREdIFPf9dpMc/Bl66tLSOOWxw4ml5xMT2ohFn7WGqoKcu/UHkT9CrC6+amEdqCNvUqI7AA==",
+      "license": "MIT",
+      "dependencies": {
+        "@solana/codecs-core": "2.0.0-rc.1",
+        "@solana/codecs-data-structures": "2.0.0-rc.1",
+        "@solana/codecs-numbers": "2.0.0-rc.1",
+        "@solana/codecs-strings": "2.0.0-rc.1",
+        "@solana/errors": "2.0.0-rc.1"
+      },
+      "peerDependencies": {
+        "typescript": ">=5"
+      }
+    },
+    "node_modules/@solana/spl-token": {
+      "version": "0.3.11",
+      "resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.11.tgz",
+      "integrity": "sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@solana/buffer-layout": "^4.0.0",
+        "@solana/buffer-layout-utils": "^0.2.0",
+        "@solana/spl-token-metadata": "^0.1.2",
+        "buffer": "^6.0.3"
+      },
+      "engines": {
+        "node": ">=16"
+      },
+      "peerDependencies": {
+        "@solana/web3.js": "^1.88.0"
+      }
+    },
+    "node_modules/@solana/spl-token-metadata": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/@solana/spl-token-metadata/-/spl-token-metadata-0.1.6.tgz",
+      "integrity": "sha512-7sMt1rsm/zQOQcUWllQX9mD2O6KhSAtY1hFR2hfFwgqfFWzSY9E9GDvFVNYUI1F0iQKcm6HmePU9QbKRXTEBiA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@solana/codecs": "2.0.0-rc.1"
+      },
+      "engines": {
+        "node": ">=16"
+      },
+      "peerDependencies": {
+        "@solana/web3.js": "^1.95.3"
+      }
+    },
+    "node_modules/@solana/web3.js": {
+      "version": "1.98.0",
+      "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.98.0.tgz",
+      "integrity": "sha512-nz3Q5OeyGFpFCR+erX2f6JPt3sKhzhYcSycBCSPkWjzSVDh/Rr1FqTVMRe58FKO16/ivTUcuJjeS5MyBvpkbzA==",
+      "license": "MIT",
+      "dependencies": {
+        "@babel/runtime": "^7.25.0",
+        "@noble/curves": "^1.4.2",
+        "@noble/hashes": "^1.4.0",
+        "@solana/buffer-layout": "^4.0.1",
+        "agentkeepalive": "^4.5.0",
+        "bigint-buffer": "^1.1.5",
+        "bn.js": "^5.2.1",
+        "borsh": "^0.7.0",
+        "bs58": "^4.0.1",
+        "buffer": "6.0.3",
+        "fast-stable-stringify": "^1.0.0",
+        "jayson": "^4.1.1",
+        "node-fetch": "^2.7.0",
+        "rpc-websockets": "^9.0.2",
+        "superstruct": "^2.0.2"
+      }
+    },
+    "node_modules/@solana/web3.js/node_modules/@types/ws": {
+      "version": "8.18.0",
+      "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.0.tgz",
+      "integrity": "sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@solana/web3.js/node_modules/eventemitter3": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+      "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+      "license": "MIT"
+    },
+    "node_modules/@solana/web3.js/node_modules/rpc-websockets": {
+      "version": "9.1.1",
+      "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-9.1.1.tgz",
+      "integrity": "sha512-1IXGM/TfPT6nfYMIXkJdzn+L4JEsmb0FL1O2OBjaH03V3yuUDdKFulGLMFG6ErV+8pZ5HVC0limve01RyO+saA==",
+      "license": "LGPL-3.0-only",
+      "dependencies": {
+        "@swc/helpers": "^0.5.11",
+        "@types/uuid": "^8.3.4",
+        "@types/ws": "^8.2.2",
+        "buffer": "^6.0.3",
+        "eventemitter3": "^5.0.1",
+        "uuid": "^8.3.2",
+        "ws": "^8.5.0"
+      },
+      "funding": {
+        "type": "paypal",
+        "url": "https://paypal.me/kozjak"
+      },
+      "optionalDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": "^5.0.2"
+      }
+    },
+    "node_modules/@solana/web3.js/node_modules/superstruct": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-2.0.2.tgz",
+      "integrity": "sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/@solana/web3.js/node_modules/ws": {
+      "version": "8.18.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
+      "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@stacks/common": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@stacks/common/-/common-7.0.2.tgz",
+      "integrity": "sha512-+RSecHdkxOtswmE4tDDoZlYEuULpnTQVeDIG5eZ32opK8cFxf4EugAcK9CsIsHx/Se1yTEaQ21WGATmJGK84lQ==",
+      "license": "MIT"
+    },
+    "node_modules/@stacks/network": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/@stacks/network/-/network-7.2.0.tgz",
+      "integrity": "sha512-AkLougCF2RLbK97TtISZxAhF3cE757XMXWOGKvEFWNauiQ5/bYyI9W5jZypG3yI/AyYIo04NKoFWWTnpJcn1iA==",
+      "license": "MIT",
+      "dependencies": {
+        "@stacks/common": "^7.0.2",
+        "cross-fetch": "^3.1.5"
+      }
+    },
+    "node_modules/@stacks/transactions": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/@stacks/transactions/-/transactions-7.2.0.tgz",
+      "integrity": "sha512-U7wjlxM9Q+408ihRsv5mlKRslXGt2WCShKi1lduiqf5+dBSRGdVi8ttCIEckSsg3ulCVF3EHTQF3LZgw4kwKlQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "1.1.5",
+        "@noble/secp256k1": "1.7.1",
+        "@stacks/common": "^7.0.2",
+        "@stacks/network": "^7.2.0",
+        "c32check": "^2.0.0",
+        "lodash.clonedeep": "^4.5.0"
+      }
+    },
+    "node_modules/@stacks/transactions/node_modules/@noble/hashes": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.5.tgz",
+      "integrity": "sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/@stacks/transactions/node_modules/@noble/secp256k1": {
+      "version": "1.7.1",
+      "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz",
+      "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/@suchipi/femver": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@suchipi/femver/-/femver-1.0.0.tgz",
+      "integrity": "sha512-bprE8+K5V+DPX7q2e2K57ImqNBdfGHDIWaGI5xHxZoxbKOuQZn4wzPiUxOAHnsUr3w3xHrWXwN7gnG/iIuEMIg==",
+      "license": "MIT"
+    },
+    "node_modules/@swc/helpers": {
+      "version": "0.5.15",
+      "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+      "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.8.0"
+      }
+    },
+    "node_modules/@szmarczak/http-timer": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
+      "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
+      "license": "MIT",
+      "dependencies": {
+        "defer-to-connect": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@terra-money/legacy.proto": {
+      "name": "@terra-money/terra.proto",
+      "version": "0.1.7",
+      "resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-0.1.7.tgz",
+      "integrity": "sha512-NXD7f6pQCulvo6+mv6MAPzhOkUzRjgYVuHZE/apih+lVnPG5hDBU0rRYnOGGofwvKT5/jQoOENnFn/gioWWnyQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "google-protobuf": "^3.17.3",
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.2"
+      }
+    },
+    "node_modules/@terra-money/legacy.proto/node_modules/protobufjs": {
+      "version": "6.11.4",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+      "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.1",
+        "@types/node": ">=13.7.0",
+        "long": "^4.0.0"
+      },
+      "bin": {
+        "pbjs": "bin/pbjs",
+        "pbts": "bin/pbts"
+      }
+    },
+    "node_modules/@terra-money/terra.js": {
+      "version": "3.1.10",
+      "resolved": "https://registry.npmjs.org/@terra-money/terra.js/-/terra.js-3.1.10.tgz",
+      "integrity": "sha512-MqR16LjTUyVD4HnEavP1iBW0c1roCoRHH/E1x9P44pXzgtv2wsMeP+2un4Bnck4Nkv/46Xvy/BSKiY90ll3BKA==",
+      "license": "MIT",
+      "dependencies": {
+        "@classic-terra/terra.proto": "^1.1.0",
+        "@terra-money/terra.proto": "^2.1.0",
+        "axios": "^0.27.2",
+        "bech32": "^2.0.0",
+        "bip32": "^2.0.6",
+        "bip39": "^3.0.3",
+        "bufferutil": "^4.0.3",
+        "decimal.js": "^10.2.1",
+        "jscrypto": "^1.0.1",
+        "readable-stream": "^3.6.0",
+        "secp256k1": "^4.0.2",
+        "tmp": "^0.2.1",
+        "utf-8-validate": "^5.0.5",
+        "ws": "^7.5.9"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@terra-money/terra.js/node_modules/axios": {
+      "version": "0.27.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.14.9",
+        "form-data": "^4.0.0"
+      }
+    },
+    "node_modules/@terra-money/terra.proto": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@terra-money/terra.proto/-/terra.proto-2.1.0.tgz",
+      "integrity": "sha512-rhaMslv3Rkr+QsTQEZs64FKA4QlfO0DfQHaR6yct/EovenMkibDEQ63dEL6yJA6LCaEQGYhyVB9JO9pTUA8ybw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@improbable-eng/grpc-web": "^0.14.1",
+        "google-protobuf": "^3.17.3",
+        "long": "^4.0.0",
+        "protobufjs": "~6.11.2"
+      }
+    },
+    "node_modules/@terra-money/terra.proto/node_modules/@improbable-eng/grpc-web": {
+      "version": "0.14.1",
+      "resolved": "https://registry.npmjs.org/@improbable-eng/grpc-web/-/grpc-web-0.14.1.tgz",
+      "integrity": "sha512-XaIYuunepPxoiGVLLHmlnVminUGzBTnXr8Wv7khzmLWbNw4TCwJKX09GSMJlKhu/TRk6gms0ySFxewaETSBqgw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "browser-headers": "^0.4.1"
+      },
+      "peerDependencies": {
+        "google-protobuf": "^3.14.0"
+      }
+    },
+    "node_modules/@terra-money/terra.proto/node_modules/protobufjs": {
+      "version": "6.11.4",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.4.tgz",
+      "integrity": "sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.1",
+        "@types/node": ">=13.7.0",
+        "long": "^4.0.0"
+      },
+      "bin": {
+        "pbjs": "bin/pbjs",
+        "pbts": "bin/pbts"
+      }
+    },
+    "node_modules/@tsconfig/node10": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
+      "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@tsconfig/node12": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+      "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@tsconfig/node14": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+      "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@tsconfig/node16": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+      "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/@types/bn.js": {
+      "version": "5.1.6",
+      "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.6.tgz",
+      "integrity": "sha512-Xh8vSwUeMKeYYrj3cX4lGQgFSF/N03r+tv4AiLl1SucqV+uTQpxRcnM8AkXKHwYP9ZPXOYXRr2KPXpVlIvqh9w==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/cacheable-request": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
+      "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/http-cache-semantics": "*",
+        "@types/keyv": "^3.1.4",
+        "@types/node": "*",
+        "@types/responselike": "^1.0.0"
+      }
+    },
+    "node_modules/@types/config": {
+      "version": "3.3.5",
+      "resolved": "https://registry.npmjs.org/@types/config/-/config-3.3.5.tgz",
+      "integrity": "sha512-itq2HtXQBrNUKwMNZnb9mBRE3T99VYCdl1gjST9rq+9kFaB1iMMGuDeZnP88qid73DnpAMKH9ZolqDpS1Lz7+w==",
+      "license": "MIT"
+    },
+    "node_modules/@types/connect": {
+      "version": "3.4.38",
+      "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+      "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/estree": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+      "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+      "license": "MIT"
+    },
+    "node_modules/@types/http-cache-semantics": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
+      "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/keyv": {
+      "version": "3.1.4",
+      "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
+      "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/lodash": {
+      "version": "4.17.16",
+      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz",
+      "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/@types/lodash.values": {
+      "version": "4.3.9",
+      "resolved": "https://registry.npmjs.org/@types/lodash.values/-/lodash.values-4.3.9.tgz",
+      "integrity": "sha512-IJ20OEfqNwm3k8ENwoM3q0yOs4UMpgtD4GqxB4lwBHToGthHWqhyh5DdSgQjioocz0QK2SSBkJfCq95ZTV8BTw==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@types/lodash": "*"
+      }
+    },
+    "node_modules/@types/long": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz",
+      "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==",
+      "license": "MIT"
+    },
+    "node_modules/@types/node": {
+      "version": "20.17.25",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.25.tgz",
+      "integrity": "sha512-bT+r2haIlplJUYtlZrEanFHdPIZTeiMeh/fSOEbOOfWf9uTn+lg8g0KU6Q3iMgjd9FLuuMAgfCNSkjUbxL6E3Q==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~6.19.2"
+      }
+    },
+    "node_modules/@types/pbkdf2": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz",
+      "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/responselike": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
+      "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/secp256k1": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz",
+      "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@types/uuid": {
+      "version": "8.3.4",
+      "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz",
+      "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==",
+      "license": "MIT"
+    },
+    "node_modules/@types/ws": {
+      "version": "7.4.7",
+      "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.4.7.tgz",
+      "integrity": "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/@vitest/expect": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz",
+      "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==",
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/spy": "2.1.9",
+        "@vitest/utils": "2.1.9",
+        "chai": "^5.1.2",
+        "tinyrainbow": "^1.2.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/mocker": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz",
+      "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==",
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/spy": "2.1.9",
+        "estree-walker": "^3.0.3",
+        "magic-string": "^0.30.12"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "msw": "^2.4.9",
+        "vite": "^5.0.0"
+      },
+      "peerDependenciesMeta": {
+        "msw": {
+          "optional": true
+        },
+        "vite": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vitest/pretty-format": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz",
+      "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==",
+      "license": "MIT",
+      "dependencies": {
+        "tinyrainbow": "^1.2.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/runner": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz",
+      "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==",
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/utils": "2.1.9",
+        "pathe": "^1.1.2"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/snapshot": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz",
+      "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/pretty-format": "2.1.9",
+        "magic-string": "^0.30.12",
+        "pathe": "^1.1.2"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/spy": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz",
+      "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==",
+      "license": "MIT",
+      "dependencies": {
+        "tinyspy": "^3.0.2"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@vitest/utils": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz",
+      "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/pretty-format": "2.1.9",
+        "loupe": "^3.1.2",
+        "tinyrainbow": "^1.2.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/@wormhole-foundation/wormhole-cli": {
+      "version": "0.0.4",
+      "resolved": "https://registry.npmjs.org/@wormhole-foundation/wormhole-cli/-/wormhole-cli-0.0.4.tgz",
+      "integrity": "sha512-mtAegluKtPp8zn32XSJJotanWxi+9uTh9rGRw7Q0QbCi/tR3YXew7HRYqpLRt0J0KJGR/raMYIIefDSgYbj8bg==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@celo-tools/celo-ethers-wrapper": "^0.1.0",
+        "@certusone/wormhole-sdk": "^0.10.15",
+        "@cosmjs/encoding": "^0.26.2",
+        "@improbable-eng/grpc-web-node-http-transport": "^0.15.0",
+        "@injectivelabs/networks": "^1.10.7",
+        "@injectivelabs/sdk-ts": "^1.10.47",
+        "@injectivelabs/utils": "^1.10.5",
+        "@mysten/sui.js": "^0.32.2",
+        "@sei-js/core": "^1.3.2",
+        "@solana/spl-token": "^0.3.5",
+        "@solana/web3.js": "^1.22.0",
+        "@terra-money/terra.js": "^3.1.9",
+        "@types/config": "^3.3.0",
+        "@xpla/xpla.js": "^0.2.1",
+        "algosdk": "^2.4.0",
+        "aptos": "^1.3.16",
+        "axios": "^0.24.0",
+        "base-64": "^1.0.0",
+        "binary-parser": "^2.0.2",
+        "bn.js": "^5.2.0",
+        "bs58": "^4.0.1",
+        "buffer-layout": "^1.2.2",
+        "config": "^3.3.7",
+        "dotenv": "^10.0.0",
+        "esbuild": "0.17.18",
+        "ethers": "^5.6.8",
+        "js-base64": "^3.6.1",
+        "near-api-js": "^1.0.0",
+        "near-seed-phrase": "^0.2.0",
+        "yargs": "^17.7.2"
+      },
+      "bin": {
+        "worm": "build/main.js"
+      }
+    },
+    "node_modules/@wormhole-foundation/wormhole-cli/node_modules/@celo-tools/celo-ethers-wrapper": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/@celo-tools/celo-ethers-wrapper/-/celo-ethers-wrapper-0.1.1.tgz",
+      "integrity": "sha512-kf/UykMf7byi+gO8OlHhufDqtXfTgXC5D6KdiMN4ibkDSqhicoqtWMh1N/9JnjIC2Qa9BBrrY61Z18gNYTDZwA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "peerDependencies": {
+        "ethers": "^5"
+      }
+    },
+    "node_modules/@wry/caches": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz",
+      "integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@wry/context": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz",
+      "integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@wry/equality": {
+      "version": "0.5.7",
+      "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz",
+      "integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@wry/trie": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz",
+      "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.3.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@xpla/xpla.js": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/@xpla/xpla.js/-/xpla.js-0.2.3.tgz",
+      "integrity": "sha512-Tfk7hCGWXtwr08reY3Pi6dmzIqFbzri9jcyzJdfNmdo4cN0PMwpRJuZZcPmtxiIUnNef3AN1E/6nJUD5MKniuA==",
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/bytes": "^5.6.1",
+        "@ethersproject/keccak256": "^5.6.1",
+        "@ethersproject/signing-key": "^5.6.2",
+        "@terra-money/legacy.proto": "npm:@terra-money/terra.proto@^0.1.7",
+        "@terra-money/terra.proto": "^2.1.0",
+        "axios": "^0.26.1",
+        "bech32": "^2.0.0",
+        "bip32": "^2.0.6",
+        "bip39": "^3.0.3",
+        "bufferutil": "^4.0.3",
+        "crypto-addr-codec": "^0.1.7",
+        "decimal.js": "^10.2.1",
+        "elliptic": "^6.5.4",
+        "ethereumjs-util": "^7.1.5",
+        "jscrypto": "^1.0.1",
+        "readable-stream": "^3.6.0",
+        "secp256k1": "^4.0.2",
+        "tmp": "^0.2.1",
+        "utf-8-validate": "^5.0.5",
+        "ws": "^7.5.8"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@xpla/xpla.js/node_modules/axios": {
+      "version": "0.26.1",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
+      "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.14.8"
+      }
+    },
+    "node_modules/acorn": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.1.1.tgz",
+      "integrity": "sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==",
+      "license": "MIT",
+      "optional": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-walk": {
+      "version": "8.3.4",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
+      "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "acorn": "^8.11.0"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/acorn-walk/node_modules/acorn": {
+      "version": "8.14.1",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
+      "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/aes-js": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz",
+      "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==",
+      "license": "MIT"
+    },
+    "node_modules/agentkeepalive": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz",
+      "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==",
+      "license": "MIT",
+      "dependencies": {
+        "humanize-ms": "^1.2.1"
+      },
+      "engines": {
+        "node": ">= 8.0.0"
+      }
+    },
+    "node_modules/algo-msgpack-with-bigint": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/algo-msgpack-with-bigint/-/algo-msgpack-with-bigint-2.1.1.tgz",
+      "integrity": "sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ==",
+      "license": "ISC",
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/algosdk": {
+      "version": "2.11.0",
+      "resolved": "https://registry.npmjs.org/algosdk/-/algosdk-2.11.0.tgz",
+      "integrity": "sha512-MJ9xrjBKqG6ItCAR0Z974DG7nvifXsQxlj7HbWkBat4+h/IygwKPEH/v0dQPOYWdTcf5/5lfu+OyEVOsjxvyRQ==",
+      "license": "MIT",
+      "dependencies": {
+        "algo-msgpack-with-bigint": "^2.1.1",
+        "buffer": "^6.0.3",
+        "hi-base32": "^0.5.1",
+        "js-sha256": "^0.9.0",
+        "js-sha3": "^0.8.0",
+        "js-sha512": "^0.8.0",
+        "json-bigint": "^1.0.0",
+        "tweetnacl": "^1.0.3",
+        "vlq": "^2.0.4"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "license": "MIT",
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/aptos": {
+      "version": "1.21.0",
+      "resolved": "https://registry.npmjs.org/aptos/-/aptos-1.21.0.tgz",
+      "integrity": "sha512-PRKjoFgL8tVEc9+oS7eJUv8GNxx8n3+0byH2+m7CP3raYOD6yFKOecuwjVMIJmgfpjp6xH0P0HDMGZAXmSyU0Q==",
+      "deprecated": "Package aptos is no longer supported, please migrate to https://www.npmjs.com/package/@aptos-labs/ts-sdk",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@aptos-labs/aptos-client": "^0.1.0",
+        "@noble/hashes": "1.3.3",
+        "@scure/bip39": "1.2.1",
+        "eventemitter3": "^5.0.1",
+        "form-data": "4.0.0",
+        "tweetnacl": "1.0.3"
+      },
+      "engines": {
+        "node": ">=11.0.0"
+      }
+    },
+    "node_modules/aptos/node_modules/@noble/hashes": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
+      "integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/aptos/node_modules/@scure/base": {
+      "version": "1.1.9",
+      "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.9.tgz",
+      "integrity": "sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/aptos/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==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "~1.3.0",
+        "@scure/base": "~1.1.0"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/aptos/node_modules/eventemitter3": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+      "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+      "license": "MIT"
+    },
+    "node_modules/arg": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+      "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/assertion-error": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+      "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
+    "node_modules/axios": {
+      "version": "0.24.0",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
+      "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
+      "license": "MIT",
+      "dependencies": {
+        "follow-redirects": "^1.14.4"
+      }
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "license": "MIT"
+    },
+    "node_modules/base-64": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz",
+      "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==",
+      "license": "MIT"
+    },
+    "node_modules/base-x": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.1.tgz",
+      "integrity": "sha512-uAZ8x6r6S3aUM9rbHGVOIsR15U/ZSc82b3ymnCPsT45Gk1DDvhDPdIgB5MrhirZWt+5K0EEPQH985kNqZgNPFw==",
+      "license": "MIT"
+    },
+    "node_modules/base64-js": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+      "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+      "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/bech32": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz",
+      "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==",
+      "license": "MIT"
+    },
+    "node_modules/big-integer": {
+      "version": "1.6.36",
+      "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.36.tgz",
+      "integrity": "sha512-t70bfa7HYEA1D9idDbmuv7YbsbVkQ+Hp+8KFSul4aE5e/i1bjCNIRYJZlA8Q8p0r9T8cF/RVvwUgRA//FydEyg==",
+      "license": "Unlicense",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/bigint-buffer": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/bigint-buffer/-/bigint-buffer-1.1.5.tgz",
+      "integrity": "sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==",
+      "hasInstallScript": true,
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bindings": "^1.3.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/bignumber.js": {
+      "version": "9.1.2",
+      "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz",
+      "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
+      "license": "MIT",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/binary-parser": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/binary-parser/-/binary-parser-2.2.1.tgz",
+      "integrity": "sha512-5ATpz/uPDgq5GgEDxTB4ouXCde7q2lqAQlSdBRQVl/AJnxmQmhIfyxJx+0MGu//D5rHQifkfGbWWlaysG0o9NA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/bindings": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+      "license": "MIT",
+      "dependencies": {
+        "file-uri-to-path": "1.0.0"
+      }
+    },
+    "node_modules/bip32": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/bip32/-/bip32-2.0.6.tgz",
+      "integrity": "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "10.12.18",
+        "bs58check": "^2.1.1",
+        "create-hash": "^1.2.0",
+        "create-hmac": "^1.1.7",
+        "tiny-secp256k1": "^1.1.3",
+        "typeforce": "^1.11.5",
+        "wif": "^2.0.6"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/bip32/node_modules/@types/node": {
+      "version": "10.12.18",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
+      "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==",
+      "license": "MIT"
+    },
+    "node_modules/bip39": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.1.0.tgz",
+      "integrity": "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A==",
+      "license": "ISC",
+      "dependencies": {
+        "@noble/hashes": "^1.2.0"
+      }
+    },
+    "node_modules/bip39-light": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/bip39-light/-/bip39-light-1.0.7.tgz",
+      "integrity": "sha512-WDTmLRQUsiioBdTs9BmSEmkJza+8xfJmptsNJjxnoq3EydSa/ZBXT6rm66KoT3PJIRYMnhSKNR7S9YL1l7R40Q==",
+      "license": "ISC",
+      "dependencies": {
+        "create-hash": "^1.1.0",
+        "pbkdf2": "^3.0.9"
+      }
+    },
+    "node_modules/bip66": {
+      "version": "1.1.5",
+      "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz",
+      "integrity": "sha512-nemMHz95EmS38a26XbbdxIYj5csHd3RMP3H5bwQknX0WYHF01qhpufP42mLOwVICuH2JmhIhXiWs89MfUGL7Xw==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/blakejs": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz",
+      "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==",
+      "license": "MIT"
+    },
+    "node_modules/bn.js": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz",
+      "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==",
+      "license": "MIT"
+    },
+    "node_modules/borsh": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz",
+      "integrity": "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "bn.js": "^5.2.0",
+        "bs58": "^4.0.0",
+        "text-encoding-utf-8": "^1.0.2"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "license": "MIT",
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/brorand": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+      "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
+      "license": "MIT"
+    },
+    "node_modules/browser-headers": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/browser-headers/-/browser-headers-0.4.1.tgz",
+      "integrity": "sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/browserify-aes": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
+      "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
+      "license": "MIT",
+      "dependencies": {
+        "buffer-xor": "^1.0.3",
+        "cipher-base": "^1.0.0",
+        "create-hash": "^1.1.0",
+        "evp_bytestokey": "^1.0.3",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/bs58": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz",
+      "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==",
+      "license": "MIT",
+      "dependencies": {
+        "base-x": "^3.0.2"
+      }
+    },
+    "node_modules/bs58/node_modules/base-x": {
+      "version": "3.0.11",
+      "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz",
+      "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/bs58check": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz",
+      "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==",
+      "license": "MIT",
+      "dependencies": {
+        "bs58": "^4.0.0",
+        "create-hash": "^1.1.0",
+        "safe-buffer": "^5.1.2"
+      }
+    },
+    "node_modules/buffer": {
+      "version": "6.0.3",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+      "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+      "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",
+      "dependencies": {
+        "base64-js": "^1.3.1",
+        "ieee754": "^1.2.1"
+      }
+    },
+    "node_modules/buffer-layout": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.2.tgz",
+      "integrity": "sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.5"
+      }
+    },
+    "node_modules/buffer-xor": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+      "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
+      "license": "MIT"
+    },
+    "node_modules/bufferutil": {
+      "version": "4.0.9",
+      "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.9.tgz",
+      "integrity": "sha512-WDtdLmJvAuNNPzByAYpRo2rF1Mmradw6gvWsQKf63476DDXmomT9zUiGypLcG4ibIM67vhAj8jJRdbmEws2Aqw==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "node-gyp-build": "^4.3.0"
+      },
+      "engines": {
+        "node": ">=6.14.2"
+      }
+    },
+    "node_modules/c32check": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/c32check/-/c32check-2.0.0.tgz",
+      "integrity": "sha512-rpwfAcS/CMqo0oCqDf3r9eeLgScRE3l/xHDCXhM3UyrfvIn7PrLq63uHh7yYbv8NzaZn5MVsVhIRpQ+5GZ5HyA==",
+      "license": "MIT",
+      "dependencies": {
+        "@noble/hashes": "^1.1.2",
+        "base-x": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cac": {
+      "version": "6.7.14",
+      "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+      "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/cacheable-lookup": {
+      "version": "5.0.4",
+      "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
+      "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.6.0"
+      }
+    },
+    "node_modules/cacheable-request": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
+      "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
+      "license": "MIT",
+      "dependencies": {
+        "clone-response": "^1.0.2",
+        "get-stream": "^5.1.0",
+        "http-cache-semantics": "^4.0.0",
+        "keyv": "^4.0.0",
+        "lowercase-keys": "^2.0.0",
+        "normalize-url": "^6.0.1",
+        "responselike": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/capability": {
+      "version": "0.2.5",
+      "resolved": "https://registry.npmjs.org/capability/-/capability-0.2.5.tgz",
+      "integrity": "sha512-rsJZYVCgXd08sPqwmaIqjAd5SUTfonV0z/gDJ8D6cN8wQphky1kkAYEqQ+hmDxTw7UihvBfjUVUSY+DBEe44jg==",
+      "license": "MIT"
+    },
+    "node_modules/chai": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.1.tgz",
+      "integrity": "sha512-5nFxhUrX0PqtyogoYOA8IPswy5sZFTOsBFl/9bNsmDLgsxYTzSZQJDPppDnZPTQbzSEm0hqGjWPzRemQCYbD6A==",
+      "license": "MIT",
+      "dependencies": {
+        "assertion-error": "^2.0.1",
+        "check-error": "^2.1.1",
+        "deep-eql": "^5.0.1",
+        "loupe": "^3.1.0",
+        "pathval": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/chalk": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+      "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/check-error": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
+      "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 16"
+      }
+    },
+    "node_modules/cipher-base": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.6.tgz",
+      "integrity": "sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==",
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.4",
+        "safe-buffer": "^5.2.1"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "license": "ISC",
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/clone-response": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
+      "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
+      "license": "MIT",
+      "dependencies": {
+        "mimic-response": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "license": "MIT",
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "license": "MIT"
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/commander": {
+      "version": "12.1.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+      "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=18"
+      }
+    },
+    "node_modules/concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "license": "MIT"
+    },
+    "node_modules/config": {
+      "version": "3.3.12",
+      "resolved": "https://registry.npmjs.org/config/-/config-3.3.12.tgz",
+      "integrity": "sha512-Vmx389R/QVM3foxqBzXO8t2tUikYZP64Q6vQxGrsMpREeJc/aWRnPRERXWsYzOHAumx/AOoILWe6nU3ZJL+6Sw==",
+      "license": "MIT",
+      "dependencies": {
+        "json5": "^2.2.3"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      }
+    },
+    "node_modules/copyfiles": {
+      "version": "2.4.1",
+      "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz",
+      "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "glob": "^7.0.5",
+        "minimatch": "^3.0.3",
+        "mkdirp": "^1.0.4",
+        "noms": "0.0.0",
+        "through2": "^2.0.1",
+        "untildify": "^4.0.0",
+        "yargs": "^16.1.0"
+      },
+      "bin": {
+        "copyfiles": "copyfiles",
+        "copyup": "copyfiles"
+      }
+    },
+    "node_modules/copyfiles/node_modules/cliui": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+      "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+      "license": "ISC",
+      "optional": true,
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.0",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "node_modules/copyfiles/node_modules/yargs": {
+      "version": "16.2.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+      "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "cliui": "^7.0.2",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.0",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^20.2.2"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/copyfiles/node_modules/yargs-parser": {
+      "version": "20.2.9",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+      "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+      "license": "ISC",
+      "optional": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/cosmjs-types": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/cosmjs-types/-/cosmjs-types-0.9.0.tgz",
+      "integrity": "sha512-MN/yUe6mkJwHnCFfsNPeCfXVhyxHYW6c/xDUzrSbBycYzw++XvWDMJArXp2pLdgD6FQ8DW79vkPjeNKVrXaHeQ==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/crc-32": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+      "license": "Apache-2.0",
+      "optional": true,
+      "bin": {
+        "crc32": "bin/crc32.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/create-hash": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
+      "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
+      "license": "MIT",
+      "dependencies": {
+        "cipher-base": "^1.0.1",
+        "inherits": "^2.0.1",
+        "md5.js": "^1.3.4",
+        "ripemd160": "^2.0.1",
+        "sha.js": "^2.4.0"
+      }
+    },
+    "node_modules/create-hmac": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
+      "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
+      "license": "MIT",
+      "dependencies": {
+        "cipher-base": "^1.0.3",
+        "create-hash": "^1.1.0",
+        "inherits": "^2.0.1",
+        "ripemd160": "^2.0.0",
+        "safe-buffer": "^5.0.1",
+        "sha.js": "^2.4.8"
+      }
+    },
+    "node_modules/create-require": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+      "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/cross-fetch": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz",
+      "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==",
+      "license": "MIT",
+      "dependencies": {
+        "node-fetch": "^2.7.0"
+      }
+    },
+    "node_modules/crypto-addr-codec": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/crypto-addr-codec/-/crypto-addr-codec-0.1.8.tgz",
+      "integrity": "sha512-GqAK90iLLgP3FvhNmHbpT3wR6dEdaM8hZyZtLX29SPardh3OA13RFLHDR6sntGCgRWOfiHqW6sIyohpNqOtV/g==",
+      "license": "MIT",
+      "dependencies": {
+        "base-x": "^3.0.8",
+        "big-integer": "1.6.36",
+        "blakejs": "^1.1.0",
+        "bs58": "^4.0.1",
+        "ripemd160-min": "0.0.6",
+        "safe-buffer": "^5.2.0",
+        "sha3": "^2.1.1"
+      }
+    },
+    "node_modules/crypto-addr-codec/node_modules/base-x": {
+      "version": "3.0.11",
+      "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.11.tgz",
+      "integrity": "sha512-xz7wQ8xDhdyP7tQxwdteLYeFfS68tSMNCZ/Y37WJ4bhGfKPpqEIlmIyueQHqOyoPhE6xNUqjzRr8ra0eF9VRvA==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/crypto-hash": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/crypto-hash/-/crypto-hash-1.3.0.tgz",
+      "integrity": "sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+      "license": "MIT"
+    },
+    "node_modules/debug": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+      "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.1.3"
+      },
+      "engines": {
+        "node": ">=6.0"
+      },
+      "peerDependenciesMeta": {
+        "supports-color": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/decimal.js": {
+      "version": "10.5.0",
+      "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz",
+      "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==",
+      "license": "MIT"
+    },
+    "node_modules/decompress-response": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+      "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+      "license": "MIT",
+      "dependencies": {
+        "mimic-response": "^3.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/decompress-response/node_modules/mimic-response": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+      "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/deep-eql": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
+      "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/defer-to-connect": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+      "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/define-data-property": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+      "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+      "license": "MIT",
+      "dependencies": {
+        "es-define-property": "^1.0.0",
+        "es-errors": "^1.3.0",
+        "gopd": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/define-properties": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+      "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+      "license": "MIT",
+      "dependencies": {
+        "define-data-property": "^1.0.1",
+        "has-property-descriptors": "^1.0.0",
+        "object-keys": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/delay": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz",
+      "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/depd": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+      "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/diff": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+      "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+      "dev": true,
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.3.1"
+      }
+    },
+    "node_modules/dot-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+      "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+      "license": "MIT",
+      "dependencies": {
+        "no-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/dotenv": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
+      "integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
+      "license": "BSD-2-Clause",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/drbg.js": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz",
+      "integrity": "sha512-F4wZ06PvqxYLFEZKkFxTDcns9oFNk34hvmJSEwdzsxVQ8YI5YaxtACgQatkYgv2VI2CFkUd2Y+xosPQnHv809g==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "browserify-aes": "^1.0.6",
+        "create-hash": "^1.1.2",
+        "create-hmac": "^1.1.4"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/eccrypto": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmjs.org/eccrypto/-/eccrypto-1.1.6.tgz",
+      "integrity": "sha512-d78ivVEzu7Tn0ZphUUaL43+jVPKTMPFGtmgtz1D0LrFn7cY3K8CdrvibuLz2AAkHBLKZtR8DMbB2ukRYFk987A==",
+      "hasInstallScript": true,
+      "license": "CC0-1.0",
+      "optional": true,
+      "dependencies": {
+        "acorn": "7.1.1",
+        "elliptic": "6.5.4",
+        "es6-promise": "4.2.8",
+        "nan": "2.14.0"
+      },
+      "optionalDependencies": {
+        "secp256k1": "3.7.1"
+      }
+    },
+    "node_modules/eccrypto/node_modules/bn.js": {
+      "version": "4.12.1",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
+      "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/eccrypto/node_modules/elliptic": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
+      "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "bn.js": "^4.11.9",
+        "brorand": "^1.1.0",
+        "hash.js": "^1.0.0",
+        "hmac-drbg": "^1.0.1",
+        "inherits": "^2.0.4",
+        "minimalistic-assert": "^1.0.1",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
+    },
+    "node_modules/eccrypto/node_modules/nan": {
+      "version": "2.14.0",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+      "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/eccrypto/node_modules/secp256k1": {
+      "version": "3.7.1",
+      "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.7.1.tgz",
+      "integrity": "sha512-1cf8sbnRreXrQFdH6qsg2H71Xw91fCCS9Yp021GnUNJzWJS/py96fS4lHbnTnouLp08Xj6jBoBB6V78Tdbdu5g==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "bindings": "^1.5.0",
+        "bip66": "^1.1.5",
+        "bn.js": "^4.11.8",
+        "create-hash": "^1.2.0",
+        "drbg.js": "^1.0.1",
+        "elliptic": "^6.4.1",
+        "nan": "^2.14.0",
+        "safe-buffer": "^5.1.2"
+      },
+      "engines": {
+        "node": ">=4.0.0"
+      }
+    },
+    "node_modules/elliptic": {
+      "version": "6.6.1",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz",
+      "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==",
+      "license": "MIT",
+      "dependencies": {
+        "bn.js": "^4.11.9",
+        "brorand": "^1.1.0",
+        "hash.js": "^1.0.0",
+        "hmac-drbg": "^1.0.1",
+        "inherits": "^2.0.4",
+        "minimalistic-assert": "^1.0.1",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
+    },
+    "node_modules/elliptic/node_modules/bn.js": {
+      "version": "4.12.1",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
+      "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
+      "license": "MIT"
+    },
+    "node_modules/emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "license": "MIT"
+    },
+    "node_modules/end-of-stream": {
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
+      "license": "MIT",
+      "dependencies": {
+        "once": "^1.4.0"
+      }
+    },
+    "node_modules/error-polyfill": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/error-polyfill/-/error-polyfill-0.1.3.tgz",
+      "integrity": "sha512-XHJk60ufE+TG/ydwp4lilOog549iiQF2OAPhkk9DdiYWMrltz5yhDz/xnKuenNwP7gy3dsibssO5QpVhkrSzzg==",
+      "license": "MIT",
+      "dependencies": {
+        "capability": "^0.2.5",
+        "o3": "^1.0.3",
+        "u3": "^0.1.1"
+      }
+    },
+    "node_modules/es-define-property": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-errors": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-module-lexer": {
+      "version": "1.7.0",
+      "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+      "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+      "license": "MIT"
+    },
+    "node_modules/es6-promise": {
+      "version": "4.2.8",
+      "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
+      "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
+      "license": "MIT"
+    },
+    "node_modules/es6-promisify": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+      "integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==",
+      "license": "MIT",
+      "dependencies": {
+        "es6-promise": "^4.0.3"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.17.18",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz",
+      "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/android-arm": "0.17.18",
+        "@esbuild/android-arm64": "0.17.18",
+        "@esbuild/android-x64": "0.17.18",
+        "@esbuild/darwin-arm64": "0.17.18",
+        "@esbuild/darwin-x64": "0.17.18",
+        "@esbuild/freebsd-arm64": "0.17.18",
+        "@esbuild/freebsd-x64": "0.17.18",
+        "@esbuild/linux-arm": "0.17.18",
+        "@esbuild/linux-arm64": "0.17.18",
+        "@esbuild/linux-ia32": "0.17.18",
+        "@esbuild/linux-loong64": "0.17.18",
+        "@esbuild/linux-mips64el": "0.17.18",
+        "@esbuild/linux-ppc64": "0.17.18",
+        "@esbuild/linux-riscv64": "0.17.18",
+        "@esbuild/linux-s390x": "0.17.18",
+        "@esbuild/linux-x64": "0.17.18",
+        "@esbuild/netbsd-x64": "0.17.18",
+        "@esbuild/openbsd-x64": "0.17.18",
+        "@esbuild/sunos-x64": "0.17.18",
+        "@esbuild/win32-arm64": "0.17.18",
+        "@esbuild/win32-ia32": "0.17.18",
+        "@esbuild/win32-x64": "0.17.18"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+      "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/estree-walker": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "^1.0.0"
+      }
+    },
+    "node_modules/eth-crypto": {
+      "version": "2.7.0",
+      "resolved": "https://registry.npmjs.org/eth-crypto/-/eth-crypto-2.7.0.tgz",
+      "integrity": "sha512-MWbDl7OAoBAjkF2a7tklffAJv68uDI/MGPJKontt460nldJ8/2xT4cQacS8sGa6XJlon4ux1nAVzRoa4GxspOQ==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@babel/runtime": "7.26.0",
+        "@ethereumjs/tx": "3.5.2",
+        "@types/bn.js": "5.1.6",
+        "eccrypto": "1.1.6",
+        "ethereumjs-util": "7.1.5",
+        "ethers": "5.7.2",
+        "secp256k1": "5.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/pubkey"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@babel/runtime": {
+      "version": "7.26.0",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
+      "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "regenerator-runtime": "^0.14.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/abi": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz",
+      "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/address": "^5.7.0",
+        "@ethersproject/bignumber": "^5.7.0",
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/constants": "^5.7.0",
+        "@ethersproject/hash": "^5.7.0",
+        "@ethersproject/keccak256": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0",
+        "@ethersproject/strings": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/abstract-provider": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz",
+      "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.7.0",
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/networks": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0",
+        "@ethersproject/transactions": "^5.7.0",
+        "@ethersproject/web": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/abstract-signer": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz",
+      "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/abstract-provider": "^5.7.0",
+        "@ethersproject/bignumber": "^5.7.0",
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/address": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz",
+      "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.7.0",
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/keccak256": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/rlp": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/base64": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz",
+      "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/basex": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz",
+      "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/bignumber": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz",
+      "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "bn.js": "^5.2.1"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/bytes": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz",
+      "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/logger": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/constants": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz",
+      "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/contracts": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz",
+      "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/abi": "^5.7.0",
+        "@ethersproject/abstract-provider": "^5.7.0",
+        "@ethersproject/abstract-signer": "^5.7.0",
+        "@ethersproject/address": "^5.7.0",
+        "@ethersproject/bignumber": "^5.7.0",
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/constants": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0",
+        "@ethersproject/transactions": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/hash": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz",
+      "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/abstract-signer": "^5.7.0",
+        "@ethersproject/address": "^5.7.0",
+        "@ethersproject/base64": "^5.7.0",
+        "@ethersproject/bignumber": "^5.7.0",
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/keccak256": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0",
+        "@ethersproject/strings": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/hdnode": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz",
+      "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/abstract-signer": "^5.7.0",
+        "@ethersproject/basex": "^5.7.0",
+        "@ethersproject/bignumber": "^5.7.0",
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/pbkdf2": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0",
+        "@ethersproject/sha2": "^5.7.0",
+        "@ethersproject/signing-key": "^5.7.0",
+        "@ethersproject/strings": "^5.7.0",
+        "@ethersproject/transactions": "^5.7.0",
+        "@ethersproject/wordlists": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/json-wallets": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz",
+      "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/abstract-signer": "^5.7.0",
+        "@ethersproject/address": "^5.7.0",
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/hdnode": "^5.7.0",
+        "@ethersproject/keccak256": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/pbkdf2": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0",
+        "@ethersproject/random": "^5.7.0",
+        "@ethersproject/strings": "^5.7.0",
+        "@ethersproject/transactions": "^5.7.0",
+        "aes-js": "3.0.0",
+        "scrypt-js": "3.0.1"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/keccak256": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz",
+      "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.7.0",
+        "js-sha3": "0.8.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/logger": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz",
+      "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/networks": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz",
+      "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/logger": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/pbkdf2": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz",
+      "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/sha2": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/properties": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz",
+      "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/logger": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/providers": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz",
+      "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/abstract-provider": "^5.7.0",
+        "@ethersproject/abstract-signer": "^5.7.0",
+        "@ethersproject/address": "^5.7.0",
+        "@ethersproject/base64": "^5.7.0",
+        "@ethersproject/basex": "^5.7.0",
+        "@ethersproject/bignumber": "^5.7.0",
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/constants": "^5.7.0",
+        "@ethersproject/hash": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/networks": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0",
+        "@ethersproject/random": "^5.7.0",
+        "@ethersproject/rlp": "^5.7.0",
+        "@ethersproject/sha2": "^5.7.0",
+        "@ethersproject/strings": "^5.7.0",
+        "@ethersproject/transactions": "^5.7.0",
+        "@ethersproject/web": "^5.7.0",
+        "bech32": "1.1.4",
+        "ws": "7.4.6"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/random": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz",
+      "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/rlp": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz",
+      "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/sha2": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz",
+      "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "hash.js": "1.1.7"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/signing-key": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz",
+      "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0",
+        "bn.js": "^5.2.1",
+        "elliptic": "6.5.4",
+        "hash.js": "1.1.7"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/signing-key/node_modules/elliptic": {
+      "version": "6.5.4",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz",
+      "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "bn.js": "^4.11.9",
+        "brorand": "^1.1.0",
+        "hash.js": "^1.0.0",
+        "hmac-drbg": "^1.0.1",
+        "inherits": "^2.0.4",
+        "minimalistic-assert": "^1.0.1",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/signing-key/node_modules/elliptic/node_modules/bn.js": {
+      "version": "4.12.1",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
+      "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/solidity": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz",
+      "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.7.0",
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/keccak256": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/sha2": "^5.7.0",
+        "@ethersproject/strings": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/strings": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz",
+      "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/constants": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/transactions": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz",
+      "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/address": "^5.7.0",
+        "@ethersproject/bignumber": "^5.7.0",
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/constants": "^5.7.0",
+        "@ethersproject/keccak256": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0",
+        "@ethersproject/rlp": "^5.7.0",
+        "@ethersproject/signing-key": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/units": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz",
+      "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bignumber": "^5.7.0",
+        "@ethersproject/constants": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/wallet": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz",
+      "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/abstract-provider": "^5.7.0",
+        "@ethersproject/abstract-signer": "^5.7.0",
+        "@ethersproject/address": "^5.7.0",
+        "@ethersproject/bignumber": "^5.7.0",
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/hash": "^5.7.0",
+        "@ethersproject/hdnode": "^5.7.0",
+        "@ethersproject/json-wallets": "^5.7.0",
+        "@ethersproject/keccak256": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0",
+        "@ethersproject/random": "^5.7.0",
+        "@ethersproject/signing-key": "^5.7.0",
+        "@ethersproject/transactions": "^5.7.0",
+        "@ethersproject/wordlists": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/web": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz",
+      "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/base64": "^5.7.0",
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0",
+        "@ethersproject/strings": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/@ethersproject/wordlists": {
+      "version": "5.7.0",
+      "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz",
+      "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/bytes": "^5.7.0",
+        "@ethersproject/hash": "^5.7.0",
+        "@ethersproject/logger": "^5.7.0",
+        "@ethersproject/properties": "^5.7.0",
+        "@ethersproject/strings": "^5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/bech32": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz",
+      "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/eth-crypto/node_modules/ethers": {
+      "version": "5.7.2",
+      "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz",
+      "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "@ethersproject/abi": "5.7.0",
+        "@ethersproject/abstract-provider": "5.7.0",
+        "@ethersproject/abstract-signer": "5.7.0",
+        "@ethersproject/address": "5.7.0",
+        "@ethersproject/base64": "5.7.0",
+        "@ethersproject/basex": "5.7.0",
+        "@ethersproject/bignumber": "5.7.0",
+        "@ethersproject/bytes": "5.7.0",
+        "@ethersproject/constants": "5.7.0",
+        "@ethersproject/contracts": "5.7.0",
+        "@ethersproject/hash": "5.7.0",
+        "@ethersproject/hdnode": "5.7.0",
+        "@ethersproject/json-wallets": "5.7.0",
+        "@ethersproject/keccak256": "5.7.0",
+        "@ethersproject/logger": "5.7.0",
+        "@ethersproject/networks": "5.7.1",
+        "@ethersproject/pbkdf2": "5.7.0",
+        "@ethersproject/properties": "5.7.0",
+        "@ethersproject/providers": "5.7.2",
+        "@ethersproject/random": "5.7.0",
+        "@ethersproject/rlp": "5.7.0",
+        "@ethersproject/sha2": "5.7.0",
+        "@ethersproject/signing-key": "5.7.0",
+        "@ethersproject/solidity": "5.7.0",
+        "@ethersproject/strings": "5.7.0",
+        "@ethersproject/transactions": "5.7.0",
+        "@ethersproject/units": "5.7.0",
+        "@ethersproject/wallet": "5.7.0",
+        "@ethersproject/web": "5.7.1",
+        "@ethersproject/wordlists": "5.7.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/node-addon-api": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
+      "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/eth-crypto/node_modules/secp256k1": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.1.tgz",
+      "integrity": "sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "elliptic": "^6.5.7",
+        "node-addon-api": "^5.0.0",
+        "node-gyp-build": "^4.2.0"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/eth-crypto/node_modules/ws": {
+      "version": "7.4.6",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+      "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=8.3.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": "^5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/ethereum-cryptography": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz",
+      "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/pbkdf2": "^3.0.0",
+        "@types/secp256k1": "^4.0.1",
+        "blakejs": "^1.1.0",
+        "browserify-aes": "^1.2.0",
+        "bs58check": "^2.1.2",
+        "create-hash": "^1.2.0",
+        "create-hmac": "^1.1.7",
+        "hash.js": "^1.1.7",
+        "keccak": "^3.0.0",
+        "pbkdf2": "^3.0.17",
+        "randombytes": "^2.1.0",
+        "safe-buffer": "^5.1.2",
+        "scrypt-js": "^3.0.0",
+        "secp256k1": "^4.0.1",
+        "setimmediate": "^1.0.5"
+      }
+    },
+    "node_modules/ethereumjs-abi": {
+      "version": "0.6.8",
+      "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz",
+      "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==",
+      "deprecated": "This library has been deprecated and usage is discouraged.",
+      "license": "MIT",
+      "dependencies": {
+        "bn.js": "^4.11.8",
+        "ethereumjs-util": "^6.0.0"
+      }
+    },
+    "node_modules/ethereumjs-abi/node_modules/@types/bn.js": {
+      "version": "4.11.6",
+      "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz",
+      "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
+    "node_modules/ethereumjs-abi/node_modules/bn.js": {
+      "version": "4.12.1",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
+      "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
+      "license": "MIT"
+    },
+    "node_modules/ethereumjs-abi/node_modules/ethereumjs-util": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz",
+      "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==",
+      "license": "MPL-2.0",
+      "dependencies": {
+        "@types/bn.js": "^4.11.3",
+        "bn.js": "^4.11.0",
+        "create-hash": "^1.1.2",
+        "elliptic": "^6.5.2",
+        "ethereum-cryptography": "^0.1.3",
+        "ethjs-util": "0.1.6",
+        "rlp": "^2.2.3"
+      }
+    },
+    "node_modules/ethereumjs-util": {
+      "version": "7.1.5",
+      "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz",
+      "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==",
+      "license": "MPL-2.0",
+      "dependencies": {
+        "@types/bn.js": "^5.1.0",
+        "bn.js": "^5.1.2",
+        "create-hash": "^1.1.2",
+        "ethereum-cryptography": "^0.1.3",
+        "rlp": "^2.2.4"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/ethers": {
+      "version": "5.8.0",
+      "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.8.0.tgz",
+      "integrity": "sha512-DUq+7fHrCg1aPDFCHx6UIPb3nmt2XMpM7Y/g2gLhsl3lIBqeAfOJIl1qEvRf2uq3BiKxmh6Fh5pfp2ieyek7Kg==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2"
+        },
+        {
+          "type": "individual",
+          "url": "https://www.buymeacoffee.com/ricmoo"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "@ethersproject/abi": "5.8.0",
+        "@ethersproject/abstract-provider": "5.8.0",
+        "@ethersproject/abstract-signer": "5.8.0",
+        "@ethersproject/address": "5.8.0",
+        "@ethersproject/base64": "5.8.0",
+        "@ethersproject/basex": "5.8.0",
+        "@ethersproject/bignumber": "5.8.0",
+        "@ethersproject/bytes": "5.8.0",
+        "@ethersproject/constants": "5.8.0",
+        "@ethersproject/contracts": "5.8.0",
+        "@ethersproject/hash": "5.8.0",
+        "@ethersproject/hdnode": "5.8.0",
+        "@ethersproject/json-wallets": "5.8.0",
+        "@ethersproject/keccak256": "5.8.0",
+        "@ethersproject/logger": "5.8.0",
+        "@ethersproject/networks": "5.8.0",
+        "@ethersproject/pbkdf2": "5.8.0",
+        "@ethersproject/properties": "5.8.0",
+        "@ethersproject/providers": "5.8.0",
+        "@ethersproject/random": "5.8.0",
+        "@ethersproject/rlp": "5.8.0",
+        "@ethersproject/sha2": "5.8.0",
+        "@ethersproject/signing-key": "5.8.0",
+        "@ethersproject/solidity": "5.8.0",
+        "@ethersproject/strings": "5.8.0",
+        "@ethersproject/transactions": "5.8.0",
+        "@ethersproject/units": "5.8.0",
+        "@ethersproject/wallet": "5.8.0",
+        "@ethersproject/web": "5.8.0",
+        "@ethersproject/wordlists": "5.8.0"
+      }
+    },
+    "node_modules/ethjs-util": {
+      "version": "0.1.6",
+      "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz",
+      "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==",
+      "license": "MIT",
+      "dependencies": {
+        "is-hex-prefixed": "1.0.0",
+        "strip-hex-prefix": "1.0.0"
+      },
+      "engines": {
+        "node": ">=6.5.0",
+        "npm": ">=3"
+      }
+    },
+    "node_modules/eventemitter3": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+      "license": "MIT"
+    },
+    "node_modules/evp_bytestokey": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
+      "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
+      "license": "MIT",
+      "dependencies": {
+        "md5.js": "^1.3.4",
+        "safe-buffer": "^5.1.1"
+      }
+    },
+    "node_modules/expect-type": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.0.tgz",
+      "integrity": "sha512-80F22aiJ3GLyVnS/B3HzgR6RelZVumzj9jkL0Rhz4h0xYbNW9PjlQz5h3J/SShErbXBc295vseR4/MIbVmUbeA==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/eyes": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
+      "integrity": "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==",
+      "engines": {
+        "node": "> 0.1.90"
+      }
+    },
+    "node_modules/fast-check": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.0.0.tgz",
+      "integrity": "sha512-aXLyLemZ7qhLNn2oq+YpjT2Xed21+i29WGAYuyrGbU4r8oinB3i4XR4e62O3NY6qmm5qHEDoc/7d+gMsri3AfA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/dubzzz"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fast-check"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "pure-rand": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12.17.0"
+      }
+    },
+    "node_modules/fast-stable-stringify": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz",
+      "integrity": "sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==",
+      "license": "MIT"
+    },
+    "node_modules/fastestsmallesttextencoderdecoder": {
+      "version": "1.0.22",
+      "resolved": "https://registry.npmjs.org/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz",
+      "integrity": "sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==",
+      "license": "CC0-1.0",
+      "peer": true
+    },
+    "node_modules/file-uri-to-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+      "license": "MIT"
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.9",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
+      "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "license": "MIT",
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "license": "ISC"
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "license": "ISC",
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/get-stream": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+      "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+      "license": "MIT",
+      "dependencies": {
+        "pump": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/glob": {
+      "version": "7.2.3",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+      "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+      "deprecated": "Glob versions prior to v9 are no longer supported",
+      "license": "ISC",
+      "dependencies": {
+        "fs.realpath": "^1.0.0",
+        "inflight": "^1.0.4",
+        "inherits": "2",
+        "minimatch": "^3.1.1",
+        "once": "^1.3.0",
+        "path-is-absolute": "^1.0.0"
+      },
+      "engines": {
+        "node": "*"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/isaacs"
+      }
+    },
+    "node_modules/globalthis": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+      "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+      "license": "MIT",
+      "dependencies": {
+        "define-properties": "^1.2.1",
+        "gopd": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/google-protobuf": {
+      "version": "3.21.4",
+      "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.4.tgz",
+      "integrity": "sha512-MnG7N936zcKTco4Jd2PX2U96Kf9PxygAPKBug+74LHzmHXmceN16MmRcdgZv+DGef/S9YvQAfRsNCn4cjf9yyQ==",
+      "license": "(BSD-3-Clause AND Apache-2.0)"
+    },
+    "node_modules/gopd": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/got": {
+      "version": "11.8.6",
+      "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
+      "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
+      "license": "MIT",
+      "dependencies": {
+        "@sindresorhus/is": "^4.0.0",
+        "@szmarczak/http-timer": "^4.0.5",
+        "@types/cacheable-request": "^6.0.1",
+        "@types/responselike": "^1.0.0",
+        "cacheable-lookup": "^5.0.3",
+        "cacheable-request": "^7.0.2",
+        "decompress-response": "^6.0.0",
+        "http2-wrapper": "^1.0.0-beta.5.2",
+        "lowercase-keys": "^2.0.0",
+        "p-cancelable": "^2.0.0",
+        "responselike": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=10.19.0"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/got?sponsor=1"
+      }
+    },
+    "node_modules/graphql": {
+      "version": "16.10.0",
+      "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.10.0.tgz",
+      "integrity": "sha512-AjqGKbDGUFRKIRCP9tCKiIGHyriz2oHEbPIbEtcSLSs4YjReZOIPQQWek4+6hjw62H9QShXHyaGivGiYVLeYFQ==",
+      "license": "MIT",
+      "engines": {
+        "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
+      }
+    },
+    "node_modules/graphql-tag": {
+      "version": "2.12.6",
+      "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz",
+      "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "peerDependencies": {
+        "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/has-property-descriptors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+      "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+      "license": "MIT",
+      "dependencies": {
+        "es-define-property": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/hash-base": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz",
+      "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==",
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.4",
+        "readable-stream": "^3.6.0",
+        "safe-buffer": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/hash.js": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
+      "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "minimalistic-assert": "^1.0.1"
+      }
+    },
+    "node_modules/hasown": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+      "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+      "license": "MIT",
+      "dependencies": {
+        "function-bind": "^1.1.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/hi-base32": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmjs.org/hi-base32/-/hi-base32-0.5.1.tgz",
+      "integrity": "sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA==",
+      "license": "MIT"
+    },
+    "node_modules/hmac-drbg": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+      "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
+      "license": "MIT",
+      "dependencies": {
+        "hash.js": "^1.0.3",
+        "minimalistic-assert": "^1.0.0",
+        "minimalistic-crypto-utils": "^1.0.1"
+      }
+    },
+    "node_modules/hoist-non-react-statics": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+      "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "react-is": "^16.7.0"
+      }
+    },
+    "node_modules/http-cache-semantics": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz",
+      "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==",
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/http-errors": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz",
+      "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==",
+      "license": "MIT",
+      "dependencies": {
+        "depd": "~1.1.2",
+        "inherits": "2.0.4",
+        "setprototypeof": "1.2.0",
+        "statuses": ">= 1.5.0 < 2",
+        "toidentifier": "1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/http-errors/node_modules/depd": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/http-status-codes": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz",
+      "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==",
+      "license": "MIT"
+    },
+    "node_modules/http2-wrapper": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
+      "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
+      "license": "MIT",
+      "dependencies": {
+        "quick-lru": "^5.1.1",
+        "resolve-alpn": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=10.19.0"
+      }
+    },
+    "node_modules/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==",
+      "license": "MIT",
+      "dependencies": {
+        "ms": "^2.0.0"
+      }
+    },
+    "node_modules/ieee754": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+      "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+      "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": "BSD-3-Clause"
+    },
+    "node_modules/inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+      "license": "ISC",
+      "dependencies": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "node_modules/inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "license": "ISC"
+    },
+    "node_modules/interpret": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
+      "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/is-core-module": {
+      "version": "2.16.1",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+      "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+      "license": "MIT",
+      "dependencies": {
+        "hasown": "^2.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-hex-prefixed": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz",
+      "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=6.5.0",
+        "npm": ">=3"
+      }
+    },
+    "node_modules/isarray": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+      "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/isomorphic-ws": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz",
+      "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==",
+      "license": "MIT",
+      "peerDependencies": {
+        "ws": "*"
+      }
+    },
+    "node_modules/jayson": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.3.tgz",
+      "integrity": "sha512-LtXh5aYZodBZ9Fc3j6f2w+MTNcnxteMOrb+QgIouguGOulWi0lieEkOUg+HkjjFs0DGoWDds6bi4E9hpNFLulQ==",
+      "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.5.10"
+      },
+      "bin": {
+        "jayson": "bin/jayson.js"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/jayson/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==",
+      "license": "MIT"
+    },
+    "node_modules/jayson/node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "license": "MIT"
+    },
+    "node_modules/js-base64": {
+      "version": "3.7.7",
+      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz",
+      "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/js-sha256": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.9.0.tgz",
+      "integrity": "sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==",
+      "license": "MIT"
+    },
+    "node_modules/js-sha3": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz",
+      "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==",
+      "license": "MIT"
+    },
+    "node_modules/js-sha512": {
+      "version": "0.8.0",
+      "resolved": "https://registry.npmjs.org/js-sha512/-/js-sha512-0.8.0.tgz",
+      "integrity": "sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ==",
+      "license": "MIT"
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+      "license": "MIT"
+    },
+    "node_modules/jscrypto": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/jscrypto/-/jscrypto-1.0.3.tgz",
+      "integrity": "sha512-lryZl0flhodv4SZHOqyb1bx5sKcJxj0VBo0Kzb4QMAg3L021IC9uGpl0RCZa+9KJwlRGSK2C80ITcwbe19OKLQ==",
+      "license": "MIT",
+      "bin": {
+        "jscrypto": "bin/cli.js"
+      }
+    },
+    "node_modules/json-bigint": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
+      "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
+      "license": "MIT",
+      "dependencies": {
+        "bignumber.js": "^9.0.0"
+      }
+    },
+    "node_modules/json-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+      "license": "MIT"
+    },
+    "node_modules/json-stringify-safe": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+      "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+      "license": "ISC"
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "license": "MIT",
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/jsonparse": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+      "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+      "engines": [
+        "node >= 0.2.0"
+      ],
+      "license": "MIT"
+    },
+    "node_modules/jsonschema": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.5.0.tgz",
+      "integrity": "sha512-K+A9hhqbn0f3pJX17Q/7H6yQfD/5OXgdrR5UE12gMXCiN9D5Xq2o5mddV2QEcX/bjla99ASsAAQUyMCCRWAEhw==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/JSONStream": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz",
+      "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==",
+      "license": "(MIT OR Apache-2.0)",
+      "dependencies": {
+        "jsonparse": "^1.2.0",
+        "through": ">=2.2.7 <3"
+      },
+      "bin": {
+        "JSONStream": "bin.js"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/keccak": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz",
+      "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "node-addon-api": "^2.0.0",
+        "node-gyp-build": "^4.2.0",
+        "readable-stream": "^3.6.0"
+      },
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
+    "node_modules/keccak256": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/keccak256/-/keccak256-1.0.6.tgz",
+      "integrity": "sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw==",
+      "license": "MIT",
+      "dependencies": {
+        "bn.js": "^5.2.0",
+        "buffer": "^6.0.3",
+        "keccak": "^3.0.2"
+      }
+    },
+    "node_modules/keyv": {
+      "version": "4.5.4",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+      "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+      "license": "MIT",
+      "dependencies": {
+        "json-buffer": "3.0.1"
+      }
+    },
+    "node_modules/libsodium": {
+      "version": "0.7.15",
+      "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.15.tgz",
+      "integrity": "sha512-sZwRknt/tUpE2AwzHq3jEyUU5uvIZHtSssktXq7owd++3CSgn8RGrv6UZJJBpP7+iBghBqe7Z06/2M31rI2NKw==",
+      "license": "ISC"
+    },
+    "node_modules/libsodium-sumo": {
+      "version": "0.7.15",
+      "resolved": "https://registry.npmjs.org/libsodium-sumo/-/libsodium-sumo-0.7.15.tgz",
+      "integrity": "sha512-5tPmqPmq8T8Nikpm1Nqj0hBHvsLFCXvdhBFV7SGOitQPZAA6jso8XoL0r4L7vmfKXr486fiQInvErHtEvizFMw==",
+      "license": "ISC"
+    },
+    "node_modules/libsodium-wrappers": {
+      "version": "0.7.15",
+      "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.15.tgz",
+      "integrity": "sha512-E4anqJQwcfiC6+Yrl01C1m8p99wEhLmJSs0VQqST66SbQXXBoaJY0pF4BNjRYa/sOQAxx6lXAaAFIlx+15tXJQ==",
+      "license": "ISC",
+      "dependencies": {
+        "libsodium": "^0.7.15"
+      }
+    },
+    "node_modules/libsodium-wrappers-sumo": {
+      "version": "0.7.15",
+      "resolved": "https://registry.npmjs.org/libsodium-wrappers-sumo/-/libsodium-wrappers-sumo-0.7.15.tgz",
+      "integrity": "sha512-aSWY8wKDZh5TC7rMvEdTHoyppVq/1dTSAeAR7H6pzd6QRT3vQWcT5pGwCotLcpPEOLXX6VvqihSPkpEhYAjANA==",
+      "license": "ISC",
+      "dependencies": {
+        "libsodium-sumo": "^0.7.15"
+      }
+    },
+    "node_modules/link-module-alias": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/link-module-alias/-/link-module-alias-1.2.0.tgz",
+      "integrity": "sha512-ahPjXepbSVKbahTB6LxR//VHm8HPfI+QQygCH+E82spBY4HR5VPJTvlhKBc9F7muVxnS6C1rRfoPOXAbWO/fyw==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "chalk": "^2.4.1"
+      },
+      "bin": {
+        "link-module-alias": "index.js"
+      },
+      "engines": {
+        "node": "> 8.0.0"
+      }
+    },
+    "node_modules/link-module-alias/node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/link-module-alias/node_modules/chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/link-module-alias/node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/link-module-alias/node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.clonedeep": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+      "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
+      "license": "MIT"
+    },
+    "node_modules/lodash.values": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.values/-/lodash.values-4.3.0.tgz",
+      "integrity": "sha512-r0RwvdCv8id9TUblb/O7rYPwVy6lerCbcawrfdo9iC/1t1wsNMJknO79WNBgwkH0hIeJ08jmvvESbFpNb4jH0Q==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "license": "MIT",
+      "dependencies": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      },
+      "bin": {
+        "loose-envify": "cli.js"
+      }
+    },
+    "node_modules/loupe": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.0.tgz",
+      "integrity": "sha512-2NCfZcT5VGVNX9mSZIxLRkEAegDGBpuQZBy13desuHeVORmBDyAET4TkJr4SjqQy3A8JDofMN6LpkK8Xcm/dlw==",
+      "license": "MIT"
+    },
+    "node_modules/lower-case": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+      "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/lowercase-keys": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+      "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.30.17",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+      "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+      "license": "MIT",
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.5.0"
+      }
+    },
+    "node_modules/make-error": {
+      "version": "1.3.6",
+      "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+      "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+      "dev": true,
+      "license": "ISC"
+    },
+    "node_modules/map-obj": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz",
+      "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/md5.js": {
+      "version": "1.3.5",
+      "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
+      "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
+      "license": "MIT",
+      "dependencies": {
+        "hash-base": "^3.0.0",
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.1.2"
+      }
+    },
+    "node_modules/micro-packed": {
+      "version": "0.7.3",
+      "resolved": "https://registry.npmjs.org/micro-packed/-/micro-packed-0.7.3.tgz",
+      "integrity": "sha512-2Milxs+WNC00TRlem41oRswvw31146GiSaoCT7s3Xi2gMUglW5QBeqlQaZeHr5tJx9nm3i57LNXPqxOOaWtTYg==",
+      "license": "MIT",
+      "dependencies": {
+        "@scure/base": "~1.2.5"
+      },
+      "funding": {
+        "url": "https://paulmillr.com/funding/"
+      }
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "license": "MIT",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mimic-response": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+      "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/minimalistic-assert": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
+      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
+      "license": "ISC"
+    },
+    "node_modules/minimalistic-crypto-utils": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+      "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==",
+      "license": "MIT"
+    },
+    "node_modules/minimatch": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+      "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+      "license": "ISC",
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/minimist": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+      "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/mkdirp": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+      "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+      "license": "MIT",
+      "optional": true,
+      "bin": {
+        "mkdirp": "bin/cmd.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/ms": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+      "license": "MIT"
+    },
+    "node_modules/mustache": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
+      "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
+      "license": "MIT",
+      "bin": {
+        "mustache": "bin/mustache"
+      }
+    },
+    "node_modules/nan": {
+      "version": "2.22.2",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.2.tgz",
+      "integrity": "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ==",
+      "license": "MIT"
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.11",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+      "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/near-api-js": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/near-api-js/-/near-api-js-1.1.0.tgz",
+      "integrity": "sha512-qYKv1mYsaDZc2uYndhS+ttDhR9+60qFc+ZjD6lWsAxr3ZskMjRwPffDGQZYhC7BRDQMe1HEbk6d5mf+TVm0Lqg==",
+      "license": "(MIT AND Apache-2.0)",
+      "dependencies": {
+        "bn.js": "5.2.1",
+        "borsh": "^0.7.0",
+        "bs58": "^4.0.0",
+        "depd": "^2.0.0",
+        "error-polyfill": "^0.1.3",
+        "http-errors": "^1.7.2",
+        "js-sha256": "^0.9.0",
+        "mustache": "^4.0.0",
+        "node-fetch": "^2.6.1",
+        "text-encoding-utf-8": "^1.0.2",
+        "tweetnacl": "^1.0.1"
+      }
+    },
+    "node_modules/near-hd-key": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/near-hd-key/-/near-hd-key-1.2.1.tgz",
+      "integrity": "sha512-SIrthcL5Wc0sps+2e1xGj3zceEa68TgNZDLuCx0daxmfTP7sFTB3/mtE2pYhlFsCxWoMn+JfID5E1NlzvvbRJg==",
+      "license": "MIT",
+      "dependencies": {
+        "bip39": "3.0.2",
+        "create-hmac": "1.1.7",
+        "tweetnacl": "1.0.3"
+      }
+    },
+    "node_modules/near-hd-key/node_modules/@types/node": {
+      "version": "11.11.6",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz",
+      "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==",
+      "license": "MIT"
+    },
+    "node_modules/near-hd-key/node_modules/bip39": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.2.tgz",
+      "integrity": "sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ==",
+      "license": "ISC",
+      "dependencies": {
+        "@types/node": "11.11.6",
+        "create-hash": "^1.1.0",
+        "pbkdf2": "^3.0.9",
+        "randombytes": "^2.0.1"
+      }
+    },
+    "node_modules/near-seed-phrase": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/near-seed-phrase/-/near-seed-phrase-0.2.1.tgz",
+      "integrity": "sha512-feMuums+kVL3LSuPcP4ld07xHCb2mu6z48SGfP3W+8tl1Qm5xIcjiQzY2IDPBvFgajRDxWSb8GzsRHoInazByw==",
+      "license": "MIT",
+      "dependencies": {
+        "bip39-light": "^1.0.7",
+        "bs58": "^4.0.1",
+        "near-hd-key": "^1.2.1",
+        "tweetnacl": "^1.0.2"
+      }
+    },
+    "node_modules/no-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+      "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+      "license": "MIT",
+      "dependencies": {
+        "lower-case": "^2.0.2",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/node-addon-api": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz",
+      "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==",
+      "license": "MIT"
+    },
+    "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==",
+      "license": "MIT",
+      "dependencies": {
+        "whatwg-url": "^5.0.0"
+      },
+      "engines": {
+        "node": "4.x || >=6.0.0"
+      },
+      "peerDependencies": {
+        "encoding": "^0.1.0"
+      },
+      "peerDependenciesMeta": {
+        "encoding": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/node-gyp-build": {
+      "version": "4.8.4",
+      "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz",
+      "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==",
+      "license": "MIT",
+      "bin": {
+        "node-gyp-build": "bin.js",
+        "node-gyp-build-optional": "optional.js",
+        "node-gyp-build-test": "build-test.js"
+      }
+    },
+    "node_modules/noms": {
+      "version": "0.0.0",
+      "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz",
+      "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==",
+      "license": "ISC",
+      "optional": true,
+      "dependencies": {
+        "inherits": "^2.0.1",
+        "readable-stream": "~1.0.31"
+      }
+    },
+    "node_modules/noms/node_modules/readable-stream": {
+      "version": "1.0.34",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+      "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.1",
+        "isarray": "0.0.1",
+        "string_decoder": "~0.10.x"
+      }
+    },
+    "node_modules/noms/node_modules/string_decoder": {
+      "version": "0.10.31",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+      "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/normalize-url": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
+      "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/o3": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/o3/-/o3-1.0.3.tgz",
+      "integrity": "sha512-f+4n+vC6s4ysy7YO7O2gslWZBUu8Qj2i2OUJOvjRxQva7jVjYjB29jrr9NCjmxZQR0gzrOcv1RnqoYOeMs5VRQ==",
+      "license": "MIT",
+      "dependencies": {
+        "capability": "^0.2.5"
+      }
+    },
+    "node_modules/object-assign": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "license": "ISC",
+      "dependencies": {
+        "wrappy": "1"
+      }
+    },
+    "node_modules/optimism": {
+      "version": "0.18.1",
+      "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.1.tgz",
+      "integrity": "sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@wry/caches": "^1.0.0",
+        "@wry/context": "^0.7.0",
+        "@wry/trie": "^0.5.0",
+        "tslib": "^2.3.0"
+      }
+    },
+    "node_modules/p-cancelable": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
+      "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/pako": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
+      "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
+      "license": "(MIT AND Zlib)"
+    },
+    "node_modules/path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "license": "MIT"
+    },
+    "node_modules/pathe": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz",
+      "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==",
+      "license": "MIT"
+    },
+    "node_modules/pathval": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz",
+      "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 14.16"
+      }
+    },
+    "node_modules/pbkdf2": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz",
+      "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==",
+      "license": "MIT",
+      "dependencies": {
+        "create-hash": "^1.1.2",
+        "create-hmac": "^1.1.4",
+        "ripemd160": "^2.0.1",
+        "safe-buffer": "^5.0.1",
+        "sha.js": "^2.4.8"
+      },
+      "engines": {
+        "node": ">=0.12"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+      "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+      "license": "ISC"
+    },
+    "node_modules/postcss": {
+      "version": "8.5.6",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+      "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        },
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/ai"
+        }
+      ],
+      "license": "MIT",
+      "dependencies": {
+        "nanoid": "^3.3.11",
+        "picocolors": "^1.1.1",
+        "source-map-js": "^1.2.1"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/prop-types": {
+      "version": "15.8.1",
+      "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
+      "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
+      "license": "MIT",
+      "dependencies": {
+        "loose-envify": "^1.4.0",
+        "object-assign": "^4.1.1",
+        "react-is": "^16.13.1"
+      }
+    },
+    "node_modules/protobufjs": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
+      "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/node": ">=13.7.0",
+        "long": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/protobufjs/node_modules/long": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/long/-/long-5.3.1.tgz",
+      "integrity": "sha512-ka87Jz3gcx/I7Hal94xaN2tZEOPoUOEVftkQqZx2EeQRN7LGdfLlI3FvZ+7WDplm+vK2Urx9ULrvSowtdCieng==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "license": "MIT"
+    },
+    "node_modules/pump": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz",
+      "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==",
+      "license": "MIT",
+      "dependencies": {
+        "end-of-stream": "^1.1.0",
+        "once": "^1.3.1"
+      }
+    },
+    "node_modules/pure-rand": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz",
+      "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/dubzzz"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fast-check"
+        }
+      ],
+      "license": "MIT"
+    },
+    "node_modules/quick-lru": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+      "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "node_modules/react-is": {
+      "version": "16.13.1",
+      "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+      "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+      "license": "MIT"
+    },
+    "node_modules/readable-stream": {
+      "version": "3.6.2",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+      "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+      "license": "MIT",
+      "dependencies": {
+        "inherits": "^2.0.3",
+        "string_decoder": "^1.1.1",
+        "util-deprecate": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/readonly-date": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/readonly-date/-/readonly-date-1.0.0.tgz",
+      "integrity": "sha512-tMKIV7hlk0h4mO3JTmmVuIlJVXjKk3Sep9Bf5OH0O+758ruuVkUy2J9SttDLm91IEX/WHlXPSpxMGjPj4beMIQ==",
+      "license": "Apache-2.0"
+    },
+    "node_modules/rechoir": {
+      "version": "0.6.2",
+      "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
+      "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
+      "dependencies": {
+        "resolve": "^1.1.6"
+      },
+      "engines": {
+        "node": ">= 0.10"
+      }
+    },
+    "node_modules/regenerator-runtime": {
+      "version": "0.14.1",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+      "license": "MIT"
+    },
+    "node_modules/rehackt": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz",
+      "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==",
+      "license": "MIT",
+      "peerDependencies": {
+        "@types/react": "*",
+        "react": "*"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/resolve": {
+      "version": "1.22.10",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+      "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+      "license": "MIT",
+      "dependencies": {
+        "is-core-module": "^2.16.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/resolve-alpn": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+      "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
+      "license": "MIT"
+    },
+    "node_modules/responselike": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
+      "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
+      "license": "MIT",
+      "dependencies": {
+        "lowercase-keys": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/ripemd160": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
+      "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
+      "license": "MIT",
+      "dependencies": {
+        "hash-base": "^3.0.0",
+        "inherits": "^2.0.1"
+      }
+    },
+    "node_modules/ripemd160-min": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/ripemd160-min/-/ripemd160-min-0.0.6.tgz",
+      "integrity": "sha512-+GcJgQivhs6S9qvLogusiTcS9kQUfgR75whKuy5jIhuiOfQuJ8fjqxV6EGD5duH1Y/FawFUMtMhyeq3Fbnib8A==",
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/rlp": {
+      "version": "2.2.7",
+      "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz",
+      "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==",
+      "license": "MPL-2.0",
+      "dependencies": {
+        "bn.js": "^5.2.0"
+      },
+      "bin": {
+        "rlp": "bin/rlp"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "4.46.2",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz",
+      "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/estree": "1.0.8"
+      },
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=18.0.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "@rollup/rollup-android-arm-eabi": "4.46.2",
+        "@rollup/rollup-android-arm64": "4.46.2",
+        "@rollup/rollup-darwin-arm64": "4.46.2",
+        "@rollup/rollup-darwin-x64": "4.46.2",
+        "@rollup/rollup-freebsd-arm64": "4.46.2",
+        "@rollup/rollup-freebsd-x64": "4.46.2",
+        "@rollup/rollup-linux-arm-gnueabihf": "4.46.2",
+        "@rollup/rollup-linux-arm-musleabihf": "4.46.2",
+        "@rollup/rollup-linux-arm64-gnu": "4.46.2",
+        "@rollup/rollup-linux-arm64-musl": "4.46.2",
+        "@rollup/rollup-linux-loongarch64-gnu": "4.46.2",
+        "@rollup/rollup-linux-ppc64-gnu": "4.46.2",
+        "@rollup/rollup-linux-riscv64-gnu": "4.46.2",
+        "@rollup/rollup-linux-riscv64-musl": "4.46.2",
+        "@rollup/rollup-linux-s390x-gnu": "4.46.2",
+        "@rollup/rollup-linux-x64-gnu": "4.46.2",
+        "@rollup/rollup-linux-x64-musl": "4.46.2",
+        "@rollup/rollup-win32-arm64-msvc": "4.46.2",
+        "@rollup/rollup-win32-ia32-msvc": "4.46.2",
+        "@rollup/rollup-win32-x64-msvc": "4.46.2",
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/rpc-websockets": {
+      "version": "7.11.2",
+      "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.11.2.tgz",
+      "integrity": "sha512-pL9r5N6AVHlMN/vT98+fcO+5+/UcPLf/4tq+WUaid/PPUGS/ttJ3y8e9IqmaWKtShNAysMSjkczuEA49NuV7UQ==",
+      "license": "LGPL-3.0-only",
+      "dependencies": {
+        "eventemitter3": "^4.0.7",
+        "uuid": "^8.3.2",
+        "ws": "^8.5.0"
+      },
+      "funding": {
+        "type": "paypal",
+        "url": "https://paypal.me/kozjak"
+      },
+      "optionalDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": "^5.0.2"
+      }
+    },
+    "node_modules/rpc-websockets/node_modules/ws": {
+      "version": "8.18.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
+      "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10.0.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": ">=5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/rxjs": {
+      "version": "7.8.2",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+      "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "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/scrypt-js": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz",
+      "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==",
+      "license": "MIT"
+    },
+    "node_modules/secp256k1": {
+      "version": "4.0.4",
+      "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.4.tgz",
+      "integrity": "sha512-6JfvwvjUOn8F/jUoBY2Q1v5WY5XS+rj8qSe0v8Y4ezH4InLgTEeOOPQsRll9OV429Pvo6BCHGavIyJfr3TAhsw==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "elliptic": "^6.5.7",
+        "node-addon-api": "^5.0.0",
+        "node-gyp-build": "^4.2.0"
+      },
+      "engines": {
+        "node": ">=18.0.0"
+      }
+    },
+    "node_modules/secp256k1/node_modules/node-addon-api": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
+      "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
+      "license": "MIT"
+    },
+    "node_modules/setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+      "license": "MIT"
+    },
+    "node_modules/setprototypeof": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+      "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+      "license": "ISC"
+    },
+    "node_modules/sha.js": {
+      "version": "2.4.11",
+      "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
+      "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
+      "license": "(MIT AND BSD-3-Clause)",
+      "dependencies": {
+        "inherits": "^2.0.1",
+        "safe-buffer": "^5.0.1"
+      },
+      "bin": {
+        "sha.js": "bin.js"
+      }
+    },
+    "node_modules/sha3": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/sha3/-/sha3-2.1.4.tgz",
+      "integrity": "sha512-S8cNxbyb0UGUM2VhRD4Poe5N58gJnJsLJ5vC7FYWGUmGhcsj4++WaIOBFVDxlG0W3To6xBuiRh+i0Qp2oNCOtg==",
+      "license": "MIT",
+      "dependencies": {
+        "buffer": "6.0.3"
+      }
+    },
+    "node_modules/shelljs": {
+      "version": "0.8.5",
+      "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
+      "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "glob": "^7.0.0",
+        "interpret": "^1.0.0",
+        "rechoir": "^0.6.2"
+      },
+      "bin": {
+        "shjs": "bin/shjs"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/shx": {
+      "version": "0.3.4",
+      "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz",
+      "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==",
+      "license": "MIT",
+      "dependencies": {
+        "minimist": "^1.2.3",
+        "shelljs": "^0.8.5"
+      },
+      "bin": {
+        "shx": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/siginfo": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+      "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+      "license": "ISC"
+    },
+    "node_modules/snake-case": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz",
+      "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==",
+      "license": "MIT",
+      "dependencies": {
+        "dot-case": "^3.0.4",
+        "tslib": "^2.0.3"
+      }
+    },
+    "node_modules/snakecase-keys": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-5.5.0.tgz",
+      "integrity": "sha512-r3kRtnoPu3FxGJ3fny6PKNnU3pteb29o6qAa0ugzhSseKNWRkw1dw8nIjXMyyKaU9vQxxVIE62Mb3bKbdrgpiw==",
+      "license": "MIT",
+      "dependencies": {
+        "map-obj": "^4.1.0",
+        "snake-case": "^3.0.4",
+        "type-fest": "^3.12.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+      "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+      "license": "BSD-3-Clause",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/stackback": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+      "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+      "license": "MIT"
+    },
+    "node_modules/statuses": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+      "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/std-env": {
+      "version": "3.8.1",
+      "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.1.tgz",
+      "integrity": "sha512-vj5lIj3Mwf9D79hBkltk5qmkFI+biIKWS2IBxEyEU3AX1tUf7AoL8nSazCOiiqQsGKIq01SClsKEzweu34uwvA==",
+      "license": "MIT"
+    },
+    "node_modules/store2": {
+      "version": "2.14.4",
+      "resolved": "https://registry.npmjs.org/store2/-/store2-2.14.4.tgz",
+      "integrity": "sha512-srTItn1GOvyvOycgxjAnPA63FZNwy0PTyUBFMHRM+hVFltAeoh0LmNBz9SZqUS9mMqGk8rfyWyXn3GH5ReJ8Zw==",
+      "license": "MIT"
+    },
+    "node_modules/string_decoder": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+      "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+      "license": "MIT",
+      "dependencies": {
+        "safe-buffer": "~5.2.0"
+      }
+    },
+    "node_modules/string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "license": "MIT",
+      "dependencies": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-regex": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/strip-hex-prefix": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz",
+      "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==",
+      "license": "MIT",
+      "dependencies": {
+        "is-hex-prefixed": "1.0.0"
+      },
+      "engines": {
+        "node": ">=6.5.0",
+        "npm": ">=3"
+      }
+    },
+    "node_modules/superstruct": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-1.0.4.tgz",
+      "integrity": "sha512-7JpaAoX2NGyoFlI9NBh66BQXGONc+uE+MRS5i2iOBKuS4e+ccgMDjATgZldkah+33DakBxDHiss9kvUcGAO8UQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/symbol-observable": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz",
+      "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/text-encoding-utf-8": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz",
+      "integrity": "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg=="
+    },
+    "node_modules/through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+      "license": "MIT"
+    },
+    "node_modules/through2": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
+      "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "readable-stream": "~2.3.6",
+        "xtend": "~4.0.1"
+      }
+    },
+    "node_modules/through2/node_modules/isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/through2/node_modules/readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/through2/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+      "license": "MIT",
+      "optional": true
+    },
+    "node_modules/through2/node_modules/string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "license": "MIT",
+      "optional": true,
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
+    "node_modules/tiny-secp256k1": {
+      "version": "1.1.7",
+      "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.7.tgz",
+      "integrity": "sha512-eb+F6NabSnjbLwNoC+2o5ItbmP1kg7HliWue71JgLegQt6A5mTN8YbvTLCazdlg6e5SV6A+r8OGvZYskdlmhqQ==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "bindings": "^1.3.0",
+        "bn.js": "^4.11.8",
+        "create-hmac": "^1.1.7",
+        "elliptic": "^6.4.0",
+        "nan": "^2.13.2"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/tiny-secp256k1/node_modules/bn.js": {
+      "version": "4.12.1",
+      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz",
+      "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==",
+      "license": "MIT"
+    },
+    "node_modules/tinybench": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+      "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+      "license": "MIT"
+    },
+    "node_modules/tinyexec": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+      "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+      "license": "MIT"
+    },
+    "node_modules/tinypool": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz",
+      "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==",
+      "license": "MIT",
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      }
+    },
+    "node_modules/tinyrainbow": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz",
+      "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/tinyspy": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
+      "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.0.0"
+      }
+    },
+    "node_modules/tmp": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz",
+      "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=14.14"
+      }
+    },
+    "node_modules/toidentifier": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+      "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.6"
+      }
+    },
+    "node_modules/toml": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
+      "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
+      "license": "MIT"
+    },
+    "node_modules/tr46": {
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+      "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+      "license": "MIT"
+    },
+    "node_modules/ts-invariant": {
+      "version": "0.10.3",
+      "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz",
+      "integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==",
+      "license": "MIT",
+      "dependencies": {
+        "tslib": "^2.1.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/ts-node": {
+      "version": "10.9.2",
+      "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
+      "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+      "dev": true,
+      "license": "MIT",
+      "dependencies": {
+        "@cspotcode/source-map-support": "^0.8.0",
+        "@tsconfig/node10": "^1.0.7",
+        "@tsconfig/node12": "^1.0.7",
+        "@tsconfig/node14": "^1.0.0",
+        "@tsconfig/node16": "^1.0.2",
+        "acorn": "^8.4.1",
+        "acorn-walk": "^8.1.1",
+        "arg": "^4.1.0",
+        "create-require": "^1.1.0",
+        "diff": "^4.0.1",
+        "make-error": "^1.1.1",
+        "v8-compile-cache-lib": "^3.0.1",
+        "yn": "3.1.1"
+      },
+      "bin": {
+        "ts-node": "dist/bin.js",
+        "ts-node-cwd": "dist/bin-cwd.js",
+        "ts-node-esm": "dist/bin-esm.js",
+        "ts-node-script": "dist/bin-script.js",
+        "ts-node-transpile-only": "dist/bin-transpile.js",
+        "ts-script": "dist/bin-script-deprecated.js"
+      },
+      "peerDependencies": {
+        "@swc/core": ">=1.2.50",
+        "@swc/wasm": ">=1.2.50",
+        "@types/node": "*",
+        "typescript": ">=2.7"
+      },
+      "peerDependenciesMeta": {
+        "@swc/core": {
+          "optional": true
+        },
+        "@swc/wasm": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/ts-node/node_modules/acorn": {
+      "version": "8.14.1",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
+      "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
+      "dev": true,
+      "license": "MIT",
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.8.1",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+      "license": "0BSD"
+    },
+    "node_modules/tweetnacl": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz",
+      "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==",
+      "license": "Unlicense"
+    },
+    "node_modules/tweetnacl-util": {
+      "version": "0.15.1",
+      "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz",
+      "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==",
+      "license": "Unlicense"
+    },
+    "node_modules/type-fest": {
+      "version": "3.13.1",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz",
+      "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==",
+      "license": "(MIT OR CC0-1.0)",
+      "engines": {
+        "node": ">=14.16"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/typeforce": {
+      "version": "1.18.0",
+      "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz",
+      "integrity": "sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g==",
+      "license": "MIT"
+    },
+    "node_modules/typescript": {
+      "version": "5.9.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+      "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+      "license": "Apache-2.0",
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
+    "node_modules/u3": {
+      "version": "0.1.1",
+      "resolved": "https://registry.npmjs.org/u3/-/u3-0.1.1.tgz",
+      "integrity": "sha512-+J5D5ir763y+Am/QY6hXNRlwljIeRMZMGs0cT6qqZVVzzT3X3nFPXVyPOFRMOR4kupB0T8JnCdpWdp6Q/iXn3w==",
+      "license": "MIT"
+    },
+    "node_modules/undici-types": {
+      "version": "6.19.8",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+      "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
+      "license": "MIT"
+    },
+    "node_modules/untildify": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz",
+      "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/utf-8-validate": {
+      "version": "5.0.10",
+      "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
+      "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "dependencies": {
+        "node-gyp-build": "^4.3.0"
+      },
+      "engines": {
+        "node": ">=6.14.2"
+      }
+    },
+    "node_modules/util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "license": "MIT"
+    },
+    "node_modules/uuid": {
+      "version": "8.3.2",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+      "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+      "license": "MIT",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
+    "node_modules/v8-compile-cache-lib": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+      "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+      "dev": true,
+      "license": "MIT"
+    },
+    "node_modules/vite": {
+      "version": "5.4.19",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz",
+      "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==",
+      "license": "MIT",
+      "dependencies": {
+        "esbuild": "^0.21.3",
+        "postcss": "^8.4.43",
+        "rollup": "^4.20.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/vitejs/vite?sponsor=1"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.3"
+      },
+      "peerDependencies": {
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "less": "*",
+        "lightningcss": "^1.21.0",
+        "sass": "*",
+        "sass-embedded": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "lightningcss": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "sass-embedded": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vite-node": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz",
+      "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==",
+      "license": "MIT",
+      "dependencies": {
+        "cac": "^6.7.14",
+        "debug": "^4.3.7",
+        "es-module-lexer": "^1.5.4",
+        "pathe": "^1.1.2",
+        "vite": "^5.0.0"
+      },
+      "bin": {
+        "vite-node": "vite-node.mjs"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/android-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+      "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/android-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+      "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/android-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+      "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+      "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/darwin-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+      "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+      "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+      "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-arm": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+      "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+      "cpu": [
+        "arm"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+      "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+      "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+      "cpu": [
+        "ia32"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-loong64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+      "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+      "cpu": [
+        "loong64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+      "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+      "cpu": [
+        "mips64el"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+      "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+      "cpu": [
+        "ppc64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+      "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+      "cpu": [
+        "riscv64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-s390x": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+      "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+      "cpu": [
+        "s390x"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/linux-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+      "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+      "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/sunos-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+      "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/win32-arm64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+      "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+      "cpu": [
+        "arm64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/win32-ia32": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+      "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+      "cpu": [
+        "ia32"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/@esbuild/win32-x64": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+      "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+      "cpu": [
+        "x64"
+      ],
+      "license": "MIT",
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/vite/node_modules/esbuild": {
+      "version": "0.21.5",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
+      "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+      "hasInstallScript": true,
+      "license": "MIT",
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/aix-ppc64": "0.21.5",
+        "@esbuild/android-arm": "0.21.5",
+        "@esbuild/android-arm64": "0.21.5",
+        "@esbuild/android-x64": "0.21.5",
+        "@esbuild/darwin-arm64": "0.21.5",
+        "@esbuild/darwin-x64": "0.21.5",
+        "@esbuild/freebsd-arm64": "0.21.5",
+        "@esbuild/freebsd-x64": "0.21.5",
+        "@esbuild/linux-arm": "0.21.5",
+        "@esbuild/linux-arm64": "0.21.5",
+        "@esbuild/linux-ia32": "0.21.5",
+        "@esbuild/linux-loong64": "0.21.5",
+        "@esbuild/linux-mips64el": "0.21.5",
+        "@esbuild/linux-ppc64": "0.21.5",
+        "@esbuild/linux-riscv64": "0.21.5",
+        "@esbuild/linux-s390x": "0.21.5",
+        "@esbuild/linux-x64": "0.21.5",
+        "@esbuild/netbsd-x64": "0.21.5",
+        "@esbuild/openbsd-x64": "0.21.5",
+        "@esbuild/sunos-x64": "0.21.5",
+        "@esbuild/win32-arm64": "0.21.5",
+        "@esbuild/win32-ia32": "0.21.5",
+        "@esbuild/win32-x64": "0.21.5"
+      }
+    },
+    "node_modules/vitest": {
+      "version": "2.1.9",
+      "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz",
+      "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==",
+      "license": "MIT",
+      "dependencies": {
+        "@vitest/expect": "2.1.9",
+        "@vitest/mocker": "2.1.9",
+        "@vitest/pretty-format": "^2.1.9",
+        "@vitest/runner": "2.1.9",
+        "@vitest/snapshot": "2.1.9",
+        "@vitest/spy": "2.1.9",
+        "@vitest/utils": "2.1.9",
+        "chai": "^5.1.2",
+        "debug": "^4.3.7",
+        "expect-type": "^1.1.0",
+        "magic-string": "^0.30.12",
+        "pathe": "^1.1.2",
+        "std-env": "^3.8.0",
+        "tinybench": "^2.9.0",
+        "tinyexec": "^0.3.1",
+        "tinypool": "^1.0.1",
+        "tinyrainbow": "^1.2.0",
+        "vite": "^5.0.0",
+        "vite-node": "2.1.9",
+        "why-is-node-running": "^2.3.0"
+      },
+      "bin": {
+        "vitest": "vitest.mjs"
+      },
+      "engines": {
+        "node": "^18.0.0 || >=20.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/vitest"
+      },
+      "peerDependencies": {
+        "@edge-runtime/vm": "*",
+        "@types/node": "^18.0.0 || >=20.0.0",
+        "@vitest/browser": "2.1.9",
+        "@vitest/ui": "2.1.9",
+        "happy-dom": "*",
+        "jsdom": "*"
+      },
+      "peerDependenciesMeta": {
+        "@edge-runtime/vm": {
+          "optional": true
+        },
+        "@types/node": {
+          "optional": true
+        },
+        "@vitest/browser": {
+          "optional": true
+        },
+        "@vitest/ui": {
+          "optional": true
+        },
+        "happy-dom": {
+          "optional": true
+        },
+        "jsdom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vlq": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/vlq/-/vlq-2.0.4.tgz",
+      "integrity": "sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA==",
+      "license": "MIT"
+    },
+    "node_modules/webidl-conversions": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+      "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+      "license": "BSD-2-Clause"
+    },
+    "node_modules/whatwg-url": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+      "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+      "license": "MIT",
+      "dependencies": {
+        "tr46": "~0.0.3",
+        "webidl-conversions": "^3.0.0"
+      }
+    },
+    "node_modules/why-is-node-running": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+      "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+      "license": "MIT",
+      "dependencies": {
+        "siginfo": "^2.0.0",
+        "stackback": "0.0.2"
+      },
+      "bin": {
+        "why-is-node-running": "cli.js"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/wif": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz",
+      "integrity": "sha512-HIanZn1zmduSF+BQhkE+YXIbEiH0xPr1012QbFEGB0xsKqJii0/SqJjyn8dFv6y36kOznMgMB+LGcbZTJ1xACQ==",
+      "license": "MIT",
+      "dependencies": {
+        "bs58check": "<3.0.0"
+      }
+    },
+    "node_modules/wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "license": "MIT",
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+      }
+    },
+    "node_modules/wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "license": "ISC"
+    },
+    "node_modules/ws": {
+      "version": "7.5.10",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
+      "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=8.3.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": "^5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/xstream": {
+      "version": "11.14.0",
+      "resolved": "https://registry.npmjs.org/xstream/-/xstream-11.14.0.tgz",
+      "integrity": "sha512-1bLb+kKKtKPbgTK6i/BaoAn03g47PpFstlbe1BA+y3pNS/LfvcaghS5BFf9+EE1J+KwSQsEpfJvFN5GqFtiNmw==",
+      "license": "MIT",
+      "dependencies": {
+        "globalthis": "^1.0.1",
+        "symbol-observable": "^2.0.3"
+      }
+    },
+    "node_modules/xstream/node_modules/symbol-observable": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-2.0.3.tgz",
+      "integrity": "sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "license": "MIT",
+      "optional": true,
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
+    "node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/yargs": {
+      "version": "17.7.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+      "license": "MIT",
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "license": "ISC",
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/yn": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+      "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+      "dev": true,
+      "license": "MIT",
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/zen-observable": {
+      "version": "0.8.15",
+      "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz",
+      "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==",
+      "license": "MIT"
+    },
+    "node_modules/zen-observable-ts": {
+      "version": "1.2.5",
+      "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz",
+      "integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==",
+      "license": "MIT",
+      "dependencies": {
+        "zen-observable": "0.8.15"
+      }
+    }
+  }
+}

+ 34 - 0
stacks/test/package.json

@@ -0,0 +1,34 @@
+{
+  "name": "@wormhole-foundation/stacks-integration-test",
+  "version": "1.0.0",
+  "type": "module",
+  "scripts": {
+    "build": "tsc",
+    "clean": "rm -rf dist",
+    "test": "vitest",
+    "test:integration": "vitest integration.test.ts"
+  },
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "@certusone/wormhole-sdk-proto-node": "^0.0.7",
+    "@fast-check/vitest": "^0.2.1",
+    "@grpc/grpc-js": "^1.13.4",
+    "@noble/curves": "^1.9.2",
+    "@noble/hashes": "^1.8.0",
+    "@noble/secp256k1": "^2.3.0",
+    "@scure/base": "^1.2.6",
+    "@stacks/transactions": "^7.2.0",
+    "@wormhole-foundation/wormhole-cli": "^0.0.4",
+    "bigint-buffer": "^1.1.5",
+    "ethers": "^5",
+    "micro-packed": "^0.7.3"
+  },
+  "devDependencies": {
+    "@types/node": "^20.17.25",
+    "elliptic": "^6.6.1",
+    "ts-node": "^10.9.2",
+    "typescript": "^5.9.3",
+    "vitest": "^2.1.6"
+  }
+}

+ 17 - 0
stacks/test/vitest.config.ts

@@ -0,0 +1,17 @@
+import { defineConfig } from "vitest/config";
+
+export default defineConfig({
+  test: {
+    testTimeout: 3_600_000, // 1 hour for integration tests
+    hookTimeout: 3_600_000, // 1 hour for setup/teardown
+    globals: true,
+    environment: "node",
+    watch: false,
+    pool: "forks",
+    poolOptions: {
+      forks: {
+        singleFork: true,
+      },
+    },
+  },
+});