From ab2db7d09544ab53b07ef9d7de047ca158ba213b Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Mon, 24 Feb 2025 12:44:05 +0100 Subject: [PATCH 01/10] chore(target_chains/sui): vendor wormhole dependencies for iota --- .../vendor/wormhole_iota_testnet/.gitignore | 2 + .../vendor/wormhole_iota_testnet/Docker.md | 13 + .../vendor/wormhole_iota_testnet/Dockerfile | 33 + .../wormhole_iota_testnet/Dockerfile.base | 24 + .../sui/vendor/wormhole_iota_testnet/Makefile | 15 + .../sui/vendor/wormhole_iota_testnet/NOTES.md | 114 + .../vendor/wormhole_iota_testnet/README.md | 130 + .../devnet/127.0.0.1-36219.yaml | 125 + .../devnet/127.0.0.1-36853.yaml | 125 + .../devnet/127.0.0.1-39101.yaml | 125 + .../devnet/127.0.0.1-39187.yaml | 125 + .../wormhole_iota_testnet/devnet/client.yaml | 12 + .../devnet/fullnode.yaml | 107 + .../wormhole_iota_testnet/devnet/genesis.blob | Bin 0 -> 329923 bytes .../devnet/genesis_config | 55 + .../wormhole_iota_testnet/devnet/network.yaml | 500 ++ .../examples/coins/.gitignore | 1 + .../examples/coins/Makefile | 15 + .../examples/coins/Move.devnet.toml | 17 + .../examples/coins/Move.lock | 52 + .../examples/coins/Move.toml | 28 + .../examples/coins/sources/coin.move | 210 + .../examples/coins/sources/coin_10.move | 72 + .../examples/coins/sources/coin_8.move | 72 + .../examples/core_messages/Makefile | 20 + .../examples/core_messages/Move.devnet.toml | 14 + .../examples/core_messages/Move.lock | 39 + .../examples/core_messages/Move.toml | 21 + .../core_messages/sources/sender.move | 149 + .../examples/templates/README.md | 3 + .../examples/templates/wrapped_coin/Move.toml | 19 + .../templates/wrapped_coin/sources/coin.move | 21 + .../wormhole_iota_testnet/scripts/deploy.sh | 119 + .../scripts/node_builder.sh | 11 + .../scripts/register_devnet.sh | 22 + .../scripts/setup_rust.sh | 3 + .../scripts/start_node.sh | 5 + .../wormhole_iota_testnet/scripts/switch.sh | 31 + .../scripts/wait_for_devnet.sh | 6 + .../wormhole_iota_testnet/testing/.gitignore | 4 + .../wormhole_iota_testnet/testing/Makefile | 13 + .../testing/js/00_environment.ts | 78 + .../testing/js/01_wormhole.ts | 109 + .../testing/js/helpers/build.ts | 32 + .../testing/js/helpers/consts.ts | 40 + .../testing/js/helpers/error/moveAbort.ts | 42 + .../testing/js/helpers/error/wormhole.ts | 22 + .../testing/js/helpers/setup.ts | 75 + .../testing/js/helpers/upgrade.ts | 73 + .../testing/js/helpers/utils.ts | 27 + .../js/helpers/wormhole/testPublishMessage.ts | 31 + .../testing/package-lock.json | 5917 +++++++++++++++++ .../testing/package.json | 22 + .../testing/run_integration_test.sh | 35 + .../testing/scripts/upgrade-token-bridge.ts | 300 + .../testing/scripts/upgrade-wormhole.ts | 267 + .../testing/sui_config/client.yaml | 12 + .../testing/sui_config/fullnode.yaml | 53 + .../testing/sui_config/genesis.blob | Bin 0 -> 237155 bytes .../testing/sui_config/network.yaml | 324 + .../testing/sui_config/sui.keystore | 7 + .../sui_config/validator-config-0.yaml | 81 + .../sui_config/validator-config-1.yaml | 81 + .../sui_config/validator-config-2.yaml | 81 + .../sui_config/validator-config-3.yaml | 81 + .../testing/tsconfig.json | 12 + .../token_bridge/.gitignore | 1 + .../token_bridge/Makefile | 18 + .../token_bridge/Move.devnet.toml | 14 + .../token_bridge/Move.lock | 39 + .../token_bridge/Move.mainnet.toml | 15 + .../token_bridge/Move.testnet.toml | 15 + .../token_bridge/Move.toml | 21 + .../token_bridge/sources/attest_token.move | 385 ++ .../sources/complete_transfer.move | 1228 ++++ .../complete_transfer_with_payload.move | 776 +++ .../token_bridge/sources/create_wrapped.move | 643 ++ .../sources/datatypes/normalized_amount.move | 167 + .../sources/governance/register_chain.move | 297 + .../sources/governance/upgrade_contract.move | 125 + .../sources/messages/asset_meta.move | 251 + .../sources/messages/transfer.move | 311 + .../messages/transfer_with_payload.move | 352 + .../token_bridge/sources/migrate.move | 216 + .../sources/resources/native_asset.move | 220 + .../sources/resources/token_registry.move | 784 +++ .../sources/resources/wrapped_asset.move | 806 +++ .../token_bridge/sources/setup.move | 78 + .../token_bridge/sources/state.move | 396 ++ .../sources/test/coin_native_10.move | 165 + .../sources/test/coin_native_4.move | 165 + .../sources/test/coin_wrapped_12.move | 193 + .../sources/test/coin_wrapped_7.move | 194 + .../sources/test/dummy_message.move | 146 + .../sources/test/token_bridge_scenario.move | 136 + .../token_bridge/sources/transfer_tokens.move | 1053 +++ .../sources/transfer_tokens_with_payload.move | 812 +++ .../sources/utils/coin_utils.move | 48 + .../sources/utils/string_utils.move | 97 + .../token_bridge/sources/vaa.move | 351 + .../token_bridge/sources/version_control.move | 72 + .../wormhole_iota_testnet/wormhole/.gitignore | 1 + .../wormhole_iota_testnet/wormhole/Makefile | 18 + .../wormhole/Move.devnet.toml | 11 + .../wormhole_iota_testnet/wormhole/Move.lock | 27 + .../wormhole/Move.mainnet.toml | 12 + .../wormhole/Move.testnet.toml | 12 + .../wormhole_iota_testnet/wormhole/Move.toml | 12 + .../wormhole_iota_testnet/wormhole/README.md | 25 + .../wormhole/sources/datatypes/bytes20.move | 176 + .../wormhole/sources/datatypes/bytes32.move | 287 + .../sources/datatypes/external_address.move | 102 + .../sources/datatypes/guardian_signature.move | 64 + .../wormhole/sources/emitter.move | 183 + .../wormhole/sources/governance/set_fee.move | 343 + .../sources/governance/transfer_fee.move | 529 ++ .../governance/update_guardian_set.move | 471 ++ .../sources/governance/upgrade_contract.move | 126 + .../wormhole/sources/governance_message.move | 694 ++ .../wormhole/sources/migrate.move | 220 + .../wormhole/sources/publish_message.move | 426 ++ .../sources/resources/consumed_vaas.move | 29 + .../sources/resources/fee_collector.move | 170 + .../wormhole/sources/resources/guardian.move | 83 + .../sources/resources/guardian_set.move | 193 + .../wormhole/sources/resources/set.move | 88 + .../wormhole/sources/setup.move | 327 + .../wormhole/sources/state.move | 468 ++ .../sources/test/wormhole_scenario.move | 224 + .../wormhole/sources/utils/bytes.move | 232 + .../wormhole/sources/utils/cursor.move | 60 + .../wormhole/sources/utils/package_utils.move | 422 ++ .../wormhole/sources/vaa.move | 782 +++ .../wormhole/sources/version_control.move | 70 + 134 files changed, 27751 insertions(+) create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/.gitignore create mode 100755 target_chains/sui/vendor/wormhole_iota_testnet/Docker.md create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/Dockerfile create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/Dockerfile.base create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/Makefile create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/NOTES.md create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/README.md create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36219.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36853.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39101.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39187.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/devnet/client.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/devnet/fullnode.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/devnet/genesis.blob create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/devnet/genesis_config create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/devnet/network.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/.gitignore create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/Makefile create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/Move.devnet.toml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/Move.lock create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/Move.toml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_10.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_8.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Makefile create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Move.devnet.toml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Move.lock create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Move.toml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/sources/sender.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/README.md create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/Move.toml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/sources/coin.move create mode 100755 target_chains/sui/vendor/wormhole_iota_testnet/scripts/deploy.sh create mode 100755 target_chains/sui/vendor/wormhole_iota_testnet/scripts/node_builder.sh create mode 100755 target_chains/sui/vendor/wormhole_iota_testnet/scripts/register_devnet.sh create mode 100755 target_chains/sui/vendor/wormhole_iota_testnet/scripts/setup_rust.sh create mode 100755 target_chains/sui/vendor/wormhole_iota_testnet/scripts/start_node.sh create mode 100755 target_chains/sui/vendor/wormhole_iota_testnet/scripts/switch.sh create mode 100755 target_chains/sui/vendor/wormhole_iota_testnet/scripts/wait_for_devnet.sh create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/.gitignore create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/Makefile create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/js/00_environment.ts create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/js/01_wormhole.ts create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/build.ts create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/consts.ts create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/error/moveAbort.ts create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/error/wormhole.ts create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/setup.ts create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/upgrade.ts create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/utils.ts create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/wormhole/testPublishMessage.ts create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/package-lock.json create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/package.json create mode 100755 target_chains/sui/vendor/wormhole_iota_testnet/testing/run_integration_test.sh create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/scripts/upgrade-token-bridge.ts create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/scripts/upgrade-wormhole.ts create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/client.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/fullnode.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/genesis.blob create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/network.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/sui.keystore create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/validator-config-0.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/validator-config-1.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/validator-config-2.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/validator-config-3.yaml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/testing/tsconfig.json create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/.gitignore create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/Makefile create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/Move.devnet.toml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/Move.lock create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/Move.mainnet.toml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/Move.testnet.toml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/Move.toml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/attest_token.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer_with_payload.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/create_wrapped.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/datatypes/normalized_amount.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/register_chain.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/upgrade_contract.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/asset_meta.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer_with_payload.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/migrate.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/native_asset.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/token_registry.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/wrapped_asset.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/setup.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/state.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_10.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_4.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_12.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_7.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/dummy_message.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/token_bridge_scenario.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens_with_payload.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/coin_utils.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/string_utils.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/vaa.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/version_control.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/.gitignore create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Makefile create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.devnet.toml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.lock create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.mainnet.toml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.testnet.toml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.toml create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/README.md create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes20.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes32.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/external_address.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/guardian_signature.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/emitter.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/set_fee.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/transfer_fee.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/update_guardian_set.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/upgrade_contract.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance_message.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/migrate.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/publish_message.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/consumed_vaas.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/fee_collector.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian_set.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/set.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/setup.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/state.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/test/wormhole_scenario.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/bytes.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/cursor.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/package_utils.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/vaa.move create mode 100644 target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/version_control.move diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/.gitignore b/target_chains/sui/vendor/wormhole_iota_testnet/.gitignore new file mode 100644 index 0000000000..6ddb4b0ac3 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/.gitignore @@ -0,0 +1,2 @@ +deploy.out +sui.log.* diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/Docker.md b/target_chains/sui/vendor/wormhole_iota_testnet/Docker.md new file mode 100755 index 0000000000..97ff842f6d --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/Docker.md @@ -0,0 +1,13 @@ +# first build the image + +cd ..; DOCKER_BUILDKIT=1 docker build --no-cache --progress plain -f sui/Dockerfile.base -t sui . + +# tag the image with the appropriate version + +docker tag sui:latest ghcr.io/wormhole-foundation/sui:1.19.1-mainnet + +# push to ghcr + +docker push ghcr.io/wormhole-foundation/sui:1.19.1-mainnet + +echo remember to update both Dockerfile and Dockerfile.export diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/Dockerfile b/target_chains/sui/vendor/wormhole_iota_testnet/Dockerfile new file mode 100644 index 0000000000..4bef3a3624 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/Dockerfile @@ -0,0 +1,33 @@ +FROM cli-gen AS cli-export +FROM const-gen AS const-export +FROM ghcr.io/wormhole-foundation/sui:1.19.1-mainnet@sha256:544a1b2aa5701fae25a19aed3c5e8c24e0caf7d1c9f511b6844d339a8f0b2a00 as sui + +# initial run +# COPY sui/devnet/genesis_config genesis_config +# RUN sui genesis -f --from-config genesis_config + +# subsequent runs after committing files from /root/.sui/sui_config/ +COPY sui/devnet/ /root/.sui/sui_config/ + +WORKDIR /tmp + +COPY sui/scripts/ scripts +COPY sui/wormhole/ wormhole +COPY sui/token_bridge/ token_bridge +COPY sui/examples/ examples +COPY sui/Makefile Makefile + +# Copy .env and CLI +COPY --from=const-export .env .env +COPY --from=cli-export clients/js /cli + +# Link `worm` +WORKDIR /cli + +RUN npm link + +FROM sui AS tests + +WORKDIR /tmp + +RUN --mount=type=cache,target=/root/.move,id=move_cache make test diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/Dockerfile.base b/target_chains/sui/vendor/wormhole_iota_testnet/Dockerfile.base new file mode 100644 index 0000000000..4c76dc016d --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/Dockerfile.base @@ -0,0 +1,24 @@ +FROM rust:1.62@sha256:5777f201f507075309c4d2d1c1e8d8219e654ae1de154c844341050016a64a0c as sui-node + +WORKDIR /tmp + +RUN curl -L https://github.com/MystenLabs/sui/releases/download/mainnet-v1.19.1/sui-mainnet-v1.19.1-ubuntu-x86_64.tgz > sui-mainnet-v1.19.1-ubuntu-x86_64.tgz +RUN echo "6a8cc96759760293143a00fe7031a5fea70d2dff5b98d18c0470c09555da63e0 sui-mainnet-v1.19.1-ubuntu-x86_64.tgz" | sha256sum -c --status + +RUN tar -xvf sui-mainnet-v1.19.1-ubuntu-x86_64.tgz +RUN mv target/release/sui-ubuntu-x86_64 /bin/sui +RUN mv target/release/sui-faucet-ubuntu-x86_64 /bin/sui-faucet +RUN mv target/release/sui-node-ubuntu-x86_64 /bin/sui-node + +RUN rm sui-mainnet-v1.19.1-ubuntu-x86_64.tgz + +RUN apt-get update +RUN apt-get install -y ca-certificates curl gnupg +RUN mkdir -p /etc/apt/keyrings +RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg + +ARG NODE_MAJOR=18 +RUN echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list + +RUN apt-get update +RUN apt-get install nodejs -y diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/Makefile b/target_chains/sui/vendor/wormhole_iota_testnet/Makefile new file mode 100644 index 0000000000..0ee0e5eb94 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/Makefile @@ -0,0 +1,15 @@ +TEST_CONTRACT_DIRS := wormhole token_bridge examples/coins examples/core_messages +CLEAN_CONTRACT_DIRS := wormhole token_bridge examples/coins examples/core_messages + +.PHONY: clean +clean: + $(foreach dir,$(TEST_CONTRACT_DIRS), make -C $(dir) $@ &&) true + +.PHONY: test +test: + $(foreach dir,$(TEST_CONTRACT_DIRS), make -C $(dir) $@ &&) true + +test-docker: + DOCKER_BUILDKIT=1 docker build --progress plain -f ../Dockerfile.cli -t cli-gen .. + DOCKER_BUILDKIT=1 docker build --build-arg num_guardians=1 --progress plain -f ../Dockerfile.const -t const-gen .. + DOCKER_BUILDKIT=1 docker build -f Dockerfile .. diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/NOTES.md b/target_chains/sui/vendor/wormhole_iota_testnet/NOTES.md new file mode 100644 index 0000000000..5677cfb924 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/NOTES.md @@ -0,0 +1,114 @@ +brew install cmake + + rustup install stable-x86_64-apple-darwin + #rustup target add stable-x86_64-apple-darwin + rustup target add x86_64-apple-darwin + +=== Building + + % ./node_builder.sh + +=== Running + + % ./start_node.sh + +# If you don't remember your newly generated address + + % sui client addresses + Showing 1 results. + 0x13b3cb89cf3226d3b860294fc75dc6c91f0c5ecf + +# Give yourself some money + + % scripts/faucet.sh `sui client addresses | tail -1` + +# Looking at the prefunded address + + % sui client objects --address 0x13b3cb89cf3226d3b860294fc75dc6c91f0c5ecf + +=== Boot tilt + +# fund our standard account + + We don't run a faucet since it doesn't always unlock the client LOCK files. So, instead we just steal a chunk of coins + from the default accounts created when the node was initialized. Once sui is showing as live... + +``` sh + % kubectl exec -it sui-0 -c sui-node -- /tmp/funder.sh +``` + +# getting into the sui k8s node (if you need to crawl around) + + kubectl exec -it sui-0 -c sui-node -- /bin/bash + kubectl exec -it guardian-0 -c guardiand -- /bin/bash + +# setup the client.yaml + +``` sh + % rm -rf $HOME/.sui + % sui keytool import "daughter exclude wheat pudding police weapon giggle taste space whip satoshi occur" ed25519 + % sui client +``` + point it at http://localhost:9000. The key you create doesn't matter. + +# edit $HOME/.sui/sui_config/client.yaml + +``` sh + sed -i -e 's/active_address.*/active_address: "0x13b3cb89cf3226d3b860294fc75dc6c91f0c5ecf"/' ~/.sui/sui_config/client.yaml +``` + + +# deploy the contract + +``` sh + % scripts/deploy.sh +``` + +# start the watcher + +``` sh + % . env.sh + % python3 tests/ws.py +``` + +# publish a message (different window) + +``` sh + % . env.sh + % scripts/publish_message.sh +``` + +== + +docker run -it -v `pwd`:`pwd` -w `pwd` --net=host ghcr.io/wormhole-foundation/sui:0.16.0 bash +dnf -y install git make + +``` sh + % rm -rf $HOME/.sui + % sui keytool import "daughter exclude wheat pudding police weapon giggle taste space whip satoshi occur" secp256k1 + % sui client +``` + +to get a new emitter + + kubectl exec -it sui-0 -c sui-node -- /tmp/funder.sh + scripts/deploy.sh + . env.sh + sui client call --function get_new_emitter --module wormhole --package $WORM_PACKAGE --gas-budget 20000 --args \"$WORM_STATE\" + + sui client objects + scripts/publish_message.sh 0x165ef7366c4267c6506bcf63d2419556f34f48d6 + + +curl -s -X POST -d '{"jsonrpc":"2.0", "id": 1, "method": "sui_getEvents", "params": [{"MoveEvent": "0xf4179152ab02e4212d7e7b20f37a9a86ab6d50fb::state::WormholeMessage"}, null, 10, true]}' -H 'Content-Type: application/json' http://127.0.0.1:9002 | jq + +curl -s -X POST -d '{"jsonrpc":"2.0", "id": 1, "method": "sui_getEvents", "params": [{"Transaction": "cL+uWFEVcQrkAiOxOJmaK7JmlOJdE3/8X5JFbJwBxCQ="}, null, 10, true]}' -H 'Content-Type: application/json' http://127.0.0.1:9002 | jq + +"txhash": "0x70bfae585115710ae40223b138999a2bb26694e25d137ffc5f92456c9c01c424", "txhash_b58": "8b8Bn8MUqAWeVz2BE5hMicC9KaRkV6UM4v1JLWGUjxcT", " +Digest: cL+uWFEVcQrkAiOxOJmaK7JmlOJdE3/8X5JFbJwBxCQ= + + kubectl exec -it guardian-0 -- /guardiand admin send-observation-request --socket /tmp/admin.sock 21 70bfae585115710ae40223b138999a2bb26694e25d137ffc5f92456c9c01c424 + +// curl -s -X POST -d '{"jsonrpc":"2.0", "id": 1, "method": "sui_getCommitteeInfo", "params": []}' -H 'Content-Type: application/json' http://127.0.0.1:9002 | jq + +// curl -s -X POST -d '{"jsonrpc":"2.0", "id": 1, "method": "sui_getLatestCheckpointSequenceNumber", "params": []}' -H 'Content-Type: application/json' http://127.0.0.1:9000 diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/README.md b/target_chains/sui/vendor/wormhole_iota_testnet/README.md new file mode 100644 index 0000000000..729a8c406c --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/README.md @@ -0,0 +1,130 @@ +# Wormhole on Sui + +This folder contains the reference implementation of the Wormhole cross-chain +messaging protocol smart contracts on the [Sui](https://mystenlabs.com/) +blockchain, implemented in the [Move](https://move-book.com/) programming +language. + +# Project structure + +The project is laid out as follows: + +- [wormhole](./wormhole) the core messaging layer +- [token_bridge](./token_bridge) the asset transfer layer +- [coin](./coin) a template for creating Wormhole wrapped coins + +# Installation + +Make sure your Cargo version is at least 1.65.0 and then follow the steps below: + +- https://docs.sui.io/build/install + +#https://docs.sui.io/guides/developer/getting-started/sui-install# Prerequisites + +Install the `Sui` CLI. This tool is used to compile the contracts and run the tests. + +```sh +cargo install --locked --git https://github.com/MystenLabs/sui.git --rev 041c5f2bae2fe52079e44b70514333532d69f4e6 sui +``` + +Some useful Sui CLI commands are + +- `sui start` to spin up a local network +- `rpc-server` to start a server for handling rpc calls + +Next, install the [worm](../clients/js/README.md) CLI tool by running + +```sh +wormhole/clients/js $ make install +``` + +`worm` is the swiss army knife for interacting with wormhole contracts on all +supported chains, and generating signed messages (VAAs) for testing. + +As an optional, but recommended step, install the +[move-analyzer](https://github.com/move-language/move/tree/main/language/move-analyzer) +Language Server (LSP): + +```sh +cargo install --git https://github.com/move-language/move.git move-analyzer --branch main --features "address32" +``` + +This installs the LSP backend which is then supported by most popular editors such as [emacs](https://github.com/emacs-lsp/lsp-mode), [vim](https://github.com/neoclide/coc.nvim), and even [vscode](https://marketplace.visualstudio.com/items?itemName=move.move-analyzer). + +
+ For emacs, you may need to add the following to your config file: + +```lisp +;; Move +(define-derived-mode move-mode rust-mode "Move" + :group 'move-mode) + +(add-to-list 'auto-mode-alist '("\\.move\\'" . move-mode)) + +(with-eval-after-load 'lsp-mode + (add-to-list 'lsp-language-id-configuration + '(move-mode . "move")) + + (lsp-register-client + (make-lsp-client :new-connection (lsp-stdio-connection "move-analyzer") + :activation-fn (lsp-activate-on "move") + :server-id 'move-analyzer))) +``` + +
+ +## Building & running tests + +The project uses a simple `make`-based build system for building and running +tests. Running `make test` in this directory will run the tests for each +contract. If you only want to run the tests for, say, the token bridge contract, +then you can run `make test` in the `token_bridge` directory, or run `make -C +token_bridge test` from this directory. + +Additionally, `make test-docker` runs the tests in a docker container which is +set up with all the necessary dependencies. This is the command that runs in CI. + +## Running a local validator and deploying the contracts to it + +Simply run + +```sh +worm start-validator sui +``` + +which will start a local sui validator with an RPC endpoint at `0.0.0.0:9000`. + +Once the validator is running, the contracts are ready to deploy. In the +[scripts](./scripts) directory, run + +```sh +scripts $ ./deploy.sh devnet +``` + +This will deploy the core contract and the token bridge. + +When you make a change to the contract, you can simply restart the validator and +run the deploy script again. + + + +# Implementation notes / coding practices + +In this section, we describe some of the implementation design decisions and +coding practices we converged on along the way. Note that the coding guidelines +are prescriptive rather than descriptive, and the goal is for the contracts to +ultimately follow these, but they might not during earlier development phases. + +### TODO diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36219.yaml b/target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36219.yaml new file mode 100644 index 0000000000..e489d5c3dc --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36219.yaml @@ -0,0 +1,125 @@ +--- +protocol-key-pair: + value: avYcyVgYMXTyaUYh9IRwLK0gSzl7YF6ZQDAbrS1Bhvo= +worker-key-pair: + value: AAvfYqj1HPsXmthZ1t2Uw19vU6tdhK48YAFgkhJ7P/sV +account-key-pair: + value: ABmHnCaxw0GWzW+1MZYfTDonS1wZsO8KO37SXgm6pqc6 +network-key-pair: + value: AEpJ6PVCvnrtaxREy8UNSiDwLPPrZMh12TbgELadmAHB +db-path: /root/.sui/sui_config/authorities_db/8dcff6d15504 +network-address: /ip4/127.0.0.1/tcp/36219/http +json-rpc-address: "127.0.0.1:37179" +enable-experimental-rest-api: true +metrics-address: "127.0.0.1:44423" +admin-interface-port: 35585 +consensus-config: + address: /ip4/127.0.0.1/tcp/35107/http + db-path: /root/.sui/sui_config/consensus_db/8dcff6d15504 + internal-worker-address: ~ + max-pending-transactions: ~ + max-submit-position: ~ + submit-delay-step-override-millis: ~ + narwhal-config: + header_num_of_batches_threshold: 32 + max_header_num_of_batches: 1000 + max_header_delay: 1000ms + min_header_delay: 500ms + gc_depth: 50 + sync_retry_delay: 5000ms + sync_retry_nodes: 3 + batch_size: 5000000 + max_batch_delay: 100ms + max_concurrent_requests: 500000 + prometheus_metrics: + socket_addr: /ip4/127.0.0.1/tcp/42177/http + network_admin_server: + primary_network_admin_server_port: 34745 + worker_network_admin_server_base_port: 43111 + anemo: + send_certificate_rate_limit: ~ + report_batch_rate_limit: ~ + request_batches_rate_limit: ~ +enable-event-processing: false +enable-index-processing: true +grpc-load-shed: ~ +grpc-concurrency-limit: 20000000000 +p2p-config: + listen-address: "127.0.0.1:41551" + external-address: /ip4/127.0.0.1/udp/41551 + state-sync: + checkpoint-content-timeout-ms: 10000 +genesis: + genesis-file-location: /root/.sui/sui_config/genesis.blob +authority-store-pruning-config: + num-latest-epoch-dbs-to-retain: 3 + epoch-db-pruning-period-secs: 3600 + num-epochs-to-retain: 0 + max-checkpoints-in-batch: 10 + max-transactions-in-batch: 1000 +end-of-epoch-broadcast-channel-capacity: 128 +checkpoint-executor-config: + checkpoint-execution-max-concurrency: 200 + local-execution-timeout-sec: 30 +db-checkpoint-config: + perform-db-checkpoints-at-epoch-end: false +indirect-objects-threshold: 18446744073709551615 +expensive-safety-check-config: + enable-epoch-sui-conservation-check: false + enable-deep-per-tx-sui-conservation-check: false + force-disable-epoch-sui-conservation-check: false + enable-state-consistency-check: false + force-disable-state-consistency-check: false + enable-secondary-index-checks: false +transaction-deny-config: + package-publish-disabled: false + package-upgrade-disabled: false + shared-object-disabled: false + user-transaction-disabled: false + receiving-objects-disabled: false + zklogin-sig-disabled: false + zklogin-disabled-providers: [] +certificate-deny-config: {} +state-debug-dump-config: {} +state-archive-write-config: + concurrency: 0 + use-for-pruning-watermark: false +state-archive-read-config: [] +state-snapshot-write-config: + concurrency: 0 +indexer-max-subscriptions: ~ +transaction-kv-store-read-config: + base-url: "" +jwk-fetch-interval-seconds: 3600 +zklogin-oauth-providers: + Mainnet: + - Facebook + - Google + - Twitch + Testnet: + - Facebook + - Google + - Twitch + Unknown: + - Apple + - Facebook + - Google + - Kakao + - Slack + - Twitch +authority-overload-config: + max-txn-age-in-queue: + secs: 1 + nanos: 0 + overload-monitor-interval: + secs: 10 + nanos: 0 + execution-queue-latency-soft-limit: + secs: 1 + nanos: 0 + execution-queue-latency-hard-limit: + secs: 10 + nanos: 0 + max-load-shedding-percentage: 95 + min-load-shedding-percentage-above-hard-limit: 50 + safe-transaction-ready-rate: 100 diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36853.yaml b/target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36853.yaml new file mode 100644 index 0000000000..ec936e990a --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36853.yaml @@ -0,0 +1,125 @@ +--- +protocol-key-pair: + value: OXnx3yM1C/ppgnDMx/o1d49fJs7E05kq11mXNae/O+I= +worker-key-pair: + value: AE4ZKvLhbIyoYlv0y7q7aPHyU/Jty/D1AzILgYUs4VqC +account-key-pair: + value: AEAh/lnBSwKKrazfLNz3J7DBu7W2EMuhcShk6MHJhxpT +network-key-pair: + value: AHdOWNkwAgBFMTlwVSGkhI4COGDX40frs5xOz72DHvNm +db-path: /root/.sui/sui_config/authorities_db/addeef94d898 +network-address: /ip4/127.0.0.1/tcp/36853/http +json-rpc-address: "127.0.0.1:34043" +enable-experimental-rest-api: true +metrics-address: "127.0.0.1:45007" +admin-interface-port: 36657 +consensus-config: + address: /ip4/127.0.0.1/tcp/45105/http + db-path: /root/.sui/sui_config/consensus_db/addeef94d898 + internal-worker-address: ~ + max-pending-transactions: ~ + max-submit-position: ~ + submit-delay-step-override-millis: ~ + narwhal-config: + header_num_of_batches_threshold: 32 + max_header_num_of_batches: 1000 + max_header_delay: 1000ms + min_header_delay: 500ms + gc_depth: 50 + sync_retry_delay: 5000ms + sync_retry_nodes: 3 + batch_size: 5000000 + max_batch_delay: 100ms + max_concurrent_requests: 500000 + prometheus_metrics: + socket_addr: /ip4/127.0.0.1/tcp/44505/http + network_admin_server: + primary_network_admin_server_port: 45567 + worker_network_admin_server_base_port: 43075 + anemo: + send_certificate_rate_limit: ~ + report_batch_rate_limit: ~ + request_batches_rate_limit: ~ +enable-event-processing: false +enable-index-processing: true +grpc-load-shed: ~ +grpc-concurrency-limit: 20000000000 +p2p-config: + listen-address: "127.0.0.1:37183" + external-address: /ip4/127.0.0.1/udp/37183 + state-sync: + checkpoint-content-timeout-ms: 10000 +genesis: + genesis-file-location: /root/.sui/sui_config/genesis.blob +authority-store-pruning-config: + num-latest-epoch-dbs-to-retain: 3 + epoch-db-pruning-period-secs: 3600 + num-epochs-to-retain: 0 + max-checkpoints-in-batch: 10 + max-transactions-in-batch: 1000 +end-of-epoch-broadcast-channel-capacity: 128 +checkpoint-executor-config: + checkpoint-execution-max-concurrency: 200 + local-execution-timeout-sec: 30 +db-checkpoint-config: + perform-db-checkpoints-at-epoch-end: false +indirect-objects-threshold: 18446744073709551615 +expensive-safety-check-config: + enable-epoch-sui-conservation-check: false + enable-deep-per-tx-sui-conservation-check: false + force-disable-epoch-sui-conservation-check: false + enable-state-consistency-check: false + force-disable-state-consistency-check: false + enable-secondary-index-checks: false +transaction-deny-config: + package-publish-disabled: false + package-upgrade-disabled: false + shared-object-disabled: false + user-transaction-disabled: false + receiving-objects-disabled: false + zklogin-sig-disabled: false + zklogin-disabled-providers: [] +certificate-deny-config: {} +state-debug-dump-config: {} +state-archive-write-config: + concurrency: 0 + use-for-pruning-watermark: false +state-archive-read-config: [] +state-snapshot-write-config: + concurrency: 0 +indexer-max-subscriptions: ~ +transaction-kv-store-read-config: + base-url: "" +jwk-fetch-interval-seconds: 3600 +zklogin-oauth-providers: + Mainnet: + - Facebook + - Google + - Twitch + Testnet: + - Facebook + - Google + - Twitch + Unknown: + - Apple + - Facebook + - Google + - Kakao + - Slack + - Twitch +authority-overload-config: + max-txn-age-in-queue: + secs: 1 + nanos: 0 + overload-monitor-interval: + secs: 10 + nanos: 0 + execution-queue-latency-soft-limit: + secs: 1 + nanos: 0 + execution-queue-latency-hard-limit: + secs: 10 + nanos: 0 + max-load-shedding-percentage: 95 + min-load-shedding-percentage-above-hard-limit: 50 + safe-transaction-ready-rate: 100 diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39101.yaml b/target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39101.yaml new file mode 100644 index 0000000000..9f5954a4d3 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39101.yaml @@ -0,0 +1,125 @@ +--- +protocol-key-pair: + value: CyNkjqNVr3HrHTH7f/NLs7u5lUHJzuPAw0PqMTD2y2s= +worker-key-pair: + value: AOuUqLZBJxwz++dkJA9sY0wvTykcCC6jSS3Jqz77IlRI +account-key-pair: + value: AEUws4dzsXHsai5hVbK1O8jWOpPAJjtzdJl32Vxvoj83 +network-key-pair: + value: ADGySwzr54kpKui4vTatL4CtV4/1ffyyHuZ6CMyzZPGI +db-path: /root/.sui/sui_config/authorities_db/b3fd5efb5c87 +network-address: /ip4/127.0.0.1/tcp/39101/http +json-rpc-address: "127.0.0.1:38815" +enable-experimental-rest-api: true +metrics-address: "127.0.0.1:32833" +admin-interface-port: 39835 +consensus-config: + address: /ip4/127.0.0.1/tcp/43831/http + db-path: /root/.sui/sui_config/consensus_db/b3fd5efb5c87 + internal-worker-address: ~ + max-pending-transactions: ~ + max-submit-position: ~ + submit-delay-step-override-millis: ~ + narwhal-config: + header_num_of_batches_threshold: 32 + max_header_num_of_batches: 1000 + max_header_delay: 1000ms + min_header_delay: 500ms + gc_depth: 50 + sync_retry_delay: 5000ms + sync_retry_nodes: 3 + batch_size: 5000000 + max_batch_delay: 100ms + max_concurrent_requests: 500000 + prometheus_metrics: + socket_addr: /ip4/127.0.0.1/tcp/40195/http + network_admin_server: + primary_network_admin_server_port: 45269 + worker_network_admin_server_base_port: 39967 + anemo: + send_certificate_rate_limit: ~ + report_batch_rate_limit: ~ + request_batches_rate_limit: ~ +enable-event-processing: false +enable-index-processing: true +grpc-load-shed: ~ +grpc-concurrency-limit: 20000000000 +p2p-config: + listen-address: "127.0.0.1:36503" + external-address: /ip4/127.0.0.1/udp/36503 + state-sync: + checkpoint-content-timeout-ms: 10000 +genesis: + genesis-file-location: /root/.sui/sui_config/genesis.blob +authority-store-pruning-config: + num-latest-epoch-dbs-to-retain: 3 + epoch-db-pruning-period-secs: 3600 + num-epochs-to-retain: 0 + max-checkpoints-in-batch: 10 + max-transactions-in-batch: 1000 +end-of-epoch-broadcast-channel-capacity: 128 +checkpoint-executor-config: + checkpoint-execution-max-concurrency: 200 + local-execution-timeout-sec: 30 +db-checkpoint-config: + perform-db-checkpoints-at-epoch-end: false +indirect-objects-threshold: 18446744073709551615 +expensive-safety-check-config: + enable-epoch-sui-conservation-check: false + enable-deep-per-tx-sui-conservation-check: false + force-disable-epoch-sui-conservation-check: false + enable-state-consistency-check: false + force-disable-state-consistency-check: false + enable-secondary-index-checks: false +transaction-deny-config: + package-publish-disabled: false + package-upgrade-disabled: false + shared-object-disabled: false + user-transaction-disabled: false + receiving-objects-disabled: false + zklogin-sig-disabled: false + zklogin-disabled-providers: [] +certificate-deny-config: {} +state-debug-dump-config: {} +state-archive-write-config: + concurrency: 0 + use-for-pruning-watermark: false +state-archive-read-config: [] +state-snapshot-write-config: + concurrency: 0 +indexer-max-subscriptions: ~ +transaction-kv-store-read-config: + base-url: "" +jwk-fetch-interval-seconds: 3600 +zklogin-oauth-providers: + Mainnet: + - Facebook + - Google + - Twitch + Testnet: + - Facebook + - Google + - Twitch + Unknown: + - Apple + - Facebook + - Google + - Kakao + - Slack + - Twitch +authority-overload-config: + max-txn-age-in-queue: + secs: 1 + nanos: 0 + overload-monitor-interval: + secs: 10 + nanos: 0 + execution-queue-latency-soft-limit: + secs: 1 + nanos: 0 + execution-queue-latency-hard-limit: + secs: 10 + nanos: 0 + max-load-shedding-percentage: 95 + min-load-shedding-percentage-above-hard-limit: 50 + safe-transaction-ready-rate: 100 diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39187.yaml b/target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39187.yaml new file mode 100644 index 0000000000..59b82dc21c --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39187.yaml @@ -0,0 +1,125 @@ +--- +protocol-key-pair: + value: VTDx4HjVmRBqdqBWg2zN+zcFE20io3CrBchGy/iV1lo= +worker-key-pair: + value: ACsedxHqp9Son+iep5m4+eKM+yMc8hYyqhrDJLUucJ+G +account-key-pair: + value: AAAujq3QBAO4JNOYeKBW5dMn+8N4zE4bEHx+Bv9Y5tKr +network-key-pair: + value: AOFPA8/e6v4OpU5U0308llf51JfsxVla/pclVq9Ztajb +db-path: /root/.sui/sui_config/authorities_db/99f25ef61f80 +network-address: /ip4/127.0.0.1/tcp/39187/http +json-rpc-address: "127.0.0.1:33519" +enable-experimental-rest-api: true +metrics-address: "127.0.0.1:33765" +admin-interface-port: 33957 +consensus-config: + address: /ip4/127.0.0.1/tcp/41413/http + db-path: /root/.sui/sui_config/consensus_db/99f25ef61f80 + internal-worker-address: ~ + max-pending-transactions: ~ + max-submit-position: ~ + submit-delay-step-override-millis: ~ + narwhal-config: + header_num_of_batches_threshold: 32 + max_header_num_of_batches: 1000 + max_header_delay: 1000ms + min_header_delay: 500ms + gc_depth: 50 + sync_retry_delay: 5000ms + sync_retry_nodes: 3 + batch_size: 5000000 + max_batch_delay: 100ms + max_concurrent_requests: 500000 + prometheus_metrics: + socket_addr: /ip4/127.0.0.1/tcp/35645/http + network_admin_server: + primary_network_admin_server_port: 44333 + worker_network_admin_server_base_port: 43351 + anemo: + send_certificate_rate_limit: ~ + report_batch_rate_limit: ~ + request_batches_rate_limit: ~ +enable-event-processing: false +enable-index-processing: true +grpc-load-shed: ~ +grpc-concurrency-limit: 20000000000 +p2p-config: + listen-address: "127.0.0.1:40869" + external-address: /ip4/127.0.0.1/udp/40869 + state-sync: + checkpoint-content-timeout-ms: 10000 +genesis: + genesis-file-location: /root/.sui/sui_config/genesis.blob +authority-store-pruning-config: + num-latest-epoch-dbs-to-retain: 3 + epoch-db-pruning-period-secs: 3600 + num-epochs-to-retain: 0 + max-checkpoints-in-batch: 10 + max-transactions-in-batch: 1000 +end-of-epoch-broadcast-channel-capacity: 128 +checkpoint-executor-config: + checkpoint-execution-max-concurrency: 200 + local-execution-timeout-sec: 30 +db-checkpoint-config: + perform-db-checkpoints-at-epoch-end: false +indirect-objects-threshold: 18446744073709551615 +expensive-safety-check-config: + enable-epoch-sui-conservation-check: false + enable-deep-per-tx-sui-conservation-check: false + force-disable-epoch-sui-conservation-check: false + enable-state-consistency-check: false + force-disable-state-consistency-check: false + enable-secondary-index-checks: false +transaction-deny-config: + package-publish-disabled: false + package-upgrade-disabled: false + shared-object-disabled: false + user-transaction-disabled: false + receiving-objects-disabled: false + zklogin-sig-disabled: false + zklogin-disabled-providers: [] +certificate-deny-config: {} +state-debug-dump-config: {} +state-archive-write-config: + concurrency: 0 + use-for-pruning-watermark: false +state-archive-read-config: [] +state-snapshot-write-config: + concurrency: 0 +indexer-max-subscriptions: ~ +transaction-kv-store-read-config: + base-url: "" +jwk-fetch-interval-seconds: 3600 +zklogin-oauth-providers: + Mainnet: + - Facebook + - Google + - Twitch + Testnet: + - Facebook + - Google + - Twitch + Unknown: + - Apple + - Facebook + - Google + - Kakao + - Slack + - Twitch +authority-overload-config: + max-txn-age-in-queue: + secs: 1 + nanos: 0 + overload-monitor-interval: + secs: 10 + nanos: 0 + execution-queue-latency-soft-limit: + secs: 1 + nanos: 0 + execution-queue-latency-hard-limit: + secs: 10 + nanos: 0 + max-load-shedding-percentage: 95 + min-load-shedding-percentage-above-hard-limit: 50 + safe-transaction-ready-rate: 100 diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/devnet/client.yaml b/target_chains/sui/vendor/wormhole_iota_testnet/devnet/client.yaml new file mode 100644 index 0000000000..c9302468b5 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/devnet/client.yaml @@ -0,0 +1,12 @@ +--- +keystore: + File: /root/.sui/sui_config/sui.keystore +envs: + - alias: localnet + rpc: "http://0.0.0.0:9000" + ws: ~ + - alias: devnet + rpc: "https://fullnode.devnet.sui.io:443" + ws: ~ +active_env: localnet +active_address: ~ diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/devnet/fullnode.yaml b/target_chains/sui/vendor/wormhole_iota_testnet/devnet/fullnode.yaml new file mode 100644 index 0000000000..c89b027b28 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/devnet/fullnode.yaml @@ -0,0 +1,107 @@ +--- +protocol-key-pair: + value: JowI/tZTaWZUl32KFepfJUNYnU+0BklUpSPuKEeFcm0= +worker-key-pair: + value: AGIw3mefR34IhvBwYUBAilZJ8Hl8IwJr/BeI1MYwalsR +account-key-pair: + value: AImtWzJTcrcQL00HzJqQy4y0i+XIfZIbK/rUyyaq0f56 +network-key-pair: + value: AGeBrH0F+ehSceQ1zOUs8r2Z2z3iEJ/3RlW0/dN+/dj0 +db-path: full_node_db/full_node_db/b10469f99d8b +network-address: /ip4/127.0.0.1/tcp/42193/http +json-rpc-address: "0.0.0.0:9000" +enable-experimental-rest-api: true +metrics-address: "127.0.0.1:38381" +admin-interface-port: 46743 +enable-event-processing: true +enable-index-processing: true +grpc-load-shed: ~ +grpc-concurrency-limit: ~ +p2p-config: + listen-address: "127.0.0.1:35253" + external-address: /ip4/127.0.0.1/udp/35253 + seed-peers: + - peer-id: e8064daeac1c8801ce3e594cae452f11b453ce616c81974c7b3395d9992a6b37 + address: /ip4/127.0.0.1/udp/40869 + - peer-id: edca3a352dc8953587ffd265df885351c842689252c141960afa0870dfee7439 + address: /ip4/127.0.0.1/udp/41551 + - peer-id: d5e061082e23e6bbe75d1f0fcfcb982967f84c700e2c558482990dde14d8fd1b + address: /ip4/127.0.0.1/udp/37183 + - peer-id: 0395788680d90c7debf0671c18a608018d5800322459c7bc35ce0ee49cf6eeba + address: /ip4/127.0.0.1/udp/36503 + state-sync: + checkpoint-content-timeout-ms: 10000 +genesis: + genesis: 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MDUCMDYCMDcCMDgCMDkCMGECMGICMGMCMGQCMGUCMGYCMTACMTECMTICMTMCMTQCMTUCMTYCMTcCMTgCMTkCMWECMWICMWMCMWQCMWUCMWYCMjACMjECMjICMjMCMjQCMjUCMjYCMjcCMjgCMjkCMmECMmICMmMCMmQCMmUCMmYCMzACMzECMzICMzMCMzQCMzUCMzYCMzcCMzgCMzkCM2ECM2ICM2MCM2QCM2UCM2YCNDACNDECNDICNDMCNDQCNDUCNDYCNDcCNDgCNDkCNGECNGICNGMCNGQCNGUCNGYCNTACNTECNTICNTMCNTQCNTUCNTYCNTcCNTgCNTkCNWECNWICNWMCNWQCNWUCNWYCNjACNjECNjICNjMCNjQCNjUCNjYCNjcCNjgCNjkCNmECNmICNmMCNmQCNmUCNmYCNzACNzECNzICNzMCNzQCNzUCNzYCNzcCNzgCNzkCN2ECN2ICN2MCN2QCN2UCN2YCODACODECODICODMCODQCODUCODYCODcCODgCODkCOGECOGICOGMCOGQCOGUCOGYCOTACOTECOTICOTMCOTQCOTUCOTYCOTcCOTgCOTkCOWECOWICOWMCOWQCOWUCOWYCYTACYTECYTICYTMCYTQCYTUCYTYCYTcCYTgCYTkCYWECYWICYWMCYWQCYWUCYWYCYjACYjECYjICYjMCYjQCYjUCYjYCYjcCYjgCYjkCYmECYmICYmMCYmQCYmUCYmYCYzACYzECYzICYzMCYzQCYzUCYzYCYzcCYzgCYzkCY2ECY2ICY2MCY2QCY2UCY2YCZDACZDECZDICZDMCZDQCZDUCZDYCZDcCZDgCZDkCZGECZGICZGMCZGQCZGUCZGYCZTACZTECZTICZTMCZTQCZTUCZTYCZTcCZTgCZTkCZWECZWICZWMCZWQCZWUCZWYCZjACZjECZjICZjMCZjQCZjUCZjYCZjcCZjgCZjkCZmECZmICZmMCZmQCZmUCZmYKAgEAAAEAAAIfBgAAAAAAAAAABwMOAEEBDAMMBAwCBwIMAQoCCgMjBB0FDg0EDgEOAAoCQgEUNEIAFDgACwIGAQAAAAAAAAAWDAIFCQsEAgEBAAAFLwYAAAAAAAAAAAcDDgBBAQwDDAQMAgoDBgIAAAAAAAAAGQYAAAAAAAAAACEEDgUQBwAnCgIKAyMELQUVDgAK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PzkQAQwKNCsdAQILPCQ6ADAmMSYvJggmLiYJJgQmDCYyJiwmOyYqJhAmNy43MDkxLSYrJicmEiY6DCU6AQYLAwEJAAEDAQsDAQkAAQsLAQkAAQYLCwEJAAEHCwMBCQABBwsLAQkAAQYLAAEJAAEGCwoBCQABBwsAAQkAAQcLCgEJAAILCgEJAAcIDwELAAEJAAELCgEJAAMHCwoBCQADBwgPAgcLCgEJAAsAAQkAAAIHCwABCQALAAEJAAMHCwABCQADBwgPAQoLAAEJAAEHCA8HCQACCgIKAgoCCwcBCBAHCA8CCwMBCQALAQEJAAMLAwEJAAsEAQkACwEBCQADBwsDAQkAAwcIDwIHCwMBCQADAgcLAwEJAAsAAQkABAcIDAcLBAEJAAUHCA8CBggMBQEBBAcLAwEJAAMFBwgPAwYLAwEJAAcLAQEJAAgIAwYLAwEJAAcLAQEJAAgGAQYLAQEJAAECAQgIAQgGAQsHAQgQAQkAAQgOAgcLCgEJAAMCBwsKAQkACwoBCQADAwMKCwABCQABBgkAAQoCAwsEAQkACwEBCQALAwEJAAELAQEJAAEIDQELBAEJAAELAgEJAAIHCwsBCQADAgcLCwEJAAsKAQkAAQgJBAcIDAMKAgUCCAkKAgEGCAkEBggMAwoCBQIJAAUBCBABCwcBCQAHQmFsYW5jZQRDb2luDENvaW5NZXRhZGF0YQ9DdXJyZW5jeUNyZWF0ZWQHRGVueUNhcAhEZW55TGlzdAJJRAZPcHRpb24VUmVndWxhdGVkQ29pbk1ldGFkYXRhBlN0cmluZwZTdXBwbHkLVHJlYXN1cnlDYXAJVHhDb250ZXh0CFR5cGVOYW1lA1VJRANVcmwDYWRkBWFzY2lpB2JhbGFuY2ULYmFsYW5jZV9tdXQEYnVybgRjb2luFGNvaW5fbWV0YWRhdGFfb2JqZWN0CGNvbnRhaW5zD2NyZWF0ZV9jdXJyZW5jeRljcmVhdGVfcmVndWxhdGVkX2N1cnJlbmN5DWNyZWF0ZV9zdXBwbHkIZGVjaW1hbHMPZGVjcmVhc2Vfc3VwcGx5BmRlbGV0ZQ9kZW55X2NhcF9vYmplY3QJZGVueV9saXN0DWRlbnlfbGlzdF9hZGQSZGVueV9saXN0X2NvbnRhaW5zEGRlbnlfbGlzdF9yZW1vdmULZGVzY3JpcHRpb24MZGVzdHJveV96ZXJvDWRpdmlkZV9pbnRvX24NZnJlZXplX29iamVjdAxmcm9tX2JhbGFuY2UMZ2V0X2RlY2ltYWxzD2dldF9kZXNjcmlwdGlvbgxnZXRfaWNvbl91cmwIZ2V0X25hbWUKZ2V0X3N5bWJvbBVnZXRfd2l0aF9vcmlnaW5hbF9pZHMIaWNvbl91cmwCaWQPaW5jcmVhc2Vfc3VwcGx5DGludG9fYmFsYW5jZQppbnRvX2J5dGVzC2ludG9fc3RyaW5nE2lzX29uZV90aW1lX3dpdG5lc3MMaXNfcHJpbWl0aXZlBGpvaW4EbWludBFtaW50X2FuZF90cmFuc2ZlcgxtaW50X2JhbGFuY2UEbmFtZQNuZXcKbmV3X3Vuc2FmZQZvYmplY3QGb3B0aW9uD3B1YmxpY190cmFuc2ZlcgNwdXQGcmVtb3ZlBHNvbWUFc3BsaXQGc3RyaW5nBnN1cHBseQxzdXBwbHlfaW1tdXQKc3VwcGx5X211dAxzdXBwbHlfdmFsdWUGc3ltYm9sBHRha2UMdG90YWxfc3VwcGx5CHRyYW5zZmVyFHRyZWFzdXJ5X2ludG9fc3VwcGx5CnR4X2NvbnRleHQJdHlwZV9uYW1lBXR5cGVzEnVwZGF0ZV9kZXNjcmlwdGlvbg91cGRhdGVfaWNvbl91cmwLdXBkYXRlX25hbWUNdXBkYXRlX3N5bWJvbAN1cmwEdXRmOAV2YWx1ZQR6ZXJvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQMIAAAAAAAAAAADCAEAAAAAAAAAAwgCAAAAAAAAAAACAi8IDhILCgEJAAECBi8IDhsCOggISQgGIwgILgsHAQgQAgIDLwgOFggNHggNAwICLwgOSwsLAQkABAIBLwgOBQIBGwIDJgAmASYEJgImAAEAABAECwA3ADgAAgEBAAADBgsAOgAMARE2CwECAgEAABADCwA3AAIDAQAAEAMLADYAAgQBAAAQBAsANwE4AQIFAQAAEAMLADcBAgYBAAAQAwsANgECBwEAABAFCwEROAsAOQECCAEAAA0GCwA6AQwBETYLAQIJAQAAEAcLAhE4CwALATgCOQECCgEAABAGCwALATgDOAQBAgsBBAANCgsBOgEM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cm93X211dApib3Jyb3dfdmFsEmNsb3NlX2FuZF93aXRoZHJhdwRjb2luB2RlZmF1bHQGZGVsZXRlBmRlbGlzdAxkZXN0cm95X3NvbWUNZHluYW1pY19maWVsZBRkeW5hbWljX29iamVjdF9maWVsZARlbWl0BWV2ZW50B2V4aXN0c18QZXhpc3RzX3dpdGhfdHlwZQNmb3IMZnJvbV9iYWxhbmNlCmhhc19hY2Nlc3MIaGFzX2l0ZW0SaGFzX2l0ZW1fd2l0aF90eXBlAmlkDGlzX2V4Y2x1c2l2ZQlpc19saXN0ZWQVaXNfbGlzdGVkX2V4Y2x1c2l2ZWx5CWlzX2xvY2tlZAdpc19zb21lCml0ZW1fY291bnQHaXRlbV9pZAVraW9zaw9raW9za19leHRlbnNpb24Ia2lvc2tfaWQTa2lvc2tfb3duZXJfY2FwX2ZvcgRsaXN0Fmxpc3Rfd2l0aF9wdXJjaGFzZV9jYXAEbG9jaw1sb2NrX2ludGVybmFsCW1pbl9wcmljZQNuZXcLbmV3X3JlcXVlc3QGb2JqZWN0Bm9wdGlvbgVvd25lcgVwbGFjZQ5wbGFjZV9hbmRfbGlzdA5wbGFjZV9pbnRlcm5hbAVwcmljZQdwcm9maXRzDnByb2ZpdHNfYW1vdW50C3Byb2ZpdHNfbXV0CHB1cmNoYXNlEXB1cmNoYXNlX2NhcF9pdGVtEnB1cmNoYXNlX2NhcF9raW9zaxZwdXJjaGFzZV9jYXBfbWluX3ByaWNlEXB1cmNoYXNlX3dpdGhfY2FwA3B1dAZyZW1vdmUQcmVtb3ZlX2lmX2V4aXN0cxNyZXR1cm5fcHVyY2hhc2VfY2FwCnJldHVybl92YWwGc2VuZGVyFHNldF9hbGxvd19leHRlbnNpb25zCXNldF9vd25lchBzZXRfb3duZXJfY3VzdG9tDHNoYXJlX29iamVjdANzdWkEdGFrZQh0cmFuc2Zlcg90cmFuc2Zlcl9wb2xpY3kKdHhfY29udGV4dAN1aWQHdWlkX211dBB1aWRfbXV0X2FzX293bmVyEHVpZF9tdXRfaW50ZXJuYWwMdWlkX3RvX2lubmVyBXZhbHVlCHdpdGhkcmF3BHplcm8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAA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CQICBggOCQACBwgOCQABBwkBAQsEAQkBAwcLEAIJAAkBCQAJAQIHCxACCQAJAQYJAAIJAAkBAggIBwsRAQgIAgcLEQEJAAYJAAIHCwoBCQADAgMLCQEJAAEKAg1BY3Rpb25SZXF1ZXN0B0JhbGFuY2UEQ29pbgJJRAZPcHRpb24HUnVsZUtleQZTdHJpbmcGU3VwcGx5BVRva2VuC1Rva2VuUG9saWN5DlRva2VuUG9saWN5Q2FwElRva2VuUG9saWN5Q3JlYXRlZAtUcmVhc3VyeUNhcAlUeENvbnRleHQIVHlwZU5hbWUDVUlEBlZlY01hcAZWZWNTZXQGYWN0aW9uA2FkZAxhZGRfYXBwcm92YWwPYWRkX3J1bGVfY29uZmlnE2FkZF9ydWxlX2Zvcl9hY3Rpb24FYWxsb3cGYW1vdW50CWFwcHJvdmFscwdiYWxhbmNlBmJvcnJvdwpib3Jyb3dfbXV0BGJ1cm4EY29pbg9jb25maXJtX3JlcXVlc3QTY29uZmlybV9yZXF1ZXN0X211dBdjb25maXJtX3dpdGhfcG9saWN5X2NhcBljb25maXJtX3dpdGhfdHJlYXN1cnlfY2FwCGNvbnRhaW5zD2RlY3JlYXNlX3N1cHBseQZkZWxldGUMZGVzdHJveV9ub25lDGRlc3Ryb3lfc29tZQxkZXN0cm95X3plcm8IZGlzYWxsb3cNZHluYW1pY19maWVsZARlbWl0BWVtcHR5BWV2ZW50B2V4aXN0c18QZXhpc3RzX3dpdGhfdHlwZQdleHRyYWN0BWZsdXNoA2Zvcgxmcm9tX2JhbGFuY2UJZnJvbV9jb2luEGZyb21fY29pbl9hY3Rpb24DZ2V0B2dldF9tdXQPaGFzX3J1bGVfY29uZmlnGWhhc19ydWxlX2NvbmZpZ193aXRoX3R5cGUCaWQPaW5jcmVhc2Vfc3VwcGx5Bmluc2VydAxpbnRvX2JhbGFuY2UJaW50b19rZXlzCmlzX2FsbG93ZWQKaXNfbXV0YWJsZQdpc19ub25lDGlzX3Byb3RlY3RlZAdpc19zb21lBGpvaW4Ea2VlcANrZXkEbWludARuYW1lA25ldwpuZXdfcG9saWN5C25ld19yZXF1ZXN0BG5vbmUGb2JqZWN0Bm9wdGlvbglyZWNpcGllbnQGcmVtb3ZlEnJlbW92ZV9y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BgkAAQcICAEIBwEGCAUBCAQBBggIAQUCCQAFAgEIBQIIBggGAQYIBgJJRAlQdWJsaXNoZXIGU3RyaW5nCVR4Q29udGV4dAhUeXBlTmFtZQNVSUQKVXBncmFkZUNhcA5VcGdyYWRlUmVjZWlwdA1VcGdyYWRlVGlja2V0D2FkZGl0aXZlX3BvbGljeQVhc2NpaRFhdXRob3JpemVfdXBncmFkZQ5idXJuX3B1Ymxpc2hlcgNjYXAFY2xhaW0OY2xhaW1fYW5kX2tlZXAOY29tbWl0X3VwZ3JhZGURY29tcGF0aWJsZV9wb2xpY3kGZGVsZXRlD2RlcF9vbmx5X3BvbGljeQZkaWdlc3QLZnJvbV9tb2R1bGUMZnJvbV9wYWNrYWdlC2dldF9hZGRyZXNzCmdldF9tb2R1bGUVZ2V0X3dpdGhfb3JpZ2luYWxfaWRzAmlkD2lkX2Zyb21fYWRkcmVzcw1pZF90b19hZGRyZXNzE2lzX29uZV90aW1lX3dpdG5lc3MObWFrZV9pbW11dGFibGULbW9kdWxlX25hbWUDbmV3Bm9iamVjdBZvbmx5X2FkZGl0aXZlX3VwZ3JhZGVzEW9ubHlfZGVwX3VwZ3JhZGVzB3BhY2thZ2UGcG9saWN5D3B1YmxpY190cmFuc2ZlchBwdWJsaXNoZWRfbW9kdWxlEXB1Ymxpc2hlZF9wYWNrYWdlC3JlY2VpcHRfY2FwD3JlY2VpcHRfcGFja2FnZQhyZXN0cmljdAZzZW5kZXINdGlja2V0X2RpZ2VzdA50aWNrZXRfcGFja2FnZQ10aWNrZXRfcG9saWN5CHRyYW5zZmVyCnR4X2NvbnRleHQJdHlwZV9uYW1lBXR5cGVzD3VwZ3JhZGVfcGFja2FnZQ51cGdyYWRlX3BvbGljeQd2ZXJzaW9uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQMIAAAAAAAAAAADCAEAAAAAAAAAAwgCAAAAAAAAAAMIAwAAAAAAAAADCAQAAAAAAAAAAgEAAgGAAgHABSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAxoIByQIBB8IBAECBBoIByQIBjYDJQICAgQNCAYkCAYlAhQKAgMCAg0IBiQI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AQgABgoLBAEIAQELBAEIAgIGCwQBCAIGCwQBCAICBgsEAQgABgsEAQgCAQYLBAEIAgIGCgsEAQgABgoLBAEIAgELBAEIAwIGCwQBCAMGCwQBCAMCBgsEAQgABgsEAQgDAQYLBAEIAwIGCwQBCAEGCwQBCAIBCAADAgYKAgEBCwQBCQABCgIDAwEHCgIDAgYLBAEJAAYLBAEJAAIIAAgAAwIGCwQBCQAGCwQBCQEBCwQBCQECCwQBCAAGCwQBCAABCAECCAAIAQICBgoCAwIGCgsEAQkABgoLBAEJAQEIAgIIAAgCAQgDAggACAMDCAEIAggDAQsEAQkCB0VsZW1lbnQCRzECRzICR1QGU2NhbGFyA2FkZAhibHMxMjM4MRZibHMxMjM4MV9taW5fcGtfdmVyaWZ5F2JsczEyMzgxX21pbl9zaWdfdmVyaWZ5A2RpdgtkdW1teV9maWVsZApmcm9tX2J5dGVzBmcxX2FkZAZnMV9kaXYNZzFfZnJvbV9ieXRlcwxnMV9nZW5lcmF0b3ILZzFfaWRlbnRpdHkGZzFfbXVsHmcxX211bHRpX3NjYWxhcl9tdWx0aXBsaWNhdGlvbgZnMV9uZWcGZzFfc3ViBmcyX2FkZAZnMl9kaXYNZzJfZnJvbV9ieXRlcwxnMl9nZW5lcmF0b3ILZzJfaWRlbnRpdHkGZzJfbXVsHmcyX211bHRpX3NjYWxhcl9tdWx0aXBsaWNhdGlvbgZnMl9uZWcGZzJfc3ViCWdyb3VwX29wcwZndF9hZGQGZ3RfZGl2DGd0X2dlbmVyYXRvcgtndF9pZGVudGl0eQZndF9tdWwGZ3RfbmVnBmd0X3N1YgdoYXNoX3RvCmhhc2hfdG9fZzEKaGFzaF90b19nMgNtdWwbbXVsdGlfc2NhbGFyX211bHRpcGxpY2F0aW9uB3BhaXJpbmcKc2NhbGFyX2FkZApzY2FsYXJfZGl2EXNjYWxhcl9mcm9tX2J5dGVzD3NjYWxhcl9mcm9tX3U2NApzY2FsYXJfaW52CnNjYWxhcl9tdWwKc2NhbGFyX25lZwpzY2FsYXJfb25lCnNjYWxhcl9zdWILc2NhbGFyX3plcm8Nc2V0X2FzX3ByZWZpeANzdWIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgoCISAAAAAAAAAAAAAAAAAA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AQkAAQgNAQsIAQkAAQsCAQkAAQsBAQkAAQYIDgEFAgkABQMDAwMBBgkAAQYLBgEJAAELBgEJAAEGCwgBCQADBwsIAQkAAwcIDgELCQEJAAMLCAEIDQgLCAoBCwQBCQACCwgBCQAHCA4HCggHCAoICgMLDwEIBwgHAwEKCQABBgsPAQkAAgYLDwEJAAYJAAIJAAkBAQkBAgsFAQkBCQIDBwgLCQAJAQIHCw8BCQAJAAIGCAsJAAEGCQECBwsIAQkACwkBCQABCwUBCQECCAcHCw8BCAcCBwgLCQACBwsPAQkABgkAB0JhbGFuY2UEQ29pbgJJRAZPcHRpb24JUHVibGlzaGVyB1J1bGVLZXkDU1VJDlRyYW5zZmVyUG9saWN5EVRyYW5zZmVyUG9saWN5Q2FwFVRyYW5zZmVyUG9saWN5Q3JlYXRlZBdUcmFuc2ZlclBvbGljeURlc3Ryb3llZA9UcmFuc2ZlclJlcXVlc3QJVHhDb250ZXh0CFR5cGVOYW1lA1VJRAZWZWNTZXQDYWRkC2FkZF9yZWNlaXB0CGFkZF9ydWxlDmFkZF90b19iYWxhbmNlB2JhbGFuY2UGYm9ycm93BGNvaW4PY29uZmlybV9yZXF1ZXN0CGNvbnRhaW5zB2RlZmF1bHQGZGVsZXRlFGRlc3Ryb3lfYW5kX3dpdGhkcmF3DGRlc3Ryb3lfc29tZQtkdW1teV9maWVsZA1keW5hbWljX2ZpZWxkBGVtaXQFZW1wdHkFZXZlbnQHZXhpc3RzXwRmcm9tDGZyb21fYmFsYW5jZQxmcm9tX3BhY2thZ2UDZ2V0CGdldF9ydWxlCGhhc19ydWxlAmlkBmluc2VydAlpbnRvX2tleXMHaXNfc29tZQRpdGVtA25ldwtuZXdfcmVxdWVzdAZvYmplY3QGb3B0aW9uB3BhY2thZ2UEcGFpZAlwb2xpY3lfaWQDcHV0CHJlY2VpcHRzBnJlbW92ZQtyZW1vdmVfcnVsZQVydWxlcwZzZW5kZXIMc2hhcmVfb2JqZWN0BHNpemUDc3VpBHRha2UIdHJhbnNmZXIPdHJhbnNmZXJfcG9saWN5CnR4X2NvbnRleHQJdHlwZV9uYW1lA3VpZBB1aWRfbXV0X2FzX293bmVyDHVpZF90b19pbm5lcgV2YWx1ZQd2ZWNfc2V0CHdpdGhkcmF3BHpl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Y2UHZ2VuZXNpcxFnZXRfdmFsaWRhdG9yX211dAlpbWFnZV91cmwWaXNfZHVwbGljYXRlX3ZhbGlkYXRvcghpc19lbXB0eQdpc19zb21lE21heF92YWxpZGF0b3JfY291bnQbbWluX3ZhbGlkYXRvcl9qb2luaW5nX3N0YWtlBG5hbWUPbmV0d29ya19hZGRyZXNzEm5ldHdvcmtfcHVibGljX2tleQNuZXcGb2JqZWN0Bm9wdGlvbgtwMnBfYWRkcmVzcw9wcmltYXJ5X2FkZHJlc3MLcHJvamVjdF91cmwTcHJvb2Zfb2ZfcG9zc2Vzc2lvbhNwcm90b2NvbF9wdWJsaWNfa2V5EHByb3RvY29sX3ZlcnNpb24RcmVjaXBpZW50X2FkZHJlc3MccmVxdWVzdF9hZGRfc3Rha2VfYXRfZ2VuZXNpcwVzcGxpdA1zdGFrZV9zdWJzaWR5G3N0YWtlX3N1YnNpZHlfZGVjcmVhc2VfcmF0ZRdzdGFrZV9zdWJzaWR5X2Z1bmRfbWlzdClzdGFrZV9zdWJzaWR5X2luaXRpYWxfZGlzdHJpYnV0aW9uX2Ftb3VudBtzdGFrZV9zdWJzaWR5X3BlcmlvZF9sZW5ndGgZc3Rha2Vfc3Vic2lkeV9zdGFydF9lcG9jaBVzdGFrZWRfd2l0aF92YWxpZGF0b3IDc3VpC3N1aV9hZGRyZXNzCnN1aV9zeXN0ZW0Wc3VpX3N5c3RlbV9zdGF0ZV9pbm5lcgh0cmFuc2Zlcgp0eF9jb250ZXh0CXZhbGlkYXRvciB2YWxpZGF0b3JfbG93X3N0YWtlX2dyYWNlX3BlcmlvZB12YWxpZGF0b3JfbG93X3N0YWtlX3RocmVzaG9sZA12YWxpZGF0b3Jfc2V0InZhbGlkYXRvcl92ZXJ5X2xvd19zdGFrZV90aHJlc2hvbGQGdmVjdG9yDndvcmtlcl9hZGRyZXNzEXdvcmtlcl9wdWJsaWNfa2V5BHplcm8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDCAAAAAAAAAAAAwgBAAAAAAAAAAACDycKAhgKAiEKAi8KAj4FHgMVAzEKAjAKAikK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CA8LCAEICgIHCA8GCAsCBggPAwEIDwEGCQAdAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBBgsFAQkAAgEBAgUHCAsBBwsFAQkAAwgJCA8FA0JhZwdCYWxhbmNlAklEBk9wdGlvbhVQb29sVG9rZW5FeGNoYW5nZVJhdGUDU1VJCVN0YWtlZFN1aQtTdGFraW5nUG9vbBNTdGFraW5nUmVxdWVzdEV2ZW50BlN0cmluZwlUeENvbnRleHQVVW5zdGFraW5nUmVxdWVzdEV2ZW50A1VybAlWYWxpZGF0b3IRVmFsaWRhdG9yTWV0YWRhdGEVVmFsaWRhdG9yT3BlcmF0aW9uQ2FwCGFjdGl2YXRlFWFjdGl2YXRlX3N0YWtpbmdfcG9vbBphZGp1c3Rfc3Rha2VfYW5kX2dhc19wcmljZQZhbW91bnQFYXNjaWkDYmFnB2JhbGFuY2UDYmNzBmJvcnJvdw9jb21taXNzaW9uX3JhdGUKZGVhY3RpdmF0ZRdkZWFjdGl2YXRlX3N0YWtpbmdfcG9vbA9kZXBvc2l0X3Jld2FyZHMVZGVwb3NpdF9zdGFrZV9yZXdhcmRzC2Rlc2NyaXB0aW9uGmVmZmVjdHVhdGVfc3RhZ2VkX21ldGFkYXRhBGVtaXQFZXBvY2gFZXZlbnQMZXh0cmFfZmllbGRzB2V4dHJhY3QKZnJvbV9hc2NpaQlnYXNfcHJpY2UHZ2VuZXNpcxRnZXRfc3Rha2luZ19wb29sX3JlZgJpZAlpbWFnZV91cmwMaXNfZHVwbGljYXRlDWlzX2VxdWFsX3NvbWUXaXNfZXF1YWxfc29tZV9hbmRfdmFsdWUHaXNfbm9uZQxpc19wcmVhY3RpdmUHaXNfc29tZQhtZXRhZGF0YQRuYW1lC25ldF9hZGRyZXNzD25ldHdvcmtfYWRkcmVzcxRuZXR3b3JrX3B1YmtleV9ieXRlcwNuZXcRbmV3X2Zyb21fbWV0YWRhdGEMbmV3X21ldGFkYXRhFW5ld191bnNhZmVfZnJvbV9ieXRlczNuZXdfdW52ZXJpZmllZF92YWxpZGF0b3Jfb3BlcmF0aW9uX2NhcF9hbmRfdHJhbnNmZXIabmV4dF9lcG9jaF9jb21taXNzaW9uX3JhdGUUbmV4dF9lcG9jaF9nYXNfcHJpY2UWbmV4dF9lcG9jaF9uZXRfYWRkcmVzcxpuZXh0X2Vwb2NoX25ldHdvcmtfYWRkcmVzcx9uZXh0X2Vwb2NoX25ldHdvcmtfcHVia2V5X2J5dGVzFm5leHRfZXBvY2hfcDJwX2FkZHJlc3MabmV4dF9lcG9jaF9wcmltYXJ5X2FkZHJlc3MebmV4dF9lcG9jaF9wcm9vZl9vZl9wb3NzZXNzaW9uIG5leHRfZXBvY2hfcHJvdG9jb2xfcHVia2V5X2J5dGVzEG5leHRfZXBvY2hfc3Rha2UZbmV4dF9lcG9jaF93b3JrZXJfYWRkcmVzcx5uZXh0X2Vwb2NoX3dvcmtlcl9wdWJrZXlfYnl0ZXMEbm9uZQZvYmplY3QQb3BlcmF0aW9uX2NhcF9pZAZvcHRpb24LcDJwX2FkZHJlc3MUcGVuZGluZ19zdGFrZV9hbW91bnQdcGVuZGluZ19zdGFrZV93aXRoZHJhd19hbW91bnQHcG9vbF9pZCFwb29sX3Rva2VuX2V4Y2hhbmdlX3JhdGVfYXRfZXBvY2gPcHJpbWFyeV9hZGRyZXNzEHByaW5jaXBhbF9hbW91bnQVcHJvY2Vzc19wZW5kaW5nX3N0YWtlJHByb2Nlc3NfcGVuZGluZ19zdGFrZXNfYW5kX3dpdGhkcmF3cwtwcm9qZWN0X3VybBNwcm9vZl9vZl9wb3NzZXNzaW9uFXByb3RvY29sX3B1YmtleV9ieXRlcw9wdWJsaWNfdHJhbnNmZXIRcmVxdWVzdF9hZGRfc3Rha2UccmVxdWVzdF9hZGRfc3Rha2VfYXRfZ2VuZXNpcxtyZXF1ZXN0X3NldF9jb21taXNzaW9uX3JhdGUVcmVxdWVzdF9zZXRfZ2FzX3ByaWNlFnJlcXVlc3Rfd2l0aGRyYXdfc3Rha2UNcmV3YXJkX2Ftb3VudAZzZW5kZXIdc2V0X2NhbmRpZGF0ZV9jb21taXNzaW9uX3JhdGUXc2V0X2NhbmRpZGF0ZV9nYXNfcHJpY2UQc2V0X3ZvdGluZ19wb3dlcgRzb21lFnN0YWtlX2FjdGl2YXRpb25fZXBvY2gMc3Rha2VfYW1vdW50EXN0YWtlZF9zdWlfYW1vdW50DnN0YWtlcl9hZGRyZXNzDHN0YWtpbmdfcG9vbA9zdGFraW5nX3Bvb2xfaWQGc3RyaW5nA3N1aQtzdWlfYWRkcmVzcwtzdWlfYmFsYW5jZRZzdWlfc3lzdGVtX3N0YXRlX2lubmVyCHRvX2J5dGVzC3RvdGFsX3N0YWtlEnRvdGFsX3N0YWtlX2Ftb3VudAh0cmFuc2Zlcgp0eF9jb250ZXh0D3Vuc3Rha2luZ19lcG9jaCB1cGRhdGVfY2FuZGlkYXRlX25ldHdvcmtfYWRkcmVzcx91cGRhdGVfY2FuZGlkYXRlX25ldHdvcmtfcHVia2V5HHVwZGF0ZV9jYW5kaWRhdGVfcDJwX2FkZHJlc3MgdXBkYXRlX2NhbmRpZGF0ZV9wcmltYXJ5X2FkZHJlc3MgdXBkYXRlX2NhbmRpZGF0ZV9wcm90b2NvbF9wdWJrZXkfdXBkYXRlX2NhbmRpZGF0ZV93b3JrZXJfYWRkcmVzcx51cGRhdGVfY2FuZGlkYXRlX3dvcmtlcl9wdWJrZXkSdXBkYXRlX2Rlc2NyaXB0aW9uEHVwZGF0ZV9pbWFnZV91cmwLdXBkYXRlX25hbWUhdXBkYXRlX25leHRfZXBvY2hfbmV0d29ya19hZGRyZXNzIHVwZGF0ZV9uZXh0X2Vwb2NoX25ldHdvcmtfcHVia2V5HXVwZGF0ZV9uZXh0X2Vwb2NoX3AycF9hZGRyZXNzIXVwZGF0ZV9uZXh0X2Vwb2NoX3ByaW1hcnlfYWRkcmVzcyF1cGRhdGVfbmV4dF9lcG9jaF9wcm90b2NvbF9wdWJrZXkgdXBkYXRlX25leHRfZXBvY2hfd29ya2VyX2FkZHJlc3MfdXBkYXRlX25leHRfZXBvY2hfd29ya2VyX3B1YmtleRJ1cGRhdGVfcHJvamVjdF91cmwDdXJsEXZhbGlkYXRlX21ldGFkYXRhFXZhbGlkYXRlX21ldGFkYXRhX2Jjcwl2YWxpZGF0b3IRdmFsaWRhdG9yX2FkZHJlc3MNdmFsaWRhdG9yX2NhcA12YWxpZGF0b3Jfc2V0EXZhbGlkYXRvcl93cmFwcGVyBXZhbHVlHnZlcmlmaWVkX29wZXJhdGlvbl9jYXBfYWRkcmVzcwx2b3RpbmdfcG93ZXIOd29ya2VyX2FkZHJlc3MTd29ya2VyX3B1YmtleV9ieXRlcwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA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dARjb2luBmNyZWF0ZQ1keW5hbWljX2ZpZWxkDGZyb21fYmFsYW5jZQdnZW5lc2lzHGdlbmVzaXNfc3lzdGVtX3N0YXRlX3ZlcnNpb24CaWQYbG9hZF9pbm5lcl9tYXliZV91cGdyYWRlEWxvYWRfc3lzdGVtX3N0YXRlFWxvYWRfc3lzdGVtX3N0YXRlX211dAZvYmplY3QGb3B0aW9uE3Bvb2xfZXhjaGFuZ2VfcmF0ZXMPcHVibGljX3RyYW5zZmVyBnJlbW92ZRByZXBvcnRfdmFsaWRhdG9yEXJlcXVlc3RfYWRkX3N0YWtlGnJlcXVlc3RfYWRkX3N0YWtlX211bF9jb2luG3JlcXVlc3RfYWRkX3N0YWtlX25vbl9lbnRyeRVyZXF1ZXN0X2FkZF92YWxpZGF0b3IfcmVxdWVzdF9hZGRfdmFsaWRhdG9yX2NhbmRpZGF0ZRhyZXF1ZXN0X3JlbW92ZV92YWxpZGF0b3IicmVxdWVzdF9yZW1vdmVfdmFsaWRhdG9yX2NhbmRpZGF0ZRtyZXF1ZXN0X3NldF9jb21taXNzaW9uX3JhdGUVcmVxdWVzdF9zZXRfZ2FzX3ByaWNlFnJlcXVlc3Rfd2l0aGRyYXdfc3Rha2UgcmVxdWVzdF93aXRoZHJhd19zdGFrZV9ub25fZW50cnkUcm90YXRlX29wZXJhdGlvbl9jYXAGc2VuZGVyJ3NldF9jYW5kaWRhdGVfdmFsaWRhdG9yX2NvbW1pc3Npb25fcmF0ZSFzZXRfY2FuZGlkYXRlX3ZhbGlkYXRvcl9nYXNfcHJpY2UMc2hhcmVfb2JqZWN0DXN0YWtlX3N1YnNpZHkMc3Rha2luZ19wb29sA3N1aQpzdWlfc3lzdGVtFnN1aV9zeXN0ZW1fc3RhdGVfaW5uZXIUc3lzdGVtX3N0YXRlX3ZlcnNpb24FdGFibGUIdHJhbnNmZXIKdHhfY29udGV4dBV1bmRvX3JlcG9ydF92YWxpZGF0b3IqdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfbmV0d29ya19hZGRyZXNzKXVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX25ldHdvcmtfcHVia2V5JnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3AycF9hZGRyZXNzKnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3ByaW1hcnlfYWRkcmVzcyp1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl9wcm90b2NvbF9wdWJrZXkpdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3Jfd29ya2VyX2FkZHJlc3ModXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3Jfd29ya2VyX3B1YmtleRx1cGRhdGVfdmFsaWRhdG9yX2Rlc2NyaXB0aW9uGnVwZGF0ZV92YWxpZGF0b3JfaW1hZ2VfdXJsFXVwZGF0ZV92YWxpZGF0b3JfbmFtZSt1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfbmV0d29ya19hZGRyZXNzKnVwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9uZXR3b3JrX3B1YmtleSd1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfcDJwX2FkZHJlc3MrdXBkYXRlX3ZhbGlkYXRvcl9uZXh0X2Vwb2NoX3ByaW1hcnlfYWRkcmVzcyt1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfcHJvdG9jb2xfcHVia2V5KnVwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF93b3JrZXJfYWRkcmVzcyl1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfd29ya2VyX3B1YmtleRx1cGRhdGVfdmFsaWRhdG9yX3Byb2plY3RfdXJsCHYxX3RvX3YyCXZhbGlkYXRvcg12YWxpZGF0b3JfY2FwB3ZlcnNpb24AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDCAAAAAAAAAAAAwgBAAAAAAAAAAUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIcCAVVAwADAAAVFwsBCwILAwsECwULBgsHETIMCREzDAoLAAoKEgAMCA0IDwALCgsJOAALCDgBAgEBBAABEwsAEScLAQsCCwMLBAsFCwYLBwsICwkLCgsLCwwLDQsOCw8ROQICAQQAAQULABEnCwEROwIDAQQAAQYLABEnCwEuETgCBAEEAAEGCwARJwsBLhE6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bl9lcG9jaAZkZWxldGUPZGVwb3NpdF9yZXdhcmRzBWVwb2NoDmV4Y2hhbmdlX3JhdGVzDGV4dHJhX2ZpZWxkcwRmaWxsDmdldF9zdWlfYW1vdW50EGdldF90b2tlbl9hbW91bnQQZ2V0X3dpdGhfZGVmYXVsdAJpZBVpbml0aWFsX2V4Y2hhbmdlX3JhdGUZaXNfZXF1YWxfc3Rha2luZ19tZXRhZGF0YQtpc19pbmFjdGl2ZQdpc19ub25lDGlzX3ByZWFjdGl2ZRVpc19wcmVhY3RpdmVfYXRfZXBvY2gHaXNfc29tZQRqb2luD2pvaW5fc3Rha2VkX3N1aQRtYXRoA21pbgNuZXcEbm9uZQZvYmplY3QGb3B0aW9uG3BlbmRpbmdfcG9vbF90b2tlbl93aXRoZHJhdw1wZW5kaW5nX3N0YWtlFHBlbmRpbmdfc3Rha2VfYW1vdW50HXBlbmRpbmdfc3Rha2Vfd2l0aGRyYXdfYW1vdW50GnBlbmRpbmdfdG90YWxfc3VpX3dpdGhkcmF3B3Bvb2xfaWQRcG9vbF90b2tlbl9hbW91bnQScG9vbF90b2tlbl9iYWxhbmNlIXBvb2xfdG9rZW5fZXhjaGFuZ2VfcmF0ZV9hdF9lcG9jaAlwcmluY2lwYWwVcHJvY2Vzc19wZW5kaW5nX3N0YWtlHnByb2Nlc3NfcGVuZGluZ19zdGFrZV93aXRoZHJhdyRwcm9jZXNzX3BlbmRpbmdfc3Rha2VzX2FuZF93aXRoZHJhd3MRcmVxdWVzdF9hZGRfc3Rha2UWcmVxdWVzdF93aXRoZHJhd19zdGFrZQxyZXdhcmRzX3Bvb2wGc2VuZGVyBHNvbWUFc3BsaXQQc3BsaXRfc3Rha2VkX3N1aRZzdGFrZV9hY3RpdmF0aW9uX2Vwb2NoEXN0YWtlZF9zdWlfYW1vdW50DHN0YWtpbmdfcG9vbANzdWkKc3VpX2Ftb3VudAtzdWlfYmFsYW5jZQV0YWJsZQh0cmFuc2Zlcgp0eF9jb250ZXh0EXVud3JhcF9zdGFrZWRfc3VpCXZhbGlkYXRvcg12YWxpZGF0b3Jfc2V0BXZhbHVlF3dpdGhkcmF3X2Zyb21fcHJpbmNpcGFsEHdpdGhkcmF3X3Jld2FyZHMEemVybwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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BQUCBwgAAwEHCgMCBwoIFAYIDgEGCggUAQcKCBQECgMDBgoDBgoDBAMLDwIDAwMLDwIDAwIGCAALDwIFCxABBQEKBQQGCggUAwMDAgoDCgMJBgoIFAMDCgMKAwMLDwIDAwMLDwIDAwYHCggUBgoDBgoDBwsHAQgLBwsHAQgLBwgOBgMGCggUBgoDBgoDBgsPAgULEAEFBgoFAgYIAAgIBgMDCwwCCAgFAwYIFAgAAggIBQEHCA4BCwwCCQAJAQEIFAEFAwcLDAIJAAkBCQAJAQELDQEJAAIICAgXAgUIFwIFAwELDwIJAAkBAQgGBAYIFAYIFAEFAgYLDAIJAAkBCQACCBQHCA4BCBcDCAgIFAUBBggOAgcLDAIJAAkBCQABCQECBwgUAwUGCBQGCBQBCBQFAgcLDQEJAAkAAwUDCwUBAwEGCwUBCQABBwsFAQkAAQkAAgYKCQAGCQABCAsBBgsHAQkABAcIFAsHAQgLBQcIDgMHCBQICAUBBggSAQYJAQEHCQEBBwgXAwcIFAgSBggODgYKBQsPAgULEAEFCgMKAwsPAgMDCw8CAwMDCgUDAwMDCgMKAwgDAwcDAwgUCBQFBggUAgYLDwIJAAkBBgkAAgcLDwIJAAkBBgkAAgkACQEDBwsPAgkACQEJAAkBAgcKCQADAgMDCgoLCQEDAwMLCgEDAwMDBggUAwYKCBQBCwkBAwIDCQABCwkBCQABCgsJAQkAAQsKAQkAAQcLCgEJAAIGCBQFAQYIEwEGCw0BCQADAwMDAgYIFAYIFAIGCw0BCQADAQYJAAELBQEJAAUFAwsFAQMDCgMEAwMLBQEDCwUBAwIHCw0BCQADAQcJAAEGCBYBBgUCAwsFAQMGAQEDAwsFAQMLBQEDBQUGCBQICAUGCBQBBggVAQgVAQYICAIDCBQBBgoJAAMDBQgIAQgEBwYFBgUDAwYFCgUHCxABBQIFCxABBQEGCw8CCQAJAQEKCQACBgsQAQkABgkAAgcLEAEJAAYJAAEGCxABCQABBwsNAQkAAQgDBgMDAwMDAwIHCBQGCA4EAwMDBggUBwsPAgMDCw8CAwMEBAMDAwUKBQYKCBQLEAEFCgUFAQcLDwIJAAkBAQsQAQkABgMDBAoDCgMDEQMDAwoDAwoDAwQDAwMDAwMDAwQIAwMLBwEICwMHCBQFBAsHAQgLAgcLBwEJAAMBCwcBCQACBwsHAQkACwcBCQACCQAFAgcIFAsHAQgLCAoFAwMDAwoFBggUBQIGCBQDAQgRAQgCBQMDCgUFBgoIFANCYWcHQmFsYW5jZQVFbnRyeQJJRAZPcHRpb24VUG9vbFRva2VuRXhjaGFuZ2VSYXRlDVByaW9yaXR5UXVldWUDU1VJCVN0YWtlZFN1aQtTdGFraW5nUG9vbAVUYWJsZQhUYWJsZVZlYwlUeENvbnRleHQfVW52ZXJpZmllZFZhbGlkYXRvck9wZXJhdGlvbkNhcAlWYWxpZGF0b3IXVmFsaWRhdG9yRXBvY2hJbmZvRXZlbnQZVmFsaWRhdG9yRXBvY2hJbmZvRXZlbnRWMhJWYWxpZGF0b3JKb2luRXZlbnQTVmFsaWRhdG9yTGVhdmVFdmVudBVWYWxpZGF0b3JPcGVyYXRpb25DYXAMVmFsaWRhdG9yU2V0EFZhbGlkYXRvcldyYXBwZXIGVmVjTWFwBlZlY1NldAhhY3RpdmF0ZRphY3RpdmVfdmFsaWRhdG9yX2FkZHJlc3NlcxFhY3RpdmVfdmFsaWRhdG9ycwNhZGQaYWRqdXN0X3N0YWtlX2FuZF9nYXNfcHJpY2UNYWR2YW5jZV9lcG9jaCZhc3NlcnRfbm9fcGVuZGluZ19vcl9hY3RpdmVfZHVwbGljYXRlcxJhdF9yaXNrX3ZhbGlkYXRvcnMDYmFnB2JhbGFuY2UGYm9ycm93CmJvcnJvd19tdXQWY2FsY3VsYXRlX3RvdGFsX3N0YWtlcyZjbGVhbl9yZXBvcnRfcmVjb3Jkc19sZWF2aW5nX3ZhbGlkYXRvcg9jb21taXNzaW9uX3JhdGUkY29tcHV0ZV9hZGp1c3RlZF9yZXdhcmRfZGlzdHJpYnV0aW9uGmNvbXB1dGVfcmV3YXJkX2FkanVzdG1lbnRzGmNvbXB1dGVfc2xhc2hlZF92YWxpZGF0b3JzJmNvbXB1dGVfdW5hZGp1c3RlZF9yZXdhcmRfZGlzdHJpYnV0aW9uCGNvbnRhaW5zGWNvdW50X2R1cGxpY2F0ZXNfdGFibGV2ZWMUY291bnRfZHVwbGljYXRlc192ZWMJY3JlYXRlX3YxCmRlYWN0aXZhdGUVZGVwb3NpdF9zdGFrZV9yZXdhcmRzGmRlcml2ZV9yZWZlcmVuY2VfZ2FzX3ByaWNlB2Rlc3Ryb3kMZGVzdHJveV9zb21lDGRlc3Ryb3lfemVybxFkaXN0cmlidXRlX3Jld2FyZBplZmZlY3R1YXRlX3N0YWdlZF9tZXRhZGF0YQRlbWl0G2VtaXRfdmFsaWRhdG9yX2Vwb2NoX2V2ZW50cwVlbXB0eQVlcG9jaAVldmVudA5leGNoYW5nZV9yYXRlcwxleHRyYV9maWVsZHMHZXh0cmFjdA5maW5kX3ZhbGlkYXRvch1maW5kX3ZhbGlkYXRvcl9mcm9tX3RhYmxlX3ZlYwlnYXNfcHJpY2UHZ2VuZXNpcwNnZXQwZ2V0X2FjdGl2ZV9vcl9wZW5kaW5nX29yX2NhbmRpZGF0ZV92YWxpZGF0b3JfbXV0MGdldF9hY3RpdmVfb3JfcGVuZGluZ19vcl9jYW5kaWRhdGVfdmFsaWRhdG9yX3JlZhhnZXRfYWN0aXZlX3ZhbGlkYXRvcl9yZWYlZ2V0X2NhbmRpZGF0ZV9vcl9hY3RpdmVfdmFsaWRhdG9yX211dAdnZXRfbXV0GWdldF9wZW5kaW5nX3ZhbGlkYXRvcl9yZWYUZ2V0X3N0YWtpbmdfcG9vbF9yZWYVZ2V0X3ZhbGlkYXRvcl9pbmRpY2VzEWdldF92YWxpZGF0b3JfbXV0GmdldF92YWxpZGF0b3JfbXV0X3dpdGhfY3R4L2dldF92YWxpZGF0b3JfbXV0X3dpdGhfY3R4X2luY2x1ZGluZ19jYW5kaWRhdGVzI2dldF92YWxpZGF0b3JfbXV0X3dpdGhfdmVyaWZpZWRfY2FwEWdldF92YWxpZGF0b3JfcmVmAmlkE2luYWN0aXZlX3ZhbGlkYXRvcnMGaW5zZXJ0CWludG9fa2V5cyJpc19hY3RpdmVfdmFsaWRhdG9yX2J5X3N1aV9hZGRyZXNzDGlzX2R1cGxpY2F0ZRZpc19kdXBsaWNhdGVfdmFsaWRhdG9yImlzX2R1cGxpY2F0ZV93aXRoX2FjdGl2ZV92YWxpZGF0b3IjaXNfZHVwbGljYXRlX3dpdGhfcGVuZGluZ192YWxpZGF0b3IIaXNfZW1wdHkVaXNfaW5hY3RpdmVfdmFsaWRhdG9yDGlzX3ByZWFjdGl2ZQdpc19zb21lFmlzX3ZhbGlkYXRvcl9jYW5kaWRhdGUMaXNfdm9sdW50YXJ5BGpvaW4Ea2V5cwZsZW5ndGgcbG9hZF92YWxpZGF0b3JfbWF5YmVfdXBncmFkZQNuZXcJbmV3X2VudHJ5E25ld19mcm9tX3VudmVyaWZpZWQabmV4dF9lcG9jaF92YWxpZGF0b3JfY291bnQEbm9uZQZvYmplY3QQb3BlcmF0aW9uX2NhcF9pZAZvcHRpb24ZcGVuZGluZ19hY3RpdmVfdmFsaWRhdG9ycxBwZW5kaW5nX3JlbW92YWxzE3Bvb2xfZXhjaGFuZ2VfcmF0ZXMHcG9vbF9pZBNwb29sX3N0YWtpbmdfcmV3YXJkGHBvb2xfdG9rZW5fZXhjaGFuZ2VfcmF0ZSFwb29sX3Rva2VuX2V4Y2hhbmdlX3JhdGVfYXRfZXBvY2gDcG9wCHBvcF9iYWNrB3BvcF9tYXgOcHJpb3JpdHlfcXVldWUYcHJvY2Vzc19wZW5kaW5nX3JlbW92YWxzJHByb2Nlc3NfcGVuZGluZ19zdGFrZXNfYW5kX3dpdGhkcmF3cxpwcm9jZXNzX3BlbmRpbmdfdmFsaWRhdG9ycxtwcm9jZXNzX3ZhbGlkYXRvcl9kZXBhcnR1cmUPcHVibGljX3RyYW5zZmVyCXB1c2hfYmFjaxBxdW9ydW1fdGhyZXNob2xkGnJlZmVyZW5jZV9nYXNfc3VydmV5X3F1b3RlBnJlbW92ZRFyZXF1ZXN0X2FkZF9zdGFrZRVyZXF1ZXN0X2FkZF92YWxpZGF0b3IfcmVxdWVzdF9hZGRfdmFsaWRhdG9yX2NhbmRpZGF0ZRhyZXF1ZXN0X3JlbW92ZV92YWxpZGF0b3IicmVxdWVzdF9yZW1vdmVfdmFsaWRhdG9yX2NhbmRpZGF0ZRtyZXF1ZXN0X3NldF9jb21taXNzaW9uX3JhdGUWcmVxdWVzdF93aXRoZHJhd19zdGFrZQZzZW5kZXIQc2V0X3ZvdGluZ19wb3dlcgRzaXplBHNvbWURc29ydF9yZW1vdmFsX2xpc3QFc3BsaXQFc3Rha2UMc3Rha2VfYW1vdW50DHN0YWtpbmdfcG9vbA9zdGFraW5nX3Bvb2xfaWQVc3Rha2luZ19wb29sX21hcHBpbmdzG3N0b3JhZ2VfZnVuZF9zdGFraW5nX3Jld2FyZANzdWkLc3VpX2FkZHJlc3MWc3VpX3N5c3RlbV9zdGF0ZV9pbm5lch1zdW1fdm90aW5nX3Bvd2VyX2J5X2FkZHJlc3NlcwV0YWJsZQl0YWJsZV92ZWMadGFsbHlpbmdfcnVsZV9nbG9iYWxfc2NvcmUXdGFsbHlpbmdfcnVsZV9yZXBvcnRlcnMLdG90YWxfc3Rha2USdG90YWxfc3Rha2VfYW1vdW50EnRvdGFsX3ZvdGluZ19wb3dlcgh0cmFuc2Zlcgp0eF9jb250ZXh0IHVudmVyaWZpZWRfb3BlcmF0aW9uX2NhcF9hZGRyZXNzJ3VwZGF0ZV9hbmRfcHJvY2Vzc19sb3dfc3Rha2VfZGVwYXJ0dXJlcwl2YWxpZGF0b3IRdmFsaWRhdG9yX2FkZHJlc3MUdmFsaWRhdG9yX2NhbmRpZGF0ZXMNdmFsaWRhdG9yX2NhcA12YWxpZGF0b3Jfc2V0FnZhbGlkYXRvcl9zdGFrZV9hbW91bnQZdmFsaWRhdG9yX3N0YWtpbmdfcG9vbF9pZBx2YWxpZGF0b3JfdG90YWxfc3Rha2VfYW1vdW50EXZhbGlkYXRvcl93cmFwcGVyBXZhbHVlB3ZlY19tYXAHdmVjX3NldAZ2ZWN0b3IedmVyaWZpZWRfb3BlcmF0aW9uX2NhcF9hZGRyZXNzCnZlcmlmeV9jYXAMdm90aW5nX3Bvd2VyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgEBAgECAgEDBBAQJwAAAAAAAAAAAAAAAAAAAwgAypo7AAAAAAMIAAAAAAAAAAADCAEAAAAAAAAAAwgCAAAAAAAAAAMIAwAAAAAAAAADCAQAAAAAAAAAAwgFAAAAAAAAAAMIBgAAAAAAAAADCAcAAAAAAAAAAwgIAAAAAAAAAAMICQAAAAAAAAADCAoAAAAAAAAAAwgLAAAAAAAAAAMIDAAAAAAAAAADCA0AAAAAAAAAAwhlAAAAAAAAAAoDAQAKBQEAAAIJmwEDGgoIFGwLDQEIFG0KA5EBCwwCCAgFUgsMAggICBekAQsMAgUIFx8LDwIFAz0IBgECCjoDowEFfgONAQMmA3ADkgEDcQgRmgEKBZkBAwICCzoDowEFfgONAQOxAQMmA3ADkgEDcQgRmgEKBZkBAwMCAzoDowEFkAEI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NBY9JkFgNyYHCggTCwcBCAoDAwgACA8HCAwBCAIIAwMDAwMDAwcIDAEIAAEIAxAHCAMKAgoCCgIKAgoCCgIKAgoCCgIKAgoCCgIDAwcIDAACBwgDBwgMAgcIAwYIDAMHCAMGCBQDAwcIAwMGCAwEBwgDCwgBCAoFBwgMAQgRBQcIAwoLCAEICgsFAQMFBwgMAwcIAwgRBggMAQsHAQgKAwcIAwYIFAUDCBUFBwsNAgULDgEFAwcIAwoCBggMBAcIAwoCCgIGCAwLBwgDAwMLBwEICgsHAQgKAwMDAwMHCAwBBggDAQMCBggDBQEICQEGCwsCCAkFAQsOAQUCBwgDBggJAQYLCwIDCBABCgUDCgsIAQgKCwUBAwcIDAIDCBYCCggTBwgMAQgWAQYIFgEIEgIFCw4BBQELDQIJAAkBAQgKAQsHAQkAAQcIDAEIBhcDAwMDAwgGCAADAwELBwEICgMDCwcBCAoIDwMIBggSAwMLDQIFCw4BBQMIFgEIEwEGCAwBBRAFCgIKAgoCCgIKAgoCCgIKAgoCCgIKAgoCAwMHCAwDBwgWCBMHCAwCBwgWBwgMAwcIFgMGCAwBBgoIEwIHCBYGCAwBCBUDBwgWBggUAgMHCBYGCBUBAQcIEwMHCBMIFQMCBwgTAwELCAEJAAQHCBYFCwcBCAoHCAwBBggRAwcIFggRBggMAgYIFgUBAQQGBQYFBQcLDgEFAQYIFQEGBQIGCw0CCQAJAQYJAAEJAAELDgEJAAMHCw0CCQAJAQkACQECBwsNAgkACQEGCQABBwkBAgYLDgEJAAYJAAIHCw4BCQAJAAIHCw4BCQAGCQABBgsOAQkAAgkACQECBwgTBwgMAgcIEwoCAgcIEwYIEwIGCBYGCBMDBwgTCgIKAiwBAwMDAwMDAQEBCwcBCAoDAwMDAwMDAwQDAwMLBwEICgMDAwsHAQgKCwcBCAoLBwEICgsHAQgKAwMDCwcBCAoECwcBCAoEAwMDAwQDAQcLBwEJAAIHCwcBCQALBwEJAAEGCBIBBgsHAQkAAQcIDwIHCwcBCQADCQcIFgcLBwEICgcLBwEICgcLDQIFCw4BBQMDAwMHCAwGBwgSCwcBCAoLBwEICgsHAQgKAwMBCAQBBgkBAgcIFgYICQULBwEICgMLBwEICgsIAQgKCwcBCAoBCwgBCAoCBwsIAQkACgsIAQkAAQYLBQEJAAELBQEJAAILBwEJAAcIDAIJAAUDQmFnB0JhbGFuY2UEQ29pbgJJRAZPcHRpb24VUG9vbFRva2VuRXhjaGFuZ2VSYXRlA1NVSQxTdGFrZVN1YnNpZHkJU3Rha2VkU3VpC1N0b3JhZ2VGdW5kE1N1aVN5c3RlbVN0YXRlSW5uZXIVU3VpU3lzdGVtU3RhdGVJbm5lclYyFFN5c3RlbUVwb2NoSW5mb0V2ZW50EFN5c3RlbVBhcmFtZXRlcnMSU3lzdGVtUGFyYW1ldGVyc1YyBVRhYmxlCVR4Q29udGV4dB9VbnZlcmlmaWVkVmFsaWRhdG9yT3BlcmF0aW9uQ2FwCVZhbGlkYXRvchVWYWxpZGF0b3JPcGVyYXRpb25DYXAMVmFsaWRhdG9yU2V0BlZlY01hcAZWZWNTZXQaYWN0aXZlX3ZhbGlkYXRvcl9hZGRyZXNzZXMRYWN0aXZlX3ZhbGlkYXRvcnMNYWR2YW5jZV9lcG9jaCZhc3NlcnRfbm9fcGVuZGluZ19vcl9hY3RpdmVfZHVwbGljYXRlcwNiYWcHYmFsYW5jZQRjb2luCGNvbnRhaW5zBmNyZWF0ZRhjcmVhdGVfc3lzdGVtX3BhcmFtZXRlcnMaZGVyaXZlX3JlZmVyZW5jZV9nYXNfcHJpY2UMZGVzdHJveV9zb21lDGRlc3Ryb3lfemVybwRlbWl0BWVtcHR5BWVwb2NoEWVwb2NoX2R1cmF0aW9uX21zGGVwb2NoX3N0YXJ0X3RpbWVzdGFtcF9tcwVldmVudAxleHRyYV9maWVsZHMUZXh0cmFjdF9jb2luX2JhbGFuY2UMZnJvbV9iYWxhbmNlB2dlbmVzaXMcZ2VuZXNpc19zeXN0ZW1fc3RhdGVfdmVyc2lvbgNnZXQHZ2V0X211dBBnZXRfcmVwb3J0ZXJzX29mH2dldF9zdG9yYWdlX2Z1bmRfb2JqZWN0X3JlYmF0ZXMeZ2V0X3N0b3JhZ2VfZnVuZF90b3RhbF9iYWxhbmNlGmdldF92YWxpZGF0b3JfbXV0X3dpdGhfY3R4L2dldF92YWxpZGF0b3JfbXV0X3dpdGhfY3R4X2luY2x1ZGluZ19jYW5kaWRhdGVzI2dldF92YWxpZGF0b3JfbXV0X3dpdGhfdmVyaWZpZWRfY2FwBmluc2VydAxpbnRvX2JhbGFuY2UiaXNfYWN0aXZlX3ZhbGlkYXRvcl9ieV9zdWlfYWRkcmVzcwhpc19lbXB0eQdpc19zb21lBGpvaW4Iam9pbl92ZWMcbGVmdG92ZXJfc3RvcmFnZV9mdW5kX2luZmxvdxNtYXhfdmFsaWRhdG9yX2NvdW50E21pbl92YWxpZGF0b3JfY291bnQbbWluX3ZhbGlkYXRvcl9qb2luaW5nX3N0YWtlA25ldzNuZXdfdW52ZXJpZmllZF92YWxpZGF0b3Jfb3BlcmF0aW9uX2NhcF9hbmRfdHJhbnNmZXIabmV4dF9lcG9jaF92YWxpZGF0b3JfY291bnQGb2JqZWN0Bm9wdGlvbgpwYXJhbWV0ZXJzA3BheRNwb29sX2V4Y2hhbmdlX3JhdGVzEHByb3RvY29sX3ZlcnNpb24PcHVibGljX3RyYW5zZmVyE3JlZmVyZW5jZV9nYXNfcHJpY2UGcmVtb3ZlEHJlcG9ydF92YWxpZGF0b3IVcmVwb3J0X3ZhbGlkYXRvcl9pbXBsEXJlcXVlc3RfYWRkX3N0YWtlGnJlcXVlc3RfYWRkX3N0YWtlX211bF9jb2luFXJlcXVlc3RfYWRkX3ZhbGlkYXRvch9yZXF1ZXN0X2FkZF92YWxpZGF0b3JfY2FuZGlkYXRlGHJlcXVlc3RfcmVtb3ZlX3ZhbGlkYXRvciJyZXF1ZXN0X3JlbW92ZV92YWxpZGF0b3JfY2FuZGlkYXRlG3JlcXVlc3Rfc2V0X2NvbW1pc3Npb25fcmF0ZRVyZXF1ZXN0X3NldF9nYXNfcHJpY2UWcmVxdWVzdF93aXRoZHJhd19zdGFrZRRyb3RhdGVfb3BlcmF0aW9uX2NhcAlzYWZlX21vZGUdc2FmZV9tb2RlX2NvbXB1dGF0aW9uX3Jld2FyZHMkc2FmZV9tb2RlX25vbl9yZWZ1bmRhYmxlX3N0b3JhZ2VfZmVlGXNhZmVfbW9kZV9zdG9yYWdlX3JlYmF0ZXMZc2FmZV9tb2RlX3N0b3JhZ2VfcmV3YXJkcwZzZW5kZXIdc2V0X2NhbmRpZGF0ZV9jb21taXNzaW9uX3JhdGUXc2V0X2NhbmRpZGF0ZV9nYXNfcHJpY2Unc2V0X2NhbmRpZGF0ZV92YWxpZGF0b3JfY29tbWlzc2lvbl9yYXRlIXNldF9jYW5kaWRhdGVfdmFsaWRhdG9yX2dhc19wcmljZQlzaW5nbGV0b24Fc3BsaXQWc3Rha2VfYWN0aXZhdGlvbl9lcG9jaA1zdGFrZV9zdWJzaWR5FHN0YWtlX3N1YnNpZHlfYW1vdW50GXN0YWtlX3N1YnNpZHlfc3RhcnRfZXBvY2gMc3Rha2luZ19wb29sFXN0YWtpbmdfcG9vbF9tYXBwaW5ncw5zdG9yYWdlX2NoYXJnZQxzdG9yYWdlX2Z1bmQUc3RvcmFnZV9mdW5kX2JhbGFuY2UZc3RvcmFnZV9mdW5kX3JlaW52ZXN0bWVudA5zdG9yYWdlX3JlYmF0ZQNzdWkKc3VpX3N5c3RlbRZzdWlfc3lzdGVtX3N0YXRlX2lubmVyFHN5c3RlbV9zdGF0ZV92ZXJzaW9uBXRhYmxlDXRvdGFsX2JhbGFuY2UOdG90YWxfZ2FzX2ZlZXMcdG90YWxfb2JqZWN0X3N0b3JhZ2VfcmViYXRlcwt0b3RhbF9zdGFrZR90b3RhbF9zdGFrZV9yZXdhcmRzX2Rpc3RyaWJ1dGVkCHRyYW5zZmVyCnR4X2NvbnRleHQVdW5kb19yZXBvcnRfdmFsaWRhdG9yGnVuZG9fcmVwb3J0X3ZhbGlkYXRvcl9pbXBsIHVwZGF0ZV9jYW5kaWRhdGVfbmV0d29ya19hZGRyZXNzH3VwZGF0ZV9jYW5kaWRhdGVfbmV0d29ya19wdWJrZXkcdXBkYXRlX2NhbmRpZGF0ZV9wMnBfYWRkcmVzcyB1cGRhdGVfY2FuZGlkYXRlX3ByaW1hcnlfYWRkcmVzcyB1cGRhdGVfY2FuZGlkYXRlX3Byb3RvY29sX3B1YmtleSp1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl9uZXR3b3JrX2FkZHJlc3MpdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfbmV0d29ya19wdWJrZXkmdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfcDJwX2FkZHJlc3MqdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfcHJpbWFyeV9hZGRyZXNzKnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3Byb3RvY29sX3B1YmtleSl1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl93b3JrZXJfYWRkcmVzcyh1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl93b3JrZXJfcHVia2V5H3VwZGF0ZV9jYW5kaWRhdGVfd29ya2VyX2FkZHJlc3MedXBkYXRlX2NhbmRpZGF0ZV93b3JrZXJfcHVia2V5EnVwZGF0ZV9kZXNjcmlwdGlvbhB1cGRhdGVfaW1hZ2VfdXJsC3VwZGF0ZV9uYW1lIXVwZGF0ZV9uZXh0X2Vwb2NoX25ldHdvcmtfYWRkcmVzcyB1cGRhdGVfbmV4dF9lcG9jaF9uZXR3b3JrX3B1YmtleR11cGRhdGVfbmV4dF9lcG9jaF9wMnBfYWRkcmVzcyF1cGRhdGVfbmV4dF9lcG9jaF9wcmltYXJ5X2FkZHJlc3MhdXBkYXRlX25leHRfZXBvY2hfcHJvdG9jb2xfcHVia2V5IHVwZGF0ZV9uZXh0X2Vwb2NoX3dvcmtlcl9hZGRyZXNzH3VwZGF0ZV9uZXh0X2Vwb2NoX3dvcmtlcl9wdWJrZXkSdXBkYXRlX3Byb2plY3RfdXJsHHVwZGF0ZV92YWxpZGF0b3JfZGVzY3JpcHRpb24adXBkYXRlX3ZhbGlkYXRvcl9pbWFnZV91cmwVdXBkYXRlX3ZhbGlkYXRvcl9uYW1lK3VwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9uZXR3b3JrX2FkZHJlc3MqdXBkYXRlX3ZhbGlkYXRvcl9uZXh0X2Vwb2NoX25ldHdvcmtfcHVia2V5J3VwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9wMnBfYWRkcmVzcyt1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfcHJpbWFyeV9hZGRyZXNzK3VwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9wcm90b2NvbF9wdWJrZXkqdXBkYXRlX3ZhbGlkYXRvcl9uZXh0X2Vwb2NoX3dvcmtlcl9hZGRyZXNzKXVwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF93b3JrZXJfcHVia2V5HHVwZGF0ZV92YWxpZGF0b3JfcHJvamVjdF91cmwIdjFfdG9fdjIJdmFsaWRhdG9yDXZhbGlkYXRvcl9jYXAgdmFsaWRhdG9yX2xvd19zdGFrZV9ncmFjZV9wZXJpb2QddmFsaWRhdG9yX2xvd19zdGFrZV90aHJlc2hvbGQYdmFsaWRhdG9y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b3dlcg9Wb3RpbmdQb3dlckluZm8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwx2b3RpbmdfcG93ZXIRVm90aW5nUG93ZXJJbmZvVjIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAxF2YWxpZGF0b3Jfd3JhcHBlchBWYWxpZGF0b3JXcmFwcGVyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMNdmFsaWRhdG9yX3NldAxWYWxpZGF0b3JTZXQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw12YWxpZGF0b3Jfc2V0F1ZhbGlkYXRvckVwb2NoSW5mb0V2ZW50AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMNdmFsaWRhdG9yX3NldBlWYWxpZGF0b3JFcG9jaEluZm9FdmVudFYyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMNdmFsaWRhdG9yX3NldBJWYWxpZGF0b3JKb2luRXZlbnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw12YWxpZGF0b3Jfc2V0E1ZhbGlkYXRvckxlYXZlRXZlbnQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwxzdG9yYWdlX2Z1bmQLU3RvcmFnZUZ1bmQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAw1zdGFrZV9zdWJzaWR5DFN0YWtlU3Vic2lkeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADFnN1aV9zeXN0ZW1fc3RhdGVfaW5uZXIQU3lzdGVtUGFyYW1ldGVycwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADFnN1aV9zeXN0ZW1fc3RhdGVfaW5uZXISU3lzdGVtUGFyYW1ldGVyc1YyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMWc3VpX3N5c3RlbV9zdGF0ZV9pbm5lchNTdWlTeXN0ZW1TdGF0ZUlubmVyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMWc3VpX3N5c3RlbV9zdGF0ZV9pbm5lchVTdWlTeXN0ZW1TdGF0ZUlubmVyVjIAAAAA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Cw4BCQALDgEJAQMBBggPAQYLDgEJAAILDgEJAAcIFh4BAQMDBwsJAQgFCw4BCQADAwMLDgEJAAMGCAQHCAQDAwMGCwwBAwMDCBILDgEJAQsOAQkBAQMDAwEDBwgFAwEGCBMBBggSAQYLCQEJAAIHCwkBCQADAQcJAAEGCxECCQAJAQEGCwwBCQACBgsRAgkACQEJAAEGCQEDBwsLAQkACBIDAgEDAgcLDgEJAAMCBwsOAQkACw4BCQACCBILEQIDAwIHCxUCCQAJAQkAAQcJAQIHCxECCQAJAQkAAgYLCQEJAAMaAQMBAwMHCwkBCAULDgEJAAMDCw4BCQADBggEBwgEAwYLDAEDAwMIEgsOAQkBAQMDCw4BCQEDBwgFAxsBAwEDAwcLCQEIBQsOAQkAAwMLDgEJAQMGCAQHCAQDAwYLDAEDAwMIEgsOAQkBAQMDCw4BCQEDBwgFAwMLDgEJAAsOAQkBCw4BCQECBwsQAQkACxABCQAHAwcLCQEIBQgEAwMDCBIDBwsLAQkABggKAwMHCwkBCQADCQADBwsRAgkACQEJAAkBAQsBAgkACQECBgsVAgkACQEJAAMHCxUCCQAJAQkACQELCw4BCQALDgEJAAsOAQkAAwMLDgEJAQsOAQkBCw4BCQEDAwgSAgYLCwEJAAgSAQsCAgkACQEBCwMCCQAJAQsDAwYLCQEIBQcLCQEIBQMBCAQDAwgSBwsRAgMDAwMHCAUIBA0DBwsJAQgFAwMBBwsJAQgFCAQDAwgSAwgSBwsRAgMDEQMDBgsJAQgFBwsJAQgFAwMBAwMDCAQDCBIDAwgSBwsRAgMDBwYIBQoIBAYIBAYLDAEDAwgSBgsRAgMDBQMDAwMIEgYDCgMDAwMKAwQDBggEBgsMAQMGCxECAwgEBAYLCQEIBQMIEgYLEQIDAwpBY2NvdW50Q2FwB0JhbGFuY2UFQ2xvY2sEQ29pbgtDcml0Yml0VHJlZQlDdXN0b2RpYW4CSUQLTGlua2VkVGFibGUGT3B0aW9uBU9yZGVyDU9yZGVyQ2FuY2VsZWQLT3JkZXJGaWxsZWQNT3JkZXJGaWxsZWRWMgtPcmRlclBsYWNlZA1PcmRlclBsYWNlZFYyBFBvb2wLUG9vbENyZWF0ZWQDU1VJBVRhYmxlCVRpY2tMZXZlbAlUeENvbnRleHQIVHlwZU5hbWUDVUlEGWFjY291bnRfYXZhaWxhYmxlX2JhbGFuY2UPYWNjb3VudF9iYWxhbmNlA2FkZARhc2tzBGJhY2sHYmFsYW5jZQpiYXNlX2Fzc2V0HGJhc2VfYXNzZXRfcXVhbnRpdHlfY2FuY2VsZWQaYmFzZV9hc3NldF9xdWFudGl0eV9maWxsZWQaYmFzZV9hc3NldF9xdWFudGl0eV9wbGFjZWQdYmFzZV9hc3NldF9xdWFudGl0eV9yZW1haW5pbmcXYmFzZV9hc3NldF90cmFkaW5nX2ZlZXMOYmFzZV9jdXN0b2RpYW4SYmF0Y2hfY2FuY2VsX29yZGVyBGJpZHMGYm9ycm93FGJvcnJvd19sZWFmX2J5X2luZGV4EmJvcnJvd19sZWFmX2J5X2tleQpib3Jyb3dfbXV0GGJvcnJvd19tdXRfbGVhZl9ieV9pbmRleBFjYW5jZWxfYWxsX29yZGVycwxjYW5jZWxfb3JkZXIEY2xvYgVjbG9jawRjb2luCGNvbnRhaW5zDmNyZWF0ZV9hY2NvdW50C2NyZWF0ZV9wb29sDGNyZWF0aW9uX2ZlZQdjcml0Yml0CWN1c3RvZGlhbh9kZWNyZWFzZV91c2VyX2F2YWlsYWJsZV9iYWxhbmNlHGRlY3JlYXNlX3VzZXJfbG9ja2VkX2JhbGFuY2UMZGVwb3NpdF9iYXNlDWRlcG9zaXRfcXVvdGUNZGVzdHJveV9lbXB0eRNkZXN0cm95X2VtcHR5X2xldmVsBGVtaXQTZW1pdF9vcmRlcl9jYW5jZWxlZBFlbWl0X29yZGVyX2ZpbGxlZAVldmVudBBleHBpcmVfdGltZXN0YW1wEGZpbmRfY2xvc2VzdF9rZXkJZmluZF9sZWFmDGZyb21fYmFsYW5jZQVmcm9udBZnZXRfbGV2ZWwyX2Jvb2tfc3RhdHVzH2dldF9sZXZlbDJfYm9va19zdGF0dXNfYXNrX3NpZGUfZ2V0X2xldmVsMl9ib29rX3N0YXR1c19iaWRfc2lkZRBnZXRfbWFya2V0X3ByaWNlEGdldF9vcmRlcl9zdGF0dXMCaWQfaW5jcmVhc2VfdXNlcl9hdmFpbGFibGVfYmFsYW5jZRJpbmplY3RfbGltaXRfb3JkZXILaW5zZXJ0X2xlYWYMaW50b19iYWxhbmNlBmlzX2JpZAhpc19lbXB0eQdpc19ub25lBGpvaW4MbGlua2VkX3RhYmxlEGxpc3Rfb3Blbl9vcmRlcnMMbG9ja19iYWxhbmNlCGxvdF9zaXplEW1ha2VyX3JlYmF0ZV9yYXRlDW1ha2VyX3JlYmF0ZXMJbWF0Y2hfYXNrCW1hdGNoX2JpZB1tYXRjaF9iaWRfd2l0aF9xdW90ZV9xdWFudGl0eQRtYXRoCG1heF9sZWFmCG1pbl9sZWFmA211bANuZXcEbmV4dBFuZXh0X2Fza19vcmRlcl9pZBFuZXh0X2JpZF9vcmRlcl9pZAluZXh0X2xlYWYGb2JqZWN0C29wZW5fb3JkZXJzBm9wdGlvbghvcmRlcl9pZAxvcmRlcl9pc19iaWQFb3duZXIRcGxhY2VfbGltaXRfb3JkZXIScGxhY2VfbWFya2V0X29yZGVyB3Bvb2xfaWQNcHJldmlvdXNfbGVhZgVwcmljZQlwdXNoX2JhY2sIcXVhbnRpdHkLcXVvdGVfYXNzZXQYcXVvdGVfYXNzZXRfdHJhZGluZ19mZWVzD3F1b3RlX2N1c3RvZGlhbgZyZW1vdmUUcmVtb3ZlX2xlYWZfYnlfaW5kZXgMcmVtb3ZlX29yZGVyBXNwbGl0A3N1aRlzd2FwX2V4YWN0X2Jhc2VfZm9yX3F1b3RlGXN3YXBfZXhhY3RfcXVvdGVfZm9yX2Jhc2UFdGFibGUQdGFrZXJfY29tbWlzc2lvbg50YWtlcl9mZWVfcmF0ZQl0aWNrX3NpemUMdGltZXN0YW1wX21zDnRvdGFsX3F1YW50aXR5CnR4X2NvbnRleHQJdHlwZV9uYW1lDHVpZF9hc19pbm5lcg51bmxvY2tfYmFsYW5jZQp1bnNhZmVfZGl2CnVuc2FmZV9tdWwQdW5zYWZlX211bF9yb3VuZA91c3Jfb3Blbl9vcmRlcnMFdmFsdWUOd2l0aGRyYXdfYXNzZXQNd2l0aGRyYXdfYmFzZQ53aXRoZHJhd19xdW90ZQR6ZXJvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3ukAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAwgAAAAAAAAAAAMIAwAAAAAAAAADCAQAAAAAAAAAAwgFAAAAAAAAAAMIBgAAAAAA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AAsUAQkBAwsUAQkACxQBCQEKCwgCCQAJAQoHCwsCCQAJAQYIDgMDAQsUAQkACxQBCQEGCBMBBwgaAwsUAQkACxQBCQELEAEKCwgCCQAJAQoHCwsCCQAJAQMDAwMBAgMGCA4HCBoBAwsHCwsCCQAJAQMDAwIBAwIGCBMGCA4HCBoEAwMBAwUDAwEDCgsIAgkACQEMBwsLAgkACQEDAwMCAQMCBggTBggOAQcIGgUDAwEDCxABCgsIAgkACQECCBYGCAkHCBYDBQYICQMDAwMHCwsCCQAJAQMGCA4FBwsNAQgKBwsVAgMDAwMFAQgJAgcLCwIJAAkBBggOAwcLCwIJAAkBCgMGCA4EBwsLAgkACQEGCBMKAwoFAgYLCwIJAAkBBggOAQoICQQDAwMDAgsQAQMLEAEDBAYLCwIJAAkBAwMGCBMCCgMKAwMGCw0BCAoDAwMGCwsCCQAJAQMGCA4BBggJBggWBQYICQMDAwELCAIJAAkBAQYLCAIJAAkBCQgWAwEFBQMDAwMBBgsNAQgKAQYICgEGCxUCAwgJAQUCCQAJAQIFCxUCAwMCBgsZAgkACQEJAAEGCQEBCQECBwsSAQkAAwELEgEJAAILEgEJAAcIGgEIFwELFQIDCAkCAwgJAQsVAgkACQEBBggaAgkABQEJAAEIGAcIEQgXBQgWCAwIFwgRAQYLEgEJAAEIEQIDAwEGCBcBBggWAQgAAQsNAQkAAQsZAgkACQEBCw8BCQABBgsUAQkAAQYIDgMHCw8BCQAFCxIBCQABCwYBCQABCwYBCQEBCwcBCQAEBwsPAQkAAwYIDgcIGgELBwEJAQQDCxQBCQALFAEJAQMFCxABCgsIAgkACQEDCxQBCQALFAEJAQMBCgsIAgkACQEBBwsQAQkAAwsSAQkACxIBCQEDAQYIEwQLEgEJAAsQAQoLCAIJAAkBCxIBCQEDLAEBAwMLEAEKCwgCCQAJAQsSAQkBCxIBCQABAwMBBQMDAwcLDQEICgsSAQkACwMCCQAJAQoLAwIJAAkBAwMDCxIBCQADBggJBwgJAwMDCgsIAgkACQEGCxABAwMDCBYLEgEJAQsSAQkBAQMDAwEDBwgKAwEGCw0BCQABCxABCQABCwMCCQAJAQIHCw0BCQADAQcJAAEGCxUCCQAJAQEGCxABCQABBgkAAgYLFQIJAAkBCQADBwsPAQkABQMCAQMCBwsSAQkACxIBCQACBwsZAgkACQEJAAEHCQECBwsVAgkACQEJAAIGCw0BCQADAQYKCQABCwQCCQAJASgBAwEDAwsQAQoLCAIJAAkBCxIBCQELEgEJAAEDAwEFAwMDBwsNAQgKCxIBCQALAwIJAAkBCgsDAgkACQEDAwsSAQkAAwYICQcICQMKCwgCCQAJAQYLEAEDAwMIFgsSAQkBAQMDCxIBCQEDBwgKAyoBAwEDAwsQAQoLCAIJAAkBCxIBCQELEgEJAAEDAwEFAwMDBwsNAQgKCxIBCQALAwIJAAkBCgsDAgkACQEDAwsSAQkBAwYICQcICQMDCgsIAgkACQEGCxABAwMDCBYLEgEJAQsSAQkBAQMDCxIBCQEDBwgKAwELEAEKCwgCCQAJAQgLEgEJAAsSAQkACxQBCQALEAEKCwgCCQAJAQsQAQoLCAIJAAkBCxABCgsIAgkACQELEgEJAQsSAQkBAgcLFAEJAAsUAQkAAwcLFAEJAAMHCBoHAwcLDQEICggJAwUDAwMHCw8BCQAGCA4DAwcLDQEJAAMJAAMHCxUCCQAJAQkACQEBCwECCQAJAQMHCxkCCQAJAQkACQEQCxABCgsIAgkACQELEgEJAAsSAQkACxIBCQADCxABCgsIAgkACQELEAEKCwgCCQAJAQsQAQoLCAIJAAkBAwMFCxIBCQELEgEJAQsSAQkBAwMCBgsPAQkABQgIFgMDAQUDAwMBCwICCQAJAQ0IFgMDAwMDAwUDAQUDAwELBQIJAAkBCwMDBgsNAQgKBwsNAQgKAwEICQUDAwcLFQIDAwMDBwgKCAkWAwMDAwcLDQEICgMDAwEFAwsDAgkACQEKCwMCCQAJAQEHCw0BCAoICQMDBQgWAwcLFQIDAxoFAwMDAwMGCw0BCAoHCw0BCAoDAwEDCwMCCQAJAQoLAwIJAAkBAwEDAwMICQMFCBYDAwcLFQIDAxwBBQMDAwMDBwsNAQgKAwMDAwsDAgkACQEKCwMCCQAJAQMBAwMDAwcLDQEICggJAwUIFgMDBwsVAgMDBwYICgoICQYICQYLEAEDAwUGCxUCAwMFAwMFAwMECxABAwsQAQMLEAEDCxABAwYDCgMDAwMKAwQDBggJBgsQAQMGCxUCAwgJBAYLDQEICgMFBgsVAgMDCkFjY291bnRDYXARQWxsT3JkZXJzQ2FuY2VsZWQaQWxsT3JkZXJzQ2FuY2VsZWRDb21wb25lbnQHQmFsYW5jZQVDbG9jawRDb2luC0NyaXRiaXRUcmVlCUN1c3RvZGlhbgxEZXBvc2l0QXNzZXQCSUQLTGlua2VkVGFibGUUTWF0Y2hlZE9yZGVyTWV0YWRhdGEGT3B0aW9uBU9yZGVyDU9yZGVyQ2FuY2VsZWQLT3JkZXJGaWxsZWQLT3JkZXJQbGFjZWQEUG9vbAtQb29sQ3JlYXRlZAxQb29sT3duZXJDYXADU1VJBVRhYmxlCVRpY2tMZXZlbAlUeENvbnRleHQIVHlwZU5hbWUDVUlEDVdpdGhkcmF3QXNzZXQZYWNjb3VudF9hdmFpbGFibGVfYmFsYW5jZQ9hY2NvdW50X2JhbGFuY2UNYWNjb3VudF9vd25lcgNhZGQEYXNrcwRiYWNrB2JhbGFuY2UKYmFzZV9hc3NldBxiYXNlX2Fzc2V0X3F1YW50aXR5X2NhbmNlbGVkGmJhc2VfYXNzZXRfcXVhbnRpdHlfZmlsbGVkGmJhc2VfYXNzZXRfcXVhbnRpdHlfcGxhY2VkHWJhc2VfYXNzZXRfcXVhbnRpdHlfcmVtYWluaW5nF2Jhc2VfYXNzZXRfdHJhZGluZ19mZWVzDmJhc2VfY3VzdG9kaWFuEmJhdGNoX2NhbmNlbF9vcmRlcgRiaWRzBmJvcnJvdxRib3Jyb3dfbGVhZl9ieV9pbmRleBJib3Jyb3dfbGVhZl9ieV9rZXkKYm9ycm93X211dBhib3Jyb3dfbXV0X2xlYWZfYnlfaW5kZXgRY2FuY2VsX2FsbF9vcmRlcnMMY2FuY2VsX29yZGVyF2NsZWFuX3VwX2V4cGlyZWRfb3JkZXJzD2NsaWVudF9vcmRlcl9pZAdjbG9iX3YyBWNsb2NrC2Nsb25lX29yZGVyBGNvaW4IY29udGFpbnMOY3JlYXRlX2FjY291bnQWY3JlYXRlX2N1c3RvbWl6ZWRfcG9vbBljcmVhdGVfY3VzdG9taXplZF9wb29sX3YyImNyZWF0ZV9jdXN0b21pemVkX3Bvb2xfd2l0aF9yZXR1cm4LY3JlYXRlX3Bvb2wMY3JlYXRlX3Bvb2xfF2NyZWF0ZV9wb29sX3dpdGhfcmV0dXJuGGNyZWF0ZV9wb29sX3dpdGhfcmV0dXJuXwxjcmVhdGlvbl9mZWUHY3JpdGJpdAxjdXN0b2RpYW5fdjIfZGVjcmVhc2VfdXNlcl9hdmFpbGFibGVfYmFsYW5jZRxkZWNyZWFzZV91c2VyX2xvY2tlZF9iYWxhbmNlBmRlbGV0ZRVkZWxldGVfcG9vbF9vd25lcl9jYXAMZGVwb3NpdF9iYXNlDWRlcG9zaXRfcXVvdGUNZGVzdHJveV9lbXB0eRNkZXN0cm95X2VtcHR5X2xldmVsBGVtaXQTZW1pdF9vcmRlcl9jYW5jZWxlZBFlbWl0X29yZGVyX2ZpbGxlZAVldmVudBBleHBpcmVfdGltZXN0YW1wB2V4dHJhY3QQZmluZF9jbG9zZXN0X2tleQlmaW5kX2xlYWYMZnJvbV9iYWxhbmNlBWZyb250A2dldBZnZXRfbGV2ZWwyX2Jvb2tfc3RhdHVzH2dldF9sZXZlbDJfYm9va19zdGF0dXNfYXNrX3NpZGUfZ2V0X2xldmVsMl9ib29rX3N0YXR1c19iaWRfc2lkZRBnZXRfbWFya2V0X3ByaWNlEGdldF9vcmRlcl9zdGF0dXMCaWQfaW5jcmVhc2VfdXNlcl9hdmFpbGFibGVfYmFsYW5jZRJpbmplY3RfbGltaXRfb3JkZXILaW5zZXJ0X2xlYWYMaW50b19iYWxhbmNlBmlzX2JpZAhpc19lbXB0eQdpc19ub25lBGpvaW4MbGlua2VkX3RhYmxlEGxpc3Rfb3Blbl9vcmRlcnMMbG9ja19iYWxhbmNlCGxvdF9zaXplDW1ha2VyX2FkZHJlc3MVbWFrZXJfY2xpZW50X29yZGVyX2lkEW1ha2VyX3JlYmF0ZV9yYXRlDW1ha2VyX3JlYmF0ZXMJbWF0Y2hfYXNrCW1hdGNoX2JpZB1tYXRjaF9iaWRfd2l0aF9xdW90ZV9xdWFudGl0eRZtYXRjaGVkX29yZGVyX21ldGFkYXRhG21hdGNoZWRfb3JkZXJfbWV0YWRhdGFfaW5mbwRtYXRoCG1heF9sZWFmCG1pbl9sZWFmEG1pbnRfYWNjb3VudF9jYXADbXVsA25ldwRuZXh0EW5leHRfYXNrX29yZGVyX2lkEW5leHRfYmlkX29yZGVyX2lkCW5leHRfbGVhZgRub25lBm9iamVjdAtvcGVuX29yZGVycwZvcHRpb24Ib3JkZXJfaWQMb3JkZXJfaXNfYmlkC29yZGVyX3F1ZXJ5D29yZGVyc19jYW5jZWxlZBFvcmlnaW5hbF9xdWFudGl0eQVvd25lchFwbGFjZV9saW1pdF9vcmRlchVwbGFjZV9saW1pdF9vcmRlcl9pbnQfcGxhY2VfbGltaXRfb3JkZXJfd2l0aF9tZXRhZGF0YRJwbGFjZV9tYXJrZXRfb3JkZXIWcGxhY2VfbWFya2V0X29yZGVyX2ludCBwbGFjZV9tYXJrZXRfb3JkZXJfd2l0aF9tZXRhZGF0YQdwb29sX2lkCXBvb2xfc2l6ZQ1wcmV2aW91c19sZWFmBXByaWNlD3B1YmxpY190cmFuc2ZlcglwdXNoX2JhY2sIcXVhbnRpdHkLcXVvdGVfYXNzZXQYcXVvdGVfYXNzZXRfdHJhZGluZ19mZWVzHnF1b3RlX2Fzc2V0X3RyYWRpbmdfZmVlc192YWx1ZQ9xdW90ZV9jdXN0b2RpYW4GcmVtb3ZlFHJlbW92ZV9sZWFmX2J5X2luZGV4DHJlbW92ZV9vcmRlchhzZWxmX21hdGNoaW5nX3ByZXZlbnRpb24Gc2VuZGVyDHNoYXJlX29iamVjdARzaXplBHNvbWUFc3BsaXQDc3VpGXN3YXBfZXhhY3RfYmFzZV9mb3JfcXVvdGUnc3dhcF9leGFjdF9iYXNlX2Zvcl9xdW90ZV93aXRoX21ldGFkYXRhGXN3YXBfZXhhY3RfcXVvdGVfZm9yX2Jhc2Unc3dhcF9leGFjdF9xdW90ZV9mb3JfYmFzZV93aXRoX21ldGFkYXRhBXRhYmxlDXRha2VyX2FkZHJlc3MVdGFrZXJfY2xpZW50X29yZGVyX2lkEHRha2VyX2NvbW1pc3Npb24OdGFrZXJfZmVlX3JhdGUKdGlja19sZXZlbAl0aWNrX3NpemUMdGltZXN0YW1wX21zCHRyYW5zZmVyCnR4X2NvbnRleHQJdHlwZV9uYW1lDHVpZF9hc19pbm5lcg51aWRfdG9fYWRkcmVzcw51bmxvY2tfYmFsYW5jZQp1bnNhZmVfZGl2CnVuc2FmZV9tdWwQdW5zYWZlX211bF9yb3VuZA91c3Jfb3Blbl9vcmRlcnMVdXNyX29wZW5fb3JkZXJzX2V4aXN0G3Vzcl9vcGVuX29yZGVyc19mb3JfYWRkcmVzcwV2YWx1ZQZ2ZWN0b3IOd2l0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AgEDAgcLAgEJAAMBCQABBwkAAQYJAAAEBwsCAQkAAwMBAwYLAgEJAAMDAgMIAQELAwIJAAkBAgMLAAEJAAEGCwMCCQAJAQIGCwMCCQAJAQkAAQYJAQQBAwMDEAMDAwMDAgYIAQEBCAEDAwsAAQkAAwMDAwcLAwIJAAkBCQAJAQEEAQIQAwMDAwMDAwMDAQMDBggBAwMJAAIHCwMCCQAJAQkAAQkBAQcJAQILAwIDCAELAwIDCwABCQACBggBAwtDcml0Yml0VHJlZQxJbnRlcm5hbE5vZGUETGVhZgVUYWJsZQlUeENvbnRleHQDYWRkBmJvcnJvdxRib3Jyb3dfbGVhZl9ieV9pbmRleBJib3Jyb3dfbGVhZl9ieV9rZXkKYm9ycm93X211dBhib3Jyb3dfbXV0X2xlYWZfYnlfaW5kZXgEY2xvYgdjbG9iX3YyE2NvdW50X2xlYWRpbmdfemVyb3MHY3JpdGJpdA1kZXN0cm95X2VtcHR5BGRyb3AQZmluZF9jbG9zZXN0X2tleQlmaW5kX2xlYWYdZ2V0X2Nsb3Nlc3RfbGVhZl9pbmRleF9ieV9rZXkLaW5zZXJ0X2xlYWYOaW50ZXJuYWxfbm9kZXMIaXNfZW1wdHkNaXNfbGVmdF9jaGlsZANrZXkGbGVhdmVzCmxlZnRfY2hpbGQObGVmdF9tb3N0X2xlYWYGbGVuZ3RoBG1hc2sEbWF0aAhtYXhfbGVhZghtaW5fbGVhZgNuZXcYbmV4dF9pbnRlcm5hbF9ub2RlX2luZGV4CW5leHRfbGVhZg9uZXh0X2xlYWZfaW5kZXgGcGFyZW50DXByZXZpb3VzX2xlYWYGcmVtb3ZlFHJlbW92ZV9sZWFmX2J5X2luZGV4C3JpZ2h0X2NoaWxkD3JpZ2h0X21vc3RfbGVhZgRyb290BHNpemUFdGFibGUKdHhfY29udGV4dAx1cGRhdGVfY2hpbGQFdmFsdWUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe6QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAwgCAAAAAAAAAAMIAwAAAAAAAAADCAQAAAAAAAAAAwgFAAAAAAAAAAMIBwAAAAAAAAADCAgAAAAAAAAAAwgAAAAAAAAAgAMI//////////8DCP////////9/AAIDGAMwCQAlAwECBB0DGgMpAyUDAgIHKwMVCwMCAwgBGQsDAgMLAAEJACADHwMiAyQDAgoACgADAAANCwcGCgA4AAsAOAEHBgcGBgAAAAAAAAAABgAAAAAAAAAAOQACAQMAAA0ECwA3ADgCAgIDAAANBAsANwA4AwIDAQAADRUKADgEIAQFBQkLAAEHAycKADcACgA3ARQ4BTcCFAsANwEUAgQBAAANFQoAOAQgBAUFCQsAAQcDJwoANwAKADcDFDgFNwIUCwA3AxQCBQEAABZOCgALATgGDAMBCgMHBiIECgUOCwABBwMnBwcKAxcMBQoANwALAzgFNwQUDAQKBAcGIgQkBR4KAAoECwU4BwwCBSYJDAILAgQyCwQMBQoANwUKBTgIEAYUDAQFGQoEBwYhBDsLAAEGAAAAAAAAAAAHBgIHBwoACgA3BQsEOAgQBxQ4CRcMAwsANwAKAzgFNwIUCwMCBgEAABZPCgALATgGDAMBCgMHBiIECgUOCwABBwMnBwcKAxcMBQoANwALAzgFNwQUDAQKBAcGIgQlBR4KAAoECwU4ByAMAgUnCQwCCwIEMwsEDAUKADcFCgU4CBAGFAwEBRkKBAcGIQQ8CwABBgAAAAAAAAAABwYCBwcKAAoANwULBDgIEAgUOAoXDAMLADcACgM4BTcCFAsDAgcAAAADEwsBDAIKAgcGIwQPBQcKADcFCwI4CBAHFAwCBQILAAELAgIIAAAAAxMLAQwCCgIHBiMEDwUHCgA3BQsCOAgQCBQMAgUCCwABCwICCQMAABf3AQoBCwIHBjkBDA8KADcGFAwQCgA3BhQGAQAAAAAAAAAWCgA2BhUKEAcIBgEAAAAAAAAAFyMEGAUcCwABBwAnCgA2AAoQCw84CwoACgEMAy4LAzgMDAcKBwcGIQRFChAGAAAAAAAAAAAhBDEFNQsAAQcBJwcHChAXCgA2BxUKEAoANgEVCxALADYDFQYAAAAAAAAAAAIKADcACwc4BTcCFAwGCgYKASIEUQVVCwABBwInMUALBgoBHTURFTFAFxcMCAYBAAAAAAAAAAsIMQEXLwwRChEHBgcGBwYSAQwMCgA3CBQMDQoANwgUBgEAAAAAAAAAFgoANggVCgA2BQoNCww4DQoANwcUDBIHBgwOChIHBiMEqQEFhwEKADcFChI4CAwJChEKCRAMFCQElQELCQEFqQELEgwOCgEKCRAMFBwGAAAAAAAAAAAhBKQBCwkQBxQMEgWoAQsJEAgUDBIFggEKDgcGIQSyAQoNCgA2BxUFwQEKAAoOChIMBQwELgsECwU4BwwKCgALDgoNCwo4DgsRCgEcBgAAAAAAAAAAIQwLCgAKDQcHChAXCgs4DgoACw0LEgsLIDgOCgA3AAoANwEUOAU3AhQKASQE4wEKEAoANgEVCgA3AAoANwMUOAU3AhQLASME8wEKEAsANgMVBfUBCwABCxACCgEAAAMbCgA4BAQICwABCQcGAgoACgE4DAwCCwA3AAoCOAU3AhQLASIEGAkHBgIICwICCwMAAAMSCgA4BAQHCwABBgAAAAAAAAAAAgoACwE4DAwCCwA3AAsCOAU3AhQCDAMAABurAQoANwAKATgFNwIUDAwKADcBFAoBIQQZCgAKDAwELgsEOA8MCQELCQoANgEVCgA3AxQKASEEKwoACwwMBS4LBTgQDAoBCwoKADYDFQoANgAKATgROgEMDwwRAQoALjgSBgAAAAAAAAAAIQROBwYKADYHFQcGCgA2ARUHBgoANgMVBgAAAAAAAAAACgA2CBUGAAAAAAAAAAALADYGFQWpAQoPBwYiBFMFVwsAAQcEJwoANwUKDzgIDA4KDhAGFAwNCgAKDwcHCwEXDAcMBi4LBgsHOAcEcQsOEAgUDAgFdQsOEAcUDAgLCAwQCg0HBiEElQEKEAcGIwSHAQcGCgA2BQoQOBMPBhUFkAEHBgoANgAHBwoQFzgUNgQVCxAKADYHFQWkAQoACg0KDwwDDAIuCwILAzgHDAsKAAsNCxALCzgOCwA2BQsPOBUBCxECDQMAAA0GCwA2AAsBOBQ2CQIOAQAADQYLADcACwE4BTcJAg8BAAADDgoACwE4BgwCBAYFCgsAAQcDJwsACwI4FgIQAwAAHw4LADoAAQEBAQwCDAEBCwE4FwsCOBgCEQMAAB8XDgA3ADgCBgAAAAAAAAAAIQQHBQkGAAAAAAAAAAAnCwA6AAEBAQEMAgwBAQsCOBkLATgaAhIAAAAgLgoANwcUDAMKAwcGIQQMCwABBwYCCgMHBiMEKAURCgA3BQsDOAgMAgoBCgIQDBQcBgAAAAAAAAAAIQQjCwIQBxQMAwUnCwIQCBQMAwUMCwABBwcLAxcCEwAAAA0wCgEHBiIEBQUJCwABBwUnCwMEEwoCCgA2BQoBOBMPBxUFGgoCCgA2BQoBOBMPCBUKAgcGJAQoCwELADYABwcLAhc4FDYEFQUvCwELADYFCwI4Ew8GFQIUAAAADQkLADcFCwE4CBAHFAsCIQICAgIDAAACBAACAgEBAwEBAQICBgIAAgUBAAABAAoBCgIKAwoECgUKCQoKCgsKDQoACwAMAAljdXN0b2RpYW72C6Ec6wsGAAAADQEADAIMMAM8mAEE1AEeBfIB7wEH4QOPBAjwB0AKsAgmC9YIBAzaCM0CDacLCA6vCwYPtQsCABYBDwEUASIBJAElAAAEAQABAAEMAAAEDAEAAQECBAEAAQIDDAEAAQMFBwADCAQABAYMAgcBBAEFBwIAACAAAQAACgIDAQAAIQAEAQAAKQUGAQAAGwcIAQAAFwkKAQAAHAsIAQAAGAwKAQAAHgkIAQAAJwwIAQAACQINAQAADAINAQAAEg4PAQABHRoNAQABIx0KAQABKBcNAQABKggKAQACGRkGAQADIQAQAAMmGxwABA0eCAIHBAQQFBYCBwQEER8gAgcEBBUUFQIHBAQhABgCBwQXExUTDxIYEwUSERIMEg0SDhIGEgcSBBIQEhQTFhMBBwgIAQgBAgYLAgEJAAgFAgMDAQsCAQkABAcLAgEJAAMGCAEHCAgBCwQBCQADBwsCAQkACAULAwEJAAADBwsCAQkABggBAwELAwEJAAMHCwIBCQAGCAELAwEJAAMHCwIBCQAIBQMBAwIHCwIBCQAIBQEHCwABCQABCAYDBgsAAQkAAwMBCQACCAULAAEJAAIGCwcCCQAJAQkAAQEBBgkBAQYLAwEJAAELBwIJAAkBAgsDAQkABwgIAgcLAwEJAAsDAQkAAQYIBgEIBQIHCwMBCQADAwcLBwIJAAkBCQAJAQIHCwcCCQAJAQkAAQcJAQdBY2NvdW50CkFjY291bnRDYXAHQmFsYW5jZQRDb2luCUN1c3RvZGlhbgJJRAVUYWJsZQlUeENvbnRleHQDVUlEGWFjY291bnRfYXZhaWxhYmxlX2JhbGFuY2UPYWNjb3VudF9iYWxhbmNlEGFjY291bnRfYmFsYW5jZXMWYWNjb3VudF9sb2NrZWRfYmFsYW5jZQNhZGQRYXZhaWxhYmxlX2JhbGFuY2UHYmFsYW5jZQZib3Jyb3cKYm9ycm93X211dBpib3Jyb3dfbXV0X2FjY291bnRfYmFsYW5jZQRjbG9iBGNvaW4IY29udGFpbnMJY3VzdG9kaWFuH2RlY3JlYXNlX3VzZXJfYXZhaWxhYmxlX2JhbGFuY2UcZGVjcmVhc2VfdXNlcl9sb2NrZWRfYmFsYW5jZQxmcm9tX2JhbGFuY2UCaWQfaW5jcmVhc2VfdXNlcl9hdmFpbGFibGVfYmFsYW5jZRxpbmNyZWFzZV91c2VyX2xvY2tlZF9iYWxhbmNlBGpvaW4MbG9ja19iYWxhbmNlDmxvY2tlZF9iYWxhbmNlEG1pbnRfYWNjb3VudF9jYXADbmV3Bm9iamVjdAVzcGxpdAV0YWJsZQp0eF9jb250ZXh0DHVpZF90b19pbm5lcg51bmxvY2tfYmFsYW5jZQV2YWx1ZQ53aXRoZHJhd19hc3NldAR6ZXJvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3ukAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgACAg4LAwEJAB8LAwEJAAECARoIBgICAhoIBgsLBwIIBQsAAQkAAhIAEgABAAAIBAsAERISAQIBAwAAERsKADcACgE4ACAECwsAAQYAAAAAAAAAAAYAAAAAAAAAAAILADcACwE4AQwCCgI3ATgCDAMLAjcCOAIMBAsDCwQCAgMAAAgGCgAREgsAOAM5AAIDAwAACAcLAAsCCwE4BAsDOAUCBAMAAAgICwALATgGNgELAjgHAQIFAwAACAkLAAsBEAMREzgGNgELAjgIAgYDAAAICgsACwEQAxETOAY2AgsCOAcBAgcDAAAIBwsACwE4BjYCCwI4CAIIAwAACgoKAAoBCwI4BAwDCwALAQsDOAkCCQMAAAoKCgAKAQsCOAoMAwsACwELAzgLAgoDAAAIBwsANwALATgBNwE4AgILAwAACAcLADcACwE4ATcCOAICDAAAAAgSCgA3AAoBOAAgBA0KADYACgE4DDgMOQE4DQsANgALATgOAgIBAAAAAQEAABIBEgISABMAC29yZGVyX3F1ZXJ5tg2hHOsLBgAAAAsBAAoCCigDMrABBOIBIAWCAu8BB/EDjwMIgAdgBuAHCgrqBxMM/Qf6BA33DAgAIwALAA4BIQIYAAQCAAEDBgABBQwCAAEAAQEGBAACAAQBBAADAgcBAAAEAQwCBwAEAQAWAAECAAAAFQABAgAAABcCAwAAJAQFAAASBAYAAB4EBwAAHQQHAAAiCAkAACcICQABBwwNAgAAAQgMDQIAAAEMCA8AARAICQABIBkaAAEiCAkAAScICQACChcYAQQCGRUWAQQCGhUWAQQCHBcWAQQCJRcWAQQDDxEQAQADExMGAQADFBMGAQADHw4RAQADJhARAQAECRweAgcEBA0cBgIHBAQRHRMCBwQEGxwTAgcECgsZCRgJCQsXCRUJEhQRFBAUGxsTFBQUHBsaGx0bFgkGBgsCAgkACQELBQEDCwUBAwsFAQMLBQEDAQEIAAYGCwQBCAMLBQEDCwUBAwsFAQMLBQEDAQEKCAEBBggAAQYKCAEBAQELBQEDAQYIAQEDBgoIAQELBQEDCwUBAwgBCggBAgkACQEBBgsCAgkACQEBBgsEAQgDAAEIAQEJAAELBQEJABMDAQMDAwMBAwMLBQEDAQEDAwsFAQMGCwYCAwgBBggBCggBAwEGCwUBCQABCAMBBgsEAQkAAgMDAgYLBAEJAAMBBgkAAQYIAwEGCwYCAwgBAgMIAQIGCwYCCQAJAQkAAQYLBgIJAAkBAQYJAQtDcml0Yml0VHJlZQtMaW5rZWRUYWJsZQZPcHRpb24FT3JkZXIJT3JkZXJQYWdlBFBvb2wJVGlja0xldmVsBGFza3MEYmlkcwZib3Jyb3cSYm9ycm93X2xlYWZfYnlfa2V5B2Nsb2JfdjILY2xvbmVfb3JkZXIIY29udGFpbnMHY3JpdGJpdAxkZXN0cm95X3NvbWUQZXhwaXJlX3RpbWVzdGFtcAVmcm9udA1oYXNfbmV4dF9wYWdlB2lzX25vbmUHaXNfc29tZQlpdGVyX2Fza3MJaXRlcl9iaWRzE2l0ZXJfdGlja3NfaW50ZXJuYWwMbGlua2VkX3RhYmxlCG1heF9sZWFmCG1pbl9sZWFmBG5leHQJbmV4dF9sZWFmDW5leHRfb3JkZXJfaWQPbmV4dF90aWNrX2xldmVsBG5vbmULb3Blbl9vcmRlcnMGb3B0aW9uCG9yZGVyX2lkC29yZGVyX3F1ZXJ5Bm9yZGVycw1wcmV2aW91c19sZWFmBHNvbWUKdGlja19sZXZlbAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAN7pAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgMIZAAAAAAAAAAAAgQkCggBEgEeCwUBAx0LBQEDAAEAAAosCwA4AAsBCwILAwsECwURAgwLDgtBDwcAJAQeDQtFDwwKCwsIDgoRDzgBDgoRDjgBDAkMCAwHDAYFJgsLCTgCOAIMCQwIDAcMBgsGCwcLCAsJEgACAQEAAAosCwA4AwsBCwILAwsECwURAgwLDgtBDwcAJAQeDQtFDwwKCwsIDgoRDzgBDgoRDjgBDAkMCAwHDAYFJgsLCTgCOAIMCQwIDAcMBgsGCwcLCAsJEgACAgAAABK1AQ4BOAQEBwsBOAUMCwUWCgUEDgoAOAYMCgwGBRIKADgHDAoMBgsGCwoBDAsLCwwYQA8AAAAAAAAAAAwXChgGAAAAAAAAAAAiBCcFHw4XQQ8HAAYBAAAAAAAAABYjDAwFKQkMDAsMBLEBCgAKGDgIEQ0MFQ4COAQEVQoCOAUMEgoVChI4CSAETwsVAQoFBEUKAAsYOAoMDgwNBUoKAAsYOAsMDgwNCw0LDgEMGAUaOAIMAgsSOAEMDwVZChU4DBQMDwsPDBQOFDgEBGcFXw4XQQ8HAAYBAAAAAAAAABYjDBAFaQkMEAsQBJ8BCxQ4BQwTChUKEzgNDBYOBDgEBHsKEwoEOAUkDBEFfQkMEQsRBIQBCxYBCxUBBZ8BChULEzgOFAwUDgM4DwSPAQgMBwWVAQoWEQwKAzgFJAwHCwcEnAENFwsWEQtEDwWeAQsWAQVbCgUEpwEKAAsYOAoMCQwIBawBCgALGDgLDAkMCAsICwkBDBgFGgsAAQsXAgMBAAAOAwsAEAACBAEAAA4ECwAQARQCBQEAAA4ECwAQAhQCBgEAAA4ECwAQAxQCBwEAAA4DCwARDgIIAQAADgMLABEPAgAAAAEAAgADAAxjdXN0b2RpYW5fdjK7DaEc6wsGAAAADgEADAIMLAM4rAEE5AEeBYIC+AEH+gPbBAjVCEAGlQkKCp8JJwvGCQQMygmcAw3mDAoO8AwGD/YMAgAXAQ8BFAElASgBKQAABAEAAQABDAAABAwBAAEBAgQBAAECAwwBAAEDBwQABAUMAgcBBAEFBgIAACMAAQAAFgIBAAAbAQMAAAwEBQAACQYHAQAAJAAIAQAALQkKAQAAHgsDAQAAGAwNAQAAHw4DAQAAGQ8NAQAAIQwDAQAAKw8DAQAACAYQAQAACwYQAQAAEhESAQABIB8QAQABJyANAQABLBwQAQABLgMNAQACHB4KAQADGhQDAAMkABQAAyoVBQAEDSEDAgcEBBAZGwIHBAQRIiMCBwQEFRkaAgcEBCQAHQIHBBsYGRgSFxwYCBcUFw8XEBcRFwkXChcHFxMXGBgaGAEHCAcBCAECBggBBwgHAAEGCAEBBQIGCwIBCQAFAgMDAQsCAQkABAcLAgEJAAMGCAEHCAcBCwQBCQADBwsCAQkABQsDAQkAAwcLAgEJAAYIAQMBCwMBCQADBwsCAQkABggBCwMBCQADBwsCAQkABQMBAwIHCwIBCQAFAQcLAAEJAAIIBQUBCAUBBggFAwYLAAEJAAMDAQkAAgULAAEJAAIGCwYCCQAJAQkAAQEBBgkBAQYLAwEJAAELBgIJAAkBAgsDAQkABwgHAgcLAwEJAAsDAQkAAgcLAwEJAAMDBwsGAgkACQEJAAkBAgcLBgIJAAkBCQABBwkBB0FjY291bnQKQWNjb3VudENhcAdCYWxhbmNlBENvaW4JQ3VzdG9kaWFuBVRhYmxlCVR4Q29udGV4dANVSUQZYWNjb3VudF9hdmFpbGFibGVfYmFsYW5jZQ9hY2NvdW50X2JhbGFuY2UQYWNjb3VudF9iYWxhbmNlcxZhY2NvdW50X2xvY2tlZF9iYWxhbmNlDWFjY291bnRfb3duZXIDYWRkEWF2YWlsYWJsZV9iYWxhbmNlB2JhbGFuY2UGYm9ycm93CmJvcnJvd19tdXQaYm9ycm93X211dF9hY2NvdW50X2JhbGFuY2UHY2xvYl92MgRjb2luCGNvbnRhaW5zGGNyZWF0ZV9jaGlsZF9hY2NvdW50X2NhcAxjdXN0b2RpYW5fdjIfZGVjcmVhc2VfdXNlcl9hdmFpbGFibGVfYmFsYW5jZRxkZWNyZWFzZV91c2VyX2xvY2tlZF9iYWxhbmNlBmRlbGV0ZRJkZWxldGVfYWNjb3VudF9jYXAMZnJvbV9iYWxhbmNlAmlkH2luY3JlYXNlX3VzZXJfYXZhaWxhYmxlX2JhbGFuY2UcaW5jcmVhc2VfdXNlcl9sb2NrZWRfYmFsYW5jZQRqb2luDGxvY2tfYmFsYW5jZQ5sb2NrZWRfYmFsYW5jZRBtaW50X2FjY291bnRf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YXRhD0N1cnJlbmN5Q3JlYXRlZAdEZW55Q2FwCERlbnlMaXN0AklEBk9wdGlvbhVSZWd1bGF0ZWRDb2luTWV0YWRhdGEGU3RyaW5nBlN1cHBseQtUcmVhc3VyeUNhcAlUeENvbnRleHQIVHlwZU5hbWUDVUlEA1VybANhZGQFYXNjaWkHYmFsYW5jZQtiYWxhbmNlX211dARidXJuBGNvaW4UY29pbl9tZXRhZGF0YV9vYmplY3QIY29udGFpbnMPY3JlYXRlX2N1cnJlbmN5GWNyZWF0ZV9yZWd1bGF0ZWRfY3VycmVuY3kNY3JlYXRlX3N1cHBseQhkZWNpbWFscw9kZWNyZWFzZV9zdXBwbHkGZGVsZXRlD2RlbnlfY2FwX29iamVjdAlkZW55X2xpc3QNZGVueV9saXN0X2FkZBJkZW55X2xpc3RfY29udGFpbnMQZGVueV9saXN0X3JlbW92ZQtkZXNjcmlwdGlvbgxkZXN0cm95X3plcm8NZGl2aWRlX2ludG9fbg1mcmVlemVfb2JqZWN0DGZyb21fYmFsYW5jZQxnZXRfZGVjaW1hbHMPZ2V0X2Rlc2NyaXB0aW9uDGdldF9pY29uX3VybAhnZXRfbmFtZQpnZXRfc3ltYm9sFWdldF93aXRoX29yaWdpbmFsX2lkcwhpY29uX3VybAJpZA9pbmNyZWFzZV9zdXBwbHkMaW50b19iYWxhbmNlCmludG9fYnl0ZXMLaW50b19zdHJpbmcTaXNfb25lX3RpbWVfd2l0bmVzcwxpc19wcmltaXRpdmUEam9pbgRtaW50EW1pbnRfYW5kX3RyYW5zZmVyDG1pbnRfYmFsYW5jZQRuYW1lA25ldwpuZXdfdW5zYWZlBm9iamVjdAZvcHRpb24PcHVibGljX3RyYW5zZmVyA3B1dAZyZW1vdmUEc29tZQVzcGxpdAZzdHJpbmcGc3VwcGx5DHN1cHBseV9pbW11dApzdXBwbHlfbXV0DHN1cHBseV92YWx1ZQZzeW1ib2wEdGFrZQx0b3RhbF9zdXBwbHkIdHJhbnNmZXIUdHJlYXN1cnlfaW50b19zdXBwbHkKdHhfY29udGV4dAl0eXBlX25hbWUFdHlwZXMSdXBkYXRlX2Rlc2NyaXB0aW9uD3VwZGF0ZV9pY29uX3VybAt1cGRhdGVfbmFtZQ11cGRh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CQABCQECCA0IDQMHCA4JAAkBAQsHAQkAAQsJAQkAAgkAAwEGCwwBCQACCAYBAgcLCwEJAAsMAQkAAQsIAQkAAwgNAwgNAQsRAQkABggNCA0DCA0IDggNBQgNCA0IDQMDAwgOCA0IDQMDAwMBBgsKAQkAAQsKAQkAAQYLCwEJAAMHCwsBCQADBwgSAQgEAgYIDgkAAQgGAQgFAQYJAQEHCQEHQmFsYW5jZQZCb3Jyb3cEQ29pbgJJRARJdGVtDEl0ZW1EZWxpc3RlZApJdGVtTGlzdGVkDUl0ZW1QdXJjaGFzZWQFS2lvc2sNS2lvc2tPd25lckNhcAdMaXN0aW5nBExvY2sGT3B0aW9uC1B1cmNoYXNlQ2FwA1NVSQ5UcmFuc2ZlclBvbGljeQ9UcmFuc2ZlclJlcXVlc3QJVHhDb250ZXh0A1VJRANhZGQQYWxsb3dfZXh0ZW5zaW9ucwdiYWxhbmNlBmJvcnJvdwpib3Jyb3dfbXV0CmJvcnJvd192YWwSY2xvc2VfYW5kX3dpdGhkcmF3BGNvaW4HZGVmYXVsdAZkZWxldGUGZGVsaXN0DGRlc3Ryb3lfc29tZQ1keW5hbWljX2ZpZWxkFGR5bmFtaWNfb2JqZWN0X2ZpZWxkBGVtaXQFZXZlbnQHZXhpc3RzXxBleGlzdHNfd2l0aF90eXBlA2Zvcgxmcm9tX2JhbGFuY2UKaGFzX2FjY2VzcwhoYXNfaXRlbRJoYXNfaXRlbV93aXRoX3R5cGUCaWQMaXNfZXhjbHVzaXZlCWlzX2xpc3RlZBVpc19saXN0ZWRfZXhjbHVzaXZlbHkJaXNfbG9ja2VkB2lzX3NvbWUKaXRlbV9jb3VudAdpdGVtX2lkBWtpb3NrD2tpb3NrX2V4dGVuc2lvbghraW9za19pZBNraW9za19vd25lcl9jYXBfZm9yBGxpc3QWbGlzdF93aXRoX3B1cmNoYXNlX2NhcARsb2NrDWxvY2tfaW50ZXJuYWwJbWluX3ByaWNlA25ldwtuZXdfcmVxdWVzdAZvYmplY3QGb3B0aW9uBW93bmVyBXBsYWNlDnBsYWNlX2FuZF9saXN0DnBsYWNlX2ludGVybmFsBXByaWNlB3Byb2ZpdHMOcHJvZml0c19hbW91bnQLcHJvZml0c19tdXQIcHVyY2hhc2URcHVyY2hhc2VfY2Fw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CwIBCQABCA4BCQABCwkBCQACCAcLEQEICAELEAIJAAkBAQYJAAEIDQELBQEJAAEGCwkBCQACCQAFAQsGAQkACQgHAwsGAQULBgELCQEJAAcIDwsLAQkAAwsJAQkACA4CCwkBCQAHCA8BCwsBCQAHCAcDCwYBBQsGAQsJAQkABwgPCwABCQADAQYLCwEJAAILCQEJAAgOAgcLCQEJAAsJAQkAAgcLCQEJAAMBBggPBggHAwsGAQULBgELCQEJAAULEQEICAEICAELEQEJAAsKCAgDCxEBCAgDCAcLBgEFBggIBgoICAMFCwYBCwkBCQABBgsGAQkAAgYLEAIJAAkBBgkAAQYJAQEKCQACBgsRAQkABgkAAgsDAQkABwgPAQcLBgEJAAUDCAcLBgEFBQsGAQsJAQkAAQcLDAEJAAEHCwoBCQACBwsKAQkACwkBCQABCQECBwsRAQkACQACCwQBCQEJAgMHCA4JAAkBAwkACQEJAgIGCA4JAAIHCA4JAAEHCQEBCwQBCQEDBwsQAgkACQEJAAkBAgcLEAIJAAkBBgkAAgkACQECCAgHCxEBCAgCBwsRAQkABgkAAgcLCgEJAAMCAwsJAQkAAQoCDUFjdGlvblJlcXVlc3QHQmFsYW5jZQRDb2luAklEBk9wdGlvbgdSdWxlS2V5BlN0cmluZwZTdXBwbHkFVG9rZW4LVG9rZW5Qb2xpY3kOVG9rZW5Qb2xpY3lDYXASVG9rZW5Qb2xpY3lDcmVhdGVkC1RyZWFzdXJ5Q2FwCVR4Q29udGV4dAhUeXBlTmFtZQNVSUQGVmVjTWFwBlZlY1NldAZhY3Rpb24DYWRkDGFkZF9hcHByb3ZhbA9hZGRfcnVsZV9jb25maWcTYWRkX3J1bGVfZm9yX2FjdGlvbgVhbGxvdwZhbW91bnQJYXBwcm92YWxzB2JhbGFuY2UGYm9ycm93CmJvcnJvd19tdXQEYnVybgRjb2luD2NvbmZpcm1fcmVxdWVzdBNjb25maXJtX3JlcXVlc3RfbXV0F2NvbmZpcm1fd2l0aF9wb2xpY3lfY2FwGWNvbmZpcm1fd2l0aF90cmVhc3VyeV9jYXAIY29udGFpbnMPZGVjcmVhc2Vfc3VwcGx5BmRlbGV0ZQxkZXN0cm95X25vbmUMZGVzdHJveV9zb21lDGRlc3Ryb3lfemVybwhkaXNhbGxvdw1keW5hbWljX2ZpZWxkBGVtaXQFZW1wdHkFZXZlbnQHZXhpc3RzXxBleGlzdHNfd2l0aF90eXBlB2V4dHJhY3QFZmx1c2gDZm9yDGZyb21fYmFsYW5jZQlmcm9tX2NvaW4QZnJvbV9jb2luX2FjdGlvbgNnZXQHZ2V0X211dA9oYXNfcnVsZV9jb25maWcZaGFzX3J1bGVfY29uZmlnX3dpdGhfdHlwZQJpZA9pbmNyZWFzZV9zdXBwbHkGaW5zZXJ0DGludG9fYmFsYW5jZQlpbnRvX2tleXMKaXNfYWxsb3dlZAppc19tdXRhYmxlB2lzX25vbmUMaXNfcHJvdGVjdGVkB2lzX3NvbWUEam9pbgRrZWVwA2tleQRtaW50BG5hbWUDbmV3Cm5ld19wb2xpY3kLbmV3X3JlcXVlc3QEbm9uZQZvYmplY3QGb3B0aW9uCXJlY2lwaWVudAZyZW1vdmUScmVtb3ZlX3J1bGVfY29uZmlnFnJlbW92ZV9ydWxlX2Zvcl9hY3Rpb24LcnVsZV9jb25maWcPcnVsZV9jb25maWdfbXV0BXJ1bGVzBnNlbmRlcgxzaGFyZV9vYmplY3QMc2hhcmVfcG9saWN5BHNvbWUFc3BlbmQMc3BlbmRfYWN0aW9uBXNwZW50DXNwZW50X2JhbGFuY2UFc3BsaXQGc3RyaW5nCnN1cHBseV9tdXQHdG9fY29pbg50b19jb2luX2FjdGlvbgV0b2tlbgh0cmFuc2Zlcg90cmFuc2Zlcl9hY3Rpb24KdHhfY29udGV4dAl0eXBlX25hbWUEdXRmOAV2YWx1ZQd2ZWNfbWFwB3ZlY19zZXQEemVybwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCAAAAAAAAAAAAwgBAAAAAAAAAAMIAgAAAAAAAAADCAMAAAAAAAAAAwgEAAAAAAAAAAMIBQAAAAAAAAADCAYAAAAAAAAAAwgHAAAAAAAAAAoCBgVzcGVuZAoCCQh0cmFuc2ZlcgoCCAd0b19jb2luCgIKCWZyb21fY29pbgACAjoIDhoLCQEJAAECAjoIDjIIDQICAzoIDl0LCQEJAFULEAIIBwsRAQgIAwIGSAgHGANWBU8LBgEFXQsGAQsJAQkA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DAMHQmFsYW5jZQRDb2luFkdlbmVzaXNDaGFpblBhcmFtZXRlcnMYR2VuZXNpc1ZhbGlkYXRvck1ldGFkYXRhBk9wdGlvbgNTVUkMU3Rha2VTdWJzaWR5EFN5c3RlbVBhcmFtZXRlcnMPVG9rZW5BbGxvY2F0aW9uGVRva2VuRGlzdHJpYnV0aW9uU2NoZWR1bGUJVHhDb250ZXh0A1VJRAlWYWxpZGF0b3IIYWN0aXZhdGUTYWN0aXZhdGVfdmFsaWRhdG9ycw9hbGxvY2F0ZV90b2tlbnMLYWxsb2NhdGlvbnMLYW1vdW50X21pc3QHYmFsYW5jZRhjaGFpbl9zdGFydF90aW1lc3RhbXBfbXMEY29pbg9jb21taXNzaW9uX3JhdGUGY3JlYXRlGGNyZWF0ZV9zeXN0ZW1fcGFyYW1ldGVycwtkZXNjcmlwdGlvbgxkZXN0cm95X3NvbWUMZGVzdHJveV96ZXJvBWVwb2NoEWVwb2NoX2R1cmF0aW9uX21zDGZyb21fYmFsYW5jZQlnYXNfcHJpY2UHZ2VuZXNpcxFnZXRfdmFsaWRhdG9yX211dAlpbWFnZV91cmwWaXNfZHVwbGljYXRlX3ZhbGlkYXRvcghpc19lbXB0eQdpc19zb21lE21heF92YWxpZGF0b3JfY291bnQbbWluX3ZhbGlkYXRvcl9qb2luaW5nX3N0YWtlBG5hbWUPbmV0d29ya19hZGRyZXNzEm5ldHdvcmtfcHVibGljX2tleQNuZXcGb2JqZWN0Bm9wdGlvbgtwMnBfYWRkcmVzcw9wcmltYXJ5X2FkZHJlc3MLcHJvamVjdF91cmwTcHJvb2Zfb2ZfcG9zc2Vzc2lvbhNwcm90b2NvbF9wdWJsaWNfa2V5EHByb3RvY29sX3ZlcnNpb24RcmVjaXBpZW50X2FkZHJlc3MccmVxdWVzdF9hZGRfc3Rha2VfYXRfZ2VuZXNpcwVzcGxpdA1zdGFrZV9zdWJzaWR5G3N0YWtlX3N1YnNpZHlfZGVjcmVhc2VfcmF0ZRdzdGFrZV9zdWJzaWR5X2Z1bmRfbWlzdClzdGFrZV9zdWJzaWR5X2luaXRpYWxfZGlzdHJpYnV0aW9uX2Ftb3VudBtzdGFrZV9zdWJzaWR5X3BlcmlvZF9sZW5ndGgZc3Rha2Vfc3Vic2lkeV9zdGFydF9l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ABAFCgIKAgoCCgIKAgoCCgIKAgoCCgIKAgoCAwMHCAsBCAECBwgBAwABBwgBBAcIAQsIAQgKBQcICwEIDgMHCAEIDgYICwELCAEICgMHCAEIEAMCBwgBCwgBCAoCBwgBBggLAQYIAQEBAQYIAAEFAQYIBgEGCAwBBgoCAQYLBQEIBgEGCwUBCgIBBggJAQMCBggBAwEIDQEICQIGCAEGCAECBgsFAQkABgkAAgYLBQEJAAYLBQEJAAIHCAEHCAsCBwgBCgIDBwgBCgIKAgEKAgEGCA8ECAADAwcICxYFCAYIBggGCAYLBQEKAgsFAQoCCwUBCgILBQEKAgsFAQgGCwUBCAYKAgsFAQgGCwUBCAYIBwoCCgIKAggGCAYIDAgMAQsFAQkAAQgGCAEBAQEBAQEIAAECAQgEAQgMAQcICwEIBwIHCA8DAwMDCA4BCAoBBgsIAQkAAQYICwQHCA8LCAEICgMHCAsBBwgPAQgCAQkAAgkABQUDAwMDCwgBCAoBBggOAwcIDwgOBggLAQgDAQYIEAEGBQIHCA8LCAEICgIHCA8GCAsCBggPAwEIDwEGCQAdAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBBgsFAQkAAgEBAgUHCAsBBwsFAQkAAwgJCA8FA0JhZwdCYWxhbmNlAklEBk9wdGlvbhVQb29sVG9rZW5FeGNoYW5nZVJhdGUDU1VJCVN0YWtlZFN1aQtTdGFraW5nUG9vbBNTdGFraW5nUmVxdWVzdEV2ZW50BlN0cmluZwlUeENvbnRleHQVVW5zdGFraW5nUmVxdWVzdEV2ZW50A1VybAlWYWxpZGF0b3IRVmFsaWRhdG9yTWV0YWRhdGEVVmFsaWRhdG9yT3BlcmF0aW9uQ2FwCGFjdGl2YXRlFWFjdGl2YXRlX3N0YWtpbmdfcG9vbBphZGp1c3Rfc3Rha2VfYW5kX2dhc19wcmljZQZhbW91bnQFYXNjaWkDYmFnB2JhbGFuY2UDYmNzBmJvcnJvdw9jb21taXNzaW9uX3JhdGUKZGVhY3RpdmF0ZRdkZWFjdGl2YXRlX3N0YWtpbmdfcG9vbA9kZXBvc2l0X3Jld2FyZHMVZGVwb3NpdF9zdGFrZV9yZXdhcmRzC2Rlc2NyaXB0aW9uGmVmZmVjdHVhdGVfc3RhZ2VkX21ldGFkYXRhBGVtaXQFZXBvY2gFZXZlbnQMZXh0cmFfZmllbGRzB2V4dHJhY3QKZnJvbV9hc2NpaQlnYXNfcHJpY2UHZ2VuZXNpcxRnZXRfc3Rha2luZ19wb29sX3JlZgJpZAlpbWFnZV91cmwMaXNfZHVwbGljYXRlDWlzX2VxdWFsX3NvbWUXaXNfZXF1YWxfc29tZV9hbmRfdmFsdWUHaXNfbm9uZQxpc19wcmVhY3RpdmUHaXNfc29tZQhtZXRhZGF0YQRuYW1lC25ldF9hZGRyZXNzD25ldHdvcmtfYWRkcmVzcxRuZXR3b3JrX3B1YmtleV9ieXRlcwNuZXcRbmV3X2Zyb21fbWV0YWRhdGEMbmV3X21ldGFkYXRhFW5ld191bnNhZmVfZnJvbV9ieXRlczNuZXdfdW52ZXJpZmllZF92YWxpZGF0b3Jfb3BlcmF0aW9uX2NhcF9hbmRfdHJhbnNmZXIabmV4dF9lcG9jaF9jb21taXNzaW9uX3JhdGUUbmV4dF9lcG9jaF9nYXNfcHJpY2UWbmV4dF9lcG9jaF9uZXRfYWRkcmVzcxpuZXh0X2Vwb2NoX25ldHdvcmtfYWRkcmVzcx9uZXh0X2Vwb2NoX25ldHdvcmtfcHVia2V5X2J5dGVzFm5leHRfZXBvY2hfcDJwX2FkZHJlc3MabmV4dF9lcG9jaF9wcmltYXJ5X2FkZHJlc3MebmV4dF9lcG9jaF9wcm9vZl9vZl9wb3NzZXNzaW9uIG5leHRfZXBvY2hfcHJvdG9jb2xfcHVia2V5X2J5dGVzEG5leHRfZXBvY2hfc3Rha2UZbmV4dF9lcG9jaF93b3JrZXJfYWRkcmVzcx5uZXh0X2Vwb2NoX3dvcmtlcl9wdWJrZXlfYnl0ZXMEbm9uZQZvYmplY3QQb3BlcmF0aW9uX2NhcF9pZAZvcHRpb24LcDJwX2FkZHJlc3MUcGVuZGluZ19zdGFrZV9hbW91bnQdcGVuZGluZ19zdGFrZV93aXRoZHJhd19hbW91bnQHcG9vbF9pZCFwb29sX3Rva2VuX2V4Y2hhbmdlX3JhdGVfYXRfZXBvY2gPcHJpbWFyeV9hZGRyZXNzEHByaW5jaXBhbF9hbW91bnQVcHJvY2Vzc19wZW5kaW5nX3N0YWtlJHByb2Nlc3NfcGVuZGluZ19zdGFrZXNfYW5kX3dpdGhkcmF3cwtwcm9qZWN0X3VybBNwcm9vZl9vZl9wb3NzZXNzaW9uFXByb3RvY29sX3B1YmtleV9ieXRlcw9wdWJsaWNfdHJhbnNmZXIRcmVxdWVzdF9hZGRfc3Rha2UccmVxdWVzdF9hZGRfc3Rha2VfYXRfZ2VuZXNpcxtyZXF1ZXN0X3NldF9jb21taXNzaW9uX3JhdGUVcmVxdWVzdF9zZXRfZ2FzX3ByaWNlFnJlcXVlc3Rfd2l0aGRyYXdfc3Rha2UNcmV3YXJkX2Ftb3VudAZzZW5kZXIdc2V0X2NhbmRpZGF0ZV9jb21taXNzaW9uX3JhdGUXc2V0X2NhbmRpZGF0ZV9nYXNfcHJpY2UQc2V0X3ZvdGluZ19wb3dlcgRzb21lFnN0YWtlX2FjdGl2YXRpb25fZXBvY2gMc3Rha2VfYW1vdW50EXN0YWtlZF9zdWlfYW1vdW50DnN0YWtlcl9hZGRyZXNzDHN0YWtpbmdfcG9vbA9zdGFraW5nX3Bvb2xfaWQGc3RyaW5nA3N1aQtzdWlfYWRkcmVzcwtzdWlfYmFsYW5jZRZzdWlfc3lzdGVtX3N0YXRlX2lubmVyCHRvX2J5dGVzC3RvdGFsX3N0YWtlEnRvdGFsX3N0YWtlX2Ftb3VudAh0cmFuc2Zlcgp0eF9jb250ZXh0D3Vuc3Rha2luZ19lcG9jaCB1cGRhdGVfY2FuZGlkYXRlX25ldHdvcmtfYWRkcmVzcx91cGRhdGVfY2FuZGlkYXRlX25ldHdvcmtfcHVia2V5HHVwZGF0ZV9jYW5kaWRhdGVfcDJwX2FkZHJlc3MgdXBkYXRlX2NhbmRpZGF0ZV9wcmltYXJ5X2FkZHJlc3MgdXBkYXRlX2NhbmRpZGF0ZV9wcm90b2NvbF9wdWJrZXkfdXBkYXRlX2NhbmRpZGF0ZV93b3JrZXJfYWRkcmVzcx51cGRhdGVfY2FuZGlkYXRlX3dvcmtlcl9wdWJrZXkSdXBkYXRlX2Rlc2NyaXB0aW9uEHVwZGF0ZV9pbWFnZV91cmwLdXBkYXRlX25hbWUhdXBkYXRlX25leHRfZXBvY2hfbmV0d29ya19hZGRyZXNzIHVwZGF0ZV9uZXh0X2Vwb2NoX25ldHdvcmtfcHVia2V5HXVwZGF0ZV9uZXh0X2Vwb2NoX3AycF9hZGRyZXNzIXVwZGF0ZV9uZXh0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CAUHCA0KCwMBCAYLAQEDBQcICAEIBgILAgEJAAcICAELAwEJAAELAwEIBgMHCA0ICwYICAMHCA0GCBAFAwcIDQoCBggIBAcIDQoCCgIGCAgCBwgNBggECwcIDQMDCwIBCAYLAgEIBgMDAwMDBwgIAgcIDQgNAgcIBQkAAQkBAQgNAgMIDQEHCQEHQmFsYW5jZQRDb2luAklEBk9wdGlvbhVQb29sVG9rZW5FeGNoYW5nZVJhdGUDU1VJDFN0YWtlU3Vic2lkeQlTdGFrZWRTdWkOU3VpU3lzdGVtU3RhdGUTU3VpU3lzdGVtU3RhdGVJbm5lchVTdWlTeXN0ZW1TdGF0ZUlubmVyVjIQU3lzdGVtUGFyYW1ldGVycwVUYWJsZQlUeENvbnRleHQDVUlEH1VudmVyaWZpZWRWYWxpZGF0b3JPcGVyYXRpb25DYXAJVmFsaWRhdG9yGmFjdGl2ZV92YWxpZGF0b3JfYWRkcmVzc2VzA2FkZA1hZHZhbmNlX2Vwb2NoB2JhbGFuY2UKYm9ycm93X211dARjb2luBmNyZWF0ZQ1keW5hbWljX2ZpZWxkDGZyb21fYmFsYW5jZQdnZW5lc2lzHGdlbmVzaXNfc3lzdGVtX3N0YXRlX3ZlcnNpb24CaWQYbG9hZF9pbm5lcl9tYXliZV91cGdyYWRlEWxvYWRfc3lzdGVtX3N0YXRlFWxvYWRfc3lzdGVtX3N0YXRlX211dAZvYmplY3QGb3B0aW9uE3Bvb2xfZXhjaGFuZ2VfcmF0ZXMPcHVibGljX3RyYW5zZmVyBnJlbW92ZRByZXBvcnRfdmFsaWRhdG9yEXJlcXVlc3RfYWRkX3N0YWtlGnJlcXVlc3RfYWRkX3N0YWtlX211bF9jb2luG3JlcXVlc3RfYWRkX3N0YWtlX25vbl9lbnRyeRVyZXF1ZXN0X2FkZF92YWxpZGF0b3IfcmVxdWVzdF9hZGRfdmFsaWRhdG9yX2NhbmRpZGF0ZRhyZXF1ZXN0X3JlbW92ZV92YWxpZGF0b3IicmVxdWVzdF9yZW1vdmVfdmFsaWRhdG9yX2NhbmRpZGF0ZRtyZXF1ZXN0X3NldF9jb21taXNzaW9uX3JhdGUVcmVxdWVzdF9zZXRfZ2FzX3ByaWNlFnJlcXVlc3Rfd2l0aGRyYXdfc3Rha2UgcmVxdWVzdF93aXRoZHJhd19zdGFrZV9ub25fZW50cnkUcm90YXRlX29wZXJhdGlvbl9jYXAGc2VuZGVyJ3NldF9jYW5kaWRhdGVfdmFsaWRhdG9yX2NvbW1pc3Npb25fcmF0ZSFzZXRfY2FuZGlkYXRlX3ZhbGlkYXRvcl9nYXNfcHJpY2UMc2hhcmVfb2JqZWN0DXN0YWtlX3N1YnNpZHkMc3Rha2luZ19wb29sA3N1aQpzdWlfc3lzdGVtFnN1aV9zeXN0ZW1fc3RhdGVfaW5uZXIUc3lzdGVtX3N0YXRlX3ZlcnNpb24FdGFibGUIdHJhbnNmZXIKdHhfY29udGV4dBV1bmRvX3JlcG9ydF92YWxpZGF0b3IqdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfbmV0d29ya19hZGRyZXNzKXVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX25ldHdvcmtfcHVia2V5JnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3AycF9hZGRyZXNzKnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3ByaW1hcnlfYWRkcmVzcyp1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl9wcm90b2NvbF9wdWJrZXkpdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3Jfd29ya2VyX2FkZHJlc3ModXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3Jfd29ya2VyX3B1YmtleRx1cGRhdGVfdmFsaWRhdG9yX2Rlc2NyaXB0aW9uGnVwZGF0ZV92YWxpZGF0b3JfaW1hZ2VfdXJsFXVwZGF0ZV92YWxpZGF0b3JfbmFtZSt1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfbmV0d29ya19hZGRyZXNzKnVwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9uZXR3b3JrX3B1YmtleSd1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfcDJwX2FkZHJlc3MrdXBkYXRlX3ZhbGlkYXRvcl9uZXh0X2Vwb2NoX3ByaW1hcnlfYWRkcmVzcyt1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfcHJvdG9jb2xfcHVia2V5KnVwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF93b3JrZXJfYWRkcmVzcyl1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AgMIAQIDCAEBCwkCCQAJAQEIBwELAwEJAAEICAELBQEJAAEIBAIIAgMBBgsFAQkAAQYJAAYIAgMLBQEICAMLBQEICAMBBggKAgcLBQEJAAsFAQkAAggBCwUBCAgCAwMDBwsJAgkACQEJAAkBBQMDCAEDAwIHCwUBCQADAgcLAwEJAAkAAQkAAQYLAwEJAAEFAgkABQIGCAILBQEICAIGCwMBCQAJAAIGCwkCCQAJAQkAAQYJAQMDCAEDA0JhZwdCYWxhbmNlAklEBk9wdGlvbhVQb29sVG9rZW5FeGNoYW5nZVJhdGUDU1VJCVN0YWtlZFN1aQtTdGFraW5nUG9vbAVUYWJsZQlUeENvbnRleHQDVUlEFWFjdGl2YXRlX3N0YWtpbmdfcG9vbBBhY3RpdmF0aW9uX2Vwb2NoA2FkZANiYWcHYmFsYW5jZQZib3Jyb3cYY2hlY2tfYmFsYW5jZV9pbnZhcmlhbnRzCGNvbnRhaW5zF2RlYWN0aXZhdGVfc3Rha2luZ19wb29sEmRlYWN0aXZhdGlvbl9lcG9jaAZkZWxldGUPZGVwb3NpdF9yZXdhcmRzBWVwb2NoDmV4Y2hhbmdlX3JhdGVzDGV4dHJhX2ZpZWxkcwRmaWxsDmdldF9zdWlfYW1vdW50EGdldF90b2tlbl9hbW91bnQQZ2V0X3dpdGhfZGVmYXVsdAJpZBVpbml0aWFsX2V4Y2hhbmdlX3JhdGUZaXNfZXF1YWxfc3Rha2luZ19tZXRhZGF0YQtpc19pbmFjdGl2ZQdpc19ub25lDGlzX3ByZWFjdGl2ZRVpc19wcmVhY3RpdmVfYXRfZXBvY2gHaXNfc29tZQRqb2luD2pvaW5fc3Rha2VkX3N1aQRtYXRoA21pbgNuZXcEbm9uZQZvYmplY3QGb3B0aW9uG3BlbmRpbmdfcG9vbF90b2tlbl93aXRoZHJhdw1wZW5kaW5nX3N0YWtlFHBlbmRpbmdfc3Rha2VfYW1vdW50HXBlbmRpbmdfc3Rha2Vfd2l0aGRyYXdfYW1vdW50GnBlbmRpbmdfdG90YWxfc3VpX3dpdGhkcmF3B3Bvb2xfaWQRcG9vbF90b2tlbl9hbW91bnQScG9vbF90b2tlbl9iYWxhbmNlIXBvb2xfdG9rZW5fZXhjaGFuZ2VfcmF0ZV9hdF9lcG9jaAlwcmluY2lw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AAkBAgcKCQADAgMDCgoLCQEDAwMLCgEDAwMDBggUAwYKCBQBCwkBAwIDCQABCwkBCQABCgsJAQkAAQsKAQkAAQcLCgEJAAIGCBQFAQYIEwEGCw0BCQADAwMDAgYIFAYIFAIGCw0BCQADAQYJAAELBQEJAAUFAwsFAQMDCgMEAwMLBQEDCwUBAwIHCw0BCQADAQcJAAEGCBYBBgUCAwsFAQMGAQEDAwsFAQMLBQEDBQUGCBQICAUGCBQBBggVAQgVAQYICAIDCBQBBgoJAAMDBQgIAQgEBwYFBgUDAwYFCgUHCxABBQIFCxABBQEGCw8CCQAJAQEKCQACBgsQAQkABgkAAgcLEAEJAAYJAAEGCxABCQABBwsNAQkAAQgDBgMDAwMDAwIHCBQGCA4EAwMDBggUBwsPAgMDCw8CAwMEBAMDAwUKBQYKCBQLEAEFCgUFAQcLDwIJAAkBAQsQAQkABgMDBAoDCgMDEQMDAwoDAwoDAwQDAwMDAwMDAwQIAwMLBwEICwMHCBQFBAsHAQgLAgcLBwEJAAMBCwcBCQACBwsHAQkACwcBCQACCQAFAgcIFAsHAQgLCAoFAwMDAwoFBggUBQIGCBQDAQgRAQgCBQMDCgUFBgoIFANCYWcHQmFsYW5jZQVFbnRyeQJJRAZPcHRpb24VUG9vbFRva2VuRXhjaGFuZ2VSYXRlDVByaW9yaXR5UXVldWUDU1VJCVN0YWtlZFN1aQtTdGFraW5nUG9vbAVUYWJsZQhUYWJsZVZlYwlUeENvbnRleHQfVW52ZXJpZmllZFZhbGlkYXRvck9wZXJhdGlvbkNhcAlWYWxpZGF0b3IXVmFsaWRhdG9yRXBvY2hJbmZvRXZlbnQZVmFsaWRhdG9yRXBvY2hJbmZvRXZlbnRWMhJWYWxpZGF0b3JKb2luRXZlbnQTVmFsaWRhdG9yTGVhdmVFdmVudBVWYWxpZGF0b3JPcGVyYXRpb25DYXAMVmFsaWRhdG9yU2V0EFZhbGlkYXRvcldyYXBwZXIGVmVjTWFwBlZlY1NldAhhY3RpdmF0ZRphY3RpdmVfdmFsaWRhdG9yX2FkZHJlc3NlcxFhY3RpdmVfdmFsaWRhdG9ycwNhZGQaYWRqdXN0X3N0YWtlX2FuZF9nYXNfcHJpY2UNYWR2YW5jZV9lcG9jaCZhc3NlcnRfbm9fcGVuZGluZ19vcl9hY3RpdmVfZHVwbGljYXRlcxJhdF9yaXNrX3ZhbGlkYXRvcnMDYmFnB2JhbGFuY2UGYm9ycm93CmJvcnJvd19tdXQWY2FsY3VsYXRlX3RvdGFsX3N0YWtlcyZjbGVhbl9yZXBvcnRfcmVjb3Jkc19sZWF2aW5nX3ZhbGlkYXRvcg9jb21taXNzaW9uX3JhdGUkY29tcHV0ZV9hZGp1c3RlZF9yZXdhcmRfZGlzdHJpYnV0aW9uGmNvbXB1dGVfcmV3YXJkX2FkanVzdG1lbnRzGmNvbXB1dGVfc2xhc2hlZF92YWxpZGF0b3JzJmNvbXB1dGVfdW5hZGp1c3RlZF9yZXdhcmRfZGlzdHJpYnV0aW9uCGNvbnRhaW5zGWNvdW50X2R1cGxpY2F0ZXNfdGFibGV2ZWMUY291bnRfZHVwbGljYXRlc192ZWMJY3JlYXRlX3YxCmRlYWN0aXZhdGUVZGVwb3NpdF9zdGFrZV9yZXdhcmRzGmRlcml2ZV9yZWZlcmVuY2VfZ2FzX3ByaWNlB2Rlc3Ryb3kMZGVzdHJveV9zb21lDGRlc3Ryb3lfemVybxFkaXN0cmlidXRlX3Jld2FyZBplZmZlY3R1YXRlX3N0YWdlZF9tZXRhZGF0YQRlbWl0G2VtaXRfdmFsaWRhdG9yX2Vwb2NoX2V2ZW50cwVlbXB0eQVlcG9jaAVldmVudA5leGNoYW5nZV9yYXRlcwxleHRyYV9maWVsZHMHZXh0cmFjdA5maW5kX3ZhbGlkYXRvch1maW5kX3ZhbGlkYXRvcl9mcm9tX3RhYmxlX3ZlYwlnYXNfcHJpY2UHZ2VuZXNpcwNnZXQwZ2V0X2FjdGl2ZV9vcl9wZW5kaW5nX29yX2NhbmRpZGF0ZV92YWxpZGF0b3JfbXV0MGdldF9hY3RpdmVfb3JfcGVuZGluZ19vcl9jYW5kaWRhdGVfdmFsaWRhdG9yX3JlZhhnZXRfYWN0aXZlX3ZhbGlkYXRvcl9yZWYlZ2V0X2NhbmRpZGF0ZV9vcl9hY3RpdmVfdmFsaWRhdG9yX211dAdnZXRfbXV0GWdldF9wZW5kaW5nX3ZhbGlkYXRvcl9yZWYUZ2V0X3N0YWtpbmdfcG9vbF9yZWYVZ2V0X3ZhbGlkYXRvcl9pbmRpY2VzEWdldF92YWxpZGF0b3JfbXV0GmdldF92YWxpZGF0b3JfbXV0X3dpdGhfY3R4L2dldF92YWxpZGF0b3JfbXV0X3dpdGhfY3R4X2luY2x1ZGluZ19jYW5kaWRhdGVzI2dldF92YWxpZGF0b3JfbXV0X3dpdGhfdmVyaWZpZWRfY2FwEWdldF92YWxpZGF0b3JfcmVmAmlkE2luYWN0aXZlX3ZhbGlkYXRvcnMGaW5zZXJ0CWludG9fa2V5cyJpc19hY3RpdmVfdmFsaWRhdG9yX2J5X3N1aV9hZGRyZXNzDGlzX2R1cGxpY2F0ZRZpc19kdXBsaWNhdGVfdmFsaWRhdG9yImlzX2R1cGxpY2F0ZV93aXRoX2FjdGl2ZV92YWxpZGF0b3IjaXNfZHVwbGljYXRlX3dpdGhfcGVuZGluZ192YWxpZGF0b3IIaXNfZW1wdHkVaXNfaW5hY3RpdmVfdmFsaWRhdG9yDGlzX3ByZWFjdGl2ZQdpc19zb21lFmlzX3ZhbGlkYXRvcl9jYW5kaWRhdGUMaXNfdm9sdW50YXJ5BGpvaW4Ea2V5cwZsZW5ndGgcbG9hZF92YWxpZGF0b3JfbWF5YmVfdXBncmFkZQNuZXcJbmV3X2VudHJ5E25ld19mcm9tX3VudmVyaWZpZWQabmV4dF9lcG9jaF92YWxpZGF0b3JfY291bnQEbm9uZQZvYmplY3QQb3BlcmF0aW9uX2NhcF9pZAZvcHRpb24ZcGVuZGluZ19hY3RpdmVfdmFsaWRhdG9ycxBwZW5kaW5nX3JlbW92YWxzE3Bvb2xfZXhjaGFuZ2VfcmF0ZXMHcG9vbF9pZBNwb29sX3N0YWtpbmdfcmV3YXJkGHBvb2xfdG9rZW5fZXhjaGFuZ2VfcmF0ZSFwb29sX3Rva2VuX2V4Y2hhbmdlX3JhdGVfYXRfZXBvY2gDcG9wCHBvcF9iYWNrB3BvcF9tYXgOcHJpb3JpdHlfcXVldWUYcHJvY2Vzc19wZW5kaW5nX3JlbW92YWxzJHByb2Nlc3NfcGVuZGluZ19zdGFrZXNfYW5kX3dpdGhkcmF3cxpwcm9jZXNzX3BlbmRpbmdfdmFsaWRhdG9ycxtwcm9jZXNzX3ZhbGlkYXRvcl9kZXBhcnR1cmUPcHVibGljX3RyYW5zZmVyCXB1c2hfYmFjaxBxdW9ydW1fdGhyZXNob2xkGnJlZmVyZW5jZV9nYXNfc3VydmV5X3F1b3RlBnJlbW92ZRFyZXF1ZXN0X2FkZF9zdGFrZRVyZXF1ZXN0X2FkZF92YWxpZGF0b3IfcmVxdWVzdF9hZGRfdmFsaWRhdG9yX2NhbmRpZGF0ZRhyZXF1ZXN0X3JlbW92ZV92YWxpZGF0b3IicmVxdWVzdF9yZW1vdmVfdmFsaWRhdG9yX2NhbmRpZGF0ZRtyZXF1ZXN0X3NldF9jb21taXNzaW9uX3JhdGUWcmVxdWVzdF93aXRoZHJhd19zdGFrZQZzZW5kZXIQc2V0X3ZvdGluZ19wb3dlcgRzaXplBHNvbWURc29ydF9yZW1vdmFsX2xpc3QFc3BsaXQFc3Rha2UMc3Rha2VfYW1vdW50DHN0YWtpbmdfcG9vbA9zdGFraW5nX3Bvb2xfaWQVc3Rha2luZ19wb29sX21hcHBpbmdzG3N0b3JhZ2VfZnVuZF9zdGFraW5nX3Jld2FyZANzdWkLc3VpX2FkZHJlc3MWc3VpX3N5c3RlbV9zdGF0ZV9pbm5lch1zdW1fdm90aW5nX3Bvd2VyX2J5X2FkZHJlc3NlcwV0YWJsZQl0YWJsZV92ZWMadGFsbHlpbmdfcnVsZV9nbG9iYWxfc2NvcmUXdGFsbHlpbmdfcnVsZV9yZXBvcnRlcnMLdG90YWxfc3Rha2USdG90YWxfc3Rha2VfYW1vdW50EnRvdGFsX3ZvdGluZ19wb3dlcgh0cmFuc2Zlcgp0eF9jb250ZXh0IHVudmVyaWZpZWRfb3BlcmF0aW9uX2NhcF9hZGRyZXNzJ3VwZGF0ZV9hbmRfcHJvY2Vzc19sb3dfc3Rha2VfZGVwYXJ0dXJlcwl2YWxpZGF0b3IRdmFsaWRhdG9yX2FkZHJlc3MUdmFsaWRhdG9yX2NhbmRpZGF0ZXMNdmFsaWRhdG9yX2NhcA12YWxpZGF0b3Jfc2V0FnZhbGlkYXRvcl9zdGFrZV9hbW91bnQZdmFsaWRhdG9yX3N0YWtpbmdfcG9vbF9pZBx2YWxpZGF0b3JfdG90YWxfc3Rha2VfYW1vdW50EXZhbGlkYXRvcl93cmFwcGVyBXZhbHVlB3ZlY19tYXAHdmVjX3NldAZ2ZWN0b3IedmVyaWZpZWRfb3BlcmF0aW9uX2NhcF9hZGRyZXNzCnZlcmlmeV9jYXAMdm90aW5nX3Bvd2VyAAAAAAAAAAAA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CAEJAAQHCBYFCwcBCAoHCAwBBggRAwcIFggRBggMAgYIFgUBAQQGBQYFBQcLDgEFAQYIFQEGBQIGCw0CCQAJAQYJAAEJAAELDgEJAAMHCw0CCQAJAQkACQECBwsNAgkACQEGCQABBwkBAgYLDgEJAAYJAAIHCw4BCQAJAAIHCw4BCQAGCQABBgsOAQkAAgkACQECBwgTBwgMAgcIEwoCAgcIEwYIEwIGCBYGCBMDBwgTCgIKAiwBAwMDAwMDAQEBCwcBCAoDAwMDAwMDAwQDAwMLBwEICgMDAwsHAQgKCwcBCAoLBwEICgsHAQgKAwMDCwcBCAoECwcBCAoEAwMDAwQDAQcLBwEJAAIHCwcBCQALBwEJAAEGCBIBBgsHAQkAAQcIDwIHCwcBCQADCQcIFgcLBwEICgcLBwEICgcLDQIFCw4BBQMDAwMHCAwGBwgSCwcBCAoLBwEICgsHAQgKAwMBCAQBBgkBAgcIFgYICQULBwEICgMLBwEICgsIAQgKCwcBCAoBCwgBCAoCBwsIAQkACgsIAQkAAQYLBQEJAAELBQEJAAILBwEJAAcIDAIJAAUDQmFnB0JhbGFuY2UEQ29pbgJJRAZPcHRpb24VUG9vbFRva2VuRXhjaGFuZ2VSYXRlA1NVSQxTdGFrZVN1YnNpZHkJU3Rha2VkU3VpC1N0b3JhZ2VGdW5kE1N1aVN5c3RlbVN0YXRlSW5uZXIVU3VpU3lzdGVtU3RhdGVJbm5lclYyFFN5c3RlbUVwb2NoSW5mb0V2ZW50EFN5c3RlbVBhcmFtZXRlcnMSU3lzdGVtUGFyYW1ldGVyc1YyBVRhYmxlCVR4Q29udGV4dB9VbnZlcmlmaWVkVmFsaWRhdG9yT3BlcmF0aW9uQ2FwCVZhbGlkYXRvchVWYWxpZGF0b3JPcGVyYXRpb25DYXAMVmFsaWRhdG9yU2V0BlZlY01hcAZWZWNTZXQaYWN0aXZlX3ZhbGlkYXRvcl9hZGRyZXNzZXMRYWN0aXZlX3ZhbGlkYXRvcnMNYWR2YW5jZV9lcG9jaCZhc3NlcnRfbm9fcGVuZGluZ19vcl9hY3RpdmVfZHVwbGljYXRlcwNiYWcHYmFsYW5jZQRjb2luCGNvbnRhaW5zBmNyZWF0ZRhjcmVhdGVfc3lzdGVtX3BhcmFtZXRlcnMaZGVyaXZlX3JlZmVyZW5jZV9nYXNfcHJpY2UMZGVzdHJveV9zb21lDGRlc3Ryb3lfemVybwRlbWl0BWVtcHR5BWVwb2NoEWVwb2NoX2R1cmF0aW9uX21zGGVwb2NoX3N0YXJ0X3RpbWVzdGFtcF9tcwVldmVudAxleHRyYV9maWVsZHMUZXh0cmFjdF9jb2luX2JhbGFuY2UMZnJvbV9iYWxhbmNlB2dlbmVzaXMcZ2VuZXNpc19zeXN0ZW1fc3RhdGVfdmVyc2lvbgNnZXQHZ2V0X211dBBnZXRfcmVwb3J0ZXJzX29mH2dldF9zdG9yYWdlX2Z1bmRfb2JqZWN0X3JlYmF0ZXMeZ2V0X3N0b3JhZ2VfZnVuZF90b3RhbF9iYWxhbmNlGmdldF92YWxpZGF0b3JfbXV0X3dpdGhfY3R4L2dldF92YWxpZGF0b3JfbXV0X3dpdGhfY3R4X2luY2x1ZGluZ19jYW5kaWRhdGVzI2dldF92YWxpZGF0b3JfbXV0X3dpdGhfdmVyaWZpZWRfY2FwBmluc2VydAxpbnRvX2JhbGFuY2UiaXNfYWN0aXZlX3ZhbGlkYXRvcl9ieV9zdWlfYWRkcmVzcwhpc19lbXB0eQdpc19zb21lBGpvaW4Iam9pbl92ZWMcbGVmdG92ZXJfc3RvcmFnZV9mdW5kX2luZmxvdxNtYXhfdmFsaWRhdG9yX2NvdW50E21pbl92YWxpZGF0b3JfY291bnQbbWluX3ZhbGlkYXRvcl9qb2luaW5nX3N0YWtlA25ldzNuZXdfdW52ZXJpZmllZF92YWxpZGF0b3Jfb3BlcmF0aW9uX2NhcF9hbmRfdHJhbnNmZXIabmV4dF9lcG9jaF92YWxpZGF0b3JfY291bnQGb2JqZWN0Bm9wdGlvbgpwYXJhbWV0ZXJzA3BheRNwb29sX2V4Y2hhbmdlX3JhdGVzEHByb3RvY29sX3ZlcnNpb24PcHVibGljX3RyYW5zZmVyE3JlZmVyZW5jZV9nYXNfcHJpY2UGcmVtb3ZlEHJlcG9ydF92YWxpZGF0b3IVcmVwb3J0X3ZhbGlkYXRvcl9pbXBsEXJlcXVlc3RfYWRkX3N0YWtlGnJlcXVlc3RfYWRkX3N0YWtlX211bF9jb2luFXJlcXVlc3RfYWRkX3ZhbGlkYXRvch9yZXF1ZXN0X2FkZF92YWxpZGF0b3JfY2FuZGlkYXRlGHJlcXVlc3RfcmVtb3ZlX3ZhbGlkYXRvciJyZXF1ZXN0X3JlbW92ZV92YWxpZGF0b3JfY2FuZGlkYXRlG3JlcXVlc3Rfc2V0X2NvbW1pc3Npb25fcmF0ZRVyZXF1ZXN0X3NldF9nYXNfcHJpY2UWcmVxdWVzdF93aXRoZHJhd19zdGFrZRRyb3RhdGVfb3BlcmF0aW9uX2NhcAlzYWZlX21vZGUdc2FmZV9tb2RlX2NvbXB1dGF0aW9uX3Jld2FyZHMkc2FmZV9tb2RlX25vbl9yZWZ1bmRhYmxlX3N0b3JhZ2VfZmVlGXNhZmVfbW9kZV9zdG9yYWdlX3JlYmF0ZXMZc2FmZV9tb2RlX3N0b3JhZ2VfcmV3YXJkcwZzZW5kZXIdc2V0X2NhbmRpZGF0ZV9jb21taXNzaW9uX3JhdGUXc2V0X2NhbmRpZGF0ZV9nYXNfcHJpY2Unc2V0X2NhbmRpZGF0ZV92YWxpZGF0b3JfY29tbWlzc2lvbl9yYXRlIXNldF9jYW5kaWRhdGVfdmFsaWRhdG9yX2dhc19wcmljZQlzaW5nbGV0b24Fc3BsaXQWc3Rha2VfYWN0aXZhdGlvbl9lcG9jaA1zdGFrZV9zdWJzaWR5FHN0YWtlX3N1YnNpZHlfYW1vdW50GXN0YWtlX3N1YnNpZHlfc3RhcnRfZXBvY2gMc3Rha2luZ19wb29sFXN0YWtpbmdfcG9vbF9tYXBwaW5ncw5zdG9yYWdlX2NoYXJnZQxzdG9yYWdlX2Z1bmQUc3RvcmFnZV9mdW5kX2JhbGFuY2UZc3RvcmFnZV9mdW5kX3JlaW52ZXN0bWVudA5zdG9yYWdlX3JlYmF0ZQNzdWkKc3VpX3N5c3RlbRZzdWlfc3lzdGVtX3N0YXRlX2lubmVyFHN5c3RlbV9zdGF0ZV92ZXJzaW9uBXRhYmxlDXRvdGFsX2JhbGFuY2UOdG90YWxfZ2FzX2ZlZXMcdG90YWxfb2JqZWN0X3N0b3JhZ2VfcmViYXRlcwt0b3RhbF9zdGFrZR90b3RhbF9zdGFrZV9yZXdhcmRzX2Rpc3RyaWJ1dGVkCHRyYW5zZmVyCnR4X2NvbnRleHQVdW5kb19yZXBvcnRfdmFsaWRhdG9yGnVuZG9fcmVwb3J0X3ZhbGlkYXRvcl9pbXBsIHVwZGF0ZV9jYW5kaWRhdGVfbmV0d29ya19hZGRyZXNzH3VwZGF0ZV9jYW5kaWRhdGVfbmV0d29ya19wdWJrZXkcdXBkYXRlX2NhbmRpZGF0ZV9wMnBfYWRkcmVzcyB1cGRhdGVfY2FuZGlkYXRlX3ByaW1hcnlfYWRkcmVzcyB1cGRhdGVfY2FuZGlkYXRlX3Byb3RvY29sX3B1YmtleSp1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl9uZXR3b3JrX2FkZHJlc3MpdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfbmV0d29ya19wdWJrZXkmdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfcDJwX2FkZHJlc3MqdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfcHJpbWFyeV9hZGRyZXNzKnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3Byb3RvY29sX3B1YmtleSl1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl93b3JrZXJfYWRkcmVzcyh1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl93b3JrZXJfcHVia2V5H3VwZGF0ZV9jYW5kaWRhdGVfd29ya2VyX2FkZHJlc3MedXBkYXRlX2NhbmRpZGF0ZV93b3JrZXJfcHVia2V5EnVwZGF0ZV9kZXNjcmlwdGlvbhB1cGRhdGVfaW1hZ2VfdXJsC3VwZGF0ZV9uYW1lIXVwZGF0ZV9uZXh0X2Vwb2NoX25ldHdvcmtfYWRkcmVzcyB1cGRhdGVfbmV4dF9lcG9jaF9uZXR3b3JrX3B1YmtleR11cGRhdGVfbmV4dF9lcG9jaF9wMnBfYWRkcmVzcyF1cGRhdGVfbmV4dF9lcG9jaF9wcmltYXJ5X2FkZHJlc3MhdXBkYXRlX25leHRfZXBvY2hfcHJvdG9jb2xfcHVia2V5IHVwZGF0ZV9uZXh0X2Vwb2NoX3dvcmtlcl9hZGRyZXNzH3VwZGF0ZV9uZXh0X2Vwb2NoX3dvcmtlcl9wdWJrZXkSdXBkYXRlX3Byb2plY3RfdXJsHHVwZGF0ZV92YWxpZGF0b3JfZGVzY3JpcHRpb24adXBkYXRlX3ZhbGlkYXRvcl9pbWFnZV91cmwVdXBkYXRlX3ZhbGlkYXRvcl9uYW1lK3VwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9uZXR3b3JrX2FkZHJlc3MqdXBkYXRlX3ZhbGlkYXRvcl9uZXh0X2Vwb2NoX25ldHdvcmtfcHVia2V5J3VwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9wMnBfYWRkcmVzcyt1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfcHJpbWFyeV9hZGRyZXNzK3VwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9wcm90b2NvbF9wdWJrZXkqdXBkYXRlX3ZhbGlkYXRvcl9uZXh0X2Vwb2NoX3dvcmtlcl9hZGRyZXNzKXVwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF93b3JrZXJfcHVia2V5HHVwZGF0ZV92YWxpZGF0b3JfcHJvamVjdF91cmwIdjFfdG9fdjIJdmFsaWRhdG9yDXZhbGlkYXRvcl9jYXAgdmFsaWRhdG9yX2xvd19zdGFrZV9ncmFjZV9wZXJpb2QddmFsaWRhdG9yX2xvd19zdGFrZV90aHJlc2hvbGQYdmFsaWRhdG9yX3JlcG9ydF9yZWNvcmRzDXZhbGlkYXRvcl9zZXQWdmFsaWRhdG9yX3N0YWtlX2Ftb3VudBl2YWxpZGF0b3Jfc3Rha2luZ19wb29sX2lkH3ZhbGlkYXRvcl9zdGFraW5nX3Bvb2xfbWFwcGluZ3McdmFsaWRhdG9yX3RvdGFsX3N0YWtlX2Ftb3VudCJ2YWxpZGF0b3JfdmVyeV9sb3dfc3Rha2VfdGhyZXNob2xkCnZhbGlkYXRvcnMFdmFsdWUHdmVjX21hcAd2ZWNfc2V0HnZlcmlmaWVkX29wZXJhdGlvbl9jYXBfYWRkcmVzcwp2ZXJpZnlfY2FwDHdpdGhkcmF3X2FsbAR6ZXJvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAgEBAgECAgEDAwgBAAAAAAAAAAMIAAAAAAAAAAADCAIAAAAAAAAAAwgDAAAAAAAAAAMIBAAAAAAAAAADCAUAAAAAAAAAAwgGAAAAAAAAAAMIBwAAAAAAAAADCAgAAAAAAAAABBAQJwAAAAAAAAAAAAAAAAAAAAIIJwNpAz8DQQOn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BggEBwgEAwYLDAEDAwMIEgsOAQkBAQMDCw4BCQEDBwgFAxsBAwEDAwcLCQEIBQsOAQkAAwMLDgEJAQMGCAQHCAQDAwYLDAEDAwMIEgsOAQkBAQMDCw4BCQEDBwgFAwMLDgEJAAsOAQkBCw4BCQECBwsQAQkACxABCQAHAwcLCQEIBQgEAwMDCBIDBwsLAQkABggKAwMHCwkBCQADCQADBwsRAgkACQEJAAkBAQsBAgkACQECBgsVAgkACQEJAAMHCxUCCQAJAQkACQELCw4BCQALDgEJAAsOAQkAAwMLDgEJAQsOAQkBCw4BCQEDAwgSAgYLCwEJAAgSAQsCAgkACQEBCwMCCQAJAQsDAwYLCQEIBQcLCQEIBQMBCAQDAwgSBwsRAgMDAwMHCAUIBA0DBwsJAQgFAwMBBwsJAQgFCAQDAwgSAwgSBwsRAgMDEQMDBgsJAQgFBwsJAQgFAwMBAwMDCAQDCBIDAwgSBwsRAgMDBwYIBQoIBAYIBAYLDAEDAwgSBgsRAgMDBQMDAwMIEgYDCgMDAwMKAwQDBggEBgsMAQMGCxECAwgEBAYLCQEIBQMIEgYLEQIDAwpBY2NvdW50Q2FwB0JhbGFuY2UFQ2xvY2sEQ29pbgtDcml0Yml0VHJlZQlDdXN0b2RpYW4CSUQLTGlua2VkVGFibGUGT3B0aW9uBU9yZGVyDU9yZGVyQ2FuY2VsZWQLT3JkZXJGaWxsZWQNT3JkZXJGaWxsZWRWMgtPcmRlclBsYWNlZA1PcmRlclBsYWNlZFYyBFBvb2wLUG9vbENyZWF0ZWQDU1VJBVRhYmxlCVRpY2tMZXZlbAlUeENvbnRleHQIVHlwZU5hbWUDVUlEGWFjY291bnRfYXZhaWxhYmxlX2JhbGFuY2UPYWNjb3VudF9iYWxhbmNlA2FkZARhc2tzBGJhY2sHYmFsYW5jZQpiYXNlX2Fzc2V0HGJhc2VfYXNzZXRfcXVhbnRpdHlfY2FuY2VsZWQaYmFzZV9hc3NldF9xdWFudGl0eV9maWxsZWQaYmFzZV9hc3NldF9xdWFudGl0eV9wbGFjZWQdYmFzZV9hc3NldF9xdWFudGl0eV9yZW1haW5pbmcXYmFzZV9hc3NldF90cmFkaW5nX2ZlZXMOYmFzZV9jdXN0b2RpYW4SYmF0Y2hfY2FuY2VsX29yZGVyBGJpZHMGYm9ycm93FGJvcnJvd19sZWFmX2J5X2luZGV4EmJvcnJvd19sZWFmX2J5X2tleQpib3Jyb3dfbXV0GGJvcnJvd19tdXRfbGVhZl9ieV9pbmRleBFjYW5jZWxfYWxsX29yZGVycwxjYW5jZWxfb3JkZXIEY2xvYgVjbG9jawRjb2luCGNvbnRhaW5zDmNyZWF0ZV9hY2NvdW50C2NyZWF0ZV9wb29sDGNyZWF0aW9uX2ZlZQdjcml0Yml0CWN1c3RvZGlhbh9kZWNyZWFzZV91c2VyX2F2YWlsYWJsZV9iYWxhbmNlHGRlY3JlYXNlX3VzZXJfbG9ja2VkX2JhbGFuY2UMZGVwb3NpdF9iYXNlDWRlcG9zaXRfcXVvdGUNZGVzdHJveV9lbXB0eRNkZXN0cm95X2VtcHR5X2xldmVsBGVtaXQTZW1pdF9vcmRlcl9jYW5jZWxlZBFlbWl0X29yZGVyX2ZpbGxlZAVldmVudBBleHBpcmVfdGltZXN0YW1wEGZpbmRfY2xvc2VzdF9rZXkJZmluZF9sZWFmDGZyb21fYmFsYW5jZQVmcm9udBZnZXRfbGV2ZWwyX2Jvb2tfc3RhdHVzH2dldF9sZXZlbDJfYm9va19zdGF0dXNfYXNrX3NpZGUfZ2V0X2xldmVsMl9ib29rX3N0YXR1c19iaWRfc2lkZRBnZXRfbWFya2V0X3ByaWNlEGdldF9vcmRlcl9zdGF0dXMCaWQfaW5jcmVhc2VfdXNlcl9hdmFpbGFibGVfYmFsYW5jZRJpbmplY3RfbGltaXRfb3JkZXILaW5zZXJ0X2xlYWYMaW50b19iYWxhbmNlBmlzX2JpZAhpc19lbXB0eQdpc19ub25lBGpvaW4MbGlua2VkX3RhYmxlEGxpc3Rfb3Blbl9vcmRlcnMMbG9ja19iYWxhbmNlCGxvdF9zaXplEW1ha2VyX3JlYmF0ZV9yYXRlDW1ha2VyX3JlYmF0ZXMJbWF0Y2hfYXNrCW1hdGNoX2JpZB1tYXRjaF9iaWRfd2l0aF9xdW90ZV9xdWFudGl0eQRtYXRoCG1heF9sZWFmCG1pbl9sZWFmA211bANuZXcEbmV4dBFuZXh0X2Fza19vcmRlcl9pZBFuZXh0X2JpZF9vcmRlcl9pZAluZXh0X2xlYWYGb2JqZWN0C29wZW5fb3JkZXJzBm9wdGlvbghvcmRlcl9pZAxvcmRlcl9pc19iaWQFb3duZXIRcGxhY2VfbGltaXRfb3JkZXIScGxhY2VfbWFya2V0X29yZGVyB3Bvb2xfaWQNcHJldmlvdXNfbGVhZgVwcmljZQlwdXNoX2JhY2sIcXVhbnRpdHkLcXVvdGVfYXNzZXQYcXVvdGVfYXNzZXRfdHJhZGluZ19mZWVzD3F1b3RlX2N1c3RvZGlhbgZyZW1vdmUUcmVtb3ZlX2xlYWZfYnlfaW5kZXgMcmVtb3ZlX29yZGVyBXNwbGl0A3N1aRlzd2FwX2V4YWN0X2Jhc2VfZm9yX3F1b3RlGXN3YXBfZXhhY3RfcXVvdGVfZm9yX2Jhc2UFdGFibGUQdGFrZXJfY29tbWlzc2lvbg50YWtlcl9mZWVfcmF0ZQl0aWNrX3NpemUMdGltZXN0YW1wX21zDnRvdGFsX3F1YW50aXR5CnR4X2NvbnRleHQJdHlwZV9uYW1lDHVpZF9hc19pbm5lcg51bmxvY2tfYmFsYW5jZQp1bnNhZmVfZGl2CnVuc2FmZV9tdWwQdW5zYWZlX211bF9yb3VuZA91c3Jfb3Blbl9vcmRlcnMFdmFsdWUOd2l0aGRyYXdfYXNzZXQNd2l0aGRyYXdfYmFzZQ53aXRoZHJhd19xdW90ZQR6ZXJvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3ukAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAwgAAAAAAAAAAAMIAwAAAAAAAAADCAQAAAAAAAAAAwgFAAAAAAAAAAMIBgAAAAAAAAADCAcAAAAAAAAAAwgIAAAAAAAAAAMICQAAAAAAAAADCAoAAAAAAAAAAwgLAAAAAAAAAAMIDAAAAAAAAAADCA4AAAAAAAAAAwgTAAAAAAAAAAMIAMqaOwAAAAACAQACAQECAQICAQMDCAAAAAAAAACAAAIHbQgSHQgNcggNfgNXA38DVgMBAgdtCBJoA08BaggSIANvA0ADAgIGbQgSaANPAWoIEh4DbwMDAgptCBJoA08BaggSgQEDHwMhA28DfQNYAwQCBmgDbwNxA08BaggSQAMFAgJvA2YLEQIDCAQGAg9KCBMlCwkBCAUaCwkBCAVjA2IDiQELFQII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CwIJAAkBCgMGCA4EBwsLAgkACQEGCBMKAwoFAgYLCwIJAAkBBggOAQoICQQDAwMDAgsQAQMLEAEDBAYLCwIJAAkBAwMGCBMCCgMKAwMGCw0BCAoDAwMGCwsCCQAJAQMGCA4BBggJBggWBQYICQMDAwELCAIJAAkBAQYLCAIJAAkBCQgWAwEFBQMDAwMBBgsNAQgKAQYICgEGCxUCAwgJAQUCCQAJAQIFCxUCAwMCBgsZAgkACQEJAAEGCQEBCQECBwsSAQkAAwELEgEJAAILEgEJAAcIGgEIFwELFQIDCAkCAwgJAQsVAgkACQEBBggaAgkABQEJAAEIGAcIEQgXBQgWCAwIFwgRAQYLEgEJAAEIEQIDAwEGCBcBBggWAQgAAQsNAQkAAQsZAgkACQEBCw8BCQABBgsUAQkAAQYIDgMHCw8BCQAFCxIBCQABCwYBCQABCwYBCQEBCwcBCQAEBwsPAQkAAwYIDgcIGgELBwEJAQQDCxQBCQALFAEJAQMFCxABCgsIAgkACQEDCxQBCQALFAEJAQMBCgsIAgkACQEBBwsQAQkAAwsSAQkACxIBCQEDAQYIEwQLEgEJAAsQAQoLCAIJAAkBCxIBCQEDLAEBAwMLEAEKCwgCCQAJAQsSAQkBCxIBCQABAwMBBQMDAwcLDQEICgsSAQkACwMCCQAJAQoLAwIJAAkBAwMDCxIBCQADBggJBwgJAwMDCgsIAgkACQEGCxABAwMDCBYLEgEJAQsSAQkBAQMDAwEDBwgKAwEGCw0BCQABCxABCQABCwMCCQAJAQIHCw0BCQADAQcJAAEGCxUCCQAJAQEGCxABCQABBgkAAgYLFQIJAAkBCQADBwsPAQkABQMCAQMCBwsSAQkACxIBCQACBwsZAgkACQEJAAEHCQECBwsVAgkACQEJAAIGCw0BCQADAQYKCQABCwQCCQAJASgBAwEDAwsQAQoLCAIJAAkBCxIBCQELEgEJAAEDAwEFAwMDBwsNAQgKCxIBCQALAwIJAAkBCgsDAgkACQEDAwsSAQkAAwYICQcICQMKCwgCCQAJAQYLEAEDAwMIFgsSAQkBAQMDCxIBCQEDBwgKAyoBAwEDAwsQAQoLCAIJAAkBCxIBCQELEgEJAAEDAwEFAwMDBwsNAQgKCxIBCQALAwIJAAkBCgsDAgkACQEDAwsSAQkBAwYICQcICQMDCgsIAgkACQEGCxABAwMDCBYLEgEJAQsSAQkBAQMDCxIBCQEDBwgKAwELEAEKCwgCCQAJAQgLEgEJAAsSAQkACxQBCQALEAEKCwgCCQAJAQsQAQoLCAIJAAkBCxABCgsIAgkACQELEgEJAQsSAQkBAgcLFAEJAAsUAQkAAwcLFAEJAAMHCBoHAwcLDQEICggJAwUDAwMHCw8BCQAGCA4DAwcLDQEJAAMJAAMHCxUCCQAJAQkACQEBCwECCQAJAQMHCxkCCQAJAQkACQEQCxABCgsIAgkACQELEgEJAAsSAQkACxIBCQADCxABCgsIAgkACQELEAEKCwgCCQAJAQsQAQoLCAIJAAkBAwMFCxIBCQELEgEJAQsSAQkBAwMCBgsPAQkABQgIFgMDAQUDAwMBCwICCQAJAQ0IFgMDAwMDAwUDAQUDAwELBQIJAAkBCwMDBgsNAQgKBwsNAQgKAwEICQUDAwcLFQIDAwMDBwgKCAkWAwMDAwcLDQEICgMDAwEFAwsDAgkACQEKCwMCCQAJAQEHCw0BCAoICQMDBQgWAwcLFQIDAxoFAwMDAwMGCw0BCAoHCw0BCAoDAwEDCwMCCQAJAQoLAwIJAAkBAwEDAwMICQMFCBYDAwcLFQIDAxwBBQMDAwMDBwsNAQgKAwMDAwsDAgkACQEKCwMCCQAJAQMBAwMDAwcLDQEICggJAwUIFgMDBwsVAgMDBwYICgoICQYICQYLEAEDAwUGCxUCAwMFAwMFAwMECxABAwsQAQMLEAEDCxABAwYDCgMDAwMKAwQDBggJBgsQAQMGCxUCAwgJBAYLDQEICgMFBgsVAgMDCkFjY291bnRDYXARQWxsT3JkZXJzQ2FuY2VsZWQaQWxsT3JkZXJzQ2FuY2VsZWRDb21wb25lbnQHQmFsYW5jZQVDbG9jawRDb2luC0NyaXRiaXRUcmVlCUN1c3RvZGlhbgxEZXBvc2l0QXNzZXQCSUQLTGlua2VkVGFibGUUTWF0Y2hlZE9yZGVyTWV0YWRhdGEGT3B0aW9uBU9yZGVyDU9yZGVyQ2FuY2VsZWQLT3JkZXJGaWxsZWQLT3JkZXJQbGFjZWQEUG9vbAtQb29sQ3JlYXRlZAxQb29sT3duZXJDYXADU1VJBVRhYmxlCVRpY2tMZXZlbAlUeENvbnRleHQIVHlwZU5hbWUDVUlEDVdpdGhkcmF3QXNzZXQZYWNjb3VudF9hdmFpbGFibGVfYmFsYW5jZQ9hY2NvdW50X2JhbGFuY2UNYWNjb3VudF9vd25lcgNhZGQEYXNrcwRiYWNrB2JhbGFuY2UKYmFzZV9hc3NldBxiYXNlX2Fzc2V0X3F1YW50aXR5X2NhbmNlbGVkGmJhc2VfYXNzZXRfcXVhbnRpdHlfZmlsbGVkGmJhc2VfYXNzZXRfcXVhbnRpdHlfcGxhY2VkHWJhc2VfYXNzZXRfcXVhbnRpdHlfcmVtYWluaW5nF2Jhc2VfYXNzZXRfdHJhZGluZ19mZWVzDmJhc2VfY3VzdG9kaWFuEmJhdGNoX2NhbmNlbF9vcmRlcgRiaWRzBmJvcnJvdxRib3Jyb3dfbGVhZl9ieV9pbmRleBJib3Jyb3dfbGVhZl9ieV9rZXkKYm9ycm93X211dBhib3Jyb3dfbXV0X2xlYWZfYnlfaW5kZXgRY2FuY2VsX2FsbF9vcmRlcnMMY2FuY2VsX29yZGVyF2NsZWFuX3VwX2V4cGlyZWRfb3JkZXJzD2NsaWVudF9vcmRlcl9pZAdjbG9iX3YyBWNsb2NrC2Nsb25lX29yZGVyBGNvaW4IY29udGFpbnMOY3JlYXRlX2FjY291bnQWY3JlYXRlX2N1c3RvbWl6ZWRfcG9vbBljcmVhdGVfY3VzdG9taXplZF9wb29sX3YyImNyZWF0ZV9jdXN0b21pemVkX3Bvb2xfd2l0aF9yZXR1cm4LY3JlYXRlX3Bvb2wMY3JlYXRlX3Bvb2xfF2NyZWF0ZV9wb29sX3dpdGhfcmV0dXJuGGNyZWF0ZV9wb29sX3dpdGhfcmV0dXJuXwxjcmVhdGlvbl9mZWUHY3JpdGJpdAxjdXN0b2RpYW5fdjIfZGVjcmVhc2VfdXNlcl9hdmFpbGFibGVfYmFsYW5jZRxkZWNyZWFzZV91c2VyX2xvY2tlZF9iYWxhbmNlBmRlbGV0ZRVkZWxldGVfcG9vbF9vd25lcl9jYXAMZGVwb3NpdF9iYXNlDWRlcG9zaXRfcXVvdGUNZGVzdHJveV9lbXB0eRNkZXN0cm95X2VtcHR5X2xldmVsBGVtaXQTZW1pdF9vcmRlcl9jYW5jZWxlZBFlbWl0X29yZGVyX2ZpbGxlZAVldmVudBBleHBpcmVfdGltZXN0YW1wB2V4dHJhY3QQZmluZF9jbG9zZXN0X2tleQlmaW5kX2xlYWYMZnJvbV9iYWxhbmNlBWZyb250A2dldBZnZXRfbGV2ZWwyX2Jvb2tfc3RhdHVzH2dldF9sZXZlbDJfYm9va19zdGF0dXNfYXNrX3NpZGUfZ2V0X2xldmVsMl9ib29rX3N0YXR1c19iaWRfc2lkZRBnZXRfbWFya2V0X3ByaWNlEGdldF9vcmRlcl9zdGF0dXMCaWQfaW5jcmVhc2VfdXNlcl9hdmFpbGFibGVfYmFsYW5jZRJpbmplY3RfbGltaXRfb3JkZXILaW5zZXJ0X2xlYWYMaW50b19iYWxhbmNlBmlzX2JpZAhpc19lbXB0eQdpc19ub25lBGpvaW4MbGlua2VkX3RhYmxlEGxpc3Rfb3Blbl9vcmRlcnMMbG9ja19iYWxhbmNlCGxvdF9zaXplDW1ha2VyX2FkZHJlc3MVbWFrZXJfY2xpZW50X29yZGVyX2lkEW1ha2VyX3JlYmF0ZV9yYXRlDW1ha2VyX3JlYmF0ZXMJbWF0Y2hfYXNrCW1hdGNoX2JpZB1tYXRjaF9iaWRfd2l0aF9xdW90ZV9xdWFudGl0eRZtYXRjaGVkX29yZGVyX21ldGFkYXRhG21hdGNoZWRfb3JkZXJfbWV0YWRhdGFfaW5mbwRtYXRoCG1heF9sZWFmCG1pbl9sZWFmEG1pbnRfYWNjb3VudF9jYXADbXVsA25ldwRuZXh0EW5leHRfYXNrX29yZGVyX2lkEW5leHRfYmlkX29yZGVyX2lkCW5leHRfbGVhZgRub25lBm9iamVjdAtvcGVuX29yZGVycwZvcHRpb24Ib3JkZXJfaWQMb3JkZXJfaXNfYmlkC29yZGVyX3F1ZXJ5D29yZGVyc19jYW5jZWxlZBFvcmlnaW5hbF9xdWFudGl0eQVvd25lchFwbGFjZV9saW1pdF9vcmRlchVwbGFjZV9saW1pdF9vcmRlcl9pbnQfcGxhY2VfbGltaXRfb3JkZXJfd2l0aF9tZXRhZGF0YRJwbGFjZV9tYXJrZXRfb3JkZXIWcGxhY2VfbWFya2V0X29yZGVyX2ludCBwbGFjZV9tYXJrZXRfb3JkZXJfd2l0aF9tZXRhZGF0YQdwb29sX2lkCXBvb2xfc2l6ZQ1wcmV2aW91c19sZWFmBXByaWNlD3B1YmxpY190cmFuc2ZlcglwdXNoX2JhY2sIcXVhbnRpdHkLcXVvdGVfYXNzZXQYcXVvdGVfYXNzZXRfdHJhZGluZ19mZWVzHnF1b3RlX2Fzc2V0X3RyYWRpbmdfZmVlc192YWx1ZQ9xdW90ZV9jdXN0b2RpYW4GcmVtb3ZlFHJlbW92ZV9sZWFmX2J5X2luZGV4DHJlbW92ZV9vcmRlchhzZWxmX21hdGNoaW5nX3ByZXZlbnRpb24Gc2VuZGVyDHNoYXJlX29iamVjdARzaXplBHNvbWUFc3BsaXQDc3VpGXN3YXBfZXhhY3RfYmFzZV9mb3JfcXVvdGUnc3dhcF9leGFjdF9iYXNlX2Zvcl9xdW90ZV93aXRoX21ldGFkYXRhGXN3YXBfZXhhY3RfcXVvdGVfZm9yX2Jhc2Unc3dhcF9leGFjdF9xdW90ZV9mb3JfYmFzZV93aXRoX21ldGFkYXRhBXRhYmxlDXRha2VyX2FkZHJlc3MVdGFrZXJfY2xpZW50X29yZGVyX2lkEHRha2VyX2NvbW1pc3Npb24OdGFrZXJfZmVlX3JhdGUKdGlja19sZXZlbAl0aWNrX3NpemUMdGltZXN0YW1wX21zCHRyYW5zZmVyCnR4X2NvbnRleHQJdHlwZV9uYW1lDHVpZF9hc19pbm5lcg51aWRfdG9fYWRkcmVzcw51bmxvY2tfYmFsYW5jZQp1bnNhZmVfZGl2CnVuc2FmZV9tdWwQdW5zYWZlX211bF9yb3VuZA91c3Jfb3Blbl9vcmRlcnMVdXNyX29wZW5fb3JkZXJzX2V4aXN0G3Vzcl9vcGVuX29yZGVyc19mb3JfYWRkcmVzcwV2YWx1ZQZ2ZWN0b3IOd2l0aGRyYXdfYXNzZXQNd2l0aGRyYXdfYmFzZQ13aXRoZHJhd19mZWVzDndpdGhkcmF3X3F1b3RlBHplcm8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe6QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDCAIAAAAAAAAAAwgDAAAAAAAAAAMIBAAAAAAAAAADCAUAAAAAAAAAAwgGAAAAAAAAAAMIBwAAAAAAAAADCAgAAAAAAAAAAwgJAAAAAAAAAAMICgAAAAAAAAADCAsAAAAA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BwsCAQkABQMBAwIHCwIBCQAFAQcLAAEJAAIIBQUBCAUBBggFAwYLAAEJAAMDAQkAAgULAAEJAAIGCwYCCQAJAQkAAQEBBgkBAQYLAwEJAAELBgIJAAkBAgsDAQkABwgHAgcLAwEJAAsDAQkAAgcLAwEJAAMDBwsGAgkACQEJAAkBAgcLBgIJAAkBCQABBwkBB0FjY291bnQKQWNjb3VudENhcAdCYWxhbmNlBENvaW4JQ3VzdG9kaWFuBVRhYmxlCVR4Q29udGV4dANVSUQZYWNjb3VudF9hdmFpbGFibGVfYmFsYW5jZQ9hY2NvdW50X2JhbGFuY2UQYWNjb3VudF9iYWxhbmNlcxZhY2NvdW50X2xvY2tlZF9iYWxhbmNlDWFjY291bnRfb3duZXIDYWRkEWF2YWlsYWJsZV9iYWxhbmNlB2JhbGFuY2UGYm9ycm93CmJvcnJvd19tdXQaYm9ycm93X211dF9hY2NvdW50X2JhbGFuY2UHY2xvYl92MgRjb2luCGNvbnRhaW5zGGNyZWF0ZV9jaGlsZF9hY2NvdW50X2NhcAxjdXN0b2RpYW5fdjIfZGVjcmVhc2VfdXNlcl9hdmFpbGFibGVfYmFsYW5jZRxkZWNyZWFzZV91c2VyX2xvY2tlZF9iYWxhbmNlBmRlbGV0ZRJkZWxldGVfYWNjb3VudF9jYXAMZnJvbV9iYWxhbmNlAmlkH2luY3JlYXNlX3VzZXJfYXZhaWxhYmxlX2JhbGFuY2UcaW5jcmVhc2VfdXNlcl9sb2NrZWRfYmFsYW5jZQRqb2luDGxvY2tfYmFsYW5jZQ5sb2NrZWRfYmFsYW5jZRBtaW50X2FjY291bnRfY2FwA25ldwZvYmplY3QFb3duZXIFc3BsaXQFdGFibGUKdHhfY29udGV4dA51aWRfdG9fYWRkcmVzcw51bmxvY2tfYmFsYW5jZQV2YWx1ZQ53aXRoZHJhd19hc3NldAR6ZXJvAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3ukAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgMIAgAAAAAAAAAAAgIOCwMBCQAiCwMBCQABAgIdCAUmBQICAh0IBQoLBgIFCwABCQACFwAXAAMAABMKCwARFgwBDgERFwwCCwELAhIBAgEBAAADFgoAEAAR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 +authority-store-pruning-config: + num-latest-epoch-dbs-to-retain: 3 + epoch-db-pruning-period-secs: 3600 + num-epochs-to-retain: 0 + max-checkpoints-in-batch: 10 + max-transactions-in-batch: 1000 +end-of-epoch-broadcast-channel-capacity: 128 +checkpoint-executor-config: + checkpoint-execution-max-concurrency: 200 + local-execution-timeout-sec: 30 +db-checkpoint-config: + perform-db-checkpoints-at-epoch-end: false +indirect-objects-threshold: 18446744073709551615 +expensive-safety-check-config: + enable-epoch-sui-conservation-check: true + enable-deep-per-tx-sui-conservation-check: true + force-disable-epoch-sui-conservation-check: false + enable-state-consistency-check: true + force-disable-state-consistency-check: false + enable-secondary-index-checks: false +transaction-deny-config: + package-publish-disabled: false + package-upgrade-disabled: false + shared-object-disabled: false + user-transaction-disabled: false + receiving-objects-disabled: false + zklogin-sig-disabled: false + zklogin-disabled-providers: [] +certificate-deny-config: {} +state-debug-dump-config: {} +state-archive-write-config: + concurrency: 0 + use-for-pruning-watermark: false +state-archive-read-config: [] +state-snapshot-write-config: + concurrency: 0 +indexer-max-subscriptions: ~ +transaction-kv-store-read-config: + base-url: "" +jwk-fetch-interval-seconds: 3600 +zklogin-oauth-providers: + Mainnet: + - Facebook + - Google + - Twitch + Testnet: + - Facebook + - Google + - Twitch + Unknown: + - Apple + - Facebook + - Google + - Kakao + - Slack + - Twitch +authority-overload-config: + max-txn-age-in-queue: + secs: 1 + nanos: 0 + overload-monitor-interval: + secs: 10 + nanos: 0 + execution-queue-latency-soft-limit: + secs: 1 + nanos: 0 + execution-queue-latency-hard-limit: + secs: 10 + nanos: 0 + max-load-shedding-percentage: 95 + min-load-shedding-percentage-above-hard-limit: 50 + safe-transaction-ready-rate: 100 diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/devnet/genesis.blob b/target_chains/sui/vendor/wormhole_iota_testnet/devnet/genesis.blob new file mode 100644 index 0000000000000000000000000000000000000000..5d1cb997ea2df5e1248fd6e3dd7eae1d21bf21f1 GIT binary patch literal 329923 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 + witness, + // TODO: create a version of this for each decimal to be used + 8, + ctx + ), + tx_context::sender(ctx) + ); + } + + #[test_only] + /// NOTE: Even though this module is `#[test_only]`, this method is tagged + /// with the same macro as a trick to allow another method within this + /// module to call `init` using OTW. + public fun init_test_only(ctx: &mut TxContext) { + init(COIN {}, ctx); + + // This will be created and sent to the transaction sender + // automatically when the contract is published. + transfer::public_transfer( + package::test_publish(object::id_from_address(@coins), ctx), + tx_context::sender(ctx) + ); + } +} + +#[test_only] +module coins::coin_tests { + use sui::coin::{Self}; + use sui::package::{UpgradeCap}; + use sui::test_scenario::{Self}; + use token_bridge::create_wrapped::{Self, WrappedAssetSetup}; + use token_bridge::state::{Self}; + use token_bridge::token_bridge_scenario::{ + register_dummy_emitter, + return_state, + set_up_wormhole_and_token_bridge, + take_state, + two_people + }; + use token_bridge::token_registry::{Self}; + use token_bridge::vaa::{Self}; + use token_bridge::wrapped_asset::{Self}; + use wormhole::bytes32::{Self}; + use wormhole::external_address::{Self}; + use wormhole::wormhole_scenario::{parse_and_verify_vaa}; + + use token_bridge::version_control::{V__0_2_0 as V__CURRENT}; + + use coins::coin::{COIN}; + +// +------------------------------------------------------------------------------+ +// | Wormhole VAA v1 | nonce: 1 | time: 1 | +// | guardian set #0 | #22080291 | consistency: 0 | +// |------------------------------------------------------------------------------| +// | Signature: | +// | #0: 80366065746148420220f25a6275097370e8db40984529a6676b7a5fc9fe... | +// |------------------------------------------------------------------------------| +// | Emitter: 0x00000000000000000000000000000000deadbeef (Ethereum) | +// |==============================================================================| +// | Token attestation | +// | decimals: 12 | +// | Token: 0x00000000000000000000000000000000beefface (Ethereum) | +// | Symbol: BEEF | +// | Name: Beef face Token | +// +------------------------------------------------------------------------------+ + const VAA: vector = + x"0100000000010080366065746148420220f25a6275097370e8db40984529a6676b7a5fc9feb11755ec49ca626b858ddfde88d15601f85ab7683c5f161413b0412143241c700aff010000000100000001000200000000000000000000000000000000000000000000000000000000deadbeef000000000150eb23000200000000000000000000000000000000000000000000000000000000beefface00020c424545460000000000000000000000000000000000000000000000000000000042656566206661636520546f6b656e0000000000000000000000000000000000"; + +// +------------------------------------------------------------------------------+ +// | Wormhole VAA v1 | nonce: 69 | time: 0 | +// | guardian set #0 | #1 | consistency: 15 | +// |------------------------------------------------------------------------------| +// | Signature: | +// | #0: b0571650590e147fce4eb60105e0463522c1244a97bd5dcb365d3e7bc7f3... | +// |------------------------------------------------------------------------------| +// | Emitter: 0x00000000000000000000000000000000deadbeef (Ethereum) | +// |==============================================================================| +// | Token attestation | +// | decimals: 12 | +// | Token: 0x00000000000000000000000000000000beefface (Ethereum) | +// | Symbol: BEEF??? and profit | +// | Name: Beef face Token??? and profit | +// +------------------------------------------------------------------------------+ + const UPDATED_VAA: vector = + x"0100000000010062f4dcd21bbbc4af8b8baaa2da3a0b168efc4c975de5b828c7a3c710b67a0a0d476d10a74aba7a7867866daf97d1372d8e6ee62ccc5ae522e3e603c67fa23787000000000000000045000200000000000000000000000000000000000000000000000000000000deadbeef00000000000000010f0200000000000000000000000000000000000000000000000000000000beefface00020c424545463f3f3f20616e642070726f666974000000000000000000000000000042656566206661636520546f6b656e3f3f3f20616e642070726f666974000000"; + + + #[test] + public fun test_complete_and_update_attestation() { + let (caller, coin_deployer) = two_people(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Ignore effects. Make sure `coin_deployer` receives + // `WrappedAssetSetup`. + test_scenario::next_tx(scenario, coin_deployer); + + // Publish coin. + coins::coin::init_test_only(test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, coin_deployer); + + let wrapped_asset_setup = + test_scenario::take_from_address>( + scenario, + coin_deployer + ); + + let token_bridge_state = take_state(scenario); + + let verified_vaa = parse_and_verify_vaa(scenario, VAA); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + let coin_meta = test_scenario::take_shared(scenario); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + create_wrapped::complete_registration( + &mut token_bridge_state, + &mut coin_meta, + wrapped_asset_setup, + test_scenario::take_from_address( + scenario, + coin_deployer + ), + msg + ); + + // Check registry. + { + let verified = state::verified_asset(&token_bridge_state); + assert!(token_bridge::token_registry::is_wrapped(&verified), 0); + + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = + token_registry::borrow_wrapped(registry); + assert!(wrapped_asset::total_supply(asset) == 0, 0); + + // Decimals are capped for this wrapped asset. + assert!(coin::get_decimals(&coin_meta) == 8, 0); + + // Check metadata against asset metadata. + let info = wrapped_asset::info(asset); + assert!(wrapped_asset::token_chain(info) == 2, 0); + assert!(wrapped_asset::token_address(info) == external_address::new(bytes32::from_bytes(x"00000000000000000000000000000000beefface")), 0); + assert!( + wrapped_asset::native_decimals(info) == 12, + 0 + ); + assert!(coin::get_symbol(&coin_meta) == std::ascii::string(b"BEEF"), 0); + assert!(coin::get_name(&coin_meta) == std::string::utf8(b"Beef face Token"), 0); + }; + + let verified_vaa = + parse_and_verify_vaa(scenario, UPDATED_VAA); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Now update metadata. + create_wrapped::update_attestation(&mut token_bridge_state, &mut coin_meta, msg); + + // Check updated name and symbol. + assert!( + coin::get_name(&coin_meta) == std::string::utf8(b"Beef face Token??? and profit"), + 0 + ); + assert!( + coin::get_symbol(&coin_meta) == std::ascii::string(b"BEEF??? and profit"), + 0 + ); + + // Clean up. + return_state(token_bridge_state); + test_scenario::return_shared(coin_meta); + + + // Done. + test_scenario::end(my_scenario); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_10.move b/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_10.move new file mode 100644 index 0000000000..2c98b87936 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_10.move @@ -0,0 +1,72 @@ +module coins::coin_10 { + use std::option; + use sui::coin::{Self, TreasuryCap, CoinMetadata}; + use sui::transfer; + use sui::tx_context::{Self, TxContext}; + + /// The type identifier of coin. The coin will have a type + /// tag of kind: `Coin` + /// Make sure that the name of the type matches the module's name. + struct COIN_10 has drop {} + + /// Module initializer is called once on module publish. A treasury + /// cap is sent to the publisher, who then controls minting and burning + fun init(witness: COIN_10, ctx: &mut TxContext) { + let (treasury, metadata) = create_coin(witness, ctx); + transfer::public_freeze_object(metadata); + transfer::public_transfer(treasury, tx_context::sender(ctx)); + } + + fun create_coin( + witness: COIN_10, + ctx: &mut TxContext + ): (TreasuryCap, CoinMetadata) { + coin::create_currency( + witness, + 10, // decimals + b"COIN_10", // symbol + b"10-Decimal Coin", // name + b"", // description + option::none(), // icon_url + ctx + ) + } + + #[test_only] + public fun create_coin_test_only( + ctx: &mut TxContext + ): (TreasuryCap, CoinMetadata) { + create_coin(COIN_10 {}, ctx) + } + + #[test_only] + public fun init_test_only(ctx: &mut TxContext) { + init(COIN_10 {}, ctx) + } +} + +#[test_only] +module coins::coin_10_tests { + use sui::test_scenario::{Self}; + + use coins::coin_10::{Self}; + + #[test] + public fun init_test() { + let my_scenario = test_scenario::begin(@0x0); + let scenario = &mut my_scenario; + let creator = @0xDEADBEEF; + + // Proceed. + test_scenario::next_tx(scenario, creator); + + // Init. + coin_10::init_test_only(test_scenario::ctx(scenario)); + + // Proceed. + test_scenario::next_tx(scenario, creator); + + // Done. + test_scenario::end(my_scenario); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_8.move b/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_8.move new file mode 100644 index 0000000000..0edd761603 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_8.move @@ -0,0 +1,72 @@ +module coins::coin_8 { + use std::option::{Self}; + use sui::coin::{Self, TreasuryCap, CoinMetadata}; + use sui::transfer::{Self}; + use sui::tx_context::{Self, TxContext}; + + /// The type identifier of coin. The coin will have a type + /// tag of kind: `Coin` + /// Make sure that the name of the type matches the module's name. + struct COIN_8 has drop {} + + /// Module initializer is called once on module publish. A treasury + /// cap is sent to the publisher, who then controls minting and burning + fun init(witness: COIN_8, ctx: &mut TxContext) { + let (treasury, metadata) = create_coin(witness, ctx); + transfer::public_freeze_object(metadata); + transfer::public_transfer(treasury, tx_context::sender(ctx)); + } + + fun create_coin( + witness: COIN_8, + ctx: &mut TxContext + ): (TreasuryCap, CoinMetadata) { + coin::create_currency( + witness, + 8, // decimals + b"COIN_8", // symbol + b"8-Decimal Coin", // name + b"", // description + option::none(), // icon_url + ctx + ) + } + + #[test_only] + public fun create_coin_test_only( + ctx: &mut TxContext + ): (TreasuryCap, CoinMetadata) { + create_coin(COIN_8 {}, ctx) + } + + #[test_only] + public fun init_test_only(ctx: &mut TxContext) { + init(COIN_8 {}, ctx) + } +} + +#[test_only] +module coins::coin_8_tests { + use sui::test_scenario::{Self}; + + use coins::coin_8::{Self}; + + #[test] + public fun init_test() { + let my_scenario = test_scenario::begin(@0x0); + let scenario = &mut my_scenario; + let creator = @0xDEADBEEF; + + // Proceed. + test_scenario::next_tx(scenario, creator); + + // Init. + coin_8::init_test_only(test_scenario::ctx(scenario)); + + // Proceed. + test_scenario::next_tx(scenario, creator); + + // Done. + test_scenario::end(my_scenario); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Makefile b/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Makefile new file mode 100644 index 0000000000..210a28de7a --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Makefile @@ -0,0 +1,20 @@ +-include ../../../Makefile.help + +.PHONY: artifacts +artifacts: clean + +.PHONY: clean +# Clean build artifacts +clean: + rm -rf build + +.PHONY: build +# Build contract +build: + sui move build + +.PHONY: test +# Run tests +test: + sui move build -d || exit $? + sui move test -t 1 diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Move.devnet.toml b/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Move.devnet.toml new file mode 100644 index 0000000000..d9363b2554 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Move.devnet.toml @@ -0,0 +1,14 @@ +[package] +name = "CoreMessages" +version = "1.0.0" + +[dependencies.Sui] +git = "https://github.com/MystenLabs/sui.git" +subdir = "crates/sui-framework/packages/sui-framework" +rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" + +[dependencies.Wormhole] +local = "../../wormhole" + +[addresses] +core_messages = "_" diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Move.lock b/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Move.lock new file mode 100644 index 0000000000..9bddcd1268 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Move.lock @@ -0,0 +1,39 @@ +# @generated by Move, please check-in and do not edit manually. + +[move] +version = 0 +manifest_digest = "E0D2B32F0A5B6F9A76311FD7A68260A698BD9ECCEAF95A779183CB374EC933FB" +deps_digest = "060AD7E57DFB13104F21BE5F5C3759D03F0553FC3229247D9A7A6B45F50D03A3" + +dependencies = [ + { name = "Sui" }, +] + +dev-dependencies = [ + { name = "Wormhole" }, +] + +[[move.package]] +name = "MoveStdlib" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "041c5f2bae2fe52079e44b70514333532d69f4e6", subdir = "crates/sui-framework/packages/move-stdlib" } + +[[move.package]] +name = "Sui" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "041c5f2bae2fe52079e44b70514333532d69f4e6", subdir = "crates/sui-framework/packages/sui-framework" } + +dependencies = [ + { name = "MoveStdlib" }, +] + +[[move.package]] +name = "Wormhole" +source = { local = "../../wormhole" } + +dependencies = [ + { name = "Sui" }, +] + +[move.toolchain-version] +compiler-version = "1.19.0" +edition = "legacy" +flavor = "sui" diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Move.toml b/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Move.toml new file mode 100644 index 0000000000..38872b17dc --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/Move.toml @@ -0,0 +1,21 @@ +[package] +name = "CoreMessages" +version = "1.0.0" + +[dependencies.Sui] +git = "https://github.com/MystenLabs/sui.git" +subdir = "crates/sui-framework/packages/sui-framework" +rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" + +[dependencies.Wormhole] +local = "../../wormhole" + +[addresses] +core_messages = "_" + +[dev-dependencies.Wormhole] +local = "../../wormhole" + +[dev-addresses] +wormhole = "0x100" +core_messages = "0x169" diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/sources/sender.move b/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/sources/sender.move new file mode 100644 index 0000000000..960c6355c3 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/sources/sender.move @@ -0,0 +1,149 @@ +/// A simple contracts that demonstrates how to send messages with wormhole. +module core_messages::sender { + use sui::clock::{Clock}; + use sui::coin::{Self}; + use sui::object::{Self, UID}; + use sui::transfer::{Self}; + use sui::tx_context::{TxContext}; + use wormhole::emitter::{Self, EmitterCap}; + use wormhole::state::{State as WormholeState}; + + struct State has key, store { + id: UID, + emitter_cap: EmitterCap, + } + + /// Register ourselves as a wormhole emitter. This gives back an + /// `EmitterCap` which will be required to send messages through + /// wormhole. + public fun init_with_params( + wormhole_state: &WormholeState, + ctx: &mut TxContext + ) { + transfer::share_object( + State { + id: object::new(ctx), + emitter_cap: emitter::new(wormhole_state, ctx) + } + ); + } + + public fun send_message_entry( + state: &mut State, + wormhole_state: &mut WormholeState, + payload: vector, + the_clock: &Clock, + ctx: &mut TxContext + ) { + send_message( + state, + wormhole_state, + payload, + the_clock, + ctx + ); + } + + /// NOTE: This is NOT the proper way of using the `prepare_message` and + /// `publish_message` workflow. This example app is meant for testing for + /// observing Wormhole messages via the guardian. + /// + /// See `publish_message` module for more info. + public fun send_message( + state: &mut State, + wormhole_state: &mut WormholeState, + payload: vector, + the_clock: &Clock, + ctx: &mut TxContext + ): u64 { + use wormhole::publish_message::{prepare_message, publish_message}; + + // NOTE AGAIN: Integrators should NEVER call this within their contract. + publish_message( + wormhole_state, + coin::zero(ctx), + prepare_message( + &mut state.emitter_cap, + 0, // Set nonce to 0, intended for batch VAAs. + payload + ), + the_clock + ) + } +} + +#[test_only] +module core_messages::sender_test { + use sui::test_scenario::{Self}; + use wormhole::wormhole_scenario::{ + return_clock, + return_state, + set_up_wormhole, + take_clock, + take_state, + two_people, + }; + + use core_messages::sender::{ + State, + init_with_params, + send_message, + }; + + #[test] + public fun test_send_message() { + let (user, admin) = two_people(); + let my_scenario = test_scenario::begin(admin); + let scenario = &mut my_scenario; + + // Initialize Wormhole. + let wormhole_message_fee = 0; + set_up_wormhole(scenario, wormhole_message_fee); + + // Initialize sender module. + test_scenario::next_tx(scenario, admin); + { + let wormhole_state = take_state(scenario); + init_with_params(&wormhole_state, test_scenario::ctx(scenario)); + return_state(wormhole_state); + }; + + // Send message as an ordinary user. + test_scenario::next_tx(scenario, user); + { + let state = test_scenario::take_shared(scenario); + let wormhole_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let first_message_sequence = send_message( + &mut state, + &mut wormhole_state, + b"Hello", + &the_clock, + test_scenario::ctx(scenario) + ); + assert!(first_message_sequence == 0, 0); + + let second_message_sequence = send_message( + &mut state, + &mut wormhole_state, + b"World", + &the_clock, + test_scenario::ctx(scenario) + ); + assert!(second_message_sequence == 1, 0); + + // Clean up. + test_scenario::return_shared(state); + return_state(wormhole_state); + return_clock(the_clock); + }; + + // Check effects. + let effects = test_scenario::next_tx(scenario, user); + assert!(test_scenario::num_user_events(&effects) == 2, 0); + + // End test. + test_scenario::end(my_scenario); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/README.md b/target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/README.md new file mode 100644 index 0000000000..41a8dbd907 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/README.md @@ -0,0 +1,3 @@ +# Templates + +This directory contains templates for Sui contracts. These templates aren't fully functional contracts and require substitution of variables prior to deployment. For example, the `wrapped_coin` template requires the version control struct name as well as the decimals of the wrapped token. diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/Move.toml b/target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/Move.toml new file mode 100644 index 0000000000..0c25220b1d --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/Move.toml @@ -0,0 +1,19 @@ +[package] +name = "WrappedCoin" +version = "0.0.1" + +[dependencies.Sui] +git = "https://github.com/MystenLabs/sui.git" +subdir = "crates/sui-framework/packages/sui-framework" +rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" + +[dependencies.Wormhole] +local = "../../wormhole" + +[dependencies.TokenBridge] +local = "../../token_bridge" + +[addresses] +wormhole = "_" +token_bridge = "_" +wrapped_coin = "0x0" diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/sources/coin.move b/target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/sources/coin.move new file mode 100644 index 0000000000..313b9ba919 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/sources/coin.move @@ -0,0 +1,21 @@ +module wrapped_coin::coin { + use sui::transfer::{Self}; + use sui::tx_context::{Self, TxContext}; + + use token_bridge::create_wrapped::{Self}; + + struct COIN has drop {} + + fun init(witness: COIN, ctx: &mut TxContext) { + use token_bridge::version_control::{{{VERSION}}}; + + transfer::public_transfer( + create_wrapped::prepare_registration( + witness, + {{DECIMALS}}, + ctx + ), + tx_context::sender(ctx) + ); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/scripts/deploy.sh b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/deploy.sh new file mode 100755 index 0000000000..d572f8cbf0 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/deploy.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Help message +function usage() { +cat <&2 +Deploy and initialize Sui core bridge and token bridge contracts to the +specified network. Additionally deploys an example messaging contract in +devnet. + + Usage: $(basename "$0") [options] + + Positional args: + Network to deploy to (devnet, testnet, mainnet) + + Options: + -k, --private-key Use given key to sign transactions + -h, --help Show this help message +EOF +exit 1 +} + +# If positional args are missing, print help message and exit +if [ $# -lt 1 ]; then + usage +fi + +# Default values +PRIVATE_KEY_ARG= + +# Set network +NETWORK=$1 || usage +shift + +# Set guardian address +if [ "$NETWORK" = mainnet ]; then + echo "Mainnet not supported yet" + exit 1 +elif [ "$NETWORK" = testnet ]; then + echo "Testnet not supported yet" + exit 1 +elif [ "$NETWORK" = devnet ]; then + GUARDIAN_ADDR=befa429d57cd18b7f8a4d91a2da9ab4af05d0fbe +else + usage +fi + +# Parse short/long flags +while [[ $# -gt 0 ]]; do + case "$1" in + -k|--private-key) + if [[ ! -z "$2" ]]; then + PRIVATE_KEY_ARG="-k $2" + fi + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" + usage + exit 1 + ;; + esac +done + +# Assumes this script is in a sibling directory to contract dirs +DIRNAME=$(dirname "$0") +WORMHOLE_PATH=$(realpath "$DIRNAME"/../wormhole) +TOKEN_BRIDGE_PATH=$(realpath "$DIRNAME"/../token_bridge) +EXAMPLE_APP_PATH=$(realpath "$DIRNAME"/../examples/core_messages) +EXAMPLE_COIN_PATH=$(realpath "$DIRNAME"/../examples/coins) + +echo -e "[1/4] Publishing core bridge contracts..." +WORMHOLE_PUBLISH_OUTPUT=$($(echo worm sui deploy "$WORMHOLE_PATH" -n "$NETWORK" "$PRIVATE_KEY_ARG")) +echo "$WORMHOLE_PUBLISH_OUTPUT" + +echo -e "\n[2/4] Initializing core bridge..." +WORMHOLE_PACKAGE_ID=$(echo "$WORMHOLE_PUBLISH_OUTPUT" | grep -oP 'Published to +\K.*') +WORMHOLE_INIT_OUTPUT=$($(echo worm sui init-wormhole -n "$NETWORK" --initial-guardian "$GUARDIAN_ADDR" -p "$WORMHOLE_PACKAGE_ID" "$PRIVATE_KEY_ARG")) +WORMHOLE_STATE_OBJECT_ID=$(echo "$WORMHOLE_INIT_OUTPUT" | grep -oP 'Wormhole state object ID +\K.*') +echo "$WORMHOLE_INIT_OUTPUT" + +echo -e "\n[3/4] Publishing token bridge contracts..." +TOKEN_BRIDGE_PUBLISH_OUTPUT=$($(echo worm sui deploy "$TOKEN_BRIDGE_PATH" -n "$NETWORK" "$PRIVATE_KEY_ARG")) +echo "$TOKEN_BRIDGE_PUBLISH_OUTPUT" + +echo -e "\n[4/4] Initializing token bridge..." +TOKEN_BRIDGE_PACKAGE_ID=$(echo "$TOKEN_BRIDGE_PUBLISH_OUTPUT" | grep -oP 'Published to +\K.*') +TOKEN_BRIDGE_INIT_OUTPUT=$($(echo worm sui init-token-bridge -n "$NETWORK" -p "$TOKEN_BRIDGE_PACKAGE_ID" -w "$WORMHOLE_STATE_OBJECT_ID" "$PRIVATE_KEY_ARG")) +TOKEN_BRIDGE_STATE_OBJECT_ID=$(echo "$TOKEN_BRIDGE_INIT_OUTPUT" | grep -oP 'Token bridge state object ID +\K.*') +echo "$TOKEN_BRIDGE_INIT_OUTPUT" + +if [ "$NETWORK" = devnet ]; then + echo -e "\n[+1/2] Deploying and initializing example app..." + EXAMPLE_APP_PUBLISH_OUTPUT=$($(echo worm sui deploy "$EXAMPLE_APP_PATH" -n "$NETWORK" "$PRIVATE_KEY_ARG")) + EXAMPLE_APP_PACKAGE_ID=$(echo "$EXAMPLE_APP_PUBLISH_OUTPUT" | grep -oP 'Published to +\K.*') + echo "$EXAMPLE_APP_PUBLISH_OUTPUT" + + EXAMPLE_INIT_OUTPUT=$($(echo worm sui init-example-message-app -n "$NETWORK" -p "$EXAMPLE_APP_PACKAGE_ID" -w "$WORMHOLE_STATE_OBJECT_ID" "$PRIVATE_KEY_ARG")) + EXAMPLE_APP_STATE_OBJECT_ID=$(echo "$EXAMPLE_INIT_OUTPUT" | grep -oP 'Example app state object ID +\K.*') + echo "$EXAMPLE_INIT_OUTPUT" + + echo -e "\n[+2/2] Deploying example coins..." + EXAMPLE_COIN_PUBLISH_OUTPUT=$($(echo worm sui deploy "$EXAMPLE_COIN_PATH" -n "$NETWORK" "$PRIVATE_KEY_ARG")) + echo "$EXAMPLE_COIN_PUBLISH_OUTPUT" + + echo -e "\nWormhole package ID: $WORMHOLE_PACKAGE_ID" + echo "Token bridge package ID: $TOKEN_BRIDGE_PACKAGE_ID" + echo "Wormhole state object ID: $WORMHOLE_STATE_OBJECT_ID" + echo "Token bridge state object ID: $TOKEN_BRIDGE_STATE_OBJECT_ID" + + echo -e "\nPublish message command:" worm sui publish-example-message -n devnet -p "$EXAMPLE_APP_PACKAGE_ID" -s "$EXAMPLE_APP_STATE_OBJECT_ID" -w "$WORMHOLE_STATE_OBJECT_ID" -m "hello" "$PRIVATE_KEY_ARG" +fi + +echo -e "\nDeployments successful!" diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/scripts/node_builder.sh b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/node_builder.sh new file mode 100755 index 0000000000..940017e330 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/node_builder.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +git clone https://github.com/MystenLabs/sui.git --branch devnet +cd sui +# Corresponds to https://github.com/MystenLabs/sui/releases/tag/mainnet-v1.19.1 +git reset --hard 041c5f2bae2fe52079e44b70514333532d69f4e6 + +cargo --locked install --path crates/sui +cargo --locked install --path crates/sui-faucet +cargo --locked install --path crates/sui-gateway +cargo --locked install --path crates/sui-node diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/scripts/register_devnet.sh b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/register_devnet.sh new file mode 100755 index 0000000000..79df935672 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/register_devnet.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +set -e + +DOTENV=$(realpath "$(dirname "$0")"/../.env) +[ -f $DOTENV ] || (echo "$DOTENV does not exist." >&2; exit 1) + +# 1. load variables from .env file +. $DOTENV + +# 2. next we get all the token bridge registration VAAs from the environment +# if a new VAA is added, this will automatically pick it up +VAAS=$(set | grep "REGISTER_.*_TOKEN_BRIDGE_VAA" | grep -v SUI | cut -d '=' -f1) + +# 3. use 'worm' to submit each registration VAA +for VAA in $VAAS +do + VAA=${!VAA} + worm submit $VAA --chain sui --network devnet +done + +echo "Registrations successful." diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/scripts/setup_rust.sh b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/setup_rust.sh new file mode 100755 index 0000000000..3738ba9548 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/setup_rust.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/scripts/start_node.sh b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/start_node.sh new file mode 100755 index 0000000000..2ffce35438 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/start_node.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -x + +sui start 2>&1 diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/scripts/switch.sh b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/switch.sh new file mode 100755 index 0000000000..caba0f4b70 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/switch.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +network="$1" +valid_networks=("devnet" "testnet" "mainnet" "reset") + +usage() { + echo "Usage: $0 {devnet|testnet|mainnet|reset}" >&2 + exit 1 +} + +if [[ ! " ${valid_networks[@]} " =~ " ${network} " ]]; then + echo "Error: Unrecognized network '${network}'." + usage +fi + +git ls-files | grep 'Move.toml' | while read -r file; do + if [[ "$network" == "reset" ]]; then + echo "Resetting $file" + git checkout "$file" --quiet + else + dir=$(dirname "$file") + base=$(basename "$file") + new_file="${dir}/Move.$network.toml" + if [ -f "$new_file" ]; then + echo "Switching $file to $new_file" + rm "$file" + # Create a relative symlink + (cd "$dir" && ln -s "$(basename "$new_file")" "$base") + fi + fi +done diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/scripts/wait_for_devnet.sh b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/wait_for_devnet.sh new file mode 100755 index 0000000000..ff0b00355e --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/scripts/wait_for_devnet.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -e + +# Wait for sui to start +while [[ "$(curl -X POST -H "Content-Type: application/json" -d '{ "jsonrpc":"2.0", "method":"rpc.discover","id":1 }' -s -o /dev/null -w '%{http_code}' 0.0.0.0:9000/)" != "200" ]]; do sleep 1; done diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/.gitignore b/target_chains/sui/vendor/wormhole_iota_testnet/testing/.gitignore new file mode 100644 index 0000000000..b552b7394c --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/.gitignore @@ -0,0 +1,4 @@ +node_modules +sui.log.* +./token_bridge/ +./wormhole/ diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/Makefile b/target_chains/sui/vendor/wormhole_iota_testnet/testing/Makefile new file mode 100644 index 0000000000..60c5246198 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/Makefile @@ -0,0 +1,13 @@ +-include ../Makefile.help + +.PHONY: clean +clean: + rm -rf node_modules + +node_modules: + pnpm i + +.PHONY: test +## Run tests +test: node_modules + bash run_integration_test.sh diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/00_environment.ts b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/00_environment.ts new file mode 100644 index 0000000000..3ca09f236d --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/00_environment.ts @@ -0,0 +1,78 @@ +import { expect } from "chai"; +import * as mock from "@certusone/wormhole-sdk/lib/cjs/mock"; + +import { + CREATOR_PRIVATE_KEY, + GUARDIAN_PRIVATE_KEY, + RELAYER_PRIVATE_KEY, + WALLET_PRIVATE_KEY, +} from "./helpers/consts"; +import { + Ed25519Keypair, + JsonRpcProvider, + localnetConnection, + RawSigner, +} from "@mysten/sui.js"; + +describe(" 0. Environment", () => { + const provider = new JsonRpcProvider(localnetConnection); + + // User wallet. + const wallet = new RawSigner( + Ed25519Keypair.fromSecretKey(WALLET_PRIVATE_KEY), + provider + ); + + // Relayer wallet. + const relayer = new RawSigner( + Ed25519Keypair.fromSecretKey(RELAYER_PRIVATE_KEY), + provider + ); + + // Deployer wallet. + const creator = new RawSigner( + Ed25519Keypair.fromSecretKey(CREATOR_PRIVATE_KEY), + provider + ); + + describe("Verify Local Validator", () => { + it("Balance", async () => { + // Balance check wallet. + { + const coinData = await wallet + .getAddress() + .then((owner) => + provider + .getCoins({ owner, coinType: "0x2::sui::SUI" }) + .then((result) => result.data) + ); + expect(coinData).has.length(5); + } + + // Balance check relayer. + { + const coinData = await relayer + .getAddress() + .then((owner) => + provider + .getCoins({ owner, coinType: "0x2::sui::SUI" }) + .then((result) => result.data) + ); + expect(coinData).has.length(5); + } + + // Balance check creator. This should only have one gas object at this + // point. + { + const coinData = await creator + .getAddress() + .then((owner) => + provider + .getCoins({ owner, coinType: "0x2::sui::SUI" }) + .then((result) => result.data) + ); + expect(coinData).has.length(1); + } + }); + }); +}); diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/01_wormhole.ts b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/01_wormhole.ts new file mode 100644 index 0000000000..ae9a26f08b --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/01_wormhole.ts @@ -0,0 +1,109 @@ +import { expect } from "chai"; + +import { WALLET_PRIVATE_KEY, WORMHOLE_STATE_ID } from "./helpers/consts"; +import { + Ed25519Keypair, + JsonRpcProvider, + localnetConnection, + RawSigner, + SUI_CLOCK_OBJECT_ID, + TransactionBlock, +} from "@mysten/sui.js"; +import { getPackageId } from "./helpers/utils"; +import { addPrepareMessageAndPublishMessage } from "./helpers/wormhole/testPublishMessage"; + +describe(" 1. Wormhole", () => { + const provider = new JsonRpcProvider(localnetConnection); + + // User wallet. + const wallet = new RawSigner( + Ed25519Keypair.fromSecretKey(WALLET_PRIVATE_KEY), + provider + ); + + describe("Publish Message", () => { + it("Check `WormholeMessage` Event", async () => { + const wormholePackage = await getPackageId( + wallet.provider, + WORMHOLE_STATE_ID + ); + + const owner = await wallet.getAddress(); + + // Create emitter cap. + const emitterCapId = await (async () => { + const tx = new TransactionBlock(); + const [emitterCap] = tx.moveCall({ + target: `${wormholePackage}::emitter::new`, + arguments: [tx.object(WORMHOLE_STATE_ID)], + }); + tx.transferObjects([emitterCap], tx.pure(owner)); + + // Execute and fetch created Emitter cap. + return wallet + .signAndExecuteTransactionBlock({ + transactionBlock: tx, + options: { + showObjectChanges: true, + }, + }) + .then((result) => { + const found = result.objectChanges?.filter( + (item) => "created" === item.type! + ); + if (found?.length == 1 && "objectId" in found[0]) { + return found[0].objectId; + } + + throw new Error("no objects found"); + }); + })(); + + // Publish messages using emitter cap. + { + const nonce = 69; + const basePayload = "All your base are belong to us."; + + const numMessages = 32; + const payloads: string[] = []; + const tx = new TransactionBlock(); + + // Construct transaction block to send multiple messages. + for (let i = 0; i < numMessages; ++i) { + // Make a unique message. + const payload = basePayload + `... ${i}`; + payloads.push(payload); + + addPrepareMessageAndPublishMessage( + tx, + wormholePackage, + WORMHOLE_STATE_ID, + emitterCapId, + nonce, + payload + ); + } + + const events = await wallet + .signAndExecuteTransactionBlock({ + transactionBlock: tx, + options: { + showEvents: true, + }, + }) + .then((result) => result.events!); + expect(events).has.length(numMessages); + + for (let i = 0; i < numMessages; ++i) { + const eventData = events[i].parsedJson!; + expect(eventData.consistency_level).equals(0); + expect(eventData.nonce).equals(nonce); + expect(eventData.payload).deep.equals([...Buffer.from(payloads[i])]); + expect(eventData.sender).equals(emitterCapId); + expect(eventData.sequence).equals(i.toString()); + expect(BigInt(eventData.timestamp) > 0n).is.true; + } + } + }); + }); +}); diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/build.ts b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/build.ts new file mode 100644 index 0000000000..374831ab8f --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/build.ts @@ -0,0 +1,32 @@ +import { fromB64, normalizeSuiObjectId } from "@mysten/sui.js"; +import { execSync, ExecSyncOptionsWithStringEncoding } from "child_process"; +import { UTF8 } from "./consts"; + +export const EXEC_UTF8: ExecSyncOptionsWithStringEncoding = { encoding: UTF8 }; + +export function buildForBytecode(packagePath: string) { + const buildOutput: { + modules: string[]; + dependencies: string[]; + } = JSON.parse( + execSync( + `sui move build --dump-bytecode-as-base64 -p ${packagePath} 2> /dev/null`, + EXEC_UTF8 + ) + ); + return { + modules: buildOutput.modules.map((m: string) => Array.from(fromB64(m))), + dependencies: buildOutput.dependencies.map((d: string) => + normalizeSuiObjectId(d) + ), + }; +} + +export function buildForDigest(packagePath: string) { + const digest = execSync( + `sui move build --dump-package-digest -p ${packagePath} 2> /dev/null`, + EXEC_UTF8 + ).substring(0, 64); + + return Buffer.from(digest, "hex"); +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/consts.ts b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/consts.ts new file mode 100644 index 0000000000..c4aa0787f4 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/consts.ts @@ -0,0 +1,40 @@ +// NOTE: modify these to reflect current versions of packages +export const VERSION_WORMHOLE = 1; +export const VERSION_TOKEN_BRIDGE = 1; + +// keystore +export const KEYSTORE = [ + "AB522qKKEsXMTFRD2SG3Het/02S/ZBOugmcH3R1CDG6l", + "AOmPq9B16F3W3ijO/4s9hI6v8LdiYCawKAW31PKpg4Qp", + "AGA20wtGcwbcNAG4nwapbQ5wIuXwkYQEWFUoSVAxctHb", +]; + +// wallets +export const WALLET_PRIVATE_KEY = Buffer.from(KEYSTORE[0], "base64").subarray( + 1 +); +export const RELAYER_PRIVATE_KEY = Buffer.from(KEYSTORE[1], "base64").subarray( + 1 +); +export const CREATOR_PRIVATE_KEY = Buffer.from(KEYSTORE[2], "base64").subarray( + 1 +); + +// guardian signer +export const GUARDIAN_PRIVATE_KEY = + "cfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0"; + +// wormhole +export const WORMHOLE_STATE_ID = + "0xc561a02a143575e53b87ba6c1476f053a307eac5179cb1c8121a3d3b220b81c1"; + +// token bridge +export const TOKEN_BRIDGE_STATE_ID = + "0x1c8de839f6331f2d745eb53b1b595bc466b4001c11617b0b66214b2e25ee72fc"; + +// governance +export const GOVERNANCE_EMITTER = + "0000000000000000000000000000000000000000000000000000000000000004"; + +// file encoding +export const UTF8: BufferEncoding = "utf-8"; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/error/moveAbort.ts b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/error/moveAbort.ts new file mode 100644 index 0000000000..04fdde7524 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/error/moveAbort.ts @@ -0,0 +1,42 @@ +export function parseMoveAbort(errorMessage: string) { + const parsed = errorMessage.matchAll( + /MoveAbort\(MoveLocation { module: ModuleId { address: ([0-9a-f]{64}), name: Identifier\("([A-Za-z_]+)"\) }, function: ([0-9]+), instruction: ([0-9]+), function_name: Some\("([A-Za-z_]+)"\) }, ([0-9]+)\) in command ([0-9]+)/g + ); + + return parsed.next().value.slice(1, 8); +} + +export class MoveAbort { + packageId: string; + moduleName: string; + functionName: string; + errorCode: bigint; + command: number; + + constructor( + packageId: string, + moduleName: string, + functionName: string, + errorCode: string, + command: string + ) { + this.packageId = packageId; + this.moduleName = moduleName; + this.functionName = functionName; + this.errorCode = BigInt(errorCode); + this.command = Number(command); + } + + static parseError(errorMessage: string): MoveAbort { + const [packageId, moduleName, , , functionName, errorCode, command] = + parseMoveAbort(errorMessage); + + return new MoveAbort( + "0x" + packageId, + moduleName, + functionName, + errorCode, + command + ); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/error/wormhole.ts b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/error/wormhole.ts new file mode 100644 index 0000000000..f011d606df --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/error/wormhole.ts @@ -0,0 +1,22 @@ +import { MoveAbort } from "./moveAbort"; + +export function parseWormholeError(errorMessage: string) { + const abort = MoveAbort.parseError(errorMessage); + const code = abort.errorCode; + + switch (abort.moduleName) { + case "required_version": { + switch (code) { + case 0n: { + return "E_OUTDATED_VERSION"; + } + default: { + throw new Error(`unrecognized error code: ${abort}`); + } + } + } + default: { + throw new Error(`unrecognized module: ${abort}`); + } + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/setup.ts b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/setup.ts new file mode 100644 index 0000000000..0399be84e7 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/setup.ts @@ -0,0 +1,75 @@ +import * as fs from "fs"; +import * as mock from "@certusone/wormhole-sdk/lib/cjs/mock"; +import { GUARDIAN_PRIVATE_KEY, UTF8 } from "./consts"; + +export function generateVaaFromDigest( + digest: Buffer, + governance: mock.GovernanceEmitter +) { + const timestamp = 12345678; + const published = governance.publishWormholeUpgradeContract( + timestamp, + 2, + "0x" + digest.toString("hex") + ); + + // Sui is not supported yet by the SDK, so we need to adjust the payload. + published.writeUInt16BE(21, published.length - 34); + + // We will use the signed VAA when we execute the upgrade. + const guardians = new mock.MockGuardians(0, [GUARDIAN_PRIVATE_KEY]); + return guardians.addSignatures(published, [0]); +} + +export function modifyHardCodedVersionControl( + packagePath: string, + currentVersion: number, + newVersion: number +) { + const versionControlDotMove = `${packagePath}/sources/version_control.move`; + + const contents = fs.readFileSync(versionControlDotMove, UTF8); + const src = `const CURRENT_BUILD_VERSION: u64 = ${currentVersion}`; + if (contents.indexOf(src) < 0) { + throw new Error("current version not found"); + } + + const dst = `const CURRENT_BUILD_VERSION: u64 = ${newVersion}`; + fs.writeFileSync(versionControlDotMove, contents.replace(src, dst), UTF8); +} + +export function setUpWormholeDirectory( + srcWormholePath: string, + dstWormholePath: string +) { + fs.cpSync(srcWormholePath, dstWormholePath, { recursive: true }); + + // Remove irrelevant files. This part is not necessary, but is helpful + // for debugging a clean package directory. + const removeThese = [ + "Move.devnet.toml", + "Move.lock", + "Makefile", + "README.md", + "build", + ]; + for (const basename of removeThese) { + fs.rmSync(`${dstWormholePath}/${basename}`, { + recursive: true, + force: true, + }); + } + + // Fix Move.toml file. + const moveTomlPath = `${dstWormholePath}/Move.toml`; + const moveToml = fs.readFileSync(moveTomlPath, UTF8); + fs.writeFileSync( + moveTomlPath, + moveToml.replace(`wormhole = "_"`, `wormhole = "0x0"`), + UTF8 + ); +} + +export function cleanUpPackageDirectory(packagePath: string) { + fs.rmSync(packagePath, { recursive: true, force: true }); +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/upgrade.ts b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/upgrade.ts new file mode 100644 index 0000000000..4698e9a85b --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/upgrade.ts @@ -0,0 +1,73 @@ +import { + RawSigner, + SUI_CLOCK_OBJECT_ID, + TransactionBlock, +} from "@mysten/sui.js"; +import { buildForBytecode } from "./build"; +import { getPackageId } from "./utils"; + +export async function buildAndUpgradeWormhole( + signer: RawSigner, + signedVaa: Buffer, + wormholePath: string, + wormholeStateId: string +) { + const wormholePackage = await getPackageId(signer.provider, wormholeStateId); + + const tx = new TransactionBlock(); + + // Authorize upgrade. + const [upgradeTicket] = tx.moveCall({ + target: `${wormholePackage}::upgrade_contract::authorize_upgrade`, + arguments: [ + tx.object(wormholeStateId), + tx.pure(Array.from(signedVaa)), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + }); + + // Build and generate modules and dependencies for upgrade. + const { modules, dependencies } = buildForBytecode(wormholePath); + const [upgradeReceipt] = tx.upgrade({ + modules, + dependencies, + packageId: wormholePackage, + ticket: upgradeTicket, + }); + + // Commit upgrade. + tx.moveCall({ + target: `${wormholePackage}::upgrade_contract::commit_upgrade`, + arguments: [tx.object(wormholeStateId), upgradeReceipt], + }); + + // Cannot auto compute gas budget, so we need to configure it manually. + // Gas ~215m. + tx.setGasBudget(215_000_000n); + + return signer.signAndExecuteTransactionBlock({ + transactionBlock: tx, + options: { + showEffects: true, + showEvents: true, + }, + }); +} + +export async function migrate(signer: RawSigner, stateId: string) { + const contractPackage = await getPackageId(signer.provider, stateId); + + const tx = new TransactionBlock(); + tx.moveCall({ + target: `${contractPackage}::migrate::migrate`, + arguments: [tx.object(stateId)], + }); + + return signer.signAndExecuteTransactionBlock({ + transactionBlock: tx, + options: { + showEffects: true, + showEvents: true, + }, + }); +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/utils.ts b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/utils.ts new file mode 100644 index 0000000000..58f71444e1 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/utils.ts @@ -0,0 +1,27 @@ +import { JsonRpcProvider } from "@mysten/sui.js"; + +export async function getPackageId( + provider: JsonRpcProvider, + stateId: string +): Promise { + const state = await provider + .getObject({ + id: stateId, + options: { + showContent: true, + }, + }) + .then((result) => { + if (result.data?.content?.dataType == "moveObject") { + return result.data.content.fields; + } + + throw new Error("not move object"); + }); + + if ("upgrade_cap" in state) { + return state.upgrade_cap.fields.package; + } + + throw new Error("upgrade_cap not found"); +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/wormhole/testPublishMessage.ts b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/wormhole/testPublishMessage.ts new file mode 100644 index 0000000000..35a3876a51 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/js/helpers/wormhole/testPublishMessage.ts @@ -0,0 +1,31 @@ +import { SUI_CLOCK_OBJECT_ID, TransactionBlock } from "@mysten/sui.js"; + +export function addPrepareMessageAndPublishMessage( + tx: TransactionBlock, + wormholePackage: string, + wormholeStateId: string, + emitterCapId: string, + nonce: number, + payload: number[] | string +): TransactionBlock { + const [feeAmount] = tx.moveCall({ + target: `${wormholePackage}::state::message_fee`, + arguments: [tx.object(wormholeStateId)], + }); + const [wormholeFee] = tx.splitCoins(tx.gas, [feeAmount]); + const [messageTicket] = tx.moveCall({ + target: `${wormholePackage}::publish_message::prepare_message`, + arguments: [tx.object(emitterCapId), tx.pure(nonce), tx.pure(payload)], + }); + tx.moveCall({ + target: `${wormholePackage}::publish_message::publish_message`, + arguments: [ + tx.object(wormholeStateId), + wormholeFee, + messageTicket, + tx.object(SUI_CLOCK_OBJECT_ID), + ], + }); + + return tx; +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/package-lock.json b/target_chains/sui/vendor/wormhole_iota_testnet/testing/package-lock.json new file mode 100644 index 0000000000..3b6dd637e6 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/package-lock.json @@ -0,0 +1,5917 @@ +{ + "name": "wormhole-sui-integration-test", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "wormhole-sui-integration-test", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "@certusone/wormhole-sdk": "^0.9.12", + "@mysten/sui.js": "^0.32.2", + "chai": "^4.3.7", + "mocha": "^10.2.0", + "prettier": "^2.8.7", + "ts-mocha": "^10.0.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + }, + "devDependencies": { + "@types/chai": "^4.3.4", + "@types/mocha": "^10.0.1", + "@types/node": "^18.15.11" + } + }, + "node_modules/@apollo/client": { + "version": "3.7.11", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.7.11.tgz", + "integrity": "sha512-uLg2KtxoAyj9ta7abLxXx8cGRM7HypCkXVmxtL7Ko//N5g37aoJ3ca7VYoFCMUFO1BXBulj+yKVl0U3+ILj5AQ==", + "license": "MIT", + "dependencies": { + "@graphql-typed-document-node/core": "^3.1.1", + "@wry/context": "^0.7.0", + "@wry/equality": "^0.5.0", + "@wry/trie": "^0.3.0", + "graphql-tag": "^2.12.6", + "hoist-non-react-statics": "^3.3.2", + "optimism": "^0.16.2", + "prop-types": "^15.7.2", + "response-iterator": "^0.2.6", + "symbol-observable": "^4.0.0", + "ts-invariant": "^0.10.3", + "tslib": "^2.3.0", + "zen-observable-ts": "^1.2.5" + }, + "peerDependencies": { + "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0", + "graphql-ws": "^5.5.5", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", + "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/@babel/runtime": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz", + "integrity": "sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@certusone/wormhole-sdk": { + "version": "0.9.12", + "resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.9.12.tgz", + "integrity": "sha512-ywMNc/tHg6qb9dcZLND1BMUISp7eFN+ksymOgjhwQcZZ/KUA/N1uVvbMVs0uSx+i0y4VloO9MwGc/uFnYKNsMQ==", + "license": "Apache-2.0", + "dependencies": { + "@certusone/wormhole-sdk-proto-web": "0.0.6", + "@certusone/wormhole-sdk-wasm": "^0.0.1", + "@coral-xyz/borsh": "0.2.6", + "@injectivelabs/networks": "^1.0.73", + "@injectivelabs/sdk-ts": "^1.0.368", + "@injectivelabs/utils": "^1.0.63", + "@project-serum/anchor": "^0.25.0", + "@solana/spl-token": "^0.3.5", + "@solana/web3.js": "^1.66.2", + "@terra-money/terra.js": "^3.1.3", + "@xpla/xpla.js": "^0.2.1", + "algosdk": "^1.15.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" + } + }, + "node_modules/@certusone/wormhole-sdk-proto-web": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk-proto-web/-/wormhole-sdk-proto-web-0.0.6.tgz", + "integrity": "sha512-LTyjsrWryefx5WmkoBP6FQ2EjLxhMExAGxLkloHUhufVQZdrbGh0htBBUviP+HaDSJBCMPMtulNFwkBJV6muqQ==", + "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-proto-web/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/@certusone/wormhole-sdk-proto-web/node_modules/long": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", + "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==", + "license": "Apache-2.0" + }, + "node_modules/@certusone/wormhole-sdk-proto-web/node_modules/protobufjs": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.3.tgz", + "integrity": "sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg==", + "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/@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/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/@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/@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==", + "license": "Apache-2.0", + "dependencies": { + "@noble/hashes": "^1.0.0", + "protobufjs": "^6.8.8" + } + }, + "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.30.1", + "resolved": "https://registry.npmjs.org/@cosmjs/amino/-/amino-0.30.1.tgz", + "integrity": "sha512-yNHnzmvAlkETDYIpeCTdVqgvrdt1qgkOXwuRVi8s27UKI5hfqyE9fJ/fuunXE6ZZPnKkjIecDznmuUOMrMvw4w==", + "license": "Apache-2.0", + "dependencies": { + "@cosmjs/crypto": "^0.30.1", + "@cosmjs/encoding": "^0.30.1", + "@cosmjs/math": "^0.30.1", + "@cosmjs/utils": "^0.30.1" + } + }, + "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", + "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/@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", + "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.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", + "dependencies": { + "@cosmjs/stream": "^0.30.1", + "xstream": "^11.14.0" + } + }, + "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", + "dependencies": { + "bn.js": "^5.2.0" + } + }, + "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", + "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/@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", + "dependencies": { + "@cosmjs/stream": "^0.30.1", + "isomorphic-ws": "^4.0.1", + "ws": "^7", + "xstream": "^11.14.0" + } + }, + "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", + "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/@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", + "dependencies": { + "xstream": "^11.14.0" + } + }, + "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", + "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/@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" + }, + "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==", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "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", + "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", + "dependencies": { + "@ethereumjs/common": "^2.6.4", + "ethereumjs-util": "^7.1.5" + } + }, + "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", + "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/@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", + "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/@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", + "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/@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", + "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/@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", + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "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", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "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", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "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", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "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", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "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", + "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/@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", + "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/@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", + "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/@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", + "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/@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", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "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" + }, + "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", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "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", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "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", + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "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", + "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/@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": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "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/@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", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "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", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "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", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "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", + "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/@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", + "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/@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", + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "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", + "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/@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", + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "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", + "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/@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", + "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/@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", + "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/@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/@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/@injectivelabs/core-proto-ts": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@injectivelabs/core-proto-ts/-/core-proto-ts-0.0.11.tgz", + "integrity": "sha512-gYMzkoZ0olXLbEhSQVarUCMR6VAHytvENDv2Psjl9EjO5Pg93vTGLViS4E4vA5fezRfdF/x0Uic31w+ogp66jA==", + "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/node_modules/long": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", + "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==", + "license": "Apache-2.0" + }, + "node_modules/@injectivelabs/core-proto-ts/node_modules/protobufjs": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.3.tgz", + "integrity": "sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg==", + "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/@injectivelabs/exceptions": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/@injectivelabs/exceptions/-/exceptions-1.10.2.tgz", + "integrity": "sha512-JLHgU/MjxRYSpn/9G9mJvHuNiA5ze6w86sXz09kQh7tlSaTC4PGqBBbBSu0hrUBBX86O+vk2ULkn1Ks1n7FlOw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@injectivelabs/grpc-web": "^0.0.1", + "@injectivelabs/ts-types": "^1.10.1", + "http-status-codes": "^2.2.0", + "link-module-alias": "^1.2.0", + "shx": "^0.3.2" + } + }, + "node_modules/@injectivelabs/exceptions/dist": { + "extraneous": true + }, + "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": "0.0.9", + "resolved": "https://registry.npmjs.org/@injectivelabs/indexer-proto-ts/-/indexer-proto-ts-0.0.9.tgz", + "integrity": "sha512-ZFTUKlHAY2WYnB9RPPf11nq7SNm7wcKFTmFTavTiHV8UvNEni7dCR3Un6U5Mo1qD0xHEsfoCDMdqGcIguliPMA==", + "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/indexer-proto-ts/node_modules/long": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", + "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==", + "license": "Apache-2.0" + }, + "node_modules/@injectivelabs/indexer-proto-ts/node_modules/protobufjs": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.3.tgz", + "integrity": "sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg==", + "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/@injectivelabs/mito-proto-ts": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@injectivelabs/mito-proto-ts/-/mito-proto-ts-1.0.2.tgz", + "integrity": "sha512-A/5Nf/RJiBRiwYNqH2K0nNrOuuVcYCebqgEt3btpDfQXcyaHIssjDmZOtmMT1M7P/enEVgDu0auxE7tsmSFijg==", + "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/node_modules/long": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/long/-/long-5.2.1.tgz", + "integrity": "sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A==", + "license": "Apache-2.0" + }, + "node_modules/@injectivelabs/mito-proto-ts/node_modules/protobufjs": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.2.3.tgz", + "integrity": "sha512-TtpvOqwB5Gdz/PQmOjgsrGH1nHjAQVCN7JG4A6r1sXRWESL5rNMAiRcBQlCAdKxZcAbstExQePYG8xof/JVRgg==", + "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/@injectivelabs/networks": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/@injectivelabs/networks/-/networks-1.10.4.tgz", + "integrity": "sha512-EjWdTXpU+j8YFikxiMacVhPK8dzamMD4czkrst7NfcMRoBCMNMrOp5lItF5GFq0BSx3xu/zfkb2+3wWTIdWUxQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@injectivelabs/exceptions": "^1.10.2", + "@injectivelabs/ts-types": "^1.10.1", + "@injectivelabs/utils": "^1.10.2", + "link-module-alias": "^1.2.0", + "shx": "^0.3.2" + } + }, + "node_modules/@injectivelabs/networks/dist": { + "extraneous": true + }, + "node_modules/@injectivelabs/sdk-ts": { + "version": "1.10.37", + "resolved": "https://registry.npmjs.org/@injectivelabs/sdk-ts/-/sdk-ts-1.10.37.tgz", + "integrity": "sha512-+7LzC1iDiN3oT7PZ3yV2PchsrH1WQfS+tV8/geesi0EBKT4AW4v2Ur3OYhtDXvQia1zSxWJY9phS3iAmaBd9vQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "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.11", + "@injectivelabs/exceptions": "^1.10.2", + "@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": "^0.0.9", + "@injectivelabs/mito-proto-ts": "1.0.2", + "@injectivelabs/networks": "^1.10.4", + "@injectivelabs/test-utils": "^1.10.1", + "@injectivelabs/token-metadata": "^1.10.17", + "@injectivelabs/ts-types": "^1.10.1", + "@injectivelabs/utils": "^1.10.2", + "@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/@injectivelabs/sdk-ts/dist": { + "extraneous": true + }, + "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", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/@injectivelabs/test-utils": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@injectivelabs/test-utils/-/test-utils-1.10.1.tgz", + "integrity": "sha512-ULP3XJBZN8Muv0jVpo0rfUOD/CDlyg4rij6YuRpYhTg6P0wIlKq9dL36cZlylay+F+4HeLn9qB0D2Cr3+FrhPw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "axios": "^0.21.1", + "bignumber.js": "^9.0.1", + "link-module-alias": "^1.2.0", + "shx": "^0.3.2", + "snakecase-keys": "^5.1.2", + "store2": "^2.12.0" + } + }, + "node_modules/@injectivelabs/test-utils/dist": { + "extraneous": true + }, + "node_modules/@injectivelabs/token-metadata": { + "version": "1.10.17", + "resolved": "https://registry.npmjs.org/@injectivelabs/token-metadata/-/token-metadata-1.10.17.tgz", + "integrity": "sha512-1TFZMs38B21Y0uzqxRuIHifmj6VrJCZLEJnjGuhzIfhtLqSB/ZtCf3JNAarujwwgj6xWb7vzqzqNpo+SIYKvwg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@injectivelabs/exceptions": "^1.10.2", + "@injectivelabs/networks": "^1.10.4", + "@injectivelabs/ts-types": "^1.10.1", + "@injectivelabs/utils": "^1.10.2", + "@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/token-metadata/dist": { + "extraneous": true + }, + "node_modules/@injectivelabs/ts-types": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@injectivelabs/ts-types/-/ts-types-1.10.1.tgz", + "integrity": "sha512-gQQjcnRx2TjLmZDMV8IIkRvLtAzTPptJuWKwPCfSlCRKOIv7Eafzy2qFINUIkKDOeu/lZUtSykEsAIUBEmXqFg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "link-module-alias": "^1.2.0", + "shx": "^0.3.2" + } + }, + "node_modules/@injectivelabs/ts-types/dist": { + "extraneous": true + }, + "node_modules/@injectivelabs/utils": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/@injectivelabs/utils/-/utils-1.10.2.tgz", + "integrity": "sha512-XMO7RRbXs06cChr5Wezr0Dbl1Z9hq+ceB4Dn3qyulzupGepeivkoPTcyG4IdjOiwf7PnFeGQ/aVG3hr0rJI7dQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@injectivelabs/exceptions": "^1.10.2", + "@injectivelabs/ts-types": "^1.10.1", + "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/@injectivelabs/utils/dist": { + "extraneous": true + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "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==", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "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.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "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/base-x": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", + "integrity": "sha512-FuwxlW4H5kh37X/oW59pwTzzTKRzfrrQwhmyspRM7swOEZcHtDZSCt45U6oKgtuFE+WYPblePMVIPR4RZrh/hw==", + "license": "MIT" + }, + "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==", + "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.0.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.0.0.tgz", + "integrity": "sha512-2upgEu0iLiDVDZkNLeFV2+ht0BAVgQnEmCk6JsOch9Rp8xfkMCbvbAZlA2pBHQc73dbl+vFOXfqkf4uemdn0bw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.0" + } + }, + "node_modules/@noble/ed25519": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-1.7.3.tgz", + "integrity": "sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT" + }, + "node_modules/@noble/hashes": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz", + "integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==", + "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/@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/@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/@scure/bip32": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.0.tgz", + "integrity": "sha512-bcKpo1oj54hGholplGLpqPHRbIsnbixFtc06nwuNM5/dwSXOq/AAYoIBRsBmnZJSdfeNW5rnff7NTAz3ZCqR9Q==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.0.0", + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/@scure/bip39": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.0.tgz", + "integrity": "sha512-SX/uKq52cuxm4YFXWFaVByaSHJh2w3BnokVSeUJVCv6K7WulT9u2BuNRBhuFl8vAuYnzx9bEu9WgpcNYTrYieg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + } + }, + "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/spl-token": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@solana/spl-token/-/spl-token-0.3.7.tgz", + "integrity": "sha512-bKGxWTtIw6VDdCBngjtsGlKGLSmiu/8ghSt/IOYJV24BsymRbgq7r12GToeetpxmPaZYLddKwAz7+EwprLfkfg==", + "license": "Apache-2.0", + "dependencies": { + "@solana/buffer-layout": "^4.0.0", + "@solana/buffer-layout-utils": "^0.2.0", + "buffer": "^6.0.3" + }, + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "@solana/web3.js": "^1.47.4" + } + }, + "node_modules/@solana/web3.js": { + "version": "1.75.0", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.75.0.tgz", + "integrity": "sha512-rHQgdo1EWfb+nPUpHe4O7i8qJPELHKNR5PAZRK+a7XxiykqOfbaAlPt5boDWAGPnYbSv0ziWZv5mq9DlFaQCxg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@noble/ed25519": "^1.7.0", + "@noble/hashes": "^1.1.2", + "@noble/secp256k1": "^1.6.3", + "@solana/buffer-layout": "^4.0.0", + "agentkeepalive": "^4.2.1", + "bigint-buffer": "^1.1.5", + "bn.js": "^5.0.0", + "borsh": "^0.7.0", + "bs58": "^4.0.1", + "buffer": "6.0.3", + "fast-stable-stringify": "^1.0.0", + "jayson": "^3.4.4", + "node-fetch": "^2.6.7", + "rpc-websockets": "^7.5.1", + "superstruct": "^0.14.2" + } + }, + "node_modules/@solana/web3.js/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/@solana/web3.js/node_modules/jayson": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-3.7.0.tgz", + "integrity": "sha512-tfy39KJMrrXJ+mFcMpxwBvFDetS8LAID93+rycFglIQM4kl3uNR3W4lBLE/FFhsoUCEox5Dt2adVpDm/XtebbQ==", + "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", + "lodash": "^4.17.20", + "uuid": "^8.3.2", + "ws": "^7.4.5" + }, + "bin": { + "jayson": "bin/jayson.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@solana/web3.js/node_modules/superstruct": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.14.2.tgz", + "integrity": "sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ==", + "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/@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/terra.js": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@terra-money/terra.js/-/terra.js-3.1.8.tgz", + "integrity": "sha512-Cd/fh4MswT00fDGVckoZ0cm77EpIy4+CjSDO0RqZ3Qfp4CJBp7sWTLRNsyzUWjdYOT5iTx+1wOMCYbbyKo6LAw==", + "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/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "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==" + }, + "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==" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==" + }, + "node_modules/@types/bn.js": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.1.tgz", + "integrity": "sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/lodash": { + "version": "4.14.192", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.192.tgz", + "integrity": "sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==", + "license": "MIT" + }, + "node_modules/@types/lodash.values": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/@types/lodash.values/-/lodash.values-4.3.7.tgz", + "integrity": "sha512-Moex9/sWxtKEa+BKiH5zvmhfcieDlcz4wRxMhO/oJ2qOKUdujoU6dQjUTxWA8jwEREpHXmiY4HCwNRpycW8JQA==", + "license": "MIT", + "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/mocha": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", + "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "18.15.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", + "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", + "license": "MIT" + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", + "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-Da66lEIFeIz9ltsdMZcpQvmrmmoqrfju8pm1BH8WbYjZSwUgCwXLb9C+9XYogwBITnbsSaMdVPb2ekf7TV+03w==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "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/@wry/context": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.0.tgz", + "integrity": "sha512-LcDAiYWRtwAoSOArfk7cuYvFXytxfVrdX7yxoUmK7pPITLk5jYh2F8knCwS7LjgYL8u1eidPlKKV6Ikqq0ODqQ==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wry/equality": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.3.tgz", + "integrity": "sha512-avR+UXdSrsF2v8vIqIgmeTY0UR91UT+IyablCyKe/uk22uOJ8fusKZnH9JH9e1/EtLeNJBtagNmL3eJdnOV53g==", + "license": "MIT", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wry/trie": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.3.2.tgz", + "integrity": "sha512-yRTyhWSls2OY/pYLfwff867r8ekooZ4UI+/gxot5Wj8EFwSf2rG+n+Mo/6LoLQm1TKA4GRj2+LCpbfS937dClQ==", + "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", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "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.3.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", + "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "depd": "^2.0.0", + "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": "1.24.1", + "resolved": "https://registry.npmjs.org/algosdk/-/algosdk-1.24.1.tgz", + "integrity": "sha512-9moZxdqeJ6GdE4N6fA/GlUP4LrbLZMYcYkt141J4Ss68OfEgH9qW0wBuZ3ZOKEx/xjc5bg7mLP2Gjg7nwrkmww==", + "license": "MIT", + "dependencies": { + "algo-msgpack-with-bigint": "^2.1.1", + "buffer": "^6.0.2", + "cross-fetch": "^3.1.5", + "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": ">=14.0.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "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/aptos/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/aptos/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/aptos/node_modules/@scure/bip39/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/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/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "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.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/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-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.1.tgz", + "integrity": "sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/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/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "license": "ISC" + }, + "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/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-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "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.7", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz", + "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, + "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": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "license": "MIT", + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^4.1.2", + "get-func-name": "^2.0.0", + "loupe": "^2.3.1", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "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", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "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": "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/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/copyfiles": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", + "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", + "license": "MIT", + "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/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "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" + }, + "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", + "dependencies": { + "long": "^4.0.0", + "protobufjs": "~6.11.2" + } + }, + "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", + "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==" + }, + "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/cross-fetch/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/crypto-addr-codec": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/crypto-addr-codec/-/crypto-addr-codec-0.1.7.tgz", + "integrity": "sha512-X4hzfBzNhy4mAc3UpiXEC/L0jo5E8wAa9unsnA8nNXYzXjCcGk83hfC5avJWCSGT8V91xMnAS9AKMHmjw5+XCg==", + "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-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/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "license": "MIT" + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "license": "MIT", + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/define-properties": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz", + "integrity": "sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==", + "license": "MIT", + "dependencies": { + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "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/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", + "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.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "license": "MIT", + "optional": true + }, + "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" + }, + "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/eccrypto/node_modules/secp256k1/node_modules/nan": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "license": "MIT", + "optional": true + }, + "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", + "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.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "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/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/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/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eth-crypto": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eth-crypto/-/eth-crypto-2.6.0.tgz", + "integrity": "sha512-GCX4ffFYRUGgnuWR5qxcZIRQJ1KEqPFiyXU9yVy7s6dtXIMlUXZQ2h+5ID6rFaOHWbpJbjfkC6YdhwtwRYCnug==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "7.20.13", + "@ethereumjs/tx": "3.5.2", + "@types/bn.js": "5.1.1", + "eccrypto": "1.1.6", + "ethereumjs-util": "7.1.5", + "ethers": "5.7.2", + "secp256k1": "5.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/pubkey" + } + }, + "node_modules/eth-crypto/node_modules/@babel/runtime": { + "version": "7.20.13", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz", + "integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==", + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.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" + }, + "node_modules/eth-crypto/node_modules/secp256k1": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.0.tgz", + "integrity": "sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^5.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "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==", + "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.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "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.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", + "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/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/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-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/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/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "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/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "license": "MIT" + }, + "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-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "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/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/google-protobuf": { + "version": "3.21.2", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.21.2.tgz", + "integrity": "sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA==", + "license": "(BSD-3-Clause AND Apache-2.0)" + }, + "node_modules/graphql": { + "version": "16.6.0", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.6.0.tgz", + "integrity": "sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==", + "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": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "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/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "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-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.2.0", + "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.2.0.tgz", + "integrity": "sha512-feERVo9iWxvnejp3SEfm/+oNG517npqL2/PIA8ORjyOZjGC7TwCRQsZylciLS64i6pJ0wRYz3rkXLRwbtFa8Ng==", + "license": "MIT" + }, + "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==", + "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-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "license": "MIT", + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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" + }, + "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.0.0", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-4.0.0.tgz", + "integrity": "sha512-v2RNpDCMu45fnLzSk47vx7I+QUaOsox6f5X0CUlabAFwxoP+8MfAY0NQRFwOEYXIxm8Ih5y6OaEa5KYiQMkyAA==", + "license": "MIT", + "dependencies": { + "@types/connect": "^3.4.33", + "@types/node": "^12.12.54", + "@types/ws": "^7.4.4", + "commander": "^2.20.3", + "delay": "^5.0.0", + "es6-promisify": "^5.0.0", + "eyes": "^0.1.8", + "isomorphic-ws": "^4.0.1", + "json-stringify-safe": "^5.0.1", + "JSONStream": "^1.3.5", + "uuid": "^8.3.2", + "ws": "^7.4.5" + }, + "bin": { + "jayson": "bin/jayson.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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/js-base64": { + "version": "3.7.5", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.5.tgz", + "integrity": "sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==", + "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/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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-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": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "license": "MIT", + "optional": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "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.4.1", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", + "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "license": "MIT", + "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.3", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.3.tgz", + "integrity": "sha512-JZrLIAJWuZxKbCilMpNz5Vj7Vtb4scDG3dMXLOsbzBmQGyjwE61BbW7bJkfKKCShXiQZt3T6sBgALRtmd+nZaQ==", + "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/libsodium": { + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.11.tgz", + "integrity": "sha512-WPfJ7sS53I2s4iM58QxY3Inb83/6mjlYgcmZs7DJsvDlnmVUwNinBCi5vBT43P6bHRy01O4zsMU2CoVR6xJ40A==", + "license": "ISC" + }, + "node_modules/libsodium-wrappers": { + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.11.tgz", + "integrity": "sha512-SrcLtXj7BM19vUKtQuyQKiQCRJPgbpauzl3s0rSwD+60wtHqSUuqcoawlMDheCJga85nKOQwxNYQxf/CKAvs6Q==", + "license": "ISC", + "dependencies": { + "libsodium": "^0.7.11" + } + }, + "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", + "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", + "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", + "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", + "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" + }, + "node_modules/link-module-alias/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", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/link-module-alias/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", + "engines": { + "node": ">=4" + } + }, + "node_modules/link-module-alias/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", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "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" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": "2.3.6", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz", + "integrity": "sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==", + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.0" + } + }, + "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/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "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/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/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": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "license": "MIT", + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/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/mocha/node_modules/glob/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/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "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.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "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/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.6.9", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", + "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.6.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz", + "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==", + "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", + "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", + "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" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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.16.2", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.16.2.tgz", + "integrity": "sha512-zWNbgWj+3vLEjZNIh/okkY2EUfX+vB9TJopzIZwT1xxaMqC5hRLLraePod4c5n4He08xuXNH+zhKFFCu390wiQ==", + "license": "MIT", + "dependencies": { + "@wry/context": "^0.7.0", + "@wry/trie": "^0.3.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "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/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prettier": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "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" + }, + "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": "6.11.3", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz", + "integrity": "sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==", + "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/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/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "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.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT" + }, + "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.2", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", + "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.11.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/response-iterator": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/response-iterator/-/response-iterator-0.2.6.tgz", + "integrity": "sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/rpc-websockets": { + "version": "7.5.1", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.5.1.tgz", + "integrity": "sha512-kGFkeTsmd37pHPMaHIgN1LVKXMi0JD782v4Ds9ZKtLlwdTKjn+CxM9A9/gLT2LaOuEcEFGL98h1QWQtlOIdW0w==", + "license": "LGPL-3.0-only", + "dependencies": { + "@babel/runtime": "^7.17.2", + "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.13.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", + "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", + "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.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.0.tgz", + "integrity": "sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg==", + "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.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "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/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.4.5", + "resolved": "https://registry.npmjs.org/snakecase-keys/-/snakecase-keys-5.4.5.tgz", + "integrity": "sha512-qSQVcgcWk8mQUN1miVGnRMAUye1dbj9+F9PVkR7wZUXNCidQwrl/kOKmoYf+WbH2ju6c9pXnlmbS2he7pb2/9A==", + "license": "MIT", + "dependencies": { + "map-obj": "^4.1.0", + "snake-case": "^3.0.4", + "type-fest": "^2.5.2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "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/store2": { + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/store2/-/store2-2.14.2.tgz", + "integrity": "sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w==", + "license": "(MIT OR GPL-3.0)" + }, + "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-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4" + } + }, + "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/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/superstruct": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-1.0.3.tgz", + "integrity": "sha512-8iTn3oSS8nRGn+C2pgXSKPI3jmpm6FExNazNpjvqS6ZUJQCej3PUXEKM8NjHBOs54ExM+LPW/FBRhymrdcCiSg==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "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", + "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" + }, + "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", + "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" + }, + "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", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/tiny-secp256k1": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz", + "integrity": "sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA==", + "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.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "license": "MIT", + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "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-mocha": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/ts-mocha/-/ts-mocha-10.0.0.tgz", + "integrity": "sha512-VRfgDO+iiuJFlNB18tzOfypJ21xn2xbuZyDvJvqpTbWgkAgD17ONGr8t+Tl8rcBtOBdjXp5e/Rk+d39f7XBHRw==", + "license": "MIT", + "dependencies": { + "ts-node": "7.0.1" + }, + "bin": { + "ts-mocha": "bin/ts-mocha" + }, + "engines": { + "node": ">= 6.X.X" + }, + "optionalDependencies": { + "tsconfig-paths": "^3.5.0" + }, + "peerDependencies": { + "mocha": "^3.X.X || ^4.X.X || ^5.X.X || ^6.X.X || ^7.X.X || ^8.X.X || ^9.X.X || ^10.X.X" + } + }, + "node_modules/ts-mocha/node_modules/diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/ts-mocha/node_modules/ts-node": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", + "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", + "dependencies": { + "arrify": "^1.0.0", + "buffer-from": "^1.1.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.5.6", + "yn": "^2.0.0" + }, + "bin": { + "ts-node": "dist/bin.js" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ts-mocha/node_modules/yn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", + "integrity": "sha512-uTv8J/wiWTgUTg+9vLTi//leUl5vDQS6uii/emeTb2ssY7vl6QWf2fFbIIGjnhjvbdKlU0ed7QPgY1htTC86jQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "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.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", + "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", + "license": "MIT", + "optional": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", + "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==", + "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-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "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.0.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", + "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=12.20" + } + }, + "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/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "license": "MIT", + "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==" + }, + "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/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/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "license": "Apache-2.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.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "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", + "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": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "license": "MIT", + "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/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yargs/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", + "engines": { + "node": ">=10" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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" + } + } + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/package.json b/target_chains/sui/vendor/wormhole_iota_testnet/testing/package.json new file mode 100644 index 0000000000..f29c59e15c --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/package.json @@ -0,0 +1,22 @@ +{ + "name": "@wormhole-foundation/wormhole-sui-integration-test", + "version": "0.0.1", + "description": "Wormhole Sui Integration Test", + "main": "index.js", + "license": "MIT", + "dependencies": { + "@certusone/wormhole-sdk": "^0.9.12", + "@mysten/sui.js": "^0.32.2", + "chai": "^4.3.7", + "mocha": "^10.2.0", + "prettier": "^2.8.7", + "ts-mocha": "^10.0.0", + "ts-node": "^10.9.1", + "typescript": "^5.0.4" + }, + "devDependencies": { + "@types/chai": "^4.3.4", + "@types/mocha": "^10.0.1", + "@types/node": "^18.15.11" + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/run_integration_test.sh b/target_chains/sui/vendor/wormhole_iota_testnet/testing/run_integration_test.sh new file mode 100755 index 0000000000..80da863836 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/run_integration_test.sh @@ -0,0 +1,35 @@ +#/bin/bash + +pgrep -f sui > /dev/null +if [ $? -eq 0 ]; then + echo "sui local validator already running" + exit 1; +fi + +TEST_DIR=$(dirname $0) +SUI_CONFIG=$TEST_DIR/sui_config + +### Remove databases generated by localnet +rm -rf $SUI_CONFIG/*_db + +### Start local node +echo "$(date) :: starting localnet" +sui start --network.config $SUI_CONFIG/network.yaml > /dev/null 2>&1 & +sleep 1 + +echo "$(date) :: deploying wormhole and token bridge" +cd $TEST_DIR/.. +bash scripts/deploy.sh devnet \ + -k AGA20wtGcwbcNAG4nwapbQ5wIuXwkYQEWFUoSVAxctHb > deploy.out 2>&1 +cd testing + +## run contract tests here +echo "$(date) :: running tests" +pnpm exec ts-mocha -t 1000000 $TEST_DIR/js/*.ts + +# nuke +echo "$(date) :: done" +pkill sui + +# remove databases generated by localnet +rm -rf $SUI_CONFIG/*_db diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/scripts/upgrade-token-bridge.ts b/target_chains/sui/vendor/wormhole_iota_testnet/testing/scripts/upgrade-token-bridge.ts new file mode 100644 index 0000000000..605128dddb --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/scripts/upgrade-token-bridge.ts @@ -0,0 +1,300 @@ +import * as mock from "@certusone/wormhole-sdk/lib/cjs/mock"; +import { + RawSigner, + SUI_CLOCK_OBJECT_ID, + TransactionBlock, + fromB64, + normalizeSuiObjectId, + JsonRpcProvider, + Ed25519Keypair, + testnetConnection, +} from "@mysten/sui.js"; +import { execSync } from "child_process"; +import { resolve } from "path"; +import * as fs from "fs"; + +const GOVERNANCE_EMITTER = + "0000000000000000000000000000000000000000000000000000000000000004"; + +const TOKEN_BRIDGE_STATE_ID = + "0x32422cb2f929b6a4e3f81b4791ea11ac2af896b310f3d9442aa1fe924ce0bab4"; +const WORMHOLE_STATE_ID = + "0x69ae41bdef4770895eb4e7aaefee5e4673acc08f6917b4856cf55549c4573ca8"; + +async function main() { + const guardianPrivateKey = process.env.TESTNET_GUARDIAN_PRIVATE_KEY; + if (guardianPrivateKey === undefined) { + throw new Error("TESTNET_GUARDIAN_PRIVATE_KEY unset in environment"); + } + + const walletPrivateKey = process.env.TESTNET_WALLET_PRIVATE_KEY; + if (walletPrivateKey === undefined) { + throw new Error("TESTNET_WALLET_PRIVATE_KEY unset in environment"); + } + + const provider = new JsonRpcProvider(testnetConnection); + const wallet = new RawSigner( + Ed25519Keypair.fromSecretKey( + Buffer.from(walletPrivateKey, "base64").subarray(1) + ), + provider + ); + + const dstTokenBridgePath = resolve(`${__dirname}/../../token_bridge`); + + // Build for digest. + const { modules, dependencies, digest } = + buildForBytecodeAndDigest(dstTokenBridgePath); + console.log("dependencies", dependencies); + console.log("digest", digest.toString("hex")); + + // We will use the signed VAA when we execute the upgrade. + const guardians = new mock.MockGuardians(0, [guardianPrivateKey]); + + const timestamp = 12345678; + const governance = new mock.GovernanceEmitter(GOVERNANCE_EMITTER); + const published = governance.publishWormholeUpgradeContract( + timestamp, + 2, + "0x" + digest.toString("hex") + ); + const moduleName = Buffer.alloc(32); + moduleName.write("TokenBridge", 32 - "TokenBridge".length); + published.write(moduleName.toString(), 84 - 33); + published.writeUInt16BE(21, 84); + published.writeUInt8(2, 83); + //message.writeUInt8(1, 83); + published.writeUInt16BE(21, published.length - 34); + + const signedVaa = guardians.addSignatures(published, [0]); + console.log("Upgrade VAA:", signedVaa.toString("hex")); + + // // And execute upgrade with governance VAA. + // const upgradeResults = await upgradeTokenBridge( + // wallet, + // TOKEN_BRIDGE_STATE_ID, + // WORMHOLE_STATE_ID, + // modules, + // dependencies, + // signedVaa + // ); + + // console.log("tx digest", upgradeResults.digest); + // console.log("tx effects", JSON.stringify(upgradeResults.effects!)); + // console.log("tx events", JSON.stringify(upgradeResults.events!)); + + // TODO: grab new package ID from the events above. Do not rely on the RPC + // call because it may give you a stale package ID after the upgrade. + + const migrateResults = await migrateTokenBridge( + wallet, + TOKEN_BRIDGE_STATE_ID, + WORMHOLE_STATE_ID, + signedVaa + ); + console.log("tx digest", migrateResults.digest); + console.log("tx effects", JSON.stringify(migrateResults.effects!)); + console.log("tx events", JSON.stringify(migrateResults.events!)); +} + +main(); + +// Yeah buddy. + +function buildForBytecodeAndDigest(packagePath: string) { + const buildOutput: { + modules: string[]; + dependencies: string[]; + digest: number[]; + } = JSON.parse( + execSync( + `sui move build --dump-bytecode-as-base64 -p ${packagePath} 2> /dev/null`, + { encoding: "utf-8" } + ) + ); + return { + modules: buildOutput.modules.map((m: string) => Array.from(fromB64(m))), + dependencies: buildOutput.dependencies.map((d: string) => + normalizeSuiObjectId(d) + ), + digest: Buffer.from(buildOutput.digest), + }; +} + +async function getPackageId( + provider: JsonRpcProvider, + stateId: string +): Promise { + const state = await provider + .getObject({ + id: stateId, + options: { + showContent: true, + }, + }) + .then((result) => { + if (result.data?.content?.dataType == "moveObject") { + return result.data.content.fields; + } + + throw new Error("not move object"); + }); + + if ("upgrade_cap" in state) { + return state.upgrade_cap.fields.package; + } + + throw new Error("upgrade_cap not found"); +} + +async function upgradeTokenBridge( + signer: RawSigner, + tokenBridgeStateId: string, + wormholeStateId: string, + modules: number[][], + dependencies: string[], + signedVaa: Buffer +) { + const tokenBridgePackage = await getPackageId( + signer.provider, + tokenBridgeStateId + ); + const wormholePackage = await getPackageId(signer.provider, wormholeStateId); + + const tx = new TransactionBlock(); + + const [verifiedVaa] = tx.moveCall({ + target: `${wormholePackage}::vaa::parse_and_verify`, + arguments: [ + tx.object(wormholeStateId), + tx.pure(Array.from(signedVaa)), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + }); + const [decreeTicket] = tx.moveCall({ + target: `${tokenBridgePackage}::upgrade_contract::authorize_governance`, + arguments: [tx.object(tokenBridgeStateId)], + }); + const [decreeReceipt] = tx.moveCall({ + target: `${wormholePackage}::governance_message::verify_vaa`, + arguments: [tx.object(wormholeStateId), verifiedVaa, decreeTicket], + typeArguments: [ + `${tokenBridgePackage}::upgrade_contract::GovernanceWitness`, + ], + }); + + // Authorize upgrade. + const [upgradeTicket] = tx.moveCall({ + target: `${tokenBridgePackage}::upgrade_contract::authorize_upgrade`, + arguments: [tx.object(tokenBridgeStateId), decreeReceipt], + }); + + // Build and generate modules and dependencies for upgrade. + const [upgradeReceipt] = tx.upgrade({ + modules, + dependencies, + packageId: tokenBridgePackage, + ticket: upgradeTicket, + }); + + // Commit upgrade. + tx.moveCall({ + target: `${tokenBridgePackage}::upgrade_contract::commit_upgrade`, + arguments: [tx.object(tokenBridgeStateId), upgradeReceipt], + }); + + // Cannot auto compute gas budget, so we need to configure it manually. + // Gas ~215m. + //tx.setGasBudget(1_000_000_000n); + + return signer.signAndExecuteTransactionBlock({ + transactionBlock: tx, + options: { + showEffects: true, + showEvents: true, + }, + }); +} + +async function migrateTokenBridge( + signer: RawSigner, + tokenBridgeStateId: string, + wormholeStateId: string, + signedUpgradeVaa: Buffer +) { + const tokenBridgePackage = await getPackageId( + signer.provider, + tokenBridgeStateId + ); + const wormholePackage = await getPackageId(signer.provider, wormholeStateId); + + const tx = new TransactionBlock(); + + const [verifiedVaa] = tx.moveCall({ + target: `${wormholePackage}::vaa::parse_and_verify`, + arguments: [ + tx.object(wormholeStateId), + tx.pure(Array.from(signedUpgradeVaa)), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + }); + const [decreeTicket] = tx.moveCall({ + target: `${tokenBridgePackage}::upgrade_contract::authorize_governance`, + arguments: [tx.object(tokenBridgeStateId)], + }); + const [decreeReceipt] = tx.moveCall({ + target: `${wormholePackage}::governance_message::verify_vaa`, + arguments: [tx.object(wormholeStateId), verifiedVaa, decreeTicket], + typeArguments: [ + `${tokenBridgePackage}::upgrade_contract::GovernanceWitness`, + ], + }); + tx.moveCall({ + target: `${tokenBridgePackage}::migrate::migrate`, + arguments: [tx.object(tokenBridgeStateId), decreeReceipt], + }); + + return signer.signAndExecuteTransactionBlock({ + transactionBlock: tx, + options: { + showEffects: true, + showEvents: true, + }, + }); +} + +function setUpWormholeDirectory( + srcWormholePath: string, + dstWormholePath: string +) { + fs.cpSync(srcWormholePath, dstWormholePath, { recursive: true }); + + // Remove irrelevant files. This part is not necessary, but is helpful + // for debugging a clean package directory. + const removeThese = [ + "Move.devnet.toml", + "Move.lock", + "Makefile", + "README.md", + "build", + ]; + for (const basename of removeThese) { + fs.rmSync(`${dstWormholePath}/${basename}`, { + recursive: true, + force: true, + }); + } + + // Fix Move.toml file. + const moveTomlPath = `${dstWormholePath}/Move.toml`; + const moveToml = fs.readFileSync(moveTomlPath, "utf-8"); + fs.writeFileSync( + moveTomlPath, + moveToml.replace(`wormhole = "_"`, `wormhole = "0x0"`), + "utf-8" + ); +} + +function cleanUpPackageDirectory(packagePath: string) { + fs.rmSync(packagePath, { recursive: true, force: true }); +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/scripts/upgrade-wormhole.ts b/target_chains/sui/vendor/wormhole_iota_testnet/testing/scripts/upgrade-wormhole.ts new file mode 100644 index 0000000000..82d26b0f72 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/scripts/upgrade-wormhole.ts @@ -0,0 +1,267 @@ +import * as mock from "@certusone/wormhole-sdk/lib/cjs/mock"; +import { + RawSigner, + SUI_CLOCK_OBJECT_ID, + TransactionBlock, + fromB64, + normalizeSuiObjectId, + JsonRpcProvider, + Ed25519Keypair, + testnetConnection, +} from "@mysten/sui.js"; +import { execSync } from "child_process"; +import { resolve } from "path"; +import * as fs from "fs"; + +const GOVERNANCE_EMITTER = + "0000000000000000000000000000000000000000000000000000000000000004"; + +const WORMHOLE_STATE_ID = + "0x69ae41bdef4770895eb4e7aaefee5e4673acc08f6917b4856cf55549c4573ca8"; + +async function main() { + const guardianPrivateKey = process.env.TESTNET_GUARDIAN_PRIVATE_KEY; + if (guardianPrivateKey === undefined) { + throw new Error("TESTNET_GUARDIAN_PRIVATE_KEY unset in environment"); + } + + const walletPrivateKey = process.env.TESTNET_WALLET_PRIVATE_KEY; + if (walletPrivateKey === undefined) { + throw new Error("TESTNET_WALLET_PRIVATE_KEY unset in environment"); + } + + const provider = new JsonRpcProvider(testnetConnection); + const wallet = new RawSigner( + Ed25519Keypair.fromSecretKey( + Buffer.from(walletPrivateKey, "base64").subarray(1) + ), + provider + ); + + const srcWormholePath = resolve(`${__dirname}/../../wormhole`); + const dstWormholePath = resolve(`${__dirname}/wormhole`); + + // Stage build(s). + setUpWormholeDirectory(srcWormholePath, dstWormholePath); + + // Build for digest. + const { modules, dependencies, digest } = + buildForBytecodeAndDigest(dstWormholePath); + + // We will use the signed VAA when we execute the upgrade. + const guardians = new mock.MockGuardians(0, [guardianPrivateKey]); + + const timestamp = 12345678; + const governance = new mock.GovernanceEmitter(GOVERNANCE_EMITTER); + const published = governance.publishWormholeUpgradeContract( + timestamp, + 2, + "0x" + digest.toString("hex") + ); + published.writeUInt16BE(21, published.length - 34); + + const signedVaa = guardians.addSignatures(published, [0]); + console.log("Upgrade VAA:", signedVaa.toString("hex")); + + // And execute upgrade with governance VAA. + const upgradeResults = await buildAndUpgradeWormhole( + wallet, + WORMHOLE_STATE_ID, + modules, + dependencies, + signedVaa + ); + + console.log("tx digest", upgradeResults.digest); + console.log("tx effects", JSON.stringify(upgradeResults.effects!)); + console.log("tx events", JSON.stringify(upgradeResults.events!)); + + // TODO: grab new package ID from the events above. Do not rely on the RPC + // call because it may give you a stale package ID after the upgrade. + + // const migrateResults = await migrateWormhole( + // wallet, + // WORMHOLE_STATE_ID, + // signedVaa + // ); + // console.log("tx digest", migrateResults.digest); + // console.log("tx effects", JSON.stringify(migrateResults.effects!)); + // console.log("tx events", JSON.stringify(migrateResults.events!)); + + // Clean up. + cleanUpPackageDirectory(dstWormholePath); +} + +main(); + +// Yeah buddy. + +function buildForBytecodeAndDigest(packagePath: string) { + const buildOutput: { + modules: string[]; + dependencies: string[]; + digest: number[]; + } = JSON.parse( + execSync( + `sui move build --dump-bytecode-as-base64 -p ${packagePath} 2> /dev/null`, + { encoding: "utf-8" } + ) + ); + return { + modules: buildOutput.modules.map((m: string) => Array.from(fromB64(m))), + dependencies: buildOutput.dependencies.map((d: string) => + normalizeSuiObjectId(d) + ), + digest: Buffer.from(buildOutput.digest), + }; +} + +async function getPackageId( + provider: JsonRpcProvider, + stateId: string +): Promise { + const state = await provider + .getObject({ + id: stateId, + options: { + showContent: true, + }, + }) + .then((result) => { + if (result.data?.content?.dataType == "moveObject") { + return result.data.content.fields; + } + + throw new Error("not move object"); + }); + + if ("upgrade_cap" in state) { + return state.upgrade_cap.fields.package; + } + + throw new Error("upgrade_cap not found"); +} + +async function buildAndUpgradeWormhole( + signer: RawSigner, + wormholeStateId: string, + modules: number[][], + dependencies: string[], + signedVaa: Buffer +) { + const wormholePackage = await getPackageId(signer.provider, wormholeStateId); + + const tx = new TransactionBlock(); + + const [verifiedVaa] = tx.moveCall({ + target: `${wormholePackage}::vaa::parse_and_verify`, + arguments: [ + tx.object(wormholeStateId), + tx.pure(Array.from(signedVaa)), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + }); + const [decreeTicket] = tx.moveCall({ + target: `${wormholePackage}::upgrade_contract::authorize_governance`, + arguments: [tx.object(wormholeStateId)], + }); + const [decreeReceipt] = tx.moveCall({ + target: `${wormholePackage}::governance_message::verify_vaa`, + arguments: [tx.object(wormholeStateId), verifiedVaa, decreeTicket], + typeArguments: [`${wormholePackage}::upgrade_contract::GovernanceWitness`], + }); + + // Authorize upgrade. + const [upgradeTicket] = tx.moveCall({ + target: `${wormholePackage}::upgrade_contract::authorize_upgrade`, + arguments: [tx.object(wormholeStateId), decreeReceipt], + }); + + // Build and generate modules and dependencies for upgrade. + const [upgradeReceipt] = tx.upgrade({ + modules, + dependencies, + packageId: wormholePackage, + ticket: upgradeTicket, + }); + + // Commit upgrade. + tx.moveCall({ + target: `${wormholePackage}::upgrade_contract::commit_upgrade`, + arguments: [tx.object(wormholeStateId), upgradeReceipt], + }); + + // Cannot auto compute gas budget, so we need to configure it manually. + // Gas ~215m. + //tx.setGasBudget(1_000_000_000n); + + return signer.signAndExecuteTransactionBlock({ + transactionBlock: tx, + options: { + showEffects: true, + showEvents: true, + }, + }); +} + +async function migrateWormhole( + signer: RawSigner, + wormholeStateId: string, + signedUpgradeVaa: Buffer +) { + const contractPackage = await getPackageId(signer.provider, wormholeStateId); + + const tx = new TransactionBlock(); + tx.moveCall({ + target: `${contractPackage}::migrate::migrate`, + arguments: [ + tx.object(wormholeStateId), + tx.pure(Array.from(signedUpgradeVaa)), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + }); + + return signer.signAndExecuteTransactionBlock({ + transactionBlock: tx, + options: { + showEffects: true, + showEvents: true, + }, + }); +} + +function setUpWormholeDirectory( + srcWormholePath: string, + dstWormholePath: string +) { + fs.cpSync(srcWormholePath, dstWormholePath, { recursive: true }); + + // Remove irrelevant files. This part is not necessary, but is helpful + // for debugging a clean package directory. + const removeThese = [ + "Move.devnet.toml", + "Move.lock", + "Makefile", + "README.md", + "build", + ]; + for (const basename of removeThese) { + fs.rmSync(`${dstWormholePath}/${basename}`, { + recursive: true, + force: true, + }); + } + + // Fix Move.toml file. + const moveTomlPath = `${dstWormholePath}/Move.toml`; + const moveToml = fs.readFileSync(moveTomlPath, "utf-8"); + fs.writeFileSync( + moveTomlPath, + moveToml.replace(`wormhole = "_"`, `wormhole = "0x0"`), + "utf-8" + ); +} + +function cleanUpPackageDirectory(packagePath: string) { + fs.rmSync(packagePath, { recursive: true, force: true }); +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/client.yaml b/target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/client.yaml new file mode 100644 index 0000000000..33371df956 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/client.yaml @@ -0,0 +1,12 @@ +--- +keystore: + File: sui_config/sui.keystore +envs: + - alias: localnet + rpc: "http://0.0.0.0:9000" + ws: ~ + - alias: devnet + rpc: "https://fullnode.devnet.sui.io:443" + ws: ~ +active_env: localnet +active_address: "0xed867315e3f7c83ae82e6d5858b6a6cc57c291fd84f7509646ebc8162169cf96" diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/fullnode.yaml b/target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/fullnode.yaml new file mode 100644 index 0000000000..fc71e910e6 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/fullnode.yaml @@ -0,0 +1,53 @@ +--- +protocol-key-pair: + value: W+hPTVWhdFgzHs3YuRHV6gLfgFhHA1WG0pisIXiN8E8= +worker-key-pair: + value: AApEvpZE1O+2GMqZ1AbRE3+Kmgr1O5mdsMZ6I/gLpVSy +account-key-pair: + value: AN7ZHgjN8G7Nw7Q8NtY9TisPBjmEYpdUzbczjqR98XLh +network-key-pair: + value: AAnB6/zZooq4xDtB7oM/GeTSCh5tBxKAyJwWOMPlEJ4R +db-path: sui_config/authorities_db/full_node_db +network-address: /ip4/127.0.0.1/tcp/36683/http +json-rpc-address: "0.0.0.0:9000" +metrics-address: "127.0.0.1:35915" +admin-interface-port: 44319 +enable-event-processing: true +enable-index-processing: true +grpc-load-shed: ~ +grpc-concurrency-limit: ~ +p2p-config: + listen-address: "127.0.0.1:38187" + external-address: /ip4/127.0.0.1/udp/38187 + seed-peers: + - peer-id: ce60e3077e02a3683436af450f3a4511b4c40b158956637caf9ccf11391e7e10 + address: /ip4/127.0.0.1/udp/44061 + - peer-id: 5f0f42cb3fb20dd577703388320964f9351d997313c04a032247060d214b2e71 + address: /ip4/127.0.0.1/udp/46335 + - peer-id: 6d9095130b1536c0c9218ea9feb0f36685a6fa0b3b1e67d256cc4fb340a48d69 + address: /ip4/127.0.0.1/udp/32965 + - peer-id: b2915bf787845a55c24e18fdc162a575eb02d23bae3f9e566d7c51ebcfeb4a42 + address: /ip4/127.0.0.1/udp/39889 +genesis: + genesis-file-location: sui_config/genesis.blob +authority-store-pruning-config: + num-latest-epoch-dbs-to-retain: 3 + epoch-db-pruning-period-secs: 3600 + num-epochs-to-retain: 2 + max-checkpoints-in-batch: 200 + max-transactions-in-batch: 1000 + use-range-deletion: true +end-of-epoch-broadcast-channel-capacity: 128 +checkpoint-executor-config: + checkpoint-execution-max-concurrency: 200 + local-execution-timeout-sec: 30 +db-checkpoint-config: + perform-db-checkpoints-at-epoch-end: false +indirect-objects-threshold: 18446744073709551615 +expensive-safety-check-config: + enable-epoch-sui-conservation-check: false + enable-deep-per-tx-sui-conservation-check: false + force-disable-epoch-sui-conservation-check: false + enable-state-consistency-check: false + force-disable-state-consistency-check: false + enable-move-vm-paranoid-checks: false diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/genesis.blob b/target_chains/sui/vendor/wormhole_iota_testnet/testing/sui_config/genesis.blob new file mode 100644 index 0000000000000000000000000000000000000000..0bf9f806bc756771e7b44df5a2909eb0a19868eb GIT binary patch literal 237155 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 + token_bridge_state: &mut State, + coin_meta: &CoinMetadata, + nonce: u32 + ): MessageTicket { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(token_bridge_state); + + // Encode Wormhole message payload. + let encoded_asset_meta = + serialize_asset_meta(&latest_only, token_bridge_state, coin_meta); + + // Prepare Wormhole message. + state::prepare_wormhole_message( + &latest_only, + token_bridge_state, + nonce, + encoded_asset_meta + ) + } + + fun serialize_asset_meta( + latest_only: &LatestOnly, + token_bridge_state: &mut State, + coin_meta: &CoinMetadata, + ): vector { + let registry = state::borrow_token_registry(token_bridge_state); + + // Register if it is a new asset. + // + // NOTE: We don't want to abort if the asset is already registered + // because we may want to send asset metadata again after registration + // (the owner of a particular `CoinType` can change `CoinMetadata` any + // time after we register the asset). + if (token_registry::has(registry)) { + let asset_info = token_registry::verified_asset(registry); + // If this asset is already registered, there should already + // be canonical info associated with this coin type. + assert!( + !token_registry::is_wrapped(&asset_info), + E_WRAPPED_ASSET + ); + } else { + // Before we consider registering, we should not accidentally + // perform this registration that may be the `CoinMetadata` from + // `create_wrapped::prepare_registration`, which has empty fields. + assert!( + !create_wrapped::incomplete_metadata(coin_meta), + E_FROM_CREATE_WRAPPED + ); + + // Now register it. + token_registry::add_new_native( + state::borrow_mut_token_registry( + latest_only, + token_bridge_state + ), + coin_meta + ); + }; + + asset_meta::serialize(asset_meta::from_metadata(coin_meta)) + } + + #[test_only] + public fun serialize_asset_meta_test_only( + token_bridge_state: &mut State, + coin_metadata: &CoinMetadata, + ): vector { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(token_bridge_state); + + serialize_asset_meta(&latest_only, token_bridge_state, coin_metadata) + } +} + +#[test_only] +module token_bridge::attest_token_tests { + use std::ascii::{Self}; + use std::string::{Self}; + use sui::coin::{Self}; + use sui::test_scenario::{Self}; + use wormhole::publish_message::{Self}; + use wormhole::state::{chain_id}; + + use token_bridge::asset_meta::{Self}; + use token_bridge::attest_token::{Self}; + use token_bridge::coin_native_10::{Self, COIN_NATIVE_10}; + use token_bridge::coin_wrapped_7::{Self, COIN_WRAPPED_7}; + use token_bridge::native_asset::{Self}; + use token_bridge::state::{Self}; + use token_bridge::token_bridge_scenario::{ + person, + return_state, + set_up_wormhole_and_token_bridge, + take_state, + }; + use token_bridge::token_registry::{Self}; + + #[test] + fun test_attest_token() { + use token_bridge::attest_token::{attest_token}; + + let user = person(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Publish coin. + coin_native_10::init_test_only(test_scenario::ctx(scenario)); + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Ignore effects. + test_scenario::next_tx(scenario, user); + + let token_bridge_state = take_state(scenario); + let coin_meta = coin_native_10::take_metadata(scenario); + + // Emit `AssetMeta` payload. + let prepared_msg = + attest_token( + &mut token_bridge_state, + &coin_meta, + 1234, // nonce + ); + + // Ignore effects. + test_scenario::next_tx(scenario, user); + + // Check that asset is registered. + { + let registry = + state::borrow_token_registry(&token_bridge_state); + let verified = + token_registry::verified_asset(registry); + assert!(!token_registry::is_wrapped(&verified), 0); + + let asset = token_registry::borrow_native(registry); + + let expected_token_address = + native_asset::canonical_address(&coin_meta); + assert!( + native_asset::token_address(asset) == expected_token_address, + 0 + ); + assert!(native_asset::decimals(asset) == 10, 0); + + let ( + token_chain, + token_address + ) = native_asset::canonical_info(asset); + assert!(token_chain == chain_id(), 0); + assert!(token_address == expected_token_address, 0); + + assert!(native_asset::custody(asset) == 0, 0); + }; + + // Clean up for next call. + publish_message::destroy(prepared_msg); + + // Update metadata. + let new_symbol = { + use std::vector::{Self}; + + let symbol = coin::get_symbol(&coin_meta); + let buf = ascii::into_bytes(symbol); + vector::reverse(&mut buf); + + ascii::string(buf) + }; + + let new_name = coin::get_name(&coin_meta); + string::append(&mut new_name, string::utf8(b"??? and profit")); + + let treasury_cap = coin_native_10::take_treasury_cap(scenario); + coin::update_symbol(&treasury_cap, &mut coin_meta, new_symbol); + coin::update_name(&treasury_cap, &mut coin_meta, new_name); + + // We should be able to call `attest_token` any time after. + let prepared_msg = + attest_token( + &mut token_bridge_state, + &coin_meta, + 1234, // nonce + ); + + // Clean up. + publish_message::destroy(prepared_msg); + return_state(token_bridge_state); + coin_native_10::return_globals(treasury_cap, coin_meta); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_serialize_asset_meta() { + use token_bridge::attest_token::{serialize_asset_meta_test_only}; + + let user = person(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Publish coin. + coin_native_10::init_test_only(test_scenario::ctx(scenario)); + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Proceed to next operation. + test_scenario::next_tx(scenario, user); + + let token_bridge_state = take_state(scenario); + let coin_meta = coin_native_10::take_metadata(scenario); + + // Emit `AssetMeta` payload. + let serialized = + serialize_asset_meta_test_only(&mut token_bridge_state, &coin_meta); + let expected_serialized = + asset_meta::serialize_test_only( + asset_meta::from_metadata_test_only(&coin_meta) + ); + assert!(serialized == expected_serialized, 0); + + // Update metadata. + let new_symbol = { + use std::vector::{Self}; + + let symbol = coin::get_symbol(&coin_meta); + let buf = ascii::into_bytes(symbol); + vector::reverse(&mut buf); + + ascii::string(buf) + }; + + let new_name = coin::get_name(&coin_meta); + string::append(&mut new_name, string::utf8(b"??? and profit")); + + let treasury_cap = coin_native_10::take_treasury_cap(scenario); + coin::update_symbol(&treasury_cap, &mut coin_meta, new_symbol); + coin::update_name(&treasury_cap, &mut coin_meta, new_name); + + // Check that the new serialization reflects updated metadata. + let expected_serialized = + asset_meta::serialize_test_only( + asset_meta::from_metadata_test_only(&coin_meta) + ); + assert!(serialized != expected_serialized, 0); + let updated_serialized = + serialize_asset_meta_test_only(&mut token_bridge_state, &coin_meta); + assert!(updated_serialized == expected_serialized, 0); + + // Clean up. + return_state(token_bridge_state); + coin_native_10::return_globals(treasury_cap, coin_meta); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = attest_token::E_FROM_CREATE_WRAPPED)] + fun test_cannot_attest_token_from_create_wrapped() { + use token_bridge::attest_token::{attest_token}; + + let user = person(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Publish coin. + coin_wrapped_7::init_test_only(test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, user); + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Ignore effects. + test_scenario::next_tx(scenario, user); + + let token_bridge_state = take_state(scenario); + let coin_meta = test_scenario::take_shared(scenario); + + // You shall not pass! + let prepared_msg = + attest_token( + &mut token_bridge_state, + &coin_meta, + 1234 // nonce + ); + + // Clean up. + publish_message::destroy(prepared_msg); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_attest_token_outdated_version() { + use token_bridge::attest_token::{attest_token}; + + let user = person(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Publish coin. + coin_wrapped_7::init_test_only(test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, user); + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Ignore effects. + test_scenario::next_tx(scenario, user); + + let token_bridge_state = take_state(scenario); + let coin_meta = test_scenario::take_shared(scenario); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut token_bridge_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut token_bridge_state, + token_bridge::version_control::previous_version_test_only(), + token_bridge::version_control::next_version() + ); + + // You shall not pass! + let prepared_msg = + attest_token( + &mut token_bridge_state, + &coin_meta, + 1234 // nonce + ); + + // Clean up. + publish_message::destroy(prepared_msg); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer.move new file mode 100644 index 0000000000..c9086e7dbc --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer.move @@ -0,0 +1,1228 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements two methods: `authorize_transfer` and +/// `redeem_relayer_payout`, which are to be executed in a transaction block in +/// this order. +/// +/// `authorize_transfer` allows a contract to complete a Token Bridge transfer, +/// sending assets to the encoded recipient. The coin payout incentive in +/// redeeming the transfer is packaged in a `RelayerReceipt`. +/// +/// `redeem_relayer_payout` unpacks the `RelayerReceipt` to release the coin +/// containing the relayer fee amount. +/// +/// The purpose of splitting this transfer redemption into two steps is in case +/// Token Bridge needs to be upgraded and there is a breaking change for this +/// module, an integrator would not be left broken. It is discouraged to put +/// `authorize_transfer` in an integrator's package logic. Otherwise, this +/// integrator needs to be prepared to upgrade his contract to handle the latest +/// version of `complete_transfer`. +/// +/// Instead, an integrator is encouraged to execute a transaction block, which +/// executes `authorize_transfer` using the latest Token Bridge package ID and +/// to implement `redeem_relayer_payout` in his contract to consume this receipt. +/// This is similar to how an integrator with Wormhole is not meant to use +/// `vaa::parse_and_verify` in his contract in case the `vaa` module needs to +/// be upgraded due to a breaking change. +/// +/// See `transfer` module for serialization and deserialization of Wormhole +/// message payload. +module token_bridge::complete_transfer { + use sui::balance::{Self, Balance}; + use sui::coin::{Self, Coin}; + use sui::tx_context::{Self, TxContext}; + use wormhole::external_address::{Self, ExternalAddress}; + + use token_bridge::native_asset::{Self}; + use token_bridge::normalized_amount::{Self, NormalizedAmount}; + use token_bridge::state::{Self, State, LatestOnly}; + use token_bridge::token_registry::{Self, VerifiedAsset}; + use token_bridge::transfer::{Self}; + use token_bridge::vaa::{Self, TokenBridgeMessage}; + use token_bridge::wrapped_asset::{Self}; + + // Requires `handle_complete_transfer`. + friend token_bridge::complete_transfer_with_payload; + + /// Transfer not intended to be received on Sui. + const E_TARGET_NOT_SUI: u64 = 0; + /// Input token info does not match registered info. + const E_CANONICAL_TOKEN_INFO_MISMATCH: u64 = 1; + + /// Event reflecting when a transfer via `complete_transfer` or + /// `complete_transfer_with_payload` is successfully executed. + struct TransferRedeemed has drop, copy { + emitter_chain: u16, + emitter_address: ExternalAddress, + sequence: u64 + } + + #[allow(lint(coin_field))] + /// This type is only generated from `authorize_transfer` and can only be + /// redeemed using `redeem_relayer_payout`. Integrators running relayer + /// contracts are expected to implement `redeem_relayer_payout` within their + /// contracts and call `authorize_transfer` in a transaction block preceding + /// the method that consumes this receipt. + struct RelayerReceipt { + /// Coin of relayer fee payout. + payout: Coin + } + + /// `authorize_transfer` deserializes a token transfer VAA payload. Once the + /// transfer is authorized, an event (`TransferRedeemed`) is emitted to + /// reflect which Token Bridge this transfer originated from. The + /// `RelayerReceipt` returned wraps a `Coin` object containing a payout that + /// incentivizes someone to execute a transaction on behalf of the encoded + /// recipient. + /// + /// NOTE: This method is guarded by a minimum build version check. This + /// method could break backward compatibility on an upgrade. + /// + /// It is important for integrators to refrain from calling this method + /// within their contracts. This method is meant to be called in a + /// transaction block, passing the `RelayerReceipt` to a method which calls + /// `redeem_relayer_payout` within a contract. If in a circumstance where + /// this module has a breaking change in an upgrade, `redeem_relayer_payout` + /// will not be affected by this change. + /// + /// See `redeem_relayer_payout` for more details. + public fun authorize_transfer( + token_bridge_state: &mut State, + msg: TokenBridgeMessage, + ctx: &mut TxContext + ): RelayerReceipt { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(token_bridge_state); + + // Emitting the transfer being redeemed (and disregard return value). + emit_transfer_redeemed(&msg); + + // Deserialize transfer message and process. + handle_complete_transfer( + &latest_only, + token_bridge_state, + vaa::take_payload(msg), + ctx + ) + } + + /// After a transfer is authorized, a relayer contract may unpack the + /// `RelayerReceipt` using this method. Coin representing the relaying + /// incentive from this receipt is returned. This method is meant to be + /// simple. It allows for a coordination with calling `authorize_upgrade` + /// before a method that implements `redeem_relayer_payout` in a transaction + /// block to consume this receipt. + /// + /// NOTE: Integrators of Token Bridge collecting relayer fee payouts from + /// these token transfers should be calling only this method from their + /// contracts. This method is not guarded by version control (thus not + /// requiring a reference to the Token Bridge `State` object), so it is + /// intended to work for any package version. + public fun redeem_relayer_payout( + receipt: RelayerReceipt + ): Coin { + let RelayerReceipt { payout } = receipt; + + payout + } + + /// This is a privileged method only used by `complete_transfer` and + /// `complete_transfer_with_payload` modules. This method validates the + /// encoded token info with the passed in coin type via the `TokenRegistry`. + /// The transfer amount is denormalized and either mints balance of + /// wrapped asset or withdraws balance from native asset custody. + /// + /// Depending on whether this coin is a Token Bridge wrapped asset or a + /// natively existing asset on Sui, the coin is either minted or withdrawn + /// from Token Bridge's custody. + public(friend) fun verify_and_bridge_out( + latest_only: &LatestOnly, + token_bridge_state: &mut State, + token_chain: u16, + token_address: ExternalAddress, + target_chain: u16, + amount: NormalizedAmount + ): ( + VerifiedAsset, + Balance + ) { + // Verify that the intended chain ID for this transfer is for Sui. + assert!( + target_chain == wormhole::state::chain_id(), + E_TARGET_NOT_SUI + ); + + let asset_info = state::verified_asset(token_bridge_state); + assert!( + ( + token_chain == token_registry::token_chain(&asset_info) && + token_address == token_registry::token_address(&asset_info) + ), + E_CANONICAL_TOKEN_INFO_MISMATCH + ); + + // De-normalize amount in preparation to take `Balance`. + let raw_amount = + normalized_amount::to_raw( + amount, + token_registry::coin_decimals(&asset_info) + ); + + // If the token is wrapped by Token Bridge, we will mint these tokens. + // Otherwise, we will withdraw from custody. + let bridged_out = { + let registry = + state::borrow_mut_token_registry( + latest_only, + token_bridge_state + ); + if (token_registry::is_wrapped(&asset_info)) { + wrapped_asset::mint( + token_registry::borrow_mut_wrapped(registry), + raw_amount + ) + } else { + native_asset::withdraw( + token_registry::borrow_mut_native(registry), + raw_amount + ) + } + }; + + (asset_info, bridged_out) + } + + /// This method emits source information of the token transfer. Off-chain + /// processes may want to observe when transfers have been redeemed. + public(friend) fun emit_transfer_redeemed(msg: &TokenBridgeMessage): u16 { + let emitter_chain = vaa::emitter_chain(msg); + + // Emit Sui event with `TransferRedeemed`. + sui::event::emit( + TransferRedeemed { + emitter_chain, + emitter_address: vaa::emitter_address(msg), + sequence: vaa::sequence(msg) + } + ); + + emitter_chain + } + + fun handle_complete_transfer( + latest_only: &LatestOnly, + token_bridge_state: &mut State, + transfer_vaa_payload: vector, + ctx: &mut TxContext + ): RelayerReceipt { + let ( + amount, + token_address, + token_chain, + recipient, + recipient_chain, + relayer_fee + ) = transfer::unpack(transfer::deserialize(transfer_vaa_payload)); + + let ( + asset_info, + bridged_out + ) = + verify_and_bridge_out( + latest_only, + token_bridge_state, + token_chain, + token_address, + recipient_chain, + amount + ); + + let recipient = external_address::to_address(recipient); + + // If the recipient did not redeem his own transfer, Token Bridge will + // split the withdrawn coins and send a portion to the transaction + // relayer. + let payout = if ( + normalized_amount::value(&relayer_fee) == 0 || + recipient == tx_context::sender(ctx) + ) { + balance::zero() + } else { + let payout_amount = + normalized_amount::to_raw( + relayer_fee, + token_registry::coin_decimals(&asset_info) + ); + balance::split(&mut bridged_out, payout_amount) + }; + + // Transfer tokens to the recipient. + sui::transfer::public_transfer( + coin::from_balance(bridged_out, ctx), + recipient + ); + + // Finally produce the receipt that a relayer can consume via + // `redeem_relayer_payout`. + RelayerReceipt { + payout: coin::from_balance(payout, ctx) + } + } + + #[test_only] + public fun burn(receipt: RelayerReceipt) { + coin::burn_for_testing(redeem_relayer_payout(receipt)); + } +} + +#[test_only] +module token_bridge::complete_transfer_tests { + use sui::coin::{Self, Coin}; + use sui::test_scenario::{Self}; + use wormhole::state::{chain_id}; + use wormhole::wormhole_scenario::{parse_and_verify_vaa}; + + use token_bridge::coin_wrapped_12::{Self, COIN_WRAPPED_12}; + use token_bridge::coin_wrapped_7::{Self, COIN_WRAPPED_7}; + use token_bridge::coin_native_10::{Self, COIN_NATIVE_10}; + use token_bridge::coin_native_4::{Self, COIN_NATIVE_4}; + use token_bridge::complete_transfer::{Self}; + use token_bridge::dummy_message::{Self}; + use token_bridge::native_asset::{Self}; + use token_bridge::state::{Self}; + use token_bridge::token_bridge_scenario::{ + set_up_wormhole_and_token_bridge, + register_dummy_emitter, + return_state, + take_state, + three_people, + two_people + }; + use token_bridge::token_registry::{Self}; + use token_bridge::transfer::{Self}; + use token_bridge::vaa::{Self}; + use token_bridge::wrapped_asset::{Self}; + + struct OTHER_COIN_WITNESS has drop {} + + #[test] + /// An end-to-end test for complete transfer native with VAA. + fun test_complete_transfer_native_10_relayer_fee() { + use token_bridge::complete_transfer::{ + authorize_transfer, + redeem_relayer_payout + }; + + let transfer_vaa = + dummy_message::encoded_transfer_vaa_native_with_fee(); + + let (expected_recipient, tx_relayer, coin_deployer) = three_people(); + let my_scenario = test_scenario::begin(tx_relayer); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + let custody_amount = 500000; + coin_native_10::init_register_and_deposit( + scenario, + coin_deployer, + custody_amount + ); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + let token_bridge_state = take_state(scenario); + + // These will be checked later. + let expected_relayer_fee = 100000; + let expected_recipient_amount = 200000; + let expected_amount = expected_relayer_fee + expected_recipient_amount; + + // Scope to allow immutable reference to `TokenRegistry`. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_native(registry); + assert!(native_asset::custody(asset) == custody_amount, 0); + + // Verify transfer parameters. + let parsed = + transfer::deserialize_test_only( + wormhole::vaa::take_payload( + parse_and_verify_vaa(scenario, transfer_vaa) + ) + ); + + let asset_info = + token_registry::verified_asset(registry); + let expected_token_chain = token_registry::token_chain(&asset_info); + let expected_token_address = + token_registry::token_address(&asset_info); + assert!(transfer::token_chain(&parsed) == expected_token_chain, 0); + assert!( + transfer::token_address(&parsed) == expected_token_address, + 0 + ); + + let coin_meta = test_scenario::take_shared(scenario); + + let decimals = coin::get_decimals(&coin_meta); + + test_scenario::return_shared(coin_meta); + + assert!( + transfer::raw_amount(&parsed, decimals) == expected_amount, + 0 + ); + + assert!( + transfer::raw_relayer_fee(&parsed, decimals) == expected_relayer_fee, + 0 + ); + assert!( + transfer::recipient_as_address(&parsed) == expected_recipient, + 0 + ); + assert!(transfer::recipient_chain(&parsed) == chain_id(), 0); + + // Clean up. + transfer::destroy(parsed); + }; + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + let payout = redeem_relayer_payout(receipt); + assert!(coin::value(&payout) == expected_relayer_fee, 0); + + // TODO: Check for one event? `TransferRedeemed`. + let _effects = test_scenario::next_tx(scenario, tx_relayer); + + // Check recipient's `Coin`. + let received = + test_scenario::take_from_address>( + scenario, + expected_recipient + ); + assert!(coin::value(&received) == expected_recipient_amount, 0); + + // And check remaining amount in custody. + let registry = state::borrow_token_registry(&token_bridge_state); + let remaining = custody_amount - expected_amount; + { + let asset = token_registry::borrow_native(registry); + assert!(native_asset::custody(asset) == remaining, 0); + }; + + // Clean up. + coin::burn_for_testing(payout); + coin::burn_for_testing(received); + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + /// An end-to-end test for complete transfer native with VAA. + fun test_complete_transfer_native_4_relayer_fee() { + use token_bridge::complete_transfer::{ + authorize_transfer, + redeem_relayer_payout + }; + + let transfer_vaa = + dummy_message::encoded_transfer_vaa_native_with_fee(); + + let (expected_recipient, tx_relayer, coin_deployer) = three_people(); + let my_scenario = test_scenario::begin(tx_relayer); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + let custody_amount = 5000; + coin_native_4::init_register_and_deposit( + scenario, + coin_deployer, + custody_amount + ); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + let token_bridge_state = take_state(scenario); + + // These will be checked later. + let expected_relayer_fee = 1000; + let expected_recipient_amount = 2000; + let expected_amount = expected_relayer_fee + expected_recipient_amount; + + // Scope to allow immutable reference to `TokenRegistry`. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_native(registry); + assert!(native_asset::custody(asset) == custody_amount, 0); + + // Verify transfer parameters. + let parsed = + transfer::deserialize_test_only( + wormhole::vaa::take_payload( + parse_and_verify_vaa(scenario, transfer_vaa) + ) + ); + + let asset_info = + token_registry::verified_asset(registry); + let expected_token_chain = token_registry::token_chain(&asset_info); + let expected_token_address = + token_registry::token_address(&asset_info); + assert!(transfer::token_chain(&parsed) == expected_token_chain, 0); + assert!( + transfer::token_address(&parsed) == expected_token_address, + 0 + ); + + let coin_meta = test_scenario::take_shared(scenario); + let decimals = coin::get_decimals(&coin_meta); + test_scenario::return_shared(coin_meta); + + assert!( + transfer::raw_amount(&parsed, decimals) == expected_amount, + 0 + ); + + assert!( + transfer::raw_relayer_fee(&parsed, decimals) == expected_relayer_fee, + 0 + ); + assert!( + transfer::recipient_as_address(&parsed) == expected_recipient, + 0 + ); + assert!(transfer::recipient_chain(&parsed) == chain_id(), 0); + + // Clean up. + transfer::destroy(parsed); + }; + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + let payout = redeem_relayer_payout(receipt); + assert!(coin::value(&payout) == expected_relayer_fee, 0); + + // TODO: Check for one event? `TransferRedeemed`. + let _effects = test_scenario::next_tx(scenario, tx_relayer); + + // Check recipient's `Coin`. + let received = + test_scenario::take_from_address>( + scenario, + expected_recipient + ); + assert!(coin::value(&received) == expected_recipient_amount, 0); + + // And check remaining amount in custody. + let registry = state::borrow_token_registry(&token_bridge_state); + let remaining = custody_amount - expected_amount; + { + let asset = token_registry::borrow_native(registry); + assert!(native_asset::custody(asset) == remaining, 0); + }; + + // Clean up. + coin::burn_for_testing(payout); + coin::burn_for_testing(received); + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + /// An end-to-end test for complete transfer wrapped with VAA. + fun test_complete_transfer_wrapped_7_relayer_fee() { + use token_bridge::complete_transfer::{ + authorize_transfer, + redeem_relayer_payout + }; + + let transfer_vaa = + dummy_message::encoded_transfer_vaa_wrapped_7_with_fee(); + + let (expected_recipient, tx_relayer, coin_deployer) = three_people(); + let my_scenario = test_scenario::begin(tx_relayer); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + coin_wrapped_7::init_and_register(scenario, coin_deployer); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + let token_bridge_state = take_state(scenario); + + // These will be checked later. + let expected_relayer_fee = 1000; + let expected_recipient_amount = 2000; + let expected_amount = expected_relayer_fee + expected_recipient_amount; + + // Scope to allow immutable reference to `TokenRegistry`. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = + token_registry::borrow_wrapped(registry); + assert!(wrapped_asset::total_supply(asset) == 0, 0); + + // Verify transfer parameters. + let parsed = + transfer::deserialize_test_only( + wormhole::vaa::take_payload( + parse_and_verify_vaa(scenario, transfer_vaa) + ) + ); + + let asset_info = + token_registry::verified_asset(registry); + let expected_token_chain = token_registry::token_chain(&asset_info); + let expected_token_address = + token_registry::token_address(&asset_info); + assert!(transfer::token_chain(&parsed) == expected_token_chain, 0); + assert!( + transfer::token_address(&parsed) == expected_token_address, + 0 + ); + + let coin_meta = test_scenario::take_shared(scenario); + let decimals = coin::get_decimals(&coin_meta); + test_scenario::return_shared(coin_meta); + + assert!( + transfer::raw_amount(&parsed, decimals) == expected_amount, + 0 + ); + + assert!( + transfer::raw_relayer_fee(&parsed, decimals) == expected_relayer_fee, + 0 + ); + assert!( + transfer::recipient_as_address(&parsed) == expected_recipient, + 0 + ); + assert!(transfer::recipient_chain(&parsed) == chain_id(), 0); + + // Clean up. + transfer::destroy(parsed); + }; + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + let payout = redeem_relayer_payout(receipt); + assert!(coin::value(&payout) == expected_relayer_fee, 0); + + // TODO: Check for one event? `TransferRedeemed`. + let _effects = test_scenario::next_tx(scenario, tx_relayer); + + // Check recipient's `Coin`. + let received = + test_scenario::take_from_address>( + scenario, + expected_recipient + ); + assert!(coin::value(&received) == expected_recipient_amount, 0); + + // And check that the amount is the total wrapped supply. + let registry = state::borrow_token_registry(&token_bridge_state); + { + let asset = + token_registry::borrow_wrapped(registry); + assert!(wrapped_asset::total_supply(asset) == expected_amount, 0); + }; + + // Clean up. + coin::burn_for_testing(payout); + coin::burn_for_testing(received); + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + /// An end-to-end test for complete transfer wrapped with VAA. + fun test_complete_transfer_wrapped_12_relayer_fee() { + use token_bridge::complete_transfer::{ + authorize_transfer, + redeem_relayer_payout + }; + + let transfer_vaa = + dummy_message::encoded_transfer_vaa_wrapped_12_with_fee(); + + let (expected_recipient, tx_relayer, coin_deployer) = three_people(); + let my_scenario = test_scenario::begin(tx_relayer); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + coin_wrapped_12::init_and_register(scenario, coin_deployer); + + // Ignore effects. + // + // NOTE: `tx_relayer` != `expected_recipient`. + assert!(expected_recipient != tx_relayer, 0); + test_scenario::next_tx(scenario, tx_relayer); + + let token_bridge_state = take_state(scenario); + + // These will be checked later. + let expected_relayer_fee = 1000; + let expected_recipient_amount = 2000; + let expected_amount = expected_relayer_fee + expected_recipient_amount; + + // Scope to allow immutable reference to `TokenRegistry`. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = + token_registry::borrow_wrapped(registry); + assert!(wrapped_asset::total_supply(asset) == 0, 0); + + // Verify transfer parameters. + let parsed = + transfer::deserialize_test_only( + wormhole::vaa::take_payload( + parse_and_verify_vaa(scenario, transfer_vaa) + ) + ); + + let asset_info = + token_registry::verified_asset(registry); + let expected_token_chain = token_registry::token_chain(&asset_info); + let expected_token_address = + token_registry::token_address(&asset_info); + assert!(transfer::token_chain(&parsed) == expected_token_chain, 0); + assert!(transfer::token_address(&parsed) == expected_token_address, 0); + + let coin_meta = test_scenario::take_shared(scenario); + let decimals = coin::get_decimals(&coin_meta); + test_scenario::return_shared(coin_meta); + + assert!(transfer::raw_amount(&parsed, decimals) == expected_amount, 0); + + assert!( + transfer::raw_relayer_fee(&parsed, decimals) == expected_relayer_fee, + 0 + ); + assert!( + transfer::recipient_as_address(&parsed) == expected_recipient, + 0 + ); + assert!(transfer::recipient_chain(&parsed) == chain_id(), 0); + + // Clean up. + transfer::destroy(parsed); + }; + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + let payout = redeem_relayer_payout(receipt); + assert!(coin::value(&payout) == expected_relayer_fee, 0); + + // TODO: Check for one event? `TransferRedeemed`. + let _effects = test_scenario::next_tx(scenario, tx_relayer); + + // Check recipient's `Coin`. + let received = + test_scenario::take_from_address>( + scenario, + expected_recipient + ); + assert!(coin::value(&received) == expected_recipient_amount, 0); + + // And check that the amount is the total wrapped supply. + let registry = state::borrow_token_registry(&token_bridge_state); + { + let asset = token_registry::borrow_wrapped(registry); + assert!(wrapped_asset::total_supply(asset) == expected_amount, 0); + }; + + // Clean up. + coin::burn_for_testing(payout); + coin::burn_for_testing(received); + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + /// An end-to-end test for complete transfer native with VAA. The encoded VAA + /// specifies a nonzero fee, however the `recipient` should receive the full + /// amount for self redeeming the transfer. + fun test_complete_transfer_native_10_relayer_fee_self_redemption() { + use token_bridge::complete_transfer::{ + authorize_transfer, + redeem_relayer_payout + }; + + let transfer_vaa = + dummy_message::encoded_transfer_vaa_native_with_fee(); + + let (expected_recipient, _, coin_deployer) = three_people(); + let my_scenario = test_scenario::begin(expected_recipient); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + let custody_amount = 500000; + coin_native_10::init_register_and_deposit( + scenario, + coin_deployer, + custody_amount + ); + + // Ignore effects. + test_scenario::next_tx(scenario, expected_recipient); + + let token_bridge_state = take_state(scenario); + + // NOTE: Although there is a fee encoded in the VAA, the relayer + // shouldn't receive this fee. The `expected_relayer_fee` should + // go to the recipient. + // + // These values will be used later. + let expected_relayer_fee = 0; + let encoded_relayer_fee = 100000; + let expected_recipient_amount = 300000; + let expected_amount = expected_relayer_fee + expected_recipient_amount; + + // Scope to allow immutable reference to `TokenRegistry`. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_native(registry); + assert!(native_asset::custody(asset) == custody_amount, 0); + + // Verify transfer parameters. + let parsed = + transfer::deserialize_test_only( + wormhole::vaa::take_payload( + parse_and_verify_vaa(scenario, transfer_vaa) + ) + ); + + let asset_info = + token_registry::verified_asset(registry); + let expected_token_chain = token_registry::token_chain(&asset_info); + let expected_token_address = + token_registry::token_address(&asset_info); + assert!(transfer::token_chain(&parsed) == expected_token_chain, 0); + assert!(transfer::token_address(&parsed) == expected_token_address, 0); + + let coin_meta = test_scenario::take_shared(scenario); + + let decimals = coin::get_decimals(&coin_meta); + + test_scenario::return_shared(coin_meta); + + assert!(transfer::raw_amount(&parsed, decimals) == expected_amount, 0); + assert!( + transfer::raw_relayer_fee(&parsed, decimals) == encoded_relayer_fee, + 0 + ); + assert!( + transfer::recipient_as_address(&parsed) == expected_recipient, + 0 + ); + assert!(transfer::recipient_chain(&parsed) == chain_id(), 0); + + // Clean up. + transfer::destroy(parsed); + }; + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. + test_scenario::next_tx(scenario, expected_recipient); + + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + let payout = redeem_relayer_payout(receipt); + assert!(coin::value(&payout) == expected_relayer_fee, 0); + + // TODO: Check for one event? `TransferRedeemed`. + let _effects = test_scenario::next_tx(scenario, expected_recipient); + + // Check recipient's `Coin`. + let received = + test_scenario::take_from_address>( + scenario, + expected_recipient + ); + assert!(coin::value(&received) == expected_recipient_amount, 0); + + // And check remaining amount in custody. + let registry = state::borrow_token_registry(&token_bridge_state); + let remaining = custody_amount - expected_amount; + { + let asset = token_registry::borrow_native(registry); + assert!(native_asset::custody(asset) == remaining, 0); + }; + + // Clean up. + coin::burn_for_testing(payout); + coin::burn_for_testing(received); + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure( + abort_code = complete_transfer::E_CANONICAL_TOKEN_INFO_MISMATCH + )] + /// This test verifies that `authorize_transfer` reverts when called with + /// a native COIN_TYPE that's not encoded in the VAA. + fun test_cannot_authorize_transfer_native_invalid_coin_type() { + use token_bridge::complete_transfer::{authorize_transfer}; + + let transfer_vaa = + dummy_message::encoded_transfer_vaa_native_with_fee(); + + let (_, tx_relayer, coin_deployer) = three_people(); + let my_scenario = test_scenario::begin(tx_relayer); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + let custody_amount_coin_10 = 500000; + coin_native_10::init_register_and_deposit( + scenario, + coin_deployer, + custody_amount_coin_10 + ); + + // Register a second native asset. + let custody_amount_coin_4 = 69420; + coin_native_4::init_register_and_deposit( + scenario, + coin_deployer, + custody_amount_coin_4 + ); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + let token_bridge_state = take_state(scenario); + + // Scope to allow immutable reference to `TokenRegistry`. This verifies + // that both coin types have been registered. + { + let registry = state::borrow_token_registry(&token_bridge_state); + + // COIN_10. + let coin_10 = + token_registry::borrow_native(registry); + assert!( + native_asset::custody(coin_10) == custody_amount_coin_10, + 0 + ); + + // COIN_4. + let coin_4 = token_registry::borrow_native(registry); + assert!(native_asset::custody(coin_4) == custody_amount_coin_4, 0); + }; + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + // NOTE: this call should revert since the transfer VAA is for + // a coin of type COIN_NATIVE_10. However, the `complete_transfer` + // method is called using the COIN_NATIVE_4 type. + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + + // Clean up. + complete_transfer::burn(receipt); + + abort 42 + } + + #[test] + #[expected_failure( + abort_code = complete_transfer::E_CANONICAL_TOKEN_INFO_MISMATCH + )] + /// This test verifies that `authorize_transfer` reverts when called with + /// a wrapped COIN_TYPE that's not encoded in the VAA. + fun test_cannot_authorize_transfer_wrapped_invalid_coin_type() { + use token_bridge::complete_transfer::{authorize_transfer}; + + let transfer_vaa = dummy_message::encoded_transfer_vaa_wrapped_12_with_fee(); + + let (expected_recipient, tx_relayer, coin_deployer) = three_people(); + let my_scenario = test_scenario::begin(tx_relayer); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Register both wrapped coin types (12 and 7). + coin_wrapped_12::init_and_register(scenario, coin_deployer); + coin_wrapped_7::init_and_register(scenario, coin_deployer); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + // NOTE: `tx_relayer` != `expected_recipient`. + assert!(expected_recipient != tx_relayer, 0); + + let token_bridge_state = take_state(scenario); + + // Scope to allow immutable reference to `TokenRegistry`. This verifies + // that both coin types have been registered. + { + let registry = state::borrow_token_registry(&token_bridge_state); + + let coin_12 = + token_registry::borrow_wrapped(registry); + assert!(wrapped_asset::total_supply(coin_12) == 0, 0); + + let coin_7 = + token_registry::borrow_wrapped(registry); + assert!(wrapped_asset::total_supply(coin_7) == 0, 0); + }; + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + // NOTE: this call should revert since the transfer VAA is for + // a coin of type COIN_WRAPPED_12. However, the `authorize_transfer` + // method is called using the COIN_WRAPPED_7 type. + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + + // Clean up. + complete_transfer::burn(receipt); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = complete_transfer::E_TARGET_NOT_SUI)] + /// This test verifies that `authorize_transfer` reverts when a transfer is + /// sent to the wrong target blockchain (chain ID != 21). + fun test_cannot_authorize_transfer_wrapped_12_invalid_target_chain() { + use token_bridge::complete_transfer::{authorize_transfer}; + + let transfer_vaa = + dummy_message::encoded_transfer_vaa_wrapped_12_invalid_target_chain(); + + let (expected_recipient, tx_relayer, coin_deployer) = three_people(); + let my_scenario = test_scenario::begin(tx_relayer); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + coin_wrapped_12::init_and_register(scenario, coin_deployer); + + // Ignore effects. + // + // NOTE: `tx_relayer` != `expected_recipient`. + assert!(expected_recipient != tx_relayer, 0); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + let token_bridge_state = take_state(scenario); + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + // NOTE: this call should revert since the target chain encoded is + // chain 69 instead of chain 21 (Sui). + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + + // Clean up. + complete_transfer::burn(receipt); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_complete_transfer_outdated_version() { + use token_bridge::complete_transfer::{authorize_transfer}; + + let transfer_vaa = + dummy_message::encoded_transfer_vaa_native_with_fee(); + + let (tx_relayer, coin_deployer) = two_people(); + let my_scenario = test_scenario::begin(tx_relayer); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + let custody_amount = 500000; + coin_native_10::init_register_and_deposit( + scenario, + coin_deployer, + custody_amount + ); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + let token_bridge_state = take_state(scenario); + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. + test_scenario::next_tx(scenario, tx_relayer); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut token_bridge_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut token_bridge_state, + token_bridge::version_control::previous_version_test_only(), + token_bridge::version_control::next_version() + ); + + // You shall not pass! + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + + // Clean up. + complete_transfer::burn(receipt); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer_with_payload.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer_with_payload.move new file mode 100644 index 0000000000..ba35a7a9e8 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer_with_payload.move @@ -0,0 +1,776 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements two methods: `authorize_transfer` and `redeem_coin`, +/// which are to be executed in a transaction block in this order. +/// +/// `authorize_transfer` allows a contract to complete a Token Bridge transfer +/// with arbitrary payload. This deserialized `TransferWithPayload` with the +/// bridged balance and source chain ID are packaged in a `RedeemerReceipt`. +/// +/// `redeem_coin` unpacks the `RedeemerReceipt` and checks whether the specified +/// `EmitterCap` is the specified redeemer for this transfer. If he is the +/// correct redeemer, the balance is unpacked and transformed into `Coin` and +/// is returned alongside `TransferWithPayload` and source chain ID. +/// +/// The purpose of splitting this transfer redemption into two steps is in case +/// Token Bridge needs to be upgraded and there is a breaking change for this +/// module, an integrator would not be left broken. It is discouraged to put +/// `authorize_transfer` in an integrator's package logic. Otherwise, this +/// integrator needs to be prepared to upgrade his contract to handle the latest +/// version of `complete_transfer_with_payload`. +/// +/// Instead, an integrator is encouraged to execute a transaction block, which +/// executes `authorize_transfer` using the latest Token Bridge package ID and +/// to implement `redeem_coin` in his contract to consume this receipt. This is +/// similar to how an integrator with Wormhole is not meant to use +/// `vaa::parse_and_verify` in his contract in case the `vaa` module needs to +/// be upgraded due to a breaking change. +/// +/// Like in `complete_transfer`, a VAA with an encoded transfer can be redeemed +/// only once. +/// +/// See `transfer_with_payload` module for serialization and deserialization of +/// Wormhole message payload. +module token_bridge::complete_transfer_with_payload { + use sui::coin::{Self, Coin}; + use sui::object::{Self}; + use sui::tx_context::{TxContext}; + use wormhole::emitter::{EmitterCap}; + + use token_bridge::complete_transfer::{Self}; + use token_bridge::state::{Self, State, LatestOnly}; + use token_bridge::transfer_with_payload::{Self, TransferWithPayload}; + use token_bridge::vaa::{Self, TokenBridgeMessage}; + + /// `EmitterCap` address does not agree with encoded redeemer. + const E_INVALID_REDEEMER: u64 = 0; + + #[allow(lint(coin_field))] + /// This type is only generated from `authorize_transfer` and can only be + /// redeemed using `redeem_coin`. Integrators are expected to implement + /// `redeem_coin` within their contracts and call `authorize_transfer` in a + /// transaction block preceding the method that consumes this receipt. The + /// only way to destroy this receipt is calling `redeem_coin` with an + /// `EmitterCap` generated from the `wormhole::emitter` module, whose ID is + /// the expected redeemer for this token transfer. + struct RedeemerReceipt { + /// Which chain ID this transfer originated from. + source_chain: u16, + /// Deserialized transfer info. + parsed: TransferWithPayload, + /// Coin of bridged asset. + bridged_out: Coin + } + + /// `authorize_transfer` deserializes a token transfer VAA payload, which + /// encodes its own arbitrary payload (which has meaning to the redeemer). + /// Once the transfer is authorized, an event (`TransferRedeemed`) is + /// emitted to reflect which Token Bridge this transfer originated from. + /// The `RedeemerReceipt` returned wraps a balance reflecting the encoded + /// transfer amount along with the source chain and deserialized + /// `TransferWithPayload`. + /// + /// NOTE: This method is guarded by a minimum build version check. This + /// method could break backward compatibility on an upgrade. + /// + /// It is important for integrators to refrain from calling this method + /// within their contracts. This method is meant to be called in a + /// transaction block, passing the `RedeemerReceipt` to a method which calls + /// `redeem_coin` within a contract. If in a circumstance where this module + /// has a breaking change in an upgrade, `redeem_coin` will not be affected + /// by this change. + /// + /// See `redeem_coin` for more details. + public fun authorize_transfer( + token_bridge_state: &mut State, + msg: TokenBridgeMessage, + ctx: &mut TxContext + ): RedeemerReceipt { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(token_bridge_state); + + // Emitting the transfer being redeemed. + // + // NOTE: We save the emitter chain ID to save the integrator from + // having to `parse_and_verify` the same encoded VAA to get this info. + let source_chain = + complete_transfer::emit_transfer_redeemed(&msg); + + // Finally deserialize the Wormhole message payload and handle bridging + // out token of a given coin type. + handle_authorize_transfer( + &latest_only, + token_bridge_state, + source_chain, + vaa::take_payload(msg), + ctx + ) + } + + /// After a transfer is authorized, only a valid redeemer may unpack the + /// `RedeemerReceipt`. The specified `EmitterCap` is the only authorized + /// redeemer of the transfer. Once the redeemer is validated, coin from + /// this receipt of the specified coin type is returned alongside the + /// deserialized `TransferWithPayload` and source chain ID. + /// + /// NOTE: Integrators of Token Bridge redeeming these token transfers should + /// be calling only this method from their contracts. This method is not + /// guarded by version control (thus not requiring a reference to the + /// Token Bridge `State` object), so it is intended to work for any package + /// version. + public fun redeem_coin( + emitter_cap: &EmitterCap, + receipt: RedeemerReceipt + ): ( + Coin, + TransferWithPayload, + u16 // `wormhole::vaa::emitter_chain` + ) { + let RedeemerReceipt { source_chain, parsed, bridged_out } = receipt; + + // Transfer must be redeemed by the contract's registered Wormhole + // emitter. + let redeemer = transfer_with_payload::redeemer_id(&parsed); + assert!(redeemer == object::id(emitter_cap), E_INVALID_REDEEMER); + + // Create coin from balance and return other unpacked members of receipt. + (bridged_out, parsed, source_chain) + } + + fun handle_authorize_transfer( + latest_only: &LatestOnly, + token_bridge_state: &mut State, + source_chain: u16, + transfer_vaa_payload: vector, + ctx: &mut TxContext + ): RedeemerReceipt { + // Deserialize for processing. + let parsed = transfer_with_payload::deserialize(transfer_vaa_payload); + + // Handle bridging assets out to be returned to method caller. + // + // See `complete_transfer` module for more info. + let ( + _, + bridged_out, + ) = + complete_transfer::verify_and_bridge_out( + latest_only, + token_bridge_state, + transfer_with_payload::token_chain(&parsed), + transfer_with_payload::token_address(&parsed), + transfer_with_payload::redeemer_chain(&parsed), + transfer_with_payload::amount(&parsed) + ); + + RedeemerReceipt { + source_chain, + parsed, + bridged_out: coin::from_balance(bridged_out, ctx) + } + } + + #[test_only] + public fun burn(receipt: RedeemerReceipt) { + let RedeemerReceipt { + source_chain: _, + parsed: _, + bridged_out + } = receipt; + coin::burn_for_testing(bridged_out); + } +} + +#[test_only] +module token_bridge::complete_transfer_with_payload_tests { + use sui::coin::{Self}; + use sui::object::{Self}; + use sui::test_scenario::{Self}; + use wormhole::emitter::{Self}; + use wormhole::state::{chain_id}; + use wormhole::wormhole_scenario::{new_emitter, parse_and_verify_vaa}; + + use token_bridge::coin_wrapped_12::{Self, COIN_WRAPPED_12}; + use token_bridge::complete_transfer_with_payload::{Self}; + use token_bridge::complete_transfer::{Self}; + use token_bridge::coin_native_10::{Self, COIN_NATIVE_10}; + use token_bridge::dummy_message::{Self}; + use token_bridge::native_asset::{Self}; + use token_bridge::state::{Self}; + use token_bridge::token_bridge_scenario::{ + register_dummy_emitter, + return_state, + set_up_wormhole_and_token_bridge, + take_state, + two_people + }; + use token_bridge::token_registry::{Self}; + use token_bridge::transfer_with_payload::{Self}; + use token_bridge::vaa::{Self}; + use token_bridge::wrapped_asset::{Self}; + + #[test] + /// Test the public-facing function authorize_transfer. + /// using a native transfer VAA_ATTESTED_DECIMALS_10. + fun test_complete_transfer_with_payload_native_asset() { + use token_bridge::complete_transfer_with_payload::{ + authorize_transfer, + redeem_coin + }; + + let transfer_vaa = + dummy_message::encoded_transfer_with_payload_vaa_native(); + + let (user, coin_deployer) = two_people(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Initialize Wormhole and Token Bridge. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register Sui as a foreign emitter. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Initialize native token. + let mint_amount = 1000000; + coin_native_10::init_register_and_deposit( + scenario, + coin_deployer, + mint_amount + ); + + // Ignore effects. Begin processing as arbitrary tx executor. + test_scenario::next_tx(scenario, user); + + let token_bridge_state = take_state(scenario); + + { + let asset = token_registry::borrow_native( + state::borrow_token_registry(&token_bridge_state) + ); + assert!(native_asset::custody(asset) == mint_amount, 0); + }; + + // Set up dummy `EmitterCap` as the expected redeemer. + let emitter_cap = emitter::dummy(); + + // Verify that the emitter cap is the expected redeemer. + let expected_transfer = + transfer_with_payload::deserialize( + wormhole::vaa::take_payload( + parse_and_verify_vaa(scenario, transfer_vaa) + ) + ); + assert!( + transfer_with_payload::redeemer_id(&expected_transfer) == object::id(&emitter_cap), + 0 + ); + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. Begin processing as arbitrary tx executor. + test_scenario::next_tx(scenario, user); + + // Execute authorize_transfer. + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + let ( + bridged, + parsed_transfer, + source_chain + ) = redeem_coin(&emitter_cap, receipt); + + assert!(source_chain == expected_source_chain, 0); + + // Assert coin value, source chain, and parsed transfer details are correct. + // We expect the coin value to be 300000, because that's in terms of + // 10 decimals. The amount specified in the VAA_ATTESTED_DECIMALS_12 is 3000, because that's + // in terms of 8 decimals. + let expected_bridged = 300000; + assert!(coin::value(&bridged) == expected_bridged, 0); + + // Amount left on custody should be whatever is left remaining after + // the transfer. + let remaining = mint_amount - expected_bridged; + { + let asset = token_registry::borrow_native( + state::borrow_token_registry(&token_bridge_state) + ); + assert!(native_asset::custody(asset) == remaining, 0); + }; + + // Verify token info. + let registry = state::borrow_token_registry(&token_bridge_state); + let verified = + token_registry::verified_asset(registry); + let expected_token_chain = token_registry::token_chain(&verified); + let expected_token_address = token_registry::token_address(&verified); + assert!(expected_token_chain == chain_id(), 0); + assert!( + transfer_with_payload::token_chain(&parsed_transfer) == expected_token_chain, + 0 + ); + assert!( + transfer_with_payload::token_address(&parsed_transfer) == expected_token_address, + 0 + ); + + // Verify transfer by serializing both parsed and expected. + let serialized = transfer_with_payload::serialize(parsed_transfer); + let expected_serialized = + transfer_with_payload::serialize(expected_transfer); + assert!(serialized == expected_serialized, 0); + + // Clean up. + return_state(token_bridge_state); + coin::burn_for_testing(bridged); + emitter::destroy_test_only(emitter_cap); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + /// Test the public-facing functions `authorize_transfer` and `redeem_coin`. + /// Use an actual devnet Wormhole complete transfer with payload + /// VAA_ATTESTED_DECIMALS_12. + /// + /// This test confirms that: + /// - `authorize_transfer` with `redeem_coin` deserializes the encoded + /// transfer and recovers the source chain, payload, and additional + /// transfer details wrapped in a redeemer receipt. + /// - a wrapped coin with the correct value is minted by the bridge + /// and returned by authorize_transfer + /// + fun test_complete_transfer_with_payload_wrapped_asset() { + use token_bridge::complete_transfer_with_payload::{ + authorize_transfer, + redeem_coin + }; + + let transfer_vaa = + dummy_message::encoded_transfer_with_payload_wrapped_12(); + + let (user, coin_deployer) = two_people(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Initialize Wormhole and Token Bridge. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register chain ID 2 as a foreign emitter. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Register wrapped token. + coin_wrapped_12::init_and_register(scenario, coin_deployer); + + // Ignore effects. Begin processing as arbitrary tx executor. + test_scenario::next_tx(scenario, user); + + let token_bridge_state = take_state(scenario); + + // Set up dummy `EmitterCap` as the expected redeemer. + let emitter_cap = emitter::dummy(); + + // Verify that the emitter cap is the expected redeemer. + let expected_transfer = + transfer_with_payload::deserialize( + wormhole::vaa::take_payload( + parse_and_verify_vaa(scenario, transfer_vaa) + ) + ); + assert!( + transfer_with_payload::redeemer_id(&expected_transfer) == object::id(&emitter_cap), + 0 + ); + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. Begin processing as arbitrary tx executor. + test_scenario::next_tx(scenario, user); + + // Execute authorize_transfer. + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + let ( + bridged, + parsed_transfer, + source_chain + ) = redeem_coin(&emitter_cap, receipt); + assert!(source_chain == expected_source_chain, 0); + + // Assert coin value, source chain, and parsed transfer details are correct. + let expected_bridged = 3000; + assert!(coin::value(&bridged) == expected_bridged, 0); + + // Total supply should equal the amount just minted. + let registry = state::borrow_token_registry(&token_bridge_state); + { + let asset = + token_registry::borrow_wrapped(registry); + assert!(wrapped_asset::total_supply(asset) == expected_bridged, 0); + }; + + // Verify token info. + let verified = + token_registry::verified_asset(registry); + let expected_token_chain = token_registry::token_chain(&verified); + let expected_token_address = token_registry::token_address(&verified); + assert!(expected_token_chain != chain_id(), 0); + assert!( + transfer_with_payload::token_chain(&parsed_transfer) == expected_token_chain, + 0 + ); + assert!( + transfer_with_payload::token_address(&parsed_transfer) == expected_token_address, + 0 + ); + + // Verify transfer by serializing both parsed and expected. + let serialized = transfer_with_payload::serialize(parsed_transfer); + let expected_serialized = + transfer_with_payload::serialize(expected_transfer); + assert!(serialized == expected_serialized, 0); + + // Clean up. + return_state(token_bridge_state); + coin::burn_for_testing(bridged); + emitter::destroy_test_only(emitter_cap); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure( + abort_code = complete_transfer_with_payload::E_INVALID_REDEEMER, + )] + /// Test the public-facing function authorize_transfer. + /// This test fails because the ecmitter_cap (recipient) is incorrect (0x2 instead of 0x3). + /// + fun test_cannot_complete_transfer_with_payload_invalid_redeemer() { + use token_bridge::complete_transfer_with_payload::{ + authorize_transfer, + redeem_coin + }; + + let transfer_vaa = + dummy_message::encoded_transfer_with_payload_wrapped_12(); + + let (user, coin_deployer) = two_people(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Initialize Wormhole and Token Bridge. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register chain ID 2 as a foreign emitter. + register_dummy_emitter(scenario, 2); + + // Register wrapped asset with 12 decimals. + coin_wrapped_12::init_and_register(scenario, coin_deployer); + + // Ignore effects. Begin processing as arbitrary tx executor. + test_scenario::next_tx(scenario, user); + + let token_bridge_state = take_state(scenario); + + let parsed = + transfer_with_payload::deserialize( + wormhole::vaa::take_payload( + parse_and_verify_vaa(scenario, transfer_vaa) + ) + ); + + // Because the vaa expects the dummy emitter as the redeemer, we need + // to generate another emitter. + let emitter_cap = new_emitter(scenario); + + // Ignore effects. Begin processing as arbitrary tx executor. + test_scenario::next_tx(scenario, user); + + assert!( + transfer_with_payload::redeemer_id(&parsed) != object::id(&emitter_cap), + 0 + ); + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. Begin processing as arbitrary tx executor. + test_scenario::next_tx(scenario, user); + + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + // You shall not pass! + let ( + bridged_out, + _, + _ + ) = redeem_coin(&emitter_cap, receipt); + + // Clean up. + coin::burn_for_testing(bridged_out); + + abort 42 + } + + #[test] + #[expected_failure( + abort_code = complete_transfer::E_CANONICAL_TOKEN_INFO_MISMATCH + )] + /// This test demonstrates that the `CoinType` specified for the token + /// redemption must agree with the canonical token info encoded in the VAA_ATTESTED_DECIMALS_12, + /// which is registered with the Token Bridge. + fun test_cannot_complete_transfer_with_payload_wrong_coin_type() { + use token_bridge::complete_transfer_with_payload::{ + authorize_transfer + }; + + let transfer_vaa = + dummy_message::encoded_transfer_with_payload_wrapped_12(); + + let (user, coin_deployer) = two_people(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Initialize Wormhole and Token Bridge. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register chain ID 2 as a foreign emitter. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Register wrapped token. + coin_wrapped_12::init_and_register(scenario, coin_deployer); + + // Also register unexpected token (in this case a native one). + coin_native_10::init_and_register(scenario, coin_deployer); + + // Ignore effects. Begin processing as arbitrary tx executor. + test_scenario::next_tx(scenario, user); + + let token_bridge_state = take_state(scenario); + + let registry = state::borrow_token_registry(&token_bridge_state); + + // Set up dummy `EmitterCap` as the expected redeemer. + let emitter_cap = emitter::dummy(); + + // Verify that the emitter cap is the expected redeemer. + let expected_transfer = + transfer_with_payload::deserialize( + wormhole::vaa::take_payload( + parse_and_verify_vaa(scenario, transfer_vaa) + ) + ); + assert!( + transfer_with_payload::redeemer_id(&expected_transfer) == object::id(&emitter_cap), + 0 + ); + + // Also verify that the encoded token info disagrees with the expected + // token info. + let verified = + token_registry::verified_asset(registry); + let expected_token_chain = token_registry::token_chain(&verified); + let expected_token_address = token_registry::token_address(&verified); + assert!( + transfer_with_payload::token_chain(&expected_transfer) != expected_token_chain, + 0 + ); + assert!( + transfer_with_payload::token_address(&expected_transfer) != expected_token_address, + 0 + ); + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. Begin processing as arbitrary tx executor. + test_scenario::next_tx(scenario, user); + + // You shall not pass! + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + + // Clean up. + return_state(token_bridge_state); + complete_transfer_with_payload::burn(receipt); + emitter::destroy_test_only(emitter_cap); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = complete_transfer::E_TARGET_NOT_SUI)] + /// This test verifies that `complete_transfer` reverts when a transfer is + /// sent to the wrong target blockchain (chain ID != 21). + fun test_cannot_complete_transfer_with_payload_wrapped_asset_invalid_target_chain() { + use token_bridge::complete_transfer_with_payload::{ + authorize_transfer + }; + + let transfer_vaa = + dummy_message::encoded_transfer_with_payload_wrapped_12_invalid_target_chain(); + + let (user, coin_deployer) = two_people(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Initialize Wormhole and Token Bridge. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register chain ID 2 as a foreign emitter. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Register wrapped token. + coin_wrapped_12::init_and_register(scenario, coin_deployer); + + // Ignore effects. Begin processing as arbitrary tx executor. + test_scenario::next_tx(scenario, user); + + let token_bridge_state = take_state(scenario); + + // Set up dummy `EmitterCap` as the expected redeemer. + let emitter_cap = emitter::dummy(); + + // Verify that the emitter cap is the expected redeemer. + let expected_transfer = + transfer_with_payload::deserialize( + wormhole::vaa::take_payload( + parse_and_verify_vaa(scenario, transfer_vaa) + ) + ); + assert!( + transfer_with_payload::redeemer_id(&expected_transfer) == object::id(&emitter_cap), + 0 + ); + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. Begin processing as arbitrary tx executor. + test_scenario::next_tx(scenario, user); + + // You shall not pass! + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + + // Clean up. + complete_transfer_with_payload::burn(receipt); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_complete_transfer_with_payload_outdated_version() { + use token_bridge::complete_transfer_with_payload::{authorize_transfer}; + + let transfer_vaa = + dummy_message::encoded_transfer_with_payload_vaa_native(); + + let (user, coin_deployer) = two_people(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Initialize Wormhole and Token Bridge. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register Sui as a foreign emitter. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Initialize native token. + let mint_amount = 1000000; + coin_native_10::init_register_and_deposit( + scenario, + coin_deployer, + mint_amount + ); + + // Ignore effects. Begin processing as arbitrary tx executor. + test_scenario::next_tx(scenario, user); + + let token_bridge_state = take_state(scenario); + + // Set up dummy `EmitterCap` as the expected redeemer. + let emitter_cap = emitter::dummy(); + + // Verify that the emitter cap is the expected redeemer. + let expected_transfer = + transfer_with_payload::deserialize( + wormhole::vaa::take_payload( + parse_and_verify_vaa(scenario, transfer_vaa) + ) + ); + assert!( + transfer_with_payload::redeemer_id(&expected_transfer) == object::id(&emitter_cap), + 0 + ); + + let verified_vaa = parse_and_verify_vaa(scenario, transfer_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Ignore effects. Begin processing as arbitrary tx executor. + test_scenario::next_tx(scenario, user); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut token_bridge_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut token_bridge_state, + token_bridge::version_control::previous_version_test_only(), + token_bridge::version_control::next_version() + ); + + // You shall not pass! + let receipt = + authorize_transfer( + &mut token_bridge_state, + msg, + test_scenario::ctx(scenario) + ); + + // Clean up. + complete_transfer_with_payload::burn(receipt); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/create_wrapped.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/create_wrapped.move new file mode 100644 index 0000000000..639d170373 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/create_wrapped.move @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements methods that create a specific coin type reflecting a +/// wrapped (foreign) asset, whose metadata is encoded in a VAA sent from +/// another network. +/// +/// Wrapped assets are created in two steps. +/// 1. `prepare_registration`: This method creates a new `TreasuryCap` for a +/// given coin type and wraps an encoded asset metadata VAA. We require a +/// one-time witness (OTW) to throw an explicit error (even though it is +/// redundant with what `create_currency` requires). This coin will +/// be published using this method, meaning the `init` method in that +/// untrusted package will have the asset's decimals hard-coded for its +/// coin metadata. A `WrappedAssetSetup` object is transferred to the +/// transaction sender. +/// 2. `complete_registration`: This method destroys the `WrappedAssetSetup` +/// object by unpacking its `TreasuryCap`, which will be warehoused in the +/// `TokenRegistry`. The shared coin metadata object will be updated to +/// reflect the contents of the encoded asset metadata payload. +/// +/// Wrapped asset metadata can also be updated with a new asset metadata VAA. +/// By calling `update_attestation`, Token Bridge verifies that the specific +/// coin type is registered and agrees with the encoded asset metadata's +/// canonical token info. `ForeignInfo` and the coin's metadata will be updated +/// based on the encoded asset metadata payload. +/// +/// See `state` and `wrapped_asset` modules for more details. +/// +/// References: +/// https://examples.sui.io/basics/one-time-witness.html +module token_bridge::create_wrapped { + use std::ascii::{Self}; + use std::option::{Self}; + use std::type_name::{Self}; + use sui::coin::{Self, TreasuryCap, CoinMetadata}; + use sui::object::{Self, UID}; + use sui::package::{UpgradeCap}; + use sui::transfer::{Self}; + use sui::tx_context::{TxContext}; + + use token_bridge::asset_meta::{Self}; + use token_bridge::normalized_amount::{max_decimals}; + use token_bridge::state::{Self, State}; + use token_bridge::token_registry::{Self}; + use token_bridge::vaa::{Self, TokenBridgeMessage}; + use token_bridge::wrapped_asset::{Self}; + + #[test_only] + use token_bridge::version_control::{Self, V__0_2_0 as V__CURRENT}; + + /// Failed one-time witness verification. + const E_BAD_WITNESS: u64 = 0; + /// Coin witness does not equal "COIN". + const E_INVALID_COIN_MODULE_NAME: u64 = 1; + /// Decimals value exceeds `MAX_DECIMALS` from `normalized_amount`. + const E_DECIMALS_EXCEED_WRAPPED_MAX: u64 = 2; + + /// A.K.A. "coin". + const COIN_MODULE_NAME: vector = b"coin"; + + /// Container holding new coin type's `TreasuryCap` and encoded asset metadata + /// VAA, which are required to complete this asset's registration. + struct WrappedAssetSetup has key, store { + id: UID, + treasury_cap: TreasuryCap + } + + /// This method is executed within the `init` method of an untrusted module, + /// which defines a one-time witness (OTW) type (`CoinType`). OTW is + /// required to ensure that only one `TreasuryCap` exists for `CoinType`. This + /// is similar to how a `TreasuryCap` is created in `coin::create_currency`. + /// + /// Because this method is stateless (i.e. no dependency on Token Bridge's + /// `State` object), the contract defers VAA verification to + /// `complete_registration` after this method has been executed. + public fun prepare_registration( + witness: CoinType, + decimals: u8, + ctx: &mut TxContext + ): WrappedAssetSetup { + let setup = prepare_registration_internal(witness, decimals, ctx); + + // Also make sure that this witness module name is literally "coin". + let module_name = type_name::get_module(&type_name::get()); + assert!( + ascii::into_bytes(module_name) == COIN_MODULE_NAME, + E_INVALID_COIN_MODULE_NAME + ); + + setup + } + + #[allow(lint(share_owned))] + /// This function performs the bulk of `prepare_registration`, except + /// checking the module name. This separation is useful for testing. + fun prepare_registration_internal( + witness: CoinType, + decimals: u8, + ctx: &mut TxContext + ): WrappedAssetSetup { + // Make sure there's only one instance of the type `CoinType`. This + // resembles the same check for `coin::create_currency`. + // Technically this check is redundant as it's performed by + // `coin::create_currency` below, but it doesn't hurt. + assert!(sui::types::is_one_time_witness(&witness), E_BAD_WITNESS); + + // Ensure that the decimals passed into this method do not exceed max + // decimals (see `normalized_amount` module). + assert!(decimals <= max_decimals(), E_DECIMALS_EXCEED_WRAPPED_MAX); + + // We initialise the currency with empty metadata. Later on, in the + // `complete_registration` call, when `CoinType` gets associated with a + // VAA, we update these fields. + let no_symbol = b""; + let no_name = b""; + let no_description = b""; + let no_icon_url = option::none(); + + let (treasury_cap, coin_meta) = + coin::create_currency( + witness, + decimals, + no_symbol, + no_name, + no_description, + no_icon_url, + ctx + ); + + // The CoinMetadata is turned into a shared object so that other + // functions (and wallets) can easily grab references to it. This is + // safe to do, as the metadata setters require a `TreasuryCap` for the + // coin too, which is held by the token bridge. + transfer::public_share_object(coin_meta); + + // Create `WrappedAssetSetup` object and transfer to transaction sender. + // The owner of this object will call `complete_registration` to destroy + // it. + WrappedAssetSetup { + id: object::new(ctx), + treasury_cap + } + } + + /// After executing `prepare_registration`, owner of `WrappedAssetSetup` + /// executes this method to complete this wrapped asset's registration. + /// + /// This method destroys `WrappedAssetSetup`, unpacking the `TreasuryCap` and + /// encoded asset metadata VAA. The deserialized asset metadata VAA is used + /// to update the associated `CoinMetadata`. + public fun complete_registration( + token_bridge_state: &mut State, + coin_meta: &mut CoinMetadata, + setup: WrappedAssetSetup, + coin_upgrade_cap: UpgradeCap, + msg: TokenBridgeMessage + ) { + // This capability ensures that the current build version is used. This + // call performs an additional check of whether `WrappedAssetSetup` was + // created using the current package. + let latest_only = + state::assert_latest_only_specified(token_bridge_state); + + let WrappedAssetSetup { + id, + treasury_cap + } = setup; + + // Finally destroy the object. + object::delete(id); + + // Deserialize to `AssetMeta`. + let token_meta = asset_meta::deserialize(vaa::take_payload(msg)); + + // `register_wrapped_asset` uses `token_registry::add_new_wrapped`, + // which will check whether the asset has already been registered and if + // the token chain ID is not Sui's. + // + // If both of these conditions are met, `register_wrapped_asset` will + // succeed and the new wrapped coin will be registered. + token_registry::add_new_wrapped( + state::borrow_mut_token_registry(&latest_only, token_bridge_state), + token_meta, + coin_meta, + treasury_cap, + coin_upgrade_cap + ); + } + + /// For registered wrapped assets, we can update `ForeignInfo` for a + /// given `CoinType` with a new asset meta VAA emitted from another network. + public fun update_attestation( + token_bridge_state: &mut State, + coin_meta: &mut CoinMetadata, + msg: TokenBridgeMessage + ) { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(token_bridge_state); + + // Deserialize to `AssetMeta`. + let token_meta = asset_meta::deserialize(vaa::take_payload(msg)); + + // This asset must exist in the registry. + let registry = + state::borrow_mut_token_registry(&latest_only, token_bridge_state); + token_registry::assert_has(registry); + + // Now update wrapped. + wrapped_asset::update_metadata( + token_registry::borrow_mut_wrapped(registry), + coin_meta, + token_meta + ); + } + + public fun incomplete_metadata( + coin_meta: &CoinMetadata + ): bool { + use std::string::{bytes}; + use std::vector::{is_empty}; + + ( + is_empty(ascii::as_bytes(&coin::get_symbol(coin_meta))) && + is_empty(bytes(&coin::get_name(coin_meta))) && + is_empty(bytes(&coin::get_description(coin_meta))) && + std::option::is_none(&coin::get_icon_url(coin_meta)) + ) + } + + #[test_only] + public fun new_setup_test_only( + _version: Version, + witness: CoinType, + decimals: u8, + ctx: &mut TxContext + ): (WrappedAssetSetup, UpgradeCap) { + let setup = + prepare_registration_internal( + witness, + decimals, + ctx + ); + + let upgrade_cap = + sui::package::test_publish( + object::id_from_address(@token_bridge), + ctx + ); + + (setup, upgrade_cap) + } + + #[test_only] + public fun new_setup_current( + witness: CoinType, + decimals: u8, + ctx: &mut TxContext + ): (WrappedAssetSetup, UpgradeCap) { + new_setup_test_only( + version_control::current_version_test_only(), + witness, + decimals, + ctx + ) + } + + #[test_only] + public fun take_treasury_cap( + setup: WrappedAssetSetup + ): TreasuryCap { + let WrappedAssetSetup { + id, + treasury_cap + } = setup; + object::delete(id); + + treasury_cap + } +} + +#[test_only] +module token_bridge::create_wrapped_tests { + use sui::coin::{Self}; + use sui::test_scenario::{Self}; + use sui::test_utils::{Self}; + use sui::tx_context::{Self}; + use wormhole::wormhole_scenario::{parse_and_verify_vaa}; + + use token_bridge::asset_meta::{Self}; + use token_bridge::coin_wrapped_12::{Self}; + use token_bridge::coin_wrapped_7::{Self}; + use token_bridge::create_wrapped::{Self}; + use token_bridge::state::{Self}; + use token_bridge::string_utils::{Self}; + use token_bridge::token_bridge_scenario::{ + register_dummy_emitter, + return_state, + set_up_wormhole_and_token_bridge, + take_state, + two_people + }; + use token_bridge::token_registry::{Self}; + use token_bridge::vaa::{Self}; + use token_bridge::version_control::{V__0_2_0 as V__CURRENT}; + use token_bridge::wrapped_asset::{Self}; + + struct NOT_A_WITNESS has drop {} + + struct CREATE_WRAPPED_TESTS has drop {} + + #[test] + #[expected_failure(abort_code = create_wrapped::E_BAD_WITNESS)] + fun test_cannot_prepare_registration_bad_witness() { + let ctx = &mut tx_context::dummy(); + + // You shall not pass! + let wrapped_asset_setup = + create_wrapped::prepare_registration( + NOT_A_WITNESS {}, + 3, + ctx + ); + + // Clean up. + test_utils::destroy(wrapped_asset_setup); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = create_wrapped::E_INVALID_COIN_MODULE_NAME)] + fun test_cannot_prepare_registration_invalid_coin_module_name() { + let ctx = &mut tx_context::dummy(); + + // You shall not pass! + let wrapped_asset_setup = + create_wrapped::prepare_registration< + CREATE_WRAPPED_TESTS, + V__CURRENT + >( + CREATE_WRAPPED_TESTS {}, + 3, + ctx + ); + + // Clean up. + test_utils::destroy(wrapped_asset_setup); + + abort 42 + } + + #[test] + fun test_complete_and_update_attestation() { + let (caller, coin_deployer) = two_people(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Ignore effects. Make sure `coin_deployer` receives + // `WrappedAssetSetup`. + test_scenario::next_tx(scenario, coin_deployer); + + // Publish coin. + let ( + wrapped_asset_setup, + upgrade_cap + ) = + create_wrapped::new_setup_current( + CREATE_WRAPPED_TESTS {}, + 8, + test_scenario::ctx(scenario) + ); + + let token_bridge_state = take_state(scenario); + + let verified_vaa = + parse_and_verify_vaa(scenario, coin_wrapped_12::encoded_vaa()); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + let coin_meta = test_scenario::take_shared(scenario); + + create_wrapped::complete_registration( + &mut token_bridge_state, + &mut coin_meta, + wrapped_asset_setup, + upgrade_cap, + msg + ); + + let ( + token_address, + token_chain, + native_decimals, + symbol, + name + ) = asset_meta::unpack_test_only(coin_wrapped_12::token_meta()); + + // Check registry. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let verified = + token_registry::verified_asset(registry); + assert!(token_registry::is_wrapped(&verified), 0); + + let asset = + token_registry::borrow_wrapped(registry); + assert!(wrapped_asset::total_supply(asset) == 0, 0); + + // Decimals are capped for this wrapped asset. + assert!(coin::get_decimals(&coin_meta) == 8, 0); + + // Check metadata against asset metadata. + let info = wrapped_asset::info(asset); + assert!(wrapped_asset::token_chain(info) == token_chain, 0); + assert!(wrapped_asset::token_address(info) == token_address, 0); + assert!( + wrapped_asset::native_decimals(info) == native_decimals, + 0 + ); + assert!(coin::get_symbol(&coin_meta) == string_utils::to_ascii(&symbol), 0); + assert!(coin::get_name(&coin_meta) == name, 0); + }; + + + // Now update metadata. + let verified_vaa = + parse_and_verify_vaa( + scenario, + coin_wrapped_12::encoded_updated_vaa() + ); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + create_wrapped::update_attestation( + &mut token_bridge_state, + &mut coin_meta, + msg + ); + + // Check updated name and symbol. + let ( + _, + _, + _, + new_symbol, + new_name + ) = asset_meta::unpack_test_only(coin_wrapped_12::updated_token_meta()); + + assert!(symbol != new_symbol, 0); + + assert!(coin::get_symbol(&coin_meta) == string_utils::to_ascii(&new_symbol), 0); + + assert!(name != new_name, 0); + assert!(coin::get_name(&coin_meta) == new_name, 0); + + test_scenario::return_shared(coin_meta); + + // Clean up. + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = wrapped_asset::E_ASSET_META_MISMATCH)] + fun test_cannot_update_attestation_wrong_canonical_info() { + let (caller, coin_deployer) = two_people(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Ignore effects. Make sure `coin_deployer` receives + // `WrappedAssetSetup`. + test_scenario::next_tx(scenario, coin_deployer); + + // Publish coin. + let ( + wrapped_asset_setup, + upgrade_cap + ) = + create_wrapped::new_setup_current( + CREATE_WRAPPED_TESTS {}, + 8, + test_scenario::ctx(scenario) + ); + + let token_bridge_state = take_state(scenario); + + let verified_vaa = + parse_and_verify_vaa(scenario, coin_wrapped_12::encoded_vaa()); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + let coin_meta = test_scenario::take_shared(scenario); + + create_wrapped::complete_registration( + &mut token_bridge_state, + &mut coin_meta, + wrapped_asset_setup, + upgrade_cap, + msg + ); + // This VAA is for COIN_WRAPPED_7 metadata, which disagrees with + // COIN_WRAPPED_12. + let invalid_asset_meta_vaa = coin_wrapped_7::encoded_vaa(); + + let verified_vaa = + parse_and_verify_vaa(scenario, invalid_asset_meta_vaa); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + // You shall not pass! + create_wrapped::update_attestation( + &mut token_bridge_state, + &mut coin_meta, + msg + ); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = state::E_VERSION_MISMATCH)] + fun test_cannot_complete_registration_version_mismatch() { + let (caller, coin_deployer) = two_people(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Ignore effects. Make sure `coin_deployer` receives + // `WrappedAssetSetup`. + test_scenario::next_tx(scenario, coin_deployer); + + // Publish coin. + let ( + wrapped_asset_setup, + upgrade_cap + ) = + create_wrapped::new_setup_test_only( + token_bridge::version_control::dummy(), + CREATE_WRAPPED_TESTS {}, + 8, + test_scenario::ctx(scenario) + ); + + let token_bridge_state = take_state(scenario); + + let verified_vaa = + parse_and_verify_vaa(scenario, coin_wrapped_12::encoded_vaa()); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + let coin_meta = test_scenario::take_shared(scenario); + + create_wrapped::complete_registration( + &mut token_bridge_state, + &mut coin_meta, + wrapped_asset_setup, + upgrade_cap, + msg + ); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_complete_registration_outdated_version() { + let (caller, coin_deployer) = two_people(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Ignore effects. Make sure `coin_deployer` receives + // `WrappedAssetSetup`. + test_scenario::next_tx(scenario, coin_deployer); + + // Publish coin. + let ( + wrapped_asset_setup, + upgrade_cap + ) = + create_wrapped::new_setup_current( + CREATE_WRAPPED_TESTS {}, + 8, + test_scenario::ctx(scenario) + ); + + let token_bridge_state = take_state(scenario); + + let verified_vaa = + parse_and_verify_vaa(scenario, coin_wrapped_12::encoded_vaa()); + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + let coin_meta = test_scenario::take_shared(scenario); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut token_bridge_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut token_bridge_state, + token_bridge::version_control::previous_version_test_only(), + token_bridge::version_control::next_version() + ); + + // You shall not pass! + create_wrapped::complete_registration( + &mut token_bridge_state, + &mut coin_meta, + wrapped_asset_setup, + upgrade_cap, + msg + ); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/datatypes/normalized_amount.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/datatypes/normalized_amount.move new file mode 100644 index 0000000000..e63d3d296b --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/datatypes/normalized_amount.move @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a container that stores the token transfer amount +/// encoded in a Token Bridge message. These amounts are capped at 8 decimals. +/// This means that any amount of a coin whose metadata defines its decimals +/// as some value greater than 8, the encoded amount will be normalized to +/// eight decimals (which will lead to some residual amount after the transfer). +/// For inbound transfers, this amount will be denormalized (scaled by the same +/// decimal difference). +module token_bridge::normalized_amount { + use sui::math::{Self}; + use wormhole::bytes32::{Self}; + use wormhole::cursor::{Cursor}; + + /// The amounts in the token bridge payload are truncated to 8 decimals + /// in each of the contracts when sending tokens out, so there's no + /// precision beyond 10^-8. We could preserve the original number of + /// decimals when creating wrapped assets, and "untruncate" the amounts + /// on the way out by scaling back appropriately. This is what most + /// other chains do, but untruncating from 8 decimals to 18 decimals + /// loses log2(10^10) ~ 33 bits of precision, which we cannot afford on + /// Aptos (and Solana), as the coin type only has 64bits to begin with. + /// Contrast with Ethereum, where amounts are 256 bits. + /// So we cap the maximum decimals at 8 when creating a wrapped token. + const MAX_DECIMALS: u8 = 8; + + /// Container holding the value decoded from a Token Bridge transfer. + struct NormalizedAmount has store, copy, drop { + value: u64 + } + + public fun max_decimals(): u8 { + MAX_DECIMALS + } + + /// Utility function to cap decimal amount to 8. + public fun cap_decimals(decimals: u8): u8 { + if (decimals > MAX_DECIMALS) { + MAX_DECIMALS + } else { + decimals + } + } + + /// Create new `NormalizedAmount` of zero. + public fun default(): NormalizedAmount { + new(0) + } + + /// Retrieve underlying value. + public fun value(self: &NormalizedAmount): u64 { + self.value + } + + /// Retrieve underlying value as `u256`. + public fun to_u256(norm: NormalizedAmount): u256 { + (take_value(norm) as u256) + } + + /// Create new `NormalizedAmount` using raw amount and specified decimals. + public fun from_raw(amount: u64, decimals: u8): NormalizedAmount { + if (amount == 0) { + default() + } else if (decimals > MAX_DECIMALS) { + new(amount / math::pow(10, decimals - MAX_DECIMALS)) + } else { + new(amount) + } + } + + /// Denormalize `NormalizedAmount` using specified decimals. + public fun to_raw(norm: NormalizedAmount, decimals: u8): u64 { + let value = take_value(norm); + + if (value > 0 && decimals > MAX_DECIMALS) { + value * math::pow(10, decimals - MAX_DECIMALS) + } else { + value + } + } + + /// Transform `NormalizedAmount` to serialized (big-endian) u256. + public fun to_bytes(norm: NormalizedAmount): vector { + bytes32::to_bytes(bytes32::from_u256_be(to_u256(norm))) + } + + /// Read 32 bytes from `Cursor` and deserialize to u64, ensuring no + /// overflow. + public fun take_bytes(cur: &mut Cursor): NormalizedAmount { + // Amounts are encoded with 32 bytes. + new(bytes32::to_u64_be(bytes32::take_bytes(cur))) + } + + fun new(value: u64): NormalizedAmount { + NormalizedAmount { + value + } + } + + fun take_value(norm: NormalizedAmount): u64 { + let NormalizedAmount { value } = norm; + value + } +} + +#[test_only] +module token_bridge::normalized_amount_test { + use wormhole::bytes::{Self}; + use wormhole::cursor::{Self}; + + use token_bridge::normalized_amount::{Self}; + + #[test] + fun test_from_and_to_raw() { + // Use decimals > 8 to check truncation. + let decimals = 9; + let raw_amount = 12345678910111; + let normalized = normalized_amount::from_raw(raw_amount, decimals); + let denormalized = normalized_amount::to_raw(normalized, decimals); + assert!(denormalized == 10 * (raw_amount / 10), 0); + + // Use decimals <= 8 to check raw amount recovery. + let decimals = 5; + let normalized = normalized_amount::from_raw(raw_amount, decimals); + let denormalized = normalized_amount::to_raw(normalized, decimals); + assert!(denormalized == raw_amount, 0); + } + + #[test] + fun test_take_bytes() { + let cur = + cursor::new( + x"000000000000000000000000000000000000000000000000ffffffffffffffff" + ); + + let norm = normalized_amount::take_bytes(&mut cur); + assert!( + normalized_amount::value(&norm) == ((1u256 << 64) - 1 as u64), + 0 + ); + + // Clean up. + cursor::destroy_empty(cur); + } + + #[test] + #[expected_failure(abort_code = wormhole::bytes32::E_U64_OVERFLOW)] + fun test_cannot_take_bytes_overflow() { + let encoded_overflow = + x"0000000000000000000000000000000000000000000000010000000000000000"; + + let amount = { + let cur = cursor::new(encoded_overflow); + let value = bytes::take_u256_be(&mut cur); + cursor::destroy_empty(cur); + value + }; + assert!(amount == (1 << 64), 0); + + let cur = cursor::new(encoded_overflow); + + // You shall not pass! + normalized_amount::take_bytes(&mut cur); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/register_chain.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/register_chain.move new file mode 100644 index 0000000000..90af5c1fbe --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/register_chain.move @@ -0,0 +1,297 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements handling a governance VAA to enact registering a +/// foreign Token Bridge for a particular chain ID. +module token_bridge::register_chain { + use sui::table::{Self}; + use wormhole::bytes::{Self}; + use wormhole::cursor::{Self}; + use wormhole::external_address::{Self, ExternalAddress}; + use wormhole::governance_message::{Self, DecreeTicket, DecreeReceipt}; + + use token_bridge::state::{Self, State, LatestOnly}; + + /// Cannot register chain ID == 0. + const E_INVALID_EMITTER_CHAIN: u64 = 0; + /// Emitter already exists for a given chain ID. + const E_EMITTER_ALREADY_REGISTERED: u64 = 1; + + /// Specific governance payload ID (action) for registering foreign Token + /// Bridge contract address. + const ACTION_REGISTER_CHAIN: u8 = 1; + + struct GovernanceWitness has drop {} + + struct RegisterChain { + chain: u16, + contract_address: ExternalAddress, + } + + public fun authorize_governance( + token_bridge_state: &State + ): DecreeTicket { + governance_message::authorize_verify_global( + GovernanceWitness {}, + state::governance_chain(token_bridge_state), + state::governance_contract(token_bridge_state), + state::governance_module(), + ACTION_REGISTER_CHAIN + ) + } + + public fun register_chain( + token_bridge_state: &mut State, + receipt: DecreeReceipt + ): ( + u16, + ExternalAddress + ) { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(token_bridge_state); + + let payload = + governance_message::take_payload( + state::borrow_mut_consumed_vaas( + &latest_only, + token_bridge_state + ), + receipt + ); + + handle_register_chain(&latest_only, token_bridge_state, payload) + } + + fun handle_register_chain( + latest_only: &LatestOnly, + token_bridge_state: &mut State, + governance_payload: vector + ): ( + u16, + ExternalAddress + ) { + // Deserialize the payload as amount to change the Wormhole fee. + let RegisterChain { + chain, + contract_address + } = deserialize(governance_payload); + + register_new_emitter( + latest_only, + token_bridge_state, + chain, + contract_address + ); + + (chain, contract_address) + } + + fun deserialize(payload: vector): RegisterChain { + let cur = cursor::new(payload); + + // This amount cannot be greater than max u64. + let chain = bytes::take_u16_be(&mut cur); + let contract_address = external_address::take_bytes(&mut cur); + + cursor::destroy_empty(cur); + + RegisterChain { chain, contract_address} + } + + /// Add a new Token Bridge emitter to the registry. This method will abort + /// if an emitter is already registered for a particular chain ID. + /// + /// See `register_chain` module for more info. + fun register_new_emitter( + latest_only: &LatestOnly, + token_bridge_state: &mut State, + chain: u16, + contract_address: ExternalAddress + ) { + assert!(chain != 0, E_INVALID_EMITTER_CHAIN); + + let registry = + state::borrow_mut_emitter_registry(latest_only, token_bridge_state); + assert!( + !table::contains(registry, chain), + E_EMITTER_ALREADY_REGISTERED + ); + table::add(registry, chain, contract_address); + } + + #[test_only] + public fun register_new_emitter_test_only( + token_bridge_state: &mut State, + chain: u16, + contract_address: ExternalAddress + ) { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(token_bridge_state); + + register_new_emitter( + &latest_only, + token_bridge_state, + chain, + contract_address + ); + } + + #[test_only] + public fun action(): u8 { + ACTION_REGISTER_CHAIN + } +} + +#[test_only] +module token_bridge::register_chain_tests { + use sui::table::{Self}; + use sui::test_scenario::{Self}; + use wormhole::bytes::{Self}; + use wormhole::cursor::{Self}; + use wormhole::external_address::{Self}; + use wormhole::governance_message::{Self}; + use wormhole::wormhole_scenario::{ + parse_and_verify_vaa, + verify_governance_vaa + }; + + use token_bridge::register_chain::{Self}; + use token_bridge::state::{Self}; + use token_bridge::token_bridge_scenario::{ + person, + return_state, + set_up_wormhole_and_token_bridge, + take_state + }; + + const VAA_REGISTER_CHAIN_1: vector = + x"01000000000100dd8cf046ad6dd17b2b5130d236b3545350899ac33b5c9e93e4d8c3e0da718a351c3f76cb9ddb15a0f0d7db7b1dded2b5e79c2f6e76dde6d8ed4bcb9cb461eb480100bc614e0000000000010000000000000000000000000000000000000000000000000000000000000004000000000000000101000000000000000000000000000000000000000000546f6b656e4272696467650100000002000000000000000000000000deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + const VAA_REGISTER_SAME_CHAIN: vector = + x"01000000000100847ca782db7616135de4a835ed5b12ba7946bbd39f70ecd9912ec55bdc9cb6c6215c98d6ad5c8d7253c2bb0fb0f8df0dc6591408c366cf0c09e58abcfb8c0abe0000bc614e0000000000010000000000000000000000000000000000000000000000000000000000000004000000000000000101000000000000000000000000000000000000000000546f6b656e427269646765010000000200000000000000000000000000000000000000000000000000000000deafbeef"; + + #[test] + fun test_register_chain() { + // Testing this method. + use token_bridge::register_chain::{register_chain}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Initialize Wormhole and Token Bridge. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Prepare test to execute `set_fee`. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + + // Check that the emitter is not registered. + let expected_chain = 2; + { + let registry = state::borrow_emitter_registry(&token_bridge_state); + assert!(!table::contains(registry, expected_chain), 0); + }; + + let verified_vaa = parse_and_verify_vaa(scenario, VAA_REGISTER_CHAIN_1); + let ticket = register_chain::authorize_governance(&token_bridge_state); + let receipt = + verify_governance_vaa(scenario, verified_vaa, ticket); + let ( + chain, + contract_address + ) = register_chain(&mut token_bridge_state, receipt); + assert!(chain == expected_chain, 0); + + let expected_contract = + external_address::from_address( + @0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef + ); + assert!(contract_address == expected_contract, 0); + { + let registry = state::borrow_emitter_registry(&token_bridge_state); + assert!(*table::borrow(registry, expected_chain) == expected_contract, 0); + }; + + // Clean up. + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = register_chain::E_EMITTER_ALREADY_REGISTERED)] + fun test_cannot_register_chain_already_registered() { + // Testing this method. + use token_bridge::register_chain::{register_chain}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Initialize Wormhole and Token Bridge. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Prepare test to execute `set_fee`. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + + let verified_vaa = parse_and_verify_vaa(scenario, VAA_REGISTER_CHAIN_1); + let ticket = register_chain::authorize_governance(&token_bridge_state); + let receipt = + verify_governance_vaa(scenario, verified_vaa, ticket); + let ( + chain, + _ + ) = register_chain(&mut token_bridge_state, receipt); + + // Check registry. + let expected_contract = + *table::borrow( + state::borrow_emitter_registry(&token_bridge_state), + chain + ); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let verified_vaa = + parse_and_verify_vaa(scenario, VAA_REGISTER_SAME_CHAIN); + let payload = + governance_message::take_decree( + wormhole::vaa::payload(&verified_vaa) + ); + let cur = cursor::new(payload); + + // Show this payload is attempting to register the same chain ID. + let another_chain = bytes::take_u16_be(&mut cur); + assert!(chain == another_chain, 0); + + let another_contract = external_address::take_bytes(&mut cur); + assert!(another_contract != expected_contract, 0); + + // No more payload to read. + cursor::destroy_empty(cur); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let ticket = register_chain::authorize_governance(&token_bridge_state); + let receipt = + verify_governance_vaa(scenario, verified_vaa, ticket); + + // You shall not pass! + register_chain(&mut token_bridge_state, receipt); + + abort 42 + } +} + + + + diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/upgrade_contract.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/upgrade_contract.move new file mode 100644 index 0000000000..e03729b021 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/upgrade_contract.move @@ -0,0 +1,125 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements handling a governance VAA to enact upgrading the +/// Token Bridge contract to a new build. The procedure to upgrade this contract +/// requires a Programmable Transaction, which includes the following procedure: +/// 1. Load new build. +/// 2. Authorize upgrade. +/// 3. Upgrade. +/// 4. Commit upgrade. +module token_bridge::upgrade_contract { + use sui::object::{ID}; + use sui::package::{UpgradeReceipt, UpgradeTicket}; + use wormhole::bytes32::{Self, Bytes32}; + use wormhole::cursor::{Self}; + use wormhole::governance_message::{Self, DecreeTicket, DecreeReceipt}; + + use token_bridge::state::{Self, State}; + + friend token_bridge::migrate; + + /// Digest is all zeros. + const E_DIGEST_ZERO_BYTES: u64 = 0; + + /// Specific governance payload ID (action) to complete upgrading the + /// contract. + const ACTION_UPGRADE_CONTRACT: u8 = 2; + + struct GovernanceWitness has drop {} + + // Event reflecting package upgrade. + struct ContractUpgraded has drop, copy { + old_contract: ID, + new_contract: ID + } + + struct UpgradeContract { + digest: Bytes32 + } + + public fun authorize_governance( + token_bridge_state: &State + ): DecreeTicket { + governance_message::authorize_verify_local( + GovernanceWitness {}, + state::governance_chain(token_bridge_state), + state::governance_contract(token_bridge_state), + state::governance_module(), + ACTION_UPGRADE_CONTRACT + ) + } + + /// Redeem governance VAA to issue an `UpgradeTicket` for the upgrade given + /// a contract upgrade VAA. This governance message is only relevant for Sui + /// because a contract upgrade is only relevant to one particular network + /// (in this case Sui), whose build digest is encoded in this message. + public fun authorize_upgrade( + token_bridge_state: &mut State, + receipt: DecreeReceipt + ): UpgradeTicket { + // current package checking when consuming VAA hashes. This is because + // upgrades are protected by the Sui VM, enforcing the latest package + // is the one performing the upgrade. + let consumed = + state::borrow_mut_consumed_vaas_unchecked(token_bridge_state); + + // And consume. + let payload = governance_message::take_payload(consumed, receipt); + + // Proceed with processing new implementation version. + handle_upgrade_contract(token_bridge_state, payload) + } + + /// Finalize the upgrade that ran to produce the given `receipt`. This + /// method invokes `state::commit_upgrade` which interacts with + /// `sui::package`. + public fun commit_upgrade( + self: &mut State, + receipt: UpgradeReceipt, + ) { + let (old_contract, new_contract) = state::commit_upgrade(self, receipt); + + // Emit an event reflecting package ID change. + sui::event::emit(ContractUpgraded { old_contract, new_contract }); + } + + /// Privileged method only to be used by this module and `migrate` module. + /// + /// During migration, we make sure that the digest equals what we expect by + /// passing in the same VAA used to upgrade the package. + public(friend) fun take_digest(governance_payload: vector): Bytes32 { + // Deserialize the payload as the build digest. + let UpgradeContract { digest } = deserialize(governance_payload); + + digest + } + + fun handle_upgrade_contract( + wormhole_state: &mut State, + payload: vector + ): UpgradeTicket { + state::authorize_upgrade(wormhole_state, take_digest(payload)) + } + + fun deserialize(payload: vector): UpgradeContract { + let cur = cursor::new(payload); + + // This amount cannot be greater than max u64. + let digest = bytes32::take_bytes(&mut cur); + assert!(bytes32::is_nonzero(&digest), E_DIGEST_ZERO_BYTES); + + cursor::destroy_empty(cur); + + UpgradeContract { digest } + } + + #[test_only] + public fun action(): u8 { + ACTION_UPGRADE_CONTRACT + } +} + +#[test_only] +module token_bridge::upgrade_contract_tests { + // TODO +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/asset_meta.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/asset_meta.move new file mode 100644 index 0000000000..30f03f2cde --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/asset_meta.move @@ -0,0 +1,251 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements serialization and deserialization for asset metadata, +/// which is a specific Wormhole message payload for Token Bridge. +module token_bridge::asset_meta { + use std::string::{Self, String}; + use std::vector::{Self}; + use sui::coin::{Self, CoinMetadata}; + use wormhole::bytes::{Self}; + use wormhole::bytes32::{Self}; + use wormhole::external_address::{Self, ExternalAddress}; + use wormhole::cursor::{Self}; + use wormhole::state::{chain_id}; + + use token_bridge::native_asset::{Self}; + + friend token_bridge::attest_token; + friend token_bridge::create_wrapped; + friend token_bridge::wrapped_asset; + + /// Message payload is not `AssetMeta`. + const E_INVALID_PAYLOAD: u64 = 0; + + /// Message identifier. + const PAYLOAD_ID: u8 = 2; + + /// Container that warehouses asset metadata information. This struct is + /// used only by `attest_token` and `create_wrapped` modules. + struct AssetMeta { + /// Address of the token. + token_address: ExternalAddress, + /// Chain ID of the token. + token_chain: u16, + /// Number of decimals of the token. + native_decimals: u8, + /// Symbol of the token (UTF-8). + /// TODO(csongor): maybe turn these into String32s? + symbol: String, + /// Name of the token (UTF-8). + name: String, + } + + + public(friend) fun from_metadata(metadata: &CoinMetadata): AssetMeta { + AssetMeta { + token_address: native_asset::canonical_address(metadata), + token_chain: chain_id(), + native_decimals: coin::get_decimals(metadata), + symbol: string::from_ascii(coin::get_symbol(metadata)), + name: coin::get_name(metadata) + } + } + + #[test_only] + public fun from_metadata_test_only(metadata: &CoinMetadata): AssetMeta { + from_metadata(metadata) + } + + public(friend) fun unpack( + meta: AssetMeta + ): ( + ExternalAddress, + u16, + u8, + String, + String + ) { + let AssetMeta { + token_address, + token_chain, + native_decimals, + symbol, + name + } = meta; + + ( + token_address, + token_chain, + native_decimals, + symbol, + name + ) + } + + + #[test_only] + public fun unpack_test_only( + meta: AssetMeta + ): ( + ExternalAddress, + u16, + u8, + String, + String + ) { + unpack(meta) + } + + public fun token_chain(self: &AssetMeta): u16 { + self.token_chain + } + + public fun token_address(self: &AssetMeta): ExternalAddress { + self.token_address + } + + public(friend) fun serialize(meta: AssetMeta): vector { + let ( + token_address, + token_chain, + native_decimals, + symbol, + name + ) = unpack(meta); + + let buf = vector::empty(); + bytes::push_u8(&mut buf, PAYLOAD_ID); + vector::append(&mut buf, external_address::to_bytes(token_address)); + bytes::push_u16_be(&mut buf, token_chain); + bytes::push_u8(&mut buf, native_decimals); + vector::append( + &mut buf, + bytes32::to_bytes(bytes32::from_utf8(symbol)) + ); + vector::append( + &mut buf, + bytes32::to_bytes(bytes32::from_utf8(name)) + ); + + buf + } + + #[test_only] + public fun serialize_test_only(meta: AssetMeta): vector { + serialize(meta) + } + + public(friend) fun deserialize(buf: vector): AssetMeta { + let cur = cursor::new(buf); + assert!(bytes::take_u8(&mut cur) == PAYLOAD_ID, E_INVALID_PAYLOAD); + let token_address = external_address::take_bytes(&mut cur); + let token_chain = bytes::take_u16_be(&mut cur); + let native_decimals = bytes::take_u8(&mut cur); + let symbol = bytes32::to_utf8(bytes32::take_bytes(&mut cur)); + let name = bytes32::to_utf8(bytes32::take_bytes(&mut cur)); + cursor::destroy_empty(cur); + + AssetMeta { + token_address, + token_chain, + native_decimals, + symbol, + name + } + } + + #[test_only] + public fun deserialize_test_only(buf: vector): AssetMeta { + deserialize(buf) + } + + #[test_only] + public fun new( + token_address: ExternalAddress, + token_chain: u16, + native_decimals: u8, + symbol: String, + name: String, + ): AssetMeta { + AssetMeta { + token_address, + token_chain, + native_decimals, + symbol, + name + } + } + + #[test_only] + public fun native_decimals(self: &AssetMeta): u8 { + self.native_decimals + } + + #[test_only] + public fun symbol(self: &AssetMeta): String { + self.symbol + } + + #[test_only] + public fun name(self: &AssetMeta): String { + self.name + } + + #[test_only] + public fun destroy(token_meta: AssetMeta) { + unpack(token_meta); + } + + #[test_only] + public fun payload_id(): u8 { + PAYLOAD_ID + } +} + +#[test_only] +module token_bridge::asset_meta_tests { + use std::string::{Self}; + use wormhole::external_address::{Self}; + use wormhole::vaa::{Self}; + + use token_bridge::asset_meta::{Self}; + + #[test] + fun test_serialize_deserialize() { + let token_address = external_address::from_address(@0x1122); + let symbol = string::utf8(b"a creative symbol"); + let name = string::utf8(b"a creative name"); + let asset_meta = asset_meta::new( + token_address, //token address + 3, // token chain + 4, //native decimals + symbol, // symbol + name, // name + ); + // Serialize and deserialize TransferWithPayload object. + let se = asset_meta::serialize_test_only(asset_meta); + let de = asset_meta::deserialize_test_only(se); + + // Test that the object fields are unchanged. + assert!(asset_meta::token_chain(&de) == 3, 0); + assert!(asset_meta::token_address(&de) == token_address, 0); + assert!(asset_meta::native_decimals(&de) == 4, 0); + assert!(asset_meta::symbol(&de) == symbol, 0); + assert!(asset_meta::name(&de) == name, 0); + + // Clean up. + asset_meta::destroy(de); + } + + #[test] + fun test_create_wrapped_12() { + use token_bridge::dummy_message::{encoded_asset_meta_vaa_foreign_12}; + + let payload = + vaa::peel_payload_from_vaa(&encoded_asset_meta_vaa_foreign_12()); + + let token_meta = asset_meta::deserialize_test_only(payload); + let serialized = asset_meta::serialize_test_only(token_meta); + assert!(payload == serialized, 0); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer.move new file mode 100644 index 0000000000..190afad9dc --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer.move @@ -0,0 +1,311 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements serialization and deserialization for token transfer +/// with an optional relayer fee. This message is a specific Wormhole message +/// payload for Token Bridge. +/// +/// When this transfer is redeemed, the relayer fee will be subtracted from the +/// transfer amount. If the transaction sender is the same address of the +/// recipient, the recipient will collect the full amount. +/// +/// See `transfer_tokens` and `complete_transfer` modules for more details. +module token_bridge::transfer { + use std::vector::{Self}; + use wormhole::bytes::{Self}; + use wormhole::cursor::{Self}; + use wormhole::external_address::{Self, ExternalAddress}; + + use token_bridge::normalized_amount::{Self, NormalizedAmount}; + + friend token_bridge::complete_transfer; + friend token_bridge::transfer_tokens; + + /// Message payload is not `Transfer`. + const E_INVALID_PAYLOAD: u64 = 0; + + /// Message identifier. + const PAYLOAD_ID: u8 = 1; + + /// Container that warehouses transfer information. This struct is used only + /// by `transfer_tokens` and `complete_transfer` modules. + struct Transfer { + // Amount being transferred. + amount: NormalizedAmount, + // Address of the token. Left-zero-padded if shorter than 32 bytes. + token_address: ExternalAddress, + // Chain ID of the token. + token_chain: u16, + // Address of the recipient. Left-zero-padded if shorter than 32 bytes. + recipient: ExternalAddress, + // Chain ID of the recipient. + recipient_chain: u16, + // Amount of tokens that the user is willing to pay as relayer fee. + // Must be <= amount. + relayer_fee: NormalizedAmount, + } + + /// Create new `Transfer`. + public(friend) fun new( + amount: NormalizedAmount, + token_address: ExternalAddress, + token_chain: u16, + recipient: ExternalAddress, + recipient_chain: u16, + relayer_fee: NormalizedAmount, + ): Transfer { + Transfer { + amount, + token_address, + token_chain, + recipient, + recipient_chain, + relayer_fee, + } + } + + #[test_only] + public fun new_test_only( + amount: NormalizedAmount, + token_address: ExternalAddress, + token_chain: u16, + recipient: ExternalAddress, + recipient_chain: u16, + relayer_fee: NormalizedAmount, + ): Transfer { + new( + amount, + token_address, + token_chain, + recipient, + recipient_chain, + relayer_fee + ) + } + + /// Decompose `Transfer` into its members. + public(friend) fun unpack( + transfer: Transfer + ): ( + NormalizedAmount, + ExternalAddress, + u16, + ExternalAddress, + u16, + NormalizedAmount + ) { + let Transfer { + amount, + token_address, + token_chain, + recipient, + recipient_chain, + relayer_fee, + } = transfer; + + ( + amount, + token_address, + token_chain, + recipient, + recipient_chain, + relayer_fee + ) + } + + #[test_only] + public fun unpack_test_only( + transfer: Transfer + ): ( + NormalizedAmount, + ExternalAddress, + u16, + ExternalAddress, + u16, + NormalizedAmount + ) { + unpack(transfer) + } + + /// Decode Wormhole message payload as `Transfer`. + public(friend) fun deserialize(buf: vector): Transfer { + let cur = cursor::new(buf); + assert!(bytes::take_u8(&mut cur) == PAYLOAD_ID, E_INVALID_PAYLOAD); + + let amount = normalized_amount::take_bytes(&mut cur); + let token_address = external_address::take_bytes(&mut cur); + let token_chain = bytes::take_u16_be(&mut cur); + let recipient = external_address::take_bytes(&mut cur); + let recipient_chain = bytes::take_u16_be(&mut cur); + let relayer_fee = normalized_amount::take_bytes(&mut cur); + cursor::destroy_empty(cur); + + Transfer { + amount, + token_address, + token_chain, + recipient, + recipient_chain, + relayer_fee, + } + } + + #[test_only] + public fun deserialize_test_only(buf: vector): Transfer { + deserialize(buf) + } + + /// Encode `Transfer` for Wormhole message payload. + public(friend) fun serialize(transfer: Transfer): vector { + let ( + amount, + token_address, + token_chain, + recipient, + recipient_chain, + relayer_fee, + ) = unpack(transfer); + + let buf = vector::empty(); + bytes::push_u8(&mut buf, PAYLOAD_ID); + vector::append(&mut buf, normalized_amount::to_bytes(amount)); + vector::append(&mut buf, external_address::to_bytes(token_address)); + bytes::push_u16_be(&mut buf, token_chain); + vector::append(&mut buf, external_address::to_bytes(recipient)); + bytes::push_u16_be(&mut buf, recipient_chain); + vector::append(&mut buf, normalized_amount::to_bytes(relayer_fee)); + + buf + } + + #[test_only] + public fun serialize_test_only(transfer: Transfer): vector { + serialize(transfer) + } + + #[test_only] + public fun amount(self: &Transfer): NormalizedAmount { + self.amount + } + + #[test_only] + public fun raw_amount(self: &Transfer, decimals: u8): u64 { + normalized_amount::to_raw(self.amount, decimals) + } + + #[test_only] + public fun token_address(self: &Transfer): ExternalAddress { + self.token_address + } + + #[test_only] + public fun token_chain(self: &Transfer): u16 { + self.token_chain + } + + #[test_only] + public fun recipient(self: &Transfer): ExternalAddress { + self.recipient + } + + #[test_only] + public fun recipient_as_address(self: &Transfer): address { + external_address::to_address(self.recipient) + } + + #[test_only] + public fun recipient_chain(self: &Transfer): u16 { + self.recipient_chain + } + + #[test_only] + public fun relayer_fee(self: &Transfer): NormalizedAmount { + self.relayer_fee + } + + #[test_only] + public fun raw_relayer_fee(self: &Transfer, decimals: u8): u64 { + normalized_amount::to_raw(self.relayer_fee, decimals) + } + + #[test_only] + public fun destroy(transfer: Transfer) { + unpack(transfer); + } + + #[test_only] + public fun payload_id(): u8 { + PAYLOAD_ID + } +} + +#[test_only] +module token_bridge::transfer_tests { + use std::vector::{Self}; + use wormhole::external_address::{Self}; + + use token_bridge::dummy_message::{Self}; + use token_bridge::transfer::{Self}; + use token_bridge::normalized_amount::{Self}; + + #[test] + fun test_serialize_deserialize() { + let decimals = 8; + let expected_amount = normalized_amount::from_raw(234567890, decimals); + let expected_token_address = external_address::from_address(@0xbeef); + let expected_token_chain = 1; + let expected_recipient = external_address::from_address(@0xcafe); + let expected_recipient_chain = 7; + let expected_relayer_fee = + normalized_amount::from_raw(123456789, decimals); + + let serialized = + transfer::serialize_test_only( + transfer::new_test_only( + expected_amount, + expected_token_address, + expected_token_chain, + expected_recipient, + expected_recipient_chain, + expected_relayer_fee, + ) + ); + assert!(serialized == dummy_message::encoded_transfer(), 0); + + let ( + amount, + token_address, + token_chain, + recipient, + recipient_chain, + relayer_fee + ) = transfer::unpack_test_only( + transfer::deserialize_test_only(serialized) + ); + assert!(amount == expected_amount, 0); + assert!(token_address == expected_token_address, 0); + assert!(token_chain == expected_token_chain, 0); + assert!(recipient == expected_recipient, 0); + assert!(recipient_chain == expected_recipient_chain, 0); + assert!(relayer_fee == expected_relayer_fee, 0); + } + + #[test] + #[expected_failure(abort_code = transfer::E_INVALID_PAYLOAD)] + fun test_cannot_deserialize_invalid_payload() { + let invalid_payload = dummy_message::encoded_transfer_with_payload(); + + // Show that the first byte is not the expected payload ID. + assert!( + *vector::borrow(&invalid_payload, 0) != transfer::payload_id(), + 0 + ); + + // You shall not pass! + let parsed = transfer::deserialize_test_only(invalid_payload); + + // Clean up. + transfer::destroy(parsed); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer_with_payload.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer_with_payload.move new file mode 100644 index 0000000000..3180616872 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer_with_payload.move @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements serialization and deserialization for token transfer +/// with an arbitrary payload. This message is a specific Wormhole message +/// payload for Token Bridge. +/// +/// In order to redeem these types of transfers, one must have an `EmitterCap` +/// and the specified `redeemer` must agree with this capability. +/// +/// See `transfer_tokens_with_payload` and `complete_transfer_with_payload` +/// modules for more details. +module token_bridge::transfer_with_payload { + use std::vector::{Self}; + use sui::object::{Self, ID}; + use wormhole::bytes::{Self}; + use wormhole::cursor::{Self}; + use wormhole::external_address::{Self, ExternalAddress}; + + use token_bridge::normalized_amount::{Self, NormalizedAmount}; + + friend token_bridge::transfer_tokens_with_payload; + + /// Message payload is not `TransferWithPayload`. + const E_INVALID_PAYLOAD: u64 = 0; + + /// Message identifier. + const PAYLOAD_ID: u8 = 3; + + /// Container that warehouses transfer information, including arbitrary + /// payload. + /// + /// NOTE: This struct has `drop` because we do not want to require an + /// integrator receiving transfer information to have to manually destroy. + struct TransferWithPayload has drop { + // Transfer amount. + amount: NormalizedAmount, + // Address of the token. Left-zero-padded if shorter than 32 bytes. + token_address: ExternalAddress, + // Chain ID of the token. + token_chain: u16, + // A.K.A. 32-byte representation of `EmitterCap`. + redeemer: ExternalAddress, + // Chain ID of the redeemer. + redeemer_chain: u16, + // Address of the message sender. + sender: ExternalAddress, + // An arbitrary payload. + payload: vector, + } + + /// Create new `TransferWithPayload` using a Token Bridge integrator's + /// emitter cap ID as the sender. + public(friend) fun new( + sender: ID, + amount: NormalizedAmount, + token_address: ExternalAddress, + token_chain: u16, + redeemer: ExternalAddress, + redeemer_chain: u16, + payload: vector + ): TransferWithPayload { + TransferWithPayload { + amount, + token_address, + token_chain, + redeemer, + redeemer_chain, + sender: external_address::from_id(sender), + payload + } + } + + #[test_only] + public fun new_test_only( + sender: ID, + amount: NormalizedAmount, + token_address: ExternalAddress, + token_chain: u16, + redeemer: ExternalAddress, + redeemer_chain: u16, + payload: vector + ): TransferWithPayload { + new( + sender, + amount, + token_address, + token_chain, + redeemer, + redeemer_chain, + payload + ) + } + + /// Destroy `TransferWithPayload` and take only its payload. + public fun take_payload(transfer: TransferWithPayload): vector { + let TransferWithPayload { + amount: _, + token_address: _, + token_chain: _, + redeemer: _, + redeemer_chain: _, + sender: _, + payload + } = transfer; + + payload + } + + /// Retrieve normalized amount of token transfer. + public fun amount(self: &TransferWithPayload): NormalizedAmount { + self.amount + } + + // Retrieve token's canonical address. + public fun token_address(self: &TransferWithPayload): ExternalAddress { + self.token_address + } + + /// Retrieve token's canonical chain ID. + public fun token_chain(self: &TransferWithPayload): u16 { + self.token_chain + } + + /// Retrieve redeemer. + public fun redeemer(self: &TransferWithPayload): ExternalAddress { + self.redeemer + } + + // Retrieve redeemer as `ID`. + public fun redeemer_id(self: &TransferWithPayload): ID { + object::id_from_bytes(external_address::to_bytes(self.redeemer)) + } + + /// Retrieve target chain for redeemer. + public fun redeemer_chain(self: &TransferWithPayload): u16 { + self.redeemer_chain + } + + /// Retrieve transfer sender. + public fun sender(self: &TransferWithPayload): ExternalAddress { + self.sender + } + + /// Retrieve arbitrary payload. + public fun payload(self: &TransferWithPayload): vector { + self.payload + } + + /// Decode Wormhole message payload as `TransferWithPayload`. + public fun deserialize(transfer: vector): TransferWithPayload { + let cur = cursor::new(transfer); + assert!(bytes::take_u8(&mut cur) == PAYLOAD_ID, E_INVALID_PAYLOAD); + + let amount = normalized_amount::take_bytes(&mut cur); + let token_address = external_address::take_bytes(&mut cur); + let token_chain = bytes::take_u16_be(&mut cur); + let redeemer = external_address::take_bytes(&mut cur); + let redeemer_chain = bytes::take_u16_be(&mut cur); + let sender = external_address::take_bytes(&mut cur); + + TransferWithPayload { + amount, + token_address, + token_chain, + redeemer, + redeemer_chain, + sender, + payload: cursor::take_rest(cur) + } + } + + /// Encode `TransferWithPayload` for Wormhole message payload. + public fun serialize(transfer: TransferWithPayload): vector { + let TransferWithPayload { + amount, + token_address, + token_chain, + redeemer, + redeemer_chain, + sender, + payload + } = transfer; + + let buf = vector::empty(); + bytes::push_u8(&mut buf, PAYLOAD_ID); + bytes::push_u256_be(&mut buf, normalized_amount::to_u256(amount)); + vector::append(&mut buf, external_address::to_bytes(token_address)); + bytes::push_u16_be(&mut buf, token_chain); + vector::append(&mut buf, external_address::to_bytes(redeemer)); + bytes::push_u16_be(&mut buf, redeemer_chain); + vector::append(&mut buf, external_address::to_bytes(sender)); + vector::append(&mut buf, payload); + + buf + } + + #[test_only] + public fun destroy(transfer: TransferWithPayload) { + take_payload(transfer); + } + + #[test_only] + public fun payload_id(): u8 { + PAYLOAD_ID + } +} + +#[test_only] +module token_bridge::transfer_with_payload_tests { + use std::vector::{Self}; + use sui::object::{Self}; + use wormhole::emitter::{Self}; + use wormhole::external_address::{Self}; + + use token_bridge::dummy_message::{Self}; + use token_bridge::normalized_amount::{Self}; + use token_bridge::transfer_with_payload::{Self}; + + #[test] + fun test_serialize() { + let emitter_cap = emitter::dummy(); + let amount = normalized_amount::from_raw(234567890, 8); + let token_address = external_address::from_address(@0xbeef); + let token_chain = 1; + let redeemer = external_address::from_address(@0xcafe); + let redeemer_chain = 7; + let payload = b"All your base are belong to us."; + + let new_transfer = + transfer_with_payload::new_test_only( + object::id(&emitter_cap), + amount, + token_address, + token_chain, + redeemer, + redeemer_chain, + payload + ); + + // Verify getters. + assert!( + transfer_with_payload::amount(&new_transfer) == amount, + 0 + ); + assert!( + transfer_with_payload::token_address(&new_transfer) == token_address, + 0 + ); + assert!( + transfer_with_payload::token_chain(&new_transfer) == token_chain, + 0 + ); + assert!( + transfer_with_payload::redeemer(&new_transfer) == redeemer, + 0 + ); + assert!( + transfer_with_payload::redeemer_chain(&new_transfer) == redeemer_chain, + 0 + ); + let expected_sender = + external_address::from_id(object::id(&emitter_cap)); + assert!( + transfer_with_payload::sender(&new_transfer) == expected_sender, + 0 + ); + assert!( + transfer_with_payload::payload(&new_transfer) == payload, + 0 + ); + + let serialized = transfer_with_payload::serialize(new_transfer); + let expected_serialized = + dummy_message::encoded_transfer_with_payload(); + assert!(serialized == expected_serialized, 0); + + // Clean up. + emitter::destroy_test_only(emitter_cap); + } + + #[test] + fun test_deserialize() { + let expected_amount = normalized_amount::from_raw(234567890, 8); + let expected_token_address = external_address::from_address(@0xbeef); + let expected_token_chain = 1; + let expected_recipient = external_address::from_address(@0xcafe); + let expected_recipient_chain = 7; + let expected_sender = + external_address::from_address( + @0x381dd9078c322a4663c392761a0211b527c127b29583851217f948d62131f409 + ); + let expected_payload = b"All your base are belong to us."; + + let parsed = + transfer_with_payload::deserialize( + dummy_message::encoded_transfer_with_payload() + ); + + // Verify getters. + assert!( + transfer_with_payload::amount(&parsed) == expected_amount, + 0 + ); + assert!( + transfer_with_payload::token_address(&parsed) == expected_token_address, + 0 + ); + assert!( + transfer_with_payload::token_chain(&parsed) == expected_token_chain, + 0 + ); + assert!( + transfer_with_payload::redeemer(&parsed) == expected_recipient, + 0 + ); + assert!( + transfer_with_payload::redeemer_chain(&parsed) == expected_recipient_chain, + 0 + ); + assert!( + transfer_with_payload::sender(&parsed) == expected_sender, + 0 + ); + assert!( + transfer_with_payload::payload(&parsed) == expected_payload, + 0 + ); + + let payload = transfer_with_payload::take_payload(parsed); + assert!(payload == expected_payload, 0); + } + + #[test] + #[expected_failure(abort_code = transfer_with_payload::E_INVALID_PAYLOAD)] + fun test_cannot_deserialize_invalid_payload() { + let invalid_payload = token_bridge::dummy_message::encoded_transfer(); + + // Show that the first byte is not the expected payload ID. + assert!( + *vector::borrow(&invalid_payload, 0) != transfer_with_payload::payload_id(), + 0 + ); + + // You shall not pass! + let parsed = transfer_with_payload::deserialize(invalid_payload); + + // Clean up. + transfer_with_payload::destroy(parsed); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/migrate.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/migrate.move new file mode 100644 index 0000000000..e3559de48a --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/migrate.move @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a public method intended to be called after an +/// upgrade has been committed. The purpose is to add one-off migration logic +/// that would alter Token Bridge `State`. +/// +/// Included in migration is the ability to ensure that breaking changes for +/// any of Token Bridge's methods by enforcing the current build version as +/// their required minimum version. +module token_bridge::migrate { + use sui::object::{ID}; + use wormhole::governance_message::{Self, DecreeReceipt}; + + use token_bridge::state::{Self, State}; + use token_bridge::upgrade_contract::{Self}; + + /// Event reflecting when `migrate` is successfully executed. + struct MigrateComplete has drop, copy { + package: ID + } + + /// Execute migration logic. See `token_bridge::migrate` description for + /// more info. + public fun migrate( + token_bridge_state: &mut State, + receipt: DecreeReceipt + ) { + state::migrate__v__0_2_0(token_bridge_state); + + // Perform standard migrate. + handle_migrate(token_bridge_state, receipt); + + //////////////////////////////////////////////////////////////////////// + // + // NOTE: Put any one-off migration logic here. + // + // Most upgrades likely won't need to do anything, in which case the + // rest of this function's body may be empty. Make sure to delete it + // after the migration has gone through successfully. + // + // WARNING: The migration does *not* proceed atomically with the + // upgrade (as they are done in separate transactions). + // If the nature of this migration absolutely requires the migration to + // happen before certain other functionality is available, then guard + // that functionality with the `assert!` from above. + // + //////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + } + + fun handle_migrate( + token_bridge_state: &mut State, + receipt: DecreeReceipt + ) { + // Update the version first. + // + // See `version_control` module for hard-coded configuration. + state::migrate_version(token_bridge_state); + + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(token_bridge_state); + + // Check if build digest is the current one. + let digest = + upgrade_contract::take_digest( + governance_message::payload(&receipt) + ); + state::assert_authorized_digest( + &latest_only, + token_bridge_state, + digest + ); + governance_message::destroy(receipt); + + // Finally emit an event reflecting a successful migrate. + let package = state::current_package(&latest_only, token_bridge_state); + sui::event::emit(MigrateComplete { package }); + } + + #[test_only] + public fun set_up_migrate(token_bridge_state: &mut State) { + state::reverse_migrate__v__dummy(token_bridge_state); + } +} + +#[test_only] +module token_bridge::migrate_tests { + use sui::test_scenario::{Self}; + use wormhole::wormhole_scenario::{ + parse_and_verify_vaa, + verify_governance_vaa + }; + + use token_bridge::state::{Self}; + use token_bridge::upgrade_contract::{Self}; + use token_bridge::token_bridge_scenario::{ + person, + return_state, + set_up_wormhole_and_token_bridge, + take_state, + upgrade_token_bridge + }; + + const UPGRADE_VAA: vector = + x"010000000001005b18d7710c442414435162dc2b46a421c3018a7ff03290eff112a828b7927e4a6a624174cb8385210f4684ac2dbde6e01e4046218f7f245af53e85c97a48e21a0100bc614e0000000000010000000000000000000000000000000000000000000000000000000000000004000000000000000101000000000000000000000000000000000000000000546f6b656e42726964676502001500000000000000000000000000000000000000000000006e6577206275696c64"; + + #[test] + fun test_migrate() { + use token_bridge::migrate::{migrate}; + + let user = person(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Initialize Wormhole. + let wormhole_message_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_message_fee); + + // Next transaction should be conducted as an ordinary user. + test_scenario::next_tx(scenario, user); + + // Upgrade (digest is just b"new build") for testing purposes. + upgrade_token_bridge(scenario); + + // Ignore effects. + test_scenario::next_tx(scenario, user); + + let token_bridge_state = take_state(scenario); + + // Set up migrate (which prepares this package to be the same state as + // a previous release). + token_bridge::migrate::set_up_migrate(&mut token_bridge_state); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut token_bridge_state); + + let verified_vaa = parse_and_verify_vaa(scenario, UPGRADE_VAA); + let ticket = + upgrade_contract::authorize_governance(&token_bridge_state); + let receipt = + verify_governance_vaa(scenario, verified_vaa, ticket); + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + migrate(&mut token_bridge_state, receipt); + + // Make sure we emitted an event. + let effects = test_scenario::next_tx(scenario, user); + assert!(test_scenario::num_user_events(&effects) == 1, 0); + + // Clean up. + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_INCORRECT_OLD_VERSION)] + /// ^ This expected error may change depending on the migration. In most + /// cases, this will abort with `wormhole::package_utils::E_INCORRECT_OLD_VERSION`. + fun test_cannot_migrate_again() { + use token_bridge::migrate::{migrate}; + + let user = person(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Initialize Wormhole. + let wormhole_message_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_message_fee); + + // Next transaction should be conducted as an ordinary user. + test_scenario::next_tx(scenario, user); + + // Upgrade (digest is just b"new build") for testing purposes. + upgrade_token_bridge(scenario); + + // Ignore effects. + test_scenario::next_tx(scenario, user); + + let token_bridge_state = take_state(scenario); + + // Set up migrate (which prepares this package to be the same state as + // a previous release). + token_bridge::migrate::set_up_migrate(&mut token_bridge_state); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut token_bridge_state); + + let verified_vaa = parse_and_verify_vaa(scenario, UPGRADE_VAA); + let ticket = + upgrade_contract::authorize_governance(&token_bridge_state); + let receipt = + verify_governance_vaa(scenario, verified_vaa, ticket); + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + migrate(&mut token_bridge_state, receipt); + + // Make sure we emitted an event. + let effects = test_scenario::next_tx(scenario, user); + assert!(test_scenario::num_user_events(&effects) == 1, 0); + + let verified_vaa = parse_and_verify_vaa(scenario, UPGRADE_VAA); + let ticket = + upgrade_contract::authorize_governance(&token_bridge_state); + let receipt = + verify_governance_vaa(scenario, verified_vaa, ticket); + // You shall not pass! + migrate(&mut token_bridge_state, receipt); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/native_asset.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/native_asset.move new file mode 100644 index 0000000000..dba6b91960 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/native_asset.move @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a custom type that keeps track of info relating to +/// assets (coin types) native to Sui. Token Bridge takes custody of these +/// assets when someone invokes a token transfer outbound. Likewise, Token +/// Bridge releases some of its balance from its custody of when someone redeems +/// an inbound token transfer intended for Sui. +/// +/// See `token_registry` module for more details. +module token_bridge::native_asset { + use sui::balance::{Self, Balance}; + use sui::coin::{Self, CoinMetadata}; + use sui::object::{Self}; + use wormhole::external_address::{Self, ExternalAddress}; + use wormhole::state::{chain_id}; + + friend token_bridge::complete_transfer; + friend token_bridge::token_registry; + friend token_bridge::transfer_tokens; + + /// Container for storing canonical token address and custodied `Balance`. + struct NativeAsset has store { + custody: Balance, + token_address: ExternalAddress, + decimals: u8 + } + + /// Token Bridge identifies native assets using `CoinMetadata` object `ID`. + /// This method converts this `ID` to `ExternalAddress`. + public fun canonical_address( + metadata: &CoinMetadata + ): ExternalAddress { + external_address::from_id(object::id(metadata)) + } + + /// Create new `NativeAsset`. + /// + /// NOTE: The canonical token address is determined by the coin metadata's + /// object ID. + public(friend) fun new(metadata: &CoinMetadata): NativeAsset { + NativeAsset { + custody: balance::zero(), + token_address: canonical_address(metadata), + decimals: coin::get_decimals(metadata) + } + } + + #[test_only] + public fun new_test_only(metadata: &CoinMetadata): NativeAsset { + new(metadata) + } + + /// Retrieve canonical token address. + public fun token_address(self: &NativeAsset): ExternalAddress { + self.token_address + } + + /// Retrieve decimals, which originated from `CoinMetadata`. + public fun decimals(self: &NativeAsset): u8 { + self.decimals + } + + /// Retrieve custodied `Balance` value. + public fun custody(self: &NativeAsset): u64 { + balance::value(&self.custody) + } + + /// Retrieve canonical token chain ID (Sui's) and token address. + public fun canonical_info( + self: &NativeAsset + ): (u16, ExternalAddress) { + (chain_id(), self.token_address) + } + + /// Deposit a given `Balance`. `Balance` originates from an outbound token + /// transfer for a native asset. + /// + /// See `transfer_tokens` module for more info. + public(friend) fun deposit( + self: &mut NativeAsset, + deposited: Balance + ) { + balance::join(&mut self.custody, deposited); + } + + #[test_only] + public fun deposit_test_only( + self: &mut NativeAsset, + deposited: Balance + ) { + deposit(self, deposited) + } + + /// Withdraw a given amount from custody. This amount is determiend by an + /// inbound token transfer payload for a native asset. + /// + /// See `complete_transfer` module for more info. + public(friend) fun withdraw( + self: &mut NativeAsset, + amount: u64 + ): Balance { + balance::split(&mut self.custody, amount) + } + + #[test_only] + public fun withdraw_test_only( + self: &mut NativeAsset, + amount: u64 + ): Balance { + withdraw(self, amount) + } + + #[test_only] + public fun destroy(asset: NativeAsset) { + let NativeAsset { + custody, + token_address: _, + decimals: _ + } = asset; + balance::destroy_for_testing(custody); + } +} + +#[test_only] +module token_bridge::native_asset_tests { + use sui::balance::{Self}; + use sui::coin::{Self}; + use sui::object::{Self}; + use sui::test_scenario::{Self}; + use wormhole::external_address::{Self}; + use wormhole::state::{chain_id}; + + use token_bridge::coin_native_10::{Self, COIN_NATIVE_10}; + use token_bridge::native_asset::{Self}; + use token_bridge::token_bridge_scenario::{person}; + + #[test] + /// In this test, we exercise all the functionalities of a native asset + /// object, including new, deposit, withdraw, to_token_info, as well as + /// getting fields token_address, decimals, balance. + fun test_native_asset() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Publish coin. + coin_native_10::init_test_only(test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let coin_meta = coin_native_10::take_metadata(scenario); + + // Make new. + let asset = native_asset::new_test_only(&coin_meta); + + // Assert token address and decimals are correct. + let expected_token_address = + external_address::from_id(object::id(&coin_meta)); + assert!( + native_asset::token_address(&asset) == expected_token_address, + 0 + ); + assert!( + native_asset::decimals(&asset) == coin::get_decimals(&coin_meta), + 0 + ); + assert!(native_asset::custody(&asset) == 0, 0); + + // deposit some coins into the NativeAsset coin custody + let deposit_amount = 1000; + let (i, n) = (0, 8); + while (i < n) { + native_asset::deposit_test_only( + &mut asset, + balance::create_for_testing( + deposit_amount + ) + ); + i = i + 1; + }; + let total_deposited = n * deposit_amount; + assert!(native_asset::custody(&asset) == total_deposited, 0); + + let withdraw_amount = 690; + let total_withdrawn = balance::zero(); + let i = 0; + while (i < n) { + let withdrawn = native_asset::withdraw_test_only( + &mut asset, + withdraw_amount + ); + assert!(balance::value(&withdrawn) == withdraw_amount, 0); + balance::join(&mut total_withdrawn, withdrawn); + i = i + 1; + }; + + // convert to token info and assert convrsion is correct + let ( + token_chain, + token_address + ) = native_asset::canonical_info(&asset); + + assert!(token_chain == chain_id(), 0); + assert!(token_address == expected_token_address, 0); + + // check that updated balance is correct + let expected_remaining = total_deposited - n * withdraw_amount; + let remaining = native_asset::custody(&asset); + assert!(remaining == expected_remaining, 0); + + // Clean up. + coin_native_10::return_metadata(coin_meta); + balance::destroy_for_testing(total_withdrawn); + native_asset::destroy(asset); + + // Done. + test_scenario::end(my_scenario); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/token_registry.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/token_registry.move new file mode 100644 index 0000000000..f03f1d3383 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/token_registry.move @@ -0,0 +1,784 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a custom type that keeps track of both native and +/// wrapped assets via dynamic fields. These dynamic fields are keyed off using +/// coin types. This registry lives in `State`. +/// +/// See `state` module for more details. +module token_bridge::token_registry { + use std::ascii::{String}; + use std::type_name::{Self}; + use sui::coin::{TreasuryCap, CoinMetadata}; + use sui::dynamic_field::{Self}; + use sui::object::{Self, UID}; + use sui::package::{UpgradeCap}; + use sui::table::{Self, Table}; + use sui::tx_context::{TxContext}; + use wormhole::external_address::{Self, ExternalAddress}; + + use token_bridge::asset_meta::{Self, AssetMeta}; + use token_bridge::native_asset::{Self, NativeAsset}; + use token_bridge::wrapped_asset::{Self, WrappedAsset}; + + friend token_bridge::attest_token; + friend token_bridge::complete_transfer; + friend token_bridge::create_wrapped; + friend token_bridge::state; + friend token_bridge::transfer_tokens; + + /// Asset is not registered yet. + const E_UNREGISTERED: u64 = 0; + /// Cannot register wrapped asset with same canonical token info. + const E_ALREADY_WRAPPED: u64 = 1; + + /// This container is used to store native and wrapped assets of coin type + /// as dynamic fields under its `UID`. It also uses a mechanism to generate + /// arbitrary token addresses for native assets. + struct TokenRegistry has key, store { + id: UID, + num_wrapped: u64, + num_native: u64, + coin_types: Table + } + + /// Container to provide convenient checking of whether an asset is wrapped + /// or native. `VerifiedAsset` can only be created either by passing in a + /// resource with `CoinType` or by verifying input token info against the + /// canonical info that exists in `TokenRegistry`. + /// + /// NOTE: This container can be dropped after it was created. + struct VerifiedAsset has drop { + is_wrapped: bool, + chain: u16, + addr: ExternalAddress, + coin_decimals: u8 + } + + /// Wrapper of coin type to act as dynamic field key. + struct Key has copy, drop, store {} + + /// This struct is not used for anything within the contract. It exists + /// purely for someone with an RPC query to be able to fetch the type name + /// of coin type as a string via `TokenRegistry`. + struct CoinTypeKey has drop, copy, store { + chain: u16, + addr: vector + } + + /// Create new `TokenRegistry`. + /// + /// See `setup` module for more info. + public(friend) fun new(ctx: &mut TxContext): TokenRegistry { + TokenRegistry { + id: object::new(ctx), + num_wrapped: 0, + num_native: 0, + coin_types: table::new(ctx) + } + } + + #[test_only] + public fun new_test_only(ctx: &mut TxContext): TokenRegistry { + new(ctx) + } + + /// Determine whether a particular coin type is registered. + public fun has(self: &TokenRegistry): bool { + dynamic_field::exists_(&self.id, Key {}) + } + + public fun assert_has(self: &TokenRegistry) { + assert!(has(self), E_UNREGISTERED); + } + + public fun verified_asset( + self: &TokenRegistry + ): VerifiedAsset { + // We check specifically whether `CoinType` is associated with a dynamic + // field for `WrappedAsset`. This boolean will be used as the underlying + // value for `VerifiedAsset`. + let is_wrapped = + dynamic_field::exists_with_type, WrappedAsset>( + &self.id, + Key {} + ); + if (is_wrapped) { + let asset = borrow_wrapped(self); + let (chain, addr) = wrapped_asset::canonical_info(asset); + let coin_decimals = wrapped_asset::decimals(asset); + + VerifiedAsset { is_wrapped, chain, addr, coin_decimals } + } else { + let asset = borrow_native(self); + let (chain, addr) = native_asset::canonical_info(asset); + let coin_decimals = native_asset::decimals(asset); + + VerifiedAsset { is_wrapped, chain, addr, coin_decimals } + } + } + + /// Determine whether a given `CoinType` is a wrapped asset. + public fun is_wrapped(verified: &VerifiedAsset): bool { + verified.is_wrapped + } + + /// Retrieve canonical token chain ID from `VerifiedAsset`. + public fun token_chain( + verified: &VerifiedAsset + ): u16 { + verified.chain + } + + /// Retrieve canonical token address from `VerifiedAsset`. + public fun token_address( + verified: &VerifiedAsset + ): ExternalAddress { + verified.addr + } + + /// Retrieve decimals for a `VerifiedAsset`. + public fun coin_decimals( + verified: &VerifiedAsset + ): u8 { + verified.coin_decimals + } + + /// Add a new wrapped asset to the registry and return the canonical token + /// address. + /// + /// See `state` module for more info. + public(friend) fun add_new_wrapped( + self: &mut TokenRegistry, + token_meta: AssetMeta, + coin_meta: &mut CoinMetadata, + treasury_cap: TreasuryCap, + upgrade_cap: UpgradeCap + ): ExternalAddress { + // Grab canonical token info. + let token_chain = asset_meta::token_chain(&token_meta); + let token_addr = asset_meta::token_address(&token_meta); + + let coin_types = &mut self.coin_types; + let key = + CoinTypeKey { + chain: token_chain, + addr: external_address::to_bytes(token_addr) + }; + // We need to make sure that the canonical token info has not been + // created for another coin type. This can happen if asset metadata + // is attested again from a foreign chain and another coin type is + // published using its VAA. + assert!(!table::contains(coin_types, key), E_ALREADY_WRAPPED); + + // Now add the coin type. + table::add( + coin_types, + key, + type_name::into_string(type_name::get()) + ); + + // NOTE: We do not assert that the coin type has not already been + // registered using !has(self) because `wrapped_asset::new` + // consumes `TreasuryCap`. This `TreasuryCap` is only created once for a particuar + // coin type via `create_wrapped::prepare_registration`. Because the + // `TreasuryCap` is globally unique and can only be created once, there is no + // risk that `add_new_wrapped` can be called again on the same coin + // type. + let asset = + wrapped_asset::new( + token_meta, + coin_meta, + treasury_cap, + upgrade_cap + ); + dynamic_field::add(&mut self.id, Key {}, asset); + self.num_wrapped = self.num_wrapped + 1; + + token_addr + } + + #[test_only] + public fun add_new_wrapped_test_only( + self: &mut TokenRegistry, + token_meta: AssetMeta, + coin_meta: &mut CoinMetadata, + treasury_cap: TreasuryCap, + ctx: &mut TxContext + ): ExternalAddress { + add_new_wrapped( + self, + token_meta, + coin_meta, + treasury_cap, + sui::package::test_publish( + object::id_from_address(@token_bridge), + ctx + ) + ) + } + + /// Add a new native asset to the registry and return the canonical token + /// address. + /// + /// NOTE: This method does not verify if `CoinType` is already in the + /// registry because `attest_token` already takes care of this check. If + /// This method were to be called on an already-registered asset, this + /// will throw with an error from `sui::dynamic_field` reflectina duplicate + /// field. + /// + /// See `attest_token` module for more info. + public(friend) fun add_new_native( + self: &mut TokenRegistry, + metadata: &CoinMetadata, + ): ExternalAddress { + // Create new native asset. + let asset = native_asset::new(metadata); + let token_addr = native_asset::token_address(&asset); + + // Add to registry. + dynamic_field::add(&mut self.id, Key {}, asset); + self.num_native = self.num_native + 1; + + // Now add the coin type. + table::add( + &mut self.coin_types, + CoinTypeKey { + chain: wormhole::state::chain_id(), + addr: external_address::to_bytes(token_addr) + }, + type_name::into_string(type_name::get()) + ); + + // Return the token address. + token_addr + } + + #[test_only] + public fun add_new_native_test_only( + self: &mut TokenRegistry, + metadata: &CoinMetadata + ): ExternalAddress { + add_new_native(self, metadata) + } + + public fun borrow_wrapped( + self: &TokenRegistry + ): &WrappedAsset { + dynamic_field::borrow(&self.id, Key {}) + } + + public(friend) fun borrow_mut_wrapped( + self: &mut TokenRegistry + ): &mut WrappedAsset { + dynamic_field::borrow_mut(&mut self.id, Key {}) + } + + #[test_only] + public fun borrow_mut_wrapped_test_only( + self: &mut TokenRegistry + ): &mut WrappedAsset { + borrow_mut_wrapped(self) + } + + public fun borrow_native( + self: &TokenRegistry + ): &NativeAsset { + dynamic_field::borrow(&self.id, Key {}) + } + + public(friend) fun borrow_mut_native( + self: &mut TokenRegistry + ): &mut NativeAsset { + dynamic_field::borrow_mut(&mut self.id, Key {}) + } + + #[test_only] + public fun borrow_mut_native_test_only( + self: &mut TokenRegistry + ): &mut NativeAsset { + borrow_mut_native(self) + } + + #[test_only] + public fun num_native(self: &TokenRegistry): u64 { + self.num_native + } + + #[test_only] + public fun num_wrapped(self: &TokenRegistry): u64 { + self.num_wrapped + } + + #[test_only] + public fun destroy(registry: TokenRegistry) { + let TokenRegistry { + id, + num_wrapped: _, + num_native: _, + coin_types + } = registry; + object::delete(id); + table::drop(coin_types); + } + + #[test_only] + public fun coin_type_for( + self: &TokenRegistry, + chain: u16, + addr: vector + ): String { + *table::borrow(&self.coin_types, CoinTypeKey { chain, addr }) + } +} + +// In this test, we exercise the various functionalities of TokenRegistry, +// including registering native and wrapped coins via add_new_native, and +// add_new_wrapped, minting/burning/depositing/withdrawing said tokens, and also +// storing metadata about the tokens. +#[test_only] +module token_bridge::token_registry_tests { + use std::type_name::{Self}; + use sui::balance::{Self}; + use sui::coin::{CoinMetadata}; + use sui::test_scenario::{Self}; + use wormhole::external_address::{Self}; + use wormhole::state::{chain_id}; + + use token_bridge::asset_meta::{Self}; + use token_bridge::coin_native_10::{Self, COIN_NATIVE_10}; + use token_bridge::coin_wrapped_7::{Self, COIN_WRAPPED_7}; + use token_bridge::native_asset::{Self}; + use token_bridge::token_registry::{Self}; + use token_bridge::token_bridge_scenario::{person}; + use token_bridge::wrapped_asset::{Self}; + + struct SCAM_COIN has drop {} + + #[test] + fun test_registered_tokens_native() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Initialize new coin. + coin_native_10::init_test_only(test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Initialize new token registry. + let registry = + token_registry::new_test_only(test_scenario::ctx(scenario)); + + // Check initial state. + assert!(token_registry::num_native(®istry) == 0, 0); + assert!(token_registry::num_wrapped(®istry) == 0, 0); + + // Register native asset. + let coin_meta = coin_native_10::take_metadata(scenario); + let token_address = + token_registry::add_new_native_test_only( + &mut registry, + &coin_meta, + ); + let expected_token_address = + native_asset::canonical_address(&coin_meta); + assert!(token_address == expected_token_address, 0); + + // mint some native coins, then deposit them into the token registry + let deposit_amount = 69; + let (i, n) = (0, 8); + while (i < n) { + native_asset::deposit_test_only( + token_registry::borrow_mut_native_test_only( + &mut registry, + ), + balance::create_for_testing( + deposit_amount + ) + ); + i = i + 1; + }; + let total_deposited = n * deposit_amount; + { + let asset = + token_registry::borrow_native(®istry); + assert!(native_asset::custody(asset) == total_deposited, 0); + }; + + // Withdraw and check balances. + let withdraw_amount = 420; + let withdrawn = + native_asset::withdraw_test_only( + token_registry::borrow_mut_native_test_only( + &mut registry + ), + withdraw_amount + ); + assert!(balance::value(&withdrawn) == withdraw_amount, 0); + balance::destroy_for_testing(withdrawn); + + let expected_remaining = total_deposited - withdraw_amount; + { + let asset = + token_registry::borrow_native(®istry); + assert!(native_asset::custody(asset) == expected_remaining, 0); + }; + + // Verify registry values. + assert!(token_registry::num_native(®istry) == 1, 0); + assert!(token_registry::num_wrapped(®istry) == 0, 0); + + let verified = token_registry::verified_asset(®istry); + assert!(!token_registry::is_wrapped(&verified), 0); + assert!(token_registry::coin_decimals(&verified) == 10, 0); + assert!(token_registry::token_chain(&verified) == chain_id(), 0); + assert!( + token_registry::token_address(&verified) == expected_token_address, + 0 + ); + + // Check coin type. + let coin_type = + token_registry::coin_type_for( + ®istry, + token_registry::token_chain(&verified), + external_address::to_bytes( + token_registry::token_address(&verified) + ) + ); + assert!( + coin_type == type_name::into_string(type_name::get()), + 0 + ); + + // Clean up. + token_registry::destroy(registry); + coin_native_10::return_metadata(coin_meta); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_registered_tokens_wrapped() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Initialize new coin. + let treasury_cap = + coin_wrapped_7::init_and_take_treasury_cap( + scenario, + caller + ); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Initialize new token registry. + let registry = + token_registry::new_test_only(test_scenario::ctx(scenario)); + + // Check initial state. + assert!(token_registry::num_wrapped(®istry) == 0, 0); + assert!(token_registry::num_native(®istry) == 0, 0); + + let coin_meta = test_scenario::take_shared>(scenario); + + // Register wrapped asset. + let wrapped_token_meta = coin_wrapped_7::token_meta(); + token_registry::add_new_wrapped_test_only( + &mut registry, + wrapped_token_meta, + &mut coin_meta, + treasury_cap, + test_scenario::ctx(scenario) + ); + + test_scenario::return_shared(coin_meta); + + // Mint wrapped coin via `WrappedAsset` several times. + let mint_amount = 420; + let total_minted = balance::zero(); + let (i, n) = (0, 8); + while (i < n) { + let minted = + wrapped_asset::mint_test_only( + token_registry::borrow_mut_wrapped_test_only( + &mut registry, + ), + mint_amount + ); + assert!(balance::value(&minted) == mint_amount, 0); + balance::join(&mut total_minted, minted); + i = i + 1; + }; + + let total_supply = + wrapped_asset::total_supply( + token_registry::borrow_wrapped( + ®istry + ) + ); + assert!(total_supply == balance::value(&total_minted), 0); + + // withdraw, check value, and re-deposit native coins into registry + let burn_amount = 69; + let burned = + wrapped_asset::burn_test_only( + token_registry::borrow_mut_wrapped_test_only(&mut registry), + balance::split(&mut total_minted, burn_amount) + ); + assert!(burned == burn_amount, 0); + + let expected_remaining = total_supply - burn_amount; + let remaining = + wrapped_asset::total_supply( + token_registry::borrow_wrapped( + ®istry + ) + ); + assert!(remaining == expected_remaining, 0); + balance::destroy_for_testing(total_minted); + + // Verify registry values. + assert!(token_registry::num_wrapped(®istry) == 1, 0); + assert!(token_registry::num_native(®istry) == 0, 0); + + + let verified = token_registry::verified_asset(®istry); + assert!(token_registry::is_wrapped(&verified), 0); + assert!(token_registry::coin_decimals(&verified) == 7, 0); + + let wrapped_token_meta = coin_wrapped_7::token_meta(); + assert!( + token_registry::token_chain(&verified) == asset_meta::token_chain(&wrapped_token_meta), + 0 + ); + assert!( + token_registry::token_address(&verified) == asset_meta::token_address(&wrapped_token_meta), + 0 + ); + + // Check coin type. + let coin_type = + token_registry::coin_type_for( + ®istry, + token_registry::token_chain(&verified), + external_address::to_bytes( + token_registry::token_address(&verified) + ) + ); + assert!( + coin_type == type_name::into_string(type_name::get()), + 0 + ); + + + // Clean up. + token_registry::destroy(registry); + asset_meta::destroy(wrapped_token_meta); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = sui::dynamic_field::EFieldAlreadyExists)] + /// In this negative test case, we try to register a native token twice. + fun test_cannot_add_new_native_again() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Initialize new coin. + coin_native_10::init_test_only(test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Initialize new token registry. + let registry = + token_registry::new_test_only(test_scenario::ctx(scenario)); + + let coin_meta = coin_native_10::take_metadata(scenario); + + // Add new native asset. + token_registry::add_new_native_test_only( + &mut registry, + &coin_meta + ); + + // You shall not pass! + // + // NOTE: We don't have a custom error for this. This will trigger a + // `sui::dynamic_field` error. + token_registry::add_new_native_test_only( + &mut registry, + &coin_meta + ); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = sui::dynamic_field::EFieldTypeMismatch)] + // In this negative test case, we attempt to deposit a wrapped token into + // a TokenRegistry object, resulting in failure. A wrapped coin can + // only be minted and burned, not deposited. + fun test_cannot_deposit_wrapped_asset() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let treasury_cap = + coin_wrapped_7::init_and_take_treasury_cap( + scenario, + caller + ); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Initialize new token registry. + let registry = + token_registry::new_test_only(test_scenario::ctx(scenario)); + + let coin_meta = test_scenario::take_shared>(scenario); + + token_registry::add_new_wrapped_test_only( + &mut registry, + coin_wrapped_7::token_meta(), + &mut coin_meta, + treasury_cap, + test_scenario::ctx(scenario) + ); + + test_scenario::return_shared(coin_meta); + + // Mint some wrapped coins and attempt to deposit balance. + let minted = + wrapped_asset::mint_test_only( + token_registry::borrow_mut_wrapped_test_only( + &mut registry + ), + 420420420 + ); + + let verified = token_registry::verified_asset(®istry); + assert!(token_registry::is_wrapped(&verified), 0); + + // You shall not pass! + // + // NOTE: We don't have a custom error for this. This will trigger a + // `sui::dynamic_field` error. + native_asset::deposit_test_only( + token_registry::borrow_mut_native_test_only( + &mut registry + ), + minted + ); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = sui::dynamic_field::EFieldTypeMismatch)] + // In this negative test case, we attempt to deposit a wrapped token into + // a TokenRegistry object, resulting in failure. A wrapped coin can + // only be minted and burned, not deposited. + fun test_cannot_mint_native_asset() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + coin_native_10::init_test_only(test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Initialize new token registry. + let registry = + token_registry::new_test_only(test_scenario::ctx(scenario)); + + let coin_meta = coin_native_10::take_metadata(scenario); + token_registry::add_new_native_test_only( + &mut registry, + &coin_meta + ); + + // Show that this asset is not wrapped. + let verified = token_registry::verified_asset(®istry); + assert!(!token_registry::is_wrapped(&verified), 0); + + // You shall not pass! + // + // NOTE: We don't have a custom error for this. This will trigger a + // `sui::dynamic_field` error. + let minted = + wrapped_asset::mint_test_only( + token_registry::borrow_mut_wrapped_test_only( + &mut registry + ), + 420 + ); + + // Clean up. + balance::destroy_for_testing(minted); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = token_registry::E_ALREADY_WRAPPED)] + fun test_cannot_add_new_wrapped_with_same_canonical_info() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Initialize new coin. + let treasury_cap = + coin_wrapped_7::init_and_take_treasury_cap( + scenario, + caller + ); + + // Initialize other coin + coin_native_10::init_test_only(test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Initialize new token registry. + let registry = + token_registry::new_test_only(test_scenario::ctx(scenario)); + + let coin_meta = test_scenario::take_shared>(scenario); + + // Register wrapped asset. + token_registry::add_new_wrapped_test_only( + &mut registry, + coin_wrapped_7::token_meta(), + &mut coin_meta, + treasury_cap, + test_scenario::ctx(scenario) + ); + + test_scenario::return_shared(coin_meta); + + let coin_meta = coin_native_10::take_metadata(scenario); + let treasury_cap = coin_native_10::take_treasury_cap(scenario); + + // You shall not pass! + token_registry::add_new_wrapped_test_only( + &mut registry, + coin_wrapped_7::token_meta(), + &mut coin_meta, + treasury_cap, + test_scenario::ctx(scenario) + ); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/wrapped_asset.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/wrapped_asset.move new file mode 100644 index 0000000000..7df31c540e --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/wrapped_asset.move @@ -0,0 +1,806 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements two custom types relating to Token Bridge wrapped +/// assets. These assets have been attested from foreign networks, whose +/// metadata is stored in `ForeignInfo`. The Token Bridge contract is the +/// only authority that can mint and burn these assets via `Supply`. +/// +/// See `create_wrapped` and 'token_registry' modules for more details. +module token_bridge::wrapped_asset { + use std::string::{String}; + use sui::balance::{Self, Balance}; + use sui::coin::{Self, TreasuryCap, CoinMetadata}; + use sui::package::{Self, UpgradeCap}; + use wormhole::external_address::{ExternalAddress}; + use wormhole::state::{chain_id}; + + use token_bridge::string_utils; + use token_bridge::asset_meta::{Self, AssetMeta}; + use token_bridge::normalized_amount::{cap_decimals}; + + friend token_bridge::complete_transfer; + friend token_bridge::create_wrapped; + friend token_bridge::token_registry; + friend token_bridge::transfer_tokens; + + /// Token chain ID matching Sui's are not allowed. + const E_SUI_CHAIN: u64 = 0; + /// Canonical token info does match `AssetMeta` payload. + const E_ASSET_META_MISMATCH: u64 = 1; + /// Coin decimals don't match the VAA. + const E_DECIMALS_MISMATCH: u64 = 2; + + /// Container storing foreign asset info. + struct ForeignInfo has store { + token_chain: u16, + token_address: ExternalAddress, + native_decimals: u8, + symbol: String + } + + /// Container managing `ForeignInfo` and `TreasuryCap` for a wrapped asset + /// coin type. + struct WrappedAsset has store { + info: ForeignInfo, + treasury_cap: TreasuryCap, + decimals: u8, + upgrade_cap: UpgradeCap + } + + /// Create new `WrappedAsset`. + /// + /// See `token_registry` module for more info. + public(friend) fun new( + token_meta: AssetMeta, + coin_meta: &mut CoinMetadata, + treasury_cap: TreasuryCap, + upgrade_cap: UpgradeCap + ): WrappedAsset { + // Verify that the upgrade cap is from the same package as coin type. + // This cap should not have been modified prior to creating this asset + // (i.e. should have the default upgrade policy and build version == 1). + wormhole::package_utils::assert_package_upgrade_cap( + &upgrade_cap, + package::compatible_policy(), + 1 + ); + + let ( + token_address, + token_chain, + native_decimals, + symbol, + name + ) = asset_meta::unpack(token_meta); + + // Protect against adding `AssetMeta` which has Sui's chain ID. + assert!(token_chain != chain_id(), E_SUI_CHAIN); + + // Set metadata. + coin::update_name(&treasury_cap, coin_meta, name); + coin::update_symbol(&treasury_cap, coin_meta, string_utils::to_ascii(&symbol)); + + let decimals = cap_decimals(native_decimals); + + // Ensure that the `C` type has the right number of decimals. This is + // the only field in the coinmeta that cannot be changed after the fact, + // so we expect to receive one that already has the correct decimals + // set. + assert!(decimals == coin::get_decimals(coin_meta), E_DECIMALS_MISMATCH); + + let info = + ForeignInfo { + token_address, + token_chain, + native_decimals, + symbol + }; + + WrappedAsset { + info, + treasury_cap, + decimals, + upgrade_cap + } + } + + #[test_only] + public fun new_test_only( + token_meta: AssetMeta, + coin_meta: &mut CoinMetadata, + treasury_cap: TreasuryCap, + upgrade_cap: UpgradeCap + ): WrappedAsset { + new(token_meta, coin_meta, treasury_cap, upgrade_cap) + } + + /// Update existing `ForeignInfo` using new `AssetMeta`. + /// + /// See `token_registry` module for more info. + public(friend) fun update_metadata( + self: &mut WrappedAsset, + coin_meta: &mut CoinMetadata, + token_meta: AssetMeta + ) { + // NOTE: We ignore `native_decimals` because we do not enforce that + // an asset's decimals on a foreign network needs to stay the same. + let ( + token_address, + token_chain, + _native_decimals, + symbol, + name + ) = asset_meta::unpack(token_meta); + + // Verify canonical token info. Also check that the native decimals + // have not changed (because changing this info is not desirable, as + // this change means the supply changed on its native network). + // + // NOTE: This implicitly verifies that `token_chain` is not Sui's + // because this was checked already when the asset was first added. + let (expected_chain, expected_address) = canonical_info(self); + assert!( + ( + token_chain == expected_chain && + token_address == expected_address + ), + E_ASSET_META_MISMATCH + ); + + // Finally only update the name and symbol. + self.info.symbol = symbol; + coin::update_name(&self.treasury_cap, coin_meta, name); + coin::update_symbol(&self.treasury_cap, coin_meta, string_utils::to_ascii(&symbol)); + } + + #[test_only] + public fun update_metadata_test_only( + self: &mut WrappedAsset, + coin_meta: &mut CoinMetadata, + token_meta: AssetMeta + ) { + update_metadata(self, coin_meta, token_meta) + } + + /// Retrieve immutable reference to `ForeignInfo`. + public fun info(self: &WrappedAsset): &ForeignInfo { + &self.info + } + + /// Retrieve canonical token chain ID from `ForeignInfo`. + public fun token_chain(info: &ForeignInfo): u16 { + info.token_chain + } + + /// Retrieve canonical token address from `ForeignInfo`. + public fun token_address(info: &ForeignInfo): ExternalAddress { + info.token_address + } + + /// Retrieve decimal amount from `ForeignInfo`. + /// + /// NOTE: This is for informational purposes. This decimal amount is not + /// used for any calculations. + public fun native_decimals(info: &ForeignInfo): u8 { + info.native_decimals + } + + /// Retrieve asset's symbol (UTF-8) from `ForeignMetadata`. + /// + /// NOTE: This value can be updated. + public fun symbol(info: &ForeignInfo): String { + info.symbol + } + + /// Retrieve total minted supply. + public fun total_supply(self: &WrappedAsset): u64 { + coin::total_supply(&self.treasury_cap) + } + + /// Retrieve decimals for this wrapped asset. For any asset whose native + /// decimals is greater than the cap (8), this will be 8. + /// + /// See `normalized_amount` module for more info. + public fun decimals(self: &WrappedAsset): u8 { + self.decimals + } + + /// Retrieve canonical token chain ID and token address. + public fun canonical_info( + self: &WrappedAsset + ): (u16, ExternalAddress) { + (self.info.token_chain, self.info.token_address) + } + + /// Burn a given `Balance`. `Balance` originates from an outbound token + /// transfer for a wrapped asset. + /// + /// See `transfer_tokens` module for more info. + public(friend) fun burn( + self: &mut WrappedAsset, + burned: Balance + ): u64 { + balance::decrease_supply(coin::supply_mut(&mut self.treasury_cap), burned) + } + + #[test_only] + public fun burn_test_only( + self: &mut WrappedAsset, + burned: Balance + ): u64 { + burn(self, burned) + } + + /// Mint a given amount. This amount is determined by an inbound token + /// transfer payload for a wrapped asset. + /// + /// See `complete_transfer` module for more info. + public(friend) fun mint( + self: &mut WrappedAsset, + amount: u64 + ): Balance { + coin::mint_balance(&mut self.treasury_cap, amount) + } + + #[test_only] + public fun mint_test_only( + self: &mut WrappedAsset, + amount: u64 + ): Balance { + mint(self, amount) + } + + #[test_only] + public fun destroy(asset: WrappedAsset) { + let WrappedAsset { + info, + treasury_cap, + decimals: _, + upgrade_cap + } = asset; + sui::test_utils::destroy(treasury_cap); + + let ForeignInfo { + token_chain: _, + token_address: _, + native_decimals: _, + symbol: _ + } = info; + + sui::package::make_immutable(upgrade_cap); + } +} + +#[test_only] +module token_bridge::wrapped_asset_tests { + use std::string::{Self}; + use sui::balance::{Self}; + use sui::coin::{Self, CoinMetadata}; + use sui::object::{Self}; + use sui::package::{Self}; + use sui::test_scenario::{Self}; + use wormhole::external_address::{Self}; + use wormhole::state::{chain_id}; + + use token_bridge::asset_meta::{Self}; + use token_bridge::string_utils; + use token_bridge::coin_native_10::{COIN_NATIVE_10, Self}; + use token_bridge::coin_wrapped_12::{COIN_WRAPPED_12, Self}; + use token_bridge::coin_wrapped_7::{COIN_WRAPPED_7, Self}; + use token_bridge::token_bridge_scenario::{person}; + use token_bridge::wrapped_asset::{Self}; + + #[test] + fun test_wrapped_asset_7() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let parsed_meta = coin_wrapped_7::token_meta(); + let expected_token_chain = asset_meta::token_chain(&parsed_meta); + let expected_token_address = asset_meta::token_address(&parsed_meta); + let expected_native_decimals = + asset_meta::native_decimals(&parsed_meta); + let expected_symbol = asset_meta::symbol(&parsed_meta); + let expected_name = asset_meta::name(&parsed_meta); + + // Publish coin. + let treasury_cap = + coin_wrapped_7::init_and_take_treasury_cap( + scenario, + caller + ); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Upgrade cap belonging to coin type. + let upgrade_cap = + package::test_publish( + object::id_from_address(@token_bridge), + test_scenario::ctx(scenario) + ); + + let coin_meta: CoinMetadata = test_scenario::take_shared(scenario); + + // Make new. + let asset = + wrapped_asset::new_test_only( + parsed_meta, + &mut coin_meta, + treasury_cap, + upgrade_cap + ); + + // Verify members. + let info = wrapped_asset::info(&asset); + assert!( + wrapped_asset::token_chain(info) == expected_token_chain, + 0 + ); + assert!( + wrapped_asset::token_address(info) == expected_token_address, + 0 + ); + assert!( + wrapped_asset::native_decimals(info) == expected_native_decimals, + 0 + ); + assert!(coin::get_symbol(&coin_meta) == string_utils::to_ascii(&expected_symbol), 0); + assert!(coin::get_name(&coin_meta) == expected_name, 0); + assert!(wrapped_asset::total_supply(&asset) == 0, 0); + + let (token_chain, token_address) = + wrapped_asset::canonical_info(&asset); + assert!(token_chain == expected_token_chain, 0); + assert!(token_address == expected_token_address, 0); + + // Decimals are read from `CoinMetadata`, but in this case will agree + // with the value encoded in the VAA. + assert!(wrapped_asset::decimals(&asset) == expected_native_decimals, 0); + assert!(coin::get_decimals(&coin_meta) == expected_native_decimals, 0); + + // Change name and symbol for update. + let new_symbol = std::ascii::into_bytes(coin::get_symbol(&coin_meta)); + + std::vector::append(&mut new_symbol, b"??? and profit"); + assert!(new_symbol != *string::bytes(&expected_symbol), 0); + + let new_name = coin::get_name(&coin_meta); + string::append(&mut new_name, string::utf8(b"??? and profit")); + assert!(new_name != expected_name, 0); + + let updated_meta = + asset_meta::new( + expected_token_address, + expected_token_chain, + expected_native_decimals, + string::utf8(new_symbol), + new_name + ); + + // Update metadata now. + wrapped_asset::update_metadata_test_only(&mut asset, &mut coin_meta, updated_meta); + + assert!(coin::get_symbol(&coin_meta) == std::ascii::string(new_symbol), 0); + assert!(coin::get_name(&coin_meta) == new_name, 0); + + // Try to mint. + let mint_amount = 420; + let collected = balance::zero(); + let (i, n) = (0, 8); + while (i < n) { + let minted = + wrapped_asset::mint_test_only(&mut asset, mint_amount); + assert!(balance::value(&minted) == mint_amount, 0); + balance::join(&mut collected, minted); + i = i + 1; + }; + assert!(balance::value(&collected) == n * mint_amount, 0); + assert!( + wrapped_asset::total_supply(&asset) == balance::value(&collected), + 0 + ); + + // Now try to burn. + let burn_amount = 69; + let i = 0; + while (i < n) { + let burned = balance::split(&mut collected, burn_amount); + let check_amount = + wrapped_asset::burn_test_only(&mut asset, burned); + assert!(check_amount == burn_amount, 0); + i = i + 1; + }; + let remaining = n * mint_amount - n * burn_amount; + assert!(wrapped_asset::total_supply(&asset) == remaining, 0); + assert!(balance::value(&collected) == remaining, 0); + + test_scenario::return_shared(coin_meta); + + // Clean up. + balance::destroy_for_testing(collected); + wrapped_asset::destroy(asset); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_wrapped_asset_12() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let parsed_meta = coin_wrapped_12::token_meta(); + let expected_token_chain = asset_meta::token_chain(&parsed_meta); + let expected_token_address = asset_meta::token_address(&parsed_meta); + let expected_native_decimals = + asset_meta::native_decimals(&parsed_meta); + let expected_symbol = asset_meta::symbol(&parsed_meta); + let expected_name = asset_meta::name(&parsed_meta); + + // Publish coin. + let treasury_cap = + coin_wrapped_12::init_and_take_treasury_cap( + scenario, + caller + ); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Upgrade cap belonging to coin type. + let upgrade_cap = + package::test_publish( + object::id_from_address(@token_bridge), + test_scenario::ctx(scenario) + ); + + let coin_meta: CoinMetadata = test_scenario::take_shared(scenario); + + // Make new. + let asset = + wrapped_asset::new_test_only( + parsed_meta, + &mut coin_meta, + treasury_cap, + upgrade_cap + ); + + // Verify members. + let info = wrapped_asset::info(&asset); + assert!( + wrapped_asset::token_chain(info) == expected_token_chain, + 0 + ); + assert!( + wrapped_asset::token_address(info) == expected_token_address, + 0 + ); + assert!( + wrapped_asset::native_decimals(info) == expected_native_decimals, + 0 + ); + assert!(coin::get_symbol(&coin_meta) == string_utils::to_ascii(&expected_symbol), 0); + assert!(coin::get_name(&coin_meta) == expected_name, 0); + assert!(wrapped_asset::total_supply(&asset) == 0, 0); + + let (token_chain, token_address) = + wrapped_asset::canonical_info(&asset); + assert!(token_chain == expected_token_chain, 0); + assert!(token_address == expected_token_address, 0); + + // Decimals are read from `CoinMetadata`, but in this case will not + // agree with the value encoded in the VAA. + assert!(wrapped_asset::decimals(&asset) == 8, 0); + assert!( + coin::get_decimals(&coin_meta) == wrapped_asset::decimals(&asset), + 0 + ); + assert!(wrapped_asset::decimals(&asset) != expected_native_decimals, 0); + + // Change name and symbol for update. + let new_symbol = std::ascii::into_bytes(coin::get_symbol(&coin_meta)); + + std::vector::append(&mut new_symbol, b"??? and profit"); + assert!(new_symbol != *string::bytes(&expected_symbol), 0); + + let new_name = coin::get_name(&coin_meta); + string::append(&mut new_name, string::utf8(b"??? and profit")); + assert!(new_name != expected_name, 0); + + let updated_meta = + asset_meta::new( + expected_token_address, + expected_token_chain, + expected_native_decimals, + string::utf8(new_symbol), + new_name + ); + + // Update metadata now. + wrapped_asset::update_metadata_test_only(&mut asset, &mut coin_meta, updated_meta); + + assert!(coin::get_symbol(&coin_meta) == std::ascii::string(new_symbol), 0); + assert!(coin::get_name(&coin_meta) == new_name, 0); + + // Try to mint. + let mint_amount = 420; + let collected = balance::zero(); + let (i, n) = (0, 8); + while (i < n) { + let minted = + wrapped_asset::mint_test_only(&mut asset, mint_amount); + assert!(balance::value(&minted) == mint_amount, 0); + balance::join(&mut collected, minted); + i = i + 1; + }; + assert!(balance::value(&collected) == n * mint_amount, 0); + assert!( + wrapped_asset::total_supply(&asset) == balance::value(&collected), + 0 + ); + + // Now try to burn. + let burn_amount = 69; + let i = 0; + while (i < n) { + let burned = balance::split(&mut collected, burn_amount); + let check_amount = + wrapped_asset::burn_test_only(&mut asset, burned); + assert!(check_amount == burn_amount, 0); + i = i + 1; + }; + let remaining = n * mint_amount - n * burn_amount; + assert!(wrapped_asset::total_supply(&asset) == remaining, 0); + assert!(balance::value(&collected) == remaining, 0); + + // Clean up. + balance::destroy_for_testing(collected); + wrapped_asset::destroy(asset); + test_scenario::return_shared(coin_meta); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = wrapped_asset::E_SUI_CHAIN)] + // In this negative test case, we attempt to register a native coin as a + // wrapped coin. + fun test_cannot_new_sui_chain() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Initialize new coin type. + coin_native_10::init_test_only(test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Sui's chain ID is not allowed. + let invalid_meta = + asset_meta::new( + external_address::default(), + chain_id(), + 10, + string::utf8(b""), + string::utf8(b"") + ); + + // Upgrade cap belonging to coin type. + let upgrade_cap = + package::test_publish( + object::id_from_address(@token_bridge), + test_scenario::ctx(scenario) + ); + + let treasury_cap = test_scenario::take_shared>(scenario); + let coin_meta = test_scenario::take_shared>(scenario); + + // You shall not pass! + let asset = + wrapped_asset::new_test_only( + invalid_meta, + &mut coin_meta, + treasury_cap, + upgrade_cap + ); + + // Clean up. + wrapped_asset::destroy(asset); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wrapped_asset::E_ASSET_META_MISMATCH)] + /// In this negative test case, we attempt to update with a mismatching + /// chain. + fun test_cannot_update_metadata_asset_meta_mismatch_token_address() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let parsed_meta = coin_wrapped_12::token_meta(); + let expected_token_chain = asset_meta::token_chain(&parsed_meta); + let expected_token_address = asset_meta::token_address(&parsed_meta); + let expected_native_decimals = + asset_meta::native_decimals(&parsed_meta); + + // Publish coin. + let treasury_cap = + coin_wrapped_12::init_and_take_treasury_cap( + scenario, + caller + ); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Upgrade cap belonging to coin type. + let upgrade_cap = + package::test_publish( + object::id_from_address(@token_bridge), + test_scenario::ctx(scenario) + ); + + let coin_meta = test_scenario::take_shared(scenario); + + // Make new. + let asset = + wrapped_asset::new_test_only( + parsed_meta, + &mut coin_meta, + treasury_cap, + upgrade_cap + ); + + let invalid_meta = + asset_meta::new( + external_address::default(), + expected_token_chain, + expected_native_decimals, + string::utf8(b""), + string::utf8(b""), + ); + assert!( + asset_meta::token_address(&invalid_meta) != expected_token_address, + 0 + ); + assert!( + asset_meta::token_chain(&invalid_meta) == expected_token_chain, + 0 + ); + assert!( + asset_meta::native_decimals(&invalid_meta) == expected_native_decimals, + 0 + ); + + // You shall not pass! + wrapped_asset::update_metadata_test_only(&mut asset, &mut coin_meta, invalid_meta); + + // Clean up. + wrapped_asset::destroy(asset); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wrapped_asset::E_ASSET_META_MISMATCH)] + /// In this negative test case, we attempt to update with a mismatching + /// chain. + fun test_cannot_update_metadata_asset_meta_mismatch_token_chain() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let parsed_meta = coin_wrapped_12::token_meta(); + let expected_token_chain = asset_meta::token_chain(&parsed_meta); + let expected_token_address = asset_meta::token_address(&parsed_meta); + let expected_native_decimals = + asset_meta::native_decimals(&parsed_meta); + + // Publish coin. + let treasury_cap = + coin_wrapped_12::init_and_take_treasury_cap( + scenario, + caller + ); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Upgrade cap belonging to coin type. + let upgrade_cap = + package::test_publish( + object::id_from_address(@token_bridge), + test_scenario::ctx(scenario) + ); + + let coin_meta = test_scenario::take_shared(scenario); + + // Make new. + let asset = + wrapped_asset::new_test_only( + parsed_meta, + &mut coin_meta, + treasury_cap, + upgrade_cap + ); + + let invalid_meta = + asset_meta::new( + expected_token_address, + chain_id(), + expected_native_decimals, + string::utf8(b""), + string::utf8(b""), + ); + assert!( + asset_meta::token_address(&invalid_meta) == expected_token_address, + 0 + ); + assert!( + asset_meta::token_chain(&invalid_meta) != expected_token_chain, + 0 + ); + assert!( + asset_meta::native_decimals(&invalid_meta) == expected_native_decimals, + 0 + ); + + // You shall not pass! + wrapped_asset::update_metadata_test_only(&mut asset, &mut coin_meta, invalid_meta); + + // Clean up. + wrapped_asset::destroy(asset); + + abort 42 + } + + #[test] + #[expected_failure( + abort_code = wormhole::package_utils::E_INVALID_UPGRADE_CAP + )] + fun test_cannot_new_upgrade_cap_mismatch() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Publish coin. + let treasury_cap = + coin_wrapped_12::init_and_take_treasury_cap( + scenario, + caller + ); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Upgrade cap belonging to coin type. + let upgrade_cap = + package::test_publish( + object::id_from_address(@0xbadc0de), + test_scenario::ctx(scenario) + ); + + let coin_meta = test_scenario::take_shared(scenario); + + // You shall not pass! + let asset = + wrapped_asset::new_test_only( + coin_wrapped_12::token_meta(), + &mut coin_meta, + treasury_cap, + upgrade_cap + ); + + // Clean up. + wrapped_asset::destroy(asset); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/setup.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/setup.move new file mode 100644 index 0000000000..89beaf418b --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/setup.move @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements the mechanism to publish the Token Bridge contract +/// and initialize `State` as a shared object. +module token_bridge::setup { + use sui::object::{Self, UID}; + use sui::package::{Self, UpgradeCap}; + use sui::transfer::{Self}; + use sui::tx_context::{Self, TxContext}; + use wormhole::emitter::{EmitterCap}; + + use token_bridge::state::{Self}; + + /// Capability created at `init`, which will be destroyed once + /// `init_and_share_state` is called. This ensures only the deployer can + /// create the shared `State`. + struct DeployerCap has key, store { + id: UID + } + + /// Called automatically when module is first published. Transfers + /// `DeployerCap` to sender. + /// + /// Only `setup::init_and_share_state` requires `DeployerCap`. + fun init(ctx: &mut TxContext) { + let deployer = DeployerCap { id: object::new(ctx) }; + transfer::transfer(deployer, tx_context::sender(ctx)); + } + + #[test_only] + public fun init_test_only(ctx: &mut TxContext) { + // NOTE: This exists to mock up sui::package for proposed upgrades. + use sui::package::{Self}; + + init(ctx); + + // This will be created and sent to the transaction sender + // automatically when the contract is published. + transfer::public_transfer( + package::test_publish(object::id_from_address(@token_bridge), ctx), + tx_context::sender(ctx) + ); + } + + #[allow(lint(share_owned))] + /// Only the owner of the `DeployerCap` can call this method. This + /// method destroys the capability and shares the `State` object. + public fun complete( + deployer: DeployerCap, + upgrade_cap: UpgradeCap, + emitter_cap: EmitterCap, + governance_chain: u16, + governance_contract: vector, + ctx: &mut TxContext + ) { + wormhole::package_utils::assert_package_upgrade_cap( + &upgrade_cap, + package::compatible_policy(), + 1 + ); + + // Destroy deployer cap. + let DeployerCap { id } = deployer; + object::delete(id); + + // Share new state. + transfer::public_share_object( + state::new( + emitter_cap, + upgrade_cap, + governance_chain, + wormhole::external_address::new_nonzero( + wormhole::bytes32::from_bytes(governance_contract) + ), + ctx + )); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/state.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/state.move new file mode 100644 index 0000000000..fbe8dab4a4 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/state.move @@ -0,0 +1,396 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements the global state variables for Token Bridge as a +/// shared object. The `State` object is used to perform anything that requires +/// access to data that defines the Token Bridge contract. Examples of which are +/// accessing registered assets and verifying `VAA` intended for Token Bridge by +/// checking the emitter against its own registered emitters. +module token_bridge::state { + use sui::object::{Self, ID, UID}; + use sui::package::{UpgradeCap, UpgradeReceipt, UpgradeTicket}; + use sui::table::{Self, Table}; + use sui::tx_context::{TxContext}; + use wormhole::bytes32::{Self, Bytes32}; + use wormhole::consumed_vaas::{Self, ConsumedVAAs}; + use wormhole::emitter::{EmitterCap}; + use wormhole::external_address::{ExternalAddress}; + use wormhole::package_utils::{Self}; + use wormhole::publish_message::{MessageTicket}; + + use token_bridge::token_registry::{Self, TokenRegistry, VerifiedAsset}; + use token_bridge::version_control::{Self}; + + /// Build digest does not agree with current implementation. + const E_INVALID_BUILD_DIGEST: u64 = 0; + /// Specified version does not match this build's version. + const E_VERSION_MISMATCH: u64 = 1; + /// Emitter has already been used to emit Wormhole messages. + const E_USED_EMITTER: u64 = 2; + + friend token_bridge::attest_token; + friend token_bridge::complete_transfer; + friend token_bridge::complete_transfer_with_payload; + friend token_bridge::create_wrapped; + friend token_bridge::migrate; + friend token_bridge::register_chain; + friend token_bridge::setup; + friend token_bridge::transfer_tokens; + friend token_bridge::transfer_tokens_with_payload; + friend token_bridge::upgrade_contract; + friend token_bridge::vaa; + + /// Capability reflecting that the current build version is used to invoke + /// state methods. + struct LatestOnly has drop {} + + /// Container for all state variables for Token Bridge. + struct State has key, store { + id: UID, + + /// Governance chain ID. + governance_chain: u16, + + /// Governance contract address. + governance_contract: ExternalAddress, + + /// Set of consumed VAA hashes. + consumed_vaas: ConsumedVAAs, + + /// Emitter capability required to publish Wormhole messages. + emitter_cap: EmitterCap, + + /// Registry for foreign Token Bridge contracts. + emitter_registry: Table, + + /// Registry for native and wrapped assets. + token_registry: TokenRegistry, + + /// Upgrade capability. + upgrade_cap: UpgradeCap + } + + /// Create new `State`. This is only executed using the `setup` module. + public(friend) fun new( + emitter_cap: EmitterCap, + upgrade_cap: UpgradeCap, + governance_chain: u16, + governance_contract: ExternalAddress, + ctx: &mut TxContext + ): State { + assert!(wormhole::emitter::sequence(&emitter_cap) == 0, E_USED_EMITTER); + + let state = State { + id: object::new(ctx), + governance_chain, + governance_contract, + consumed_vaas: consumed_vaas::new(ctx), + emitter_cap, + emitter_registry: table::new(ctx), + token_registry: token_registry::new(ctx), + upgrade_cap + }; + + // Set first version and initialize package info. This will be used for + // emitting information of successful migrations. + let upgrade_cap = &state.upgrade_cap; + package_utils::init_package_info( + &mut state.id, + version_control::current_version(), + upgrade_cap + ); + + state + } + + //////////////////////////////////////////////////////////////////////////// + // + // Simple Getters + // + // These methods do not require `LatestOnly` for access. Anyone is free to + // access these values. + // + //////////////////////////////////////////////////////////////////////////// + + /// Retrieve governance module name. + public fun governance_module(): Bytes32 { + // A.K.A. "TokenBridge". + bytes32::new( + x"000000000000000000000000000000000000000000546f6b656e427269646765" + ) + } + + /// Retrieve governance chain ID, which is governance's emitter chain ID. + public fun governance_chain(self: &State): u16 { + self.governance_chain + } + + /// Retrieve governance emitter address. + public fun governance_contract(self: &State): ExternalAddress { + self.governance_contract + } + + /// Retrieve immutable reference to `TokenRegistry`. + public fun borrow_token_registry( + self: &State + ): &TokenRegistry { + &self.token_registry + } + + public fun borrow_emitter_registry( + self: &State + ): &Table { + &self.emitter_registry + } + + public fun verified_asset( + self: &State + ): VerifiedAsset { + token_registry::assert_has(&self.token_registry); + token_registry::verified_asset(&self.token_registry) + } + + #[test_only] + public fun borrow_mut_token_registry_test_only( + self: &mut State + ): &mut TokenRegistry { + borrow_mut_token_registry(&assert_latest_only(self), self) + } + + #[test_only] + public fun migrate_version_test_only( + self: &mut State, + old_version: Old, + new_version: New + ) { + wormhole::package_utils::update_version_type_test_only( + &mut self.id, + old_version, + new_version + ); + } + + #[test_only] + public fun test_upgrade(self: &mut State) { + let test_digest = bytes32::from_bytes(b"new build"); + let ticket = authorize_upgrade(self, test_digest); + let receipt = sui::package::test_upgrade(ticket); + commit_upgrade(self, receipt); + } + + #[test_only] + public fun reverse_migrate_version(self: &mut State) { + package_utils::update_version_type_test_only( + &mut self.id, + version_control::current_version(), + version_control::previous_version() + ); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Privileged `State` Access + // + // This section of methods require a `LatestOnly`, which can only be + // created within the Token Bridge package. This capability allows special + // access to the `State` object where we require that the latest build is + // used for these interactions. + // + // NOTE: A lot of these methods are still marked as `(friend)` as a safety + // precaution. When a package is upgraded, friend modifiers can be + // removed. + // + //////////////////////////////////////////////////////////////////////////// + + /// Obtain a capability to interact with `State` methods. This method checks + /// that we are running the current build. + /// + /// NOTE: This method allows caching the current version check so we avoid + /// multiple checks to dynamic fields. + public(friend) fun assert_latest_only(self: &State): LatestOnly { + package_utils::assert_version( + &self.id, + version_control::current_version() + ); + + LatestOnly {} + } + + /// Obtain a capability to interact with `State` methods. This method checks + /// that we are running the current build and that the specified `Version` + /// equals the current version. This method is useful when external modules + /// invoke Token Bridge and we need to check that the external module's + /// version is up-to-date (e.g. `create_wrapped::prepare_registration`). + /// + /// NOTE: This method allows caching the current version check so we avoid + /// multiple checks to dynamic fields. + public(friend) fun assert_latest_only_specified( + self: &State + ): LatestOnly { + use std::type_name::{get}; + + // Explicitly check the type names. + let current_type = + package_utils::type_of_version(version_control::current_version()); + assert!(current_type == get(), E_VERSION_MISMATCH); + + assert_latest_only(self) + } + + /// Store `VAA` hash as a way to claim a VAA. This method prevents a VAA + /// from being replayed. + public(friend) fun borrow_mut_consumed_vaas( + _: &LatestOnly, + self: &mut State + ): &mut ConsumedVAAs { + borrow_mut_consumed_vaas_unchecked(self) + } + + /// Store `VAA` hash as a way to claim a VAA. This method prevents a VAA + /// from being replayed. + /// + /// NOTE: This method does not require `LatestOnly`. Only methods in the + /// `upgrade_contract` module requires this to be unprotected to prevent + /// a corrupted upgraded contract from bricking upgradability. + public(friend) fun borrow_mut_consumed_vaas_unchecked( + self: &mut State + ): &mut ConsumedVAAs { + &mut self.consumed_vaas + } + + /// Publish Wormhole message using Token Bridge's `EmitterCap`. + public(friend) fun prepare_wormhole_message( + _: &LatestOnly, + self: &mut State, + nonce: u32, + payload: vector + ): MessageTicket { + wormhole::publish_message::prepare_message( + &mut self.emitter_cap, + nonce, + payload, + ) + } + + /// Retrieve mutable reference to `TokenRegistry`. + public(friend) fun borrow_mut_token_registry( + _: &LatestOnly, + self: &mut State + ): &mut TokenRegistry { + &mut self.token_registry + } + + public(friend) fun borrow_mut_emitter_registry( + _: &LatestOnly, + self: &mut State + ): &mut Table { + &mut self.emitter_registry + } + + public(friend) fun current_package(_: &LatestOnly, self: &State): ID { + package_utils::current_package(&self.id) + } + + //////////////////////////////////////////////////////////////////////////// + // + // Upgradability + // + // A special space that controls upgrade logic. These methods are invoked + // via the `upgrade_contract` module. + // + // Also in this section is managing contract migrations, which uses the + // `migrate` module to officially roll state access to the latest build. + // Only those methods that require `LatestOnly` will be affected by an + // upgrade. + // + //////////////////////////////////////////////////////////////////////////// + + /// Issue an `UpgradeTicket` for the upgrade. + /// + /// NOTE: The Sui VM performs a check that this method is executed from the + /// latest published package. If someone were to try to execute this using + /// a stale build, the transaction will revert with `PackageUpgradeError`, + /// specifically `PackageIDDoesNotMatch`. + public(friend) fun authorize_upgrade( + self: &mut State, + package_digest: Bytes32 + ): UpgradeTicket { + let cap = &mut self.upgrade_cap; + package_utils::authorize_upgrade(&mut self.id, cap, package_digest) + } + + /// Finalize the upgrade that ran to produce the given `receipt`. + /// + /// NOTE: The Sui VM performs a check that this method is executed from the + /// latest published package. If someone were to try to execute this using + /// a stale build, the transaction will revert with `PackageUpgradeError`, + /// specifically `PackageIDDoesNotMatch`. + public(friend) fun commit_upgrade( + self: &mut State, + receipt: UpgradeReceipt + ): (ID, ID) { + let cap = &mut self.upgrade_cap; + package_utils::commit_upgrade(&mut self.id, cap, receipt) + } + + /// Method executed by the `migrate` module to roll access from one package + /// to another. This method will be called from the upgraded package. + public(friend) fun migrate_version(self: &mut State) { + package_utils::migrate_version( + &mut self.id, + version_control::previous_version(), + version_control::current_version() + ); + } + + /// As a part of the migration, we verify that the upgrade contract VAA's + /// encoded package digest used in `migrate` equals the one used to conduct + /// the upgrade. + public(friend) fun assert_authorized_digest( + _: &LatestOnly, + self: &State, + digest: Bytes32 + ) { + let authorized = package_utils::authorized_digest(&self.id); + assert!(digest == authorized, E_INVALID_BUILD_DIGEST); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Special State Interaction via Migrate + // + // A VERY special space that manipulates `State` via calling `migrate`. + // + // PLEASE KEEP ANY METHODS HERE AS FRIENDS. We want the ability to remove + // these for future builds. + // + //////////////////////////////////////////////////////////////////////////// + + /// This method is used to make modifications to `State` when `migrate` is + /// called. This method name should change reflecting which version this + /// contract is migrating to. + /// + /// NOTE: Please keep this method as public(friend) because we never want + /// to expose this method as a public method. + public(friend) fun migrate__v__0_2_0(_self: &mut State) { + // Intentionally do nothing. + } + + #[test_only] + /// Bloody hack. + /// + /// This method is used to set up tests where we migrate to a new version, + /// which is meant to test that modules protected by version control will + /// break. + public fun reverse_migrate__v__dummy(_self: &mut State) { + // Intentionally do nothing. + } + + //////////////////////////////////////////////////////////////////////////// + // + // Deprecated + // + // Dumping grounds for old structs and methods. These things should not + // be used in future builds. + // + //////////////////////////////////////////////////////////////////////////// +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_10.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_10.move new file mode 100644 index 0000000000..c87282a157 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_10.move @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: Apache 2 + +#[test_only] +module token_bridge::coin_native_10 { + use std::option::{Self}; + use sui::balance::{Self, Balance}; + use sui::coin::{Self, CoinMetadata, TreasuryCap}; + use sui::test_scenario::{Self, Scenario}; + use sui::transfer::{Self}; + use sui::tx_context::{TxContext}; + + use token_bridge::native_asset::{Self}; + use token_bridge::state::{Self}; + use token_bridge::token_registry::{Self}; + + struct COIN_NATIVE_10 has drop {} + + // This module creates a Sui-native token for testing purposes, + // for example in complete_transfer, where we create a native coin, + // mint some and deposit in the token bridge, then complete transfer + // and ultimately transfer a portion of those native coins to a recipient. + fun init(coin_witness: COIN_NATIVE_10, ctx: &mut TxContext) { + let ( + treasury_cap, + coin_metadata + ) = + coin::create_currency( + coin_witness, + 10, + b"DEC10", + b"Decimals 10", + b"Coin with 10 decimals for testing purposes.", + option::none(), + ctx + ); + + // Allow us to mutate metadata if we need. + transfer::public_share_object(coin_metadata); + + // Give everyone access to `TrasuryCap`. + transfer::public_share_object(treasury_cap); + } + + #[test_only] + /// For a test scenario, register this native asset. + /// + /// NOTE: Even though this module is `#[test_only]`, this method is tagged + /// with the same macro as a trick to allow another method within this + /// module to call `init` using OTW. + public fun init_and_register(scenario: &mut Scenario, caller: address) { + use token_bridge::token_bridge_scenario::{return_state, take_state}; + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Publish coin. + init(COIN_NATIVE_10 {}, test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + let coin_meta = take_metadata(scenario); + + // Register asset. + let registry = + state::borrow_mut_token_registry_test_only(&mut token_bridge_state); + token_registry::add_new_native_test_only(registry, &coin_meta); + + // Clean up. + return_state(token_bridge_state); + return_metadata(coin_meta); + } + + #[test_only] + public fun init_register_and_mint( + scenario: &mut Scenario, + caller: address, + amount: u64 + ): Balance { + // First publish and register. + init_and_register(scenario, caller); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Mint. + balance::create_for_testing(amount) + } + + #[test_only] + public fun init_register_and_deposit( + scenario: &mut Scenario, + caller: address, + amount: u64 + ) { + use token_bridge::token_bridge_scenario::{return_state, take_state}; + + let minted = init_register_and_mint(scenario, caller, amount); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + native_asset::deposit_test_only( + token_registry::borrow_mut_native_test_only( + state::borrow_mut_token_registry_test_only( + &mut token_bridge_state + ) + ), + minted + ); + + return_state(token_bridge_state); + } + + #[test_only] + public fun init_test_only(ctx: &mut TxContext) { + init(COIN_NATIVE_10 {}, ctx); + } + + public fun take_metadata( + scenario: &Scenario + ): CoinMetadata { + test_scenario::take_shared(scenario) + } + + public fun return_metadata( + metadata: CoinMetadata + ) { + test_scenario::return_shared(metadata); + } + + public fun take_treasury_cap( + scenario: &Scenario + ): TreasuryCap { + test_scenario::take_shared(scenario) + } + + public fun return_treasury_cap( + treasury_cap: TreasuryCap + ) { + test_scenario::return_shared(treasury_cap); + } + + public fun take_globals( + scenario: &Scenario + ): ( + TreasuryCap, + CoinMetadata + ) { + ( + take_treasury_cap(scenario), + take_metadata(scenario) + ) + } + + public fun return_globals( + treasury_cap: TreasuryCap, + metadata: CoinMetadata + ) { + return_treasury_cap(treasury_cap); + return_metadata(metadata); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_4.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_4.move new file mode 100644 index 0000000000..889d036200 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_4.move @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: Apache 2 + +#[test_only] +module token_bridge::coin_native_4 { + use std::option::{Self}; + use sui::balance::{Self, Balance}; + use sui::coin::{Self, CoinMetadata, TreasuryCap}; + use sui::test_scenario::{Self, Scenario}; + use sui::transfer::{Self}; + use sui::tx_context::{TxContext}; + + use token_bridge::native_asset::{Self}; + use token_bridge::state::{Self}; + use token_bridge::token_registry::{Self}; + + struct COIN_NATIVE_4 has drop {} + + // This module creates a Sui-native token for testing purposes, + // for example in complete_transfer, where we create a native coin, + // mint some and deposit in the token bridge, then complete transfer + // and ultimately transfer a portion of those native coins to a recipient. + fun init(coin_witness: COIN_NATIVE_4, ctx: &mut TxContext) { + let ( + treasury_cap, + coin_metadata + ) = + coin::create_currency( + coin_witness, + 4, + b"DEC4", + b"Decimals 4", + b"Coin with 4 decimals for testing purposes.", + option::none(), + ctx + ); + + // Let's make the metadata shared. + transfer::public_share_object(coin_metadata); + + // Give everyone access to `TrasuryCap`. + transfer::public_share_object(treasury_cap); + } + + #[test_only] + /// For a test scenario, register this native asset. + /// + /// NOTE: Even though this module is `#[test_only]`, this method is tagged + /// with the same macro as a trick to allow another method within this + /// module to call `init` using OTW. + public fun init_and_register(scenario: &mut Scenario, caller: address) { + use token_bridge::token_bridge_scenario::{return_state, take_state}; + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Publish coin. + init(COIN_NATIVE_4 {}, test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + let coin_meta = take_metadata(scenario); + + // Register asset. + let registry = + state::borrow_mut_token_registry_test_only(&mut token_bridge_state); + token_registry::add_new_native_test_only(registry, &coin_meta); + + // Clean up. + return_state(token_bridge_state); + return_metadata(coin_meta); + } + + #[test_only] + public fun init_register_and_mint( + scenario: &mut Scenario, + caller: address, + amount: u64 + ): Balance { + // First publish and register. + init_and_register(scenario, caller); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Mint. + balance::create_for_testing(amount) + } + + #[test_only] + public fun init_register_and_deposit( + scenario: &mut Scenario, + caller: address, + amount: u64 + ) { + use token_bridge::token_bridge_scenario::{return_state, take_state}; + + let minted = init_register_and_mint(scenario, caller, amount); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + native_asset::deposit_test_only( + token_registry::borrow_mut_native_test_only( + state::borrow_mut_token_registry_test_only( + &mut token_bridge_state + ) + ), + minted + ); + + return_state(token_bridge_state); + } + + #[test_only] + public fun init_test_only(ctx: &mut TxContext) { + init(COIN_NATIVE_4 {}, ctx); + } + + public fun take_metadata( + scenario: &Scenario + ): CoinMetadata { + test_scenario::take_shared(scenario) + } + + public fun return_metadata( + metadata: CoinMetadata + ) { + test_scenario::return_shared(metadata); + } + + public fun take_treasury_cap( + scenario: &Scenario + ): TreasuryCap { + test_scenario::take_shared(scenario) + } + + public fun return_treasury_cap( + treasury_cap: TreasuryCap + ) { + test_scenario::return_shared(treasury_cap); + } + + public fun take_globals( + scenario: &Scenario + ): ( + TreasuryCap, + CoinMetadata + ) { + ( + take_treasury_cap(scenario), + take_metadata(scenario) + ) + } + + public fun return_globals( + treasury_cap: TreasuryCap, + metadata: CoinMetadata + ) { + return_treasury_cap(treasury_cap); + return_metadata(metadata); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_12.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_12.move new file mode 100644 index 0000000000..74932765ef --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_12.move @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: Apache 2 + +#[test_only] +module token_bridge::coin_wrapped_12 { + use sui::balance::{Balance}; + use sui::package::{UpgradeCap}; + use sui::coin::{CoinMetadata, TreasuryCap}; + use sui::test_scenario::{Self, Scenario}; + use sui::transfer::{Self}; + use sui::tx_context::{Self, TxContext}; + + use token_bridge::asset_meta::{Self, AssetMeta}; + use token_bridge::create_wrapped::{Self, WrappedAssetSetup}; + use token_bridge::state::{Self}; + use token_bridge::token_registry::{Self}; + use token_bridge::wrapped_asset::{Self}; + + use token_bridge::version_control::{V__0_2_0 as V__CURRENT}; + + struct COIN_WRAPPED_12 has drop {} + + const VAA: vector = + x"0100000000010080366065746148420220f25a6275097370e8db40984529a6676b7a5fc9feb11755ec49ca626b858ddfde88d15601f85ab7683c5f161413b0412143241c700aff010000000100000001000200000000000000000000000000000000000000000000000000000000deadbeef000000000150eb23000200000000000000000000000000000000000000000000000000000000beefface00020c424545460000000000000000000000000000000000000000000000000000000042656566206661636520546f6b656e0000000000000000000000000000000000"; + + const UPDATED_VAA: vector = + x"0100000000010062f4dcd21bbbc4af8b8baaa2da3a0b168efc4c975de5b828c7a3c710b67a0a0d476d10a74aba7a7867866daf97d1372d8e6ee62ccc5ae522e3e603c67fa23787000000000000000045000200000000000000000000000000000000000000000000000000000000deadbeef00000000000000010f0200000000000000000000000000000000000000000000000000000000beefface00020c424545463f3f3f20616e642070726f666974000000000000000000000000000042656566206661636520546f6b656e3f3f3f20616e642070726f666974000000"; + + fun init(witness: COIN_WRAPPED_12, ctx: &mut TxContext) { + let ( + setup, + upgrade_cap + ) = + create_wrapped::new_setup_current( + witness, + 8, // capped to 8 + ctx + ); + transfer::public_transfer(setup, tx_context::sender(ctx)); + transfer::public_transfer(upgrade_cap, tx_context::sender(ctx)); + } + + #[test_only] + public fun init_test_only(ctx: &mut TxContext) { + init(COIN_WRAPPED_12 {}, ctx); + } + + + public fun encoded_vaa(): vector { + VAA + } + + public fun encoded_updated_vaa(): vector { + UPDATED_VAA + } + + #[allow(implicit_const_copy)] + public fun token_meta(): AssetMeta { + asset_meta::deserialize_test_only( + wormhole::vaa::peel_payload_from_vaa(&VAA) + ) + } + + #[allow(implicit_const_copy)] + public fun updated_token_meta(): AssetMeta { + asset_meta::deserialize_test_only( + wormhole::vaa::peel_payload_from_vaa(&UPDATED_VAA) + ) + } + + #[test_only] + /// for a test scenario, simply deploy the coin and expose `Supply`. + public fun init_and_take_treasury_cap( + scenario: &mut Scenario, + caller: address + ): TreasuryCap { + use token_bridge::create_wrapped; + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Publish coin. + init(COIN_WRAPPED_12 {}, test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + create_wrapped::take_treasury_cap( + test_scenario::take_from_sender(scenario) + ) + } + + #[test_only] + /// For a test scenario, register this wrapped asset. + /// + /// NOTE: Even though this module is `#[test_only]`, this method is tagged + /// with the same macro as a trick to allow another method within this + /// module to call `init` using OTW. + public fun init_and_register( + scenario: &mut Scenario, + caller: address + ) { + use token_bridge::token_bridge_scenario::{return_state, take_state}; + use wormhole::wormhole_scenario::{parse_and_verify_vaa}; + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Publish coin. + init(COIN_WRAPPED_12 {}, test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + + let verified_vaa = parse_and_verify_vaa(scenario, VAA); + let msg = + token_bridge::vaa::verify_only_once( + &mut token_bridge_state, + verified_vaa + ); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let coin_meta = + test_scenario::take_shared>(scenario); + + // Register the attested asset. + create_wrapped::complete_registration( + &mut token_bridge_state, + &mut coin_meta, + test_scenario::take_from_sender< + WrappedAssetSetup + >( + scenario + ), + test_scenario::take_from_sender(scenario), + msg + ); + + test_scenario::return_shared(coin_meta); + + // Clean up. + return_state(token_bridge_state); + } + + #[test_only] + /// NOTE: Even though this module is `#[test_only]`, this method is tagged + /// with the same macro as a trick to allow another method within this + /// module to call `init` using OTW. + public fun init_register_and_mint( + scenario: &mut Scenario, + caller: address, + amount: u64 + ): Balance { + use token_bridge::token_bridge_scenario::{return_state, take_state}; + + // First publish and register. + init_and_register(scenario, caller); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + let minted = + wrapped_asset::mint_test_only( + token_registry::borrow_mut_wrapped_test_only( + state::borrow_mut_token_registry_test_only( + &mut token_bridge_state + ) + ), + amount + ); + + return_state(token_bridge_state); + + minted + } +} + +#[test_only] +module token_bridge::coin_wrapped_12_tests { + use token_bridge::asset_meta::{Self}; + use token_bridge::coin_wrapped_12::{token_meta}; + + #[test] + fun test_native_decimals() { + let meta = token_meta(); + assert!(asset_meta::native_decimals(&meta) == 12, 0); + asset_meta::destroy(meta); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_7.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_7.move new file mode 100644 index 0000000000..fa09e1b444 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_7.move @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: Apache 2 + +#[test_only] +module token_bridge::coin_wrapped_7 { + use sui::balance::{Balance}; + use sui::coin::{CoinMetadata, TreasuryCap}; + use sui::package::{UpgradeCap}; + use sui::test_scenario::{Self, Scenario}; + use sui::transfer::{Self}; + use sui::tx_context::{Self, TxContext}; + + use token_bridge::asset_meta::{Self, AssetMeta}; + use token_bridge::create_wrapped::{Self, WrappedAssetSetup}; + use token_bridge::state::{Self}; + use token_bridge::token_registry::{Self}; + use token_bridge::wrapped_asset::{Self}; + + use token_bridge::version_control::{V__0_2_0 as V__CURRENT}; + + struct COIN_WRAPPED_7 has drop {} + + // TODO: need to fix the emitter address + // +------------------------------------------------------------------------------+ + // | Wormhole VAA v1 | nonce: 69 | time: 0 | + // | guardian set #0 | #1 | consistency: 15 | + // |------------------------------------------------------------------------------| + // | Signature: | + // | #0: 3d8fd671611d84801dc9d14a07835e8729d217b1aac77b054175d0f91294... | + // |------------------------------------------------------------------------------| + // | Emitter: 0x00000000000000000000000000000000deadbeef (Ethereum) | + // |------------------------------------------------------------------------------| + // | Token attestation | + // | decimals: 7 | + // | Token: 0x00000000000000000000000000000000deafface (Ethereum) | + // | Symbol: DEC7 | + // | Name: DECIMALS 7 | + // +------------------------------------------------------------------------------+ + const VAA: vector = + x"010000000001003d8fd671611d84801dc9d14a07835e8729d217b1aac77b054175d0f91294040742a1ed6f3e732b2fbf208e64422816accf89dd0cd3ead20d2e0fb3d372ce221c010000000000000045000200000000000000000000000000000000000000000000000000000000deadbeef00000000000000010f0200000000000000000000000000000000000000000000000000000000deafface000207000000000000000000000000000000000000000000000000000000004445433700000000000000000000000000000000000000000000444543494d414c532037"; + + fun init(witness: COIN_WRAPPED_7, ctx: &mut TxContext) { + let ( + setup, + upgrade_cap + ) = + create_wrapped::new_setup_current( + witness, + 7, + ctx + ); + transfer::public_transfer(setup, tx_context::sender(ctx)); + transfer::public_transfer(upgrade_cap, tx_context::sender(ctx)); + } + + #[test_only] + public fun init_test_only(ctx: &mut TxContext) { + init(COIN_WRAPPED_7 {}, ctx); + } + + public fun encoded_vaa(): vector { + VAA + } + + #[allow(implicit_const_copy)] + public fun token_meta(): AssetMeta { + asset_meta::deserialize_test_only( + wormhole::vaa::peel_payload_from_vaa(&VAA) + ) + } + + #[test_only] + /// for a test scenario, simply deploy the coin and expose `TreasuryCap`. + public fun init_and_take_treasury_cap( + scenario: &mut Scenario, + caller: address + ): TreasuryCap { + use token_bridge::create_wrapped; + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Publish coin. + init(COIN_WRAPPED_7 {}, test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + create_wrapped::take_treasury_cap( + test_scenario::take_from_sender(scenario) + ) + } + + #[test_only] + /// For a test scenario, register this wrapped asset. + /// + /// NOTE: Even though this module is `#[test_only]`, this method is tagged + /// with the same macro as a trick to allow another method within this + /// module to call `init` using OTW. + public fun init_and_register( + scenario: &mut Scenario, + caller: address + ) { + use token_bridge::token_bridge_scenario::{return_state, take_state}; + use wormhole::wormhole_scenario::{parse_and_verify_vaa}; + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Publish coin. + init(COIN_WRAPPED_7 {}, test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + + let verified_vaa = parse_and_verify_vaa(scenario, VAA); + let msg = + token_bridge::vaa::verify_only_once( + &mut token_bridge_state, + verified_vaa + ); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let coin_meta = + test_scenario::take_shared>(scenario); + + // Register the attested asset. + create_wrapped::complete_registration( + &mut token_bridge_state, + &mut coin_meta, + test_scenario::take_from_sender< + WrappedAssetSetup + >( + scenario + ), + test_scenario::take_from_sender(scenario), + msg + ); + + test_scenario::return_shared(coin_meta); + + // Clean up. + return_state(token_bridge_state); + } + + #[test_only] + /// NOTE: Even though this module is `#[test_only]`, this method is tagged + /// with the same macro as a trick to allow another method within this + /// module to call `init` using OTW. + public fun init_register_and_mint( + scenario: &mut Scenario, + caller: address, + amount: u64 + ): Balance { + use token_bridge::token_bridge_scenario::{return_state, take_state}; + + // First publish and register. + init_and_register(scenario, caller); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + let minted = + wrapped_asset::mint_test_only( + token_registry::borrow_mut_wrapped_test_only( + state::borrow_mut_token_registry_test_only( + &mut token_bridge_state + ) + ), + amount + ); + + return_state(token_bridge_state); + + minted + } +} + +#[test_only] +module token_bridge::coin_wrapped_7_tests { + use token_bridge::asset_meta::{Self}; + use token_bridge::coin_wrapped_7::{token_meta}; + + #[test] + fun test_native_decimals() { + let meta = token_meta(); + assert!(asset_meta::native_decimals(&meta) == 7, 0); + asset_meta::destroy(meta); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/dummy_message.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/dummy_message.move new file mode 100644 index 0000000000..d3784e5a97 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/dummy_message.move @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: Apache 2 + +#[test_only] +module token_bridge::dummy_message { + public fun encoded_transfer(): vector { + // let decimals = 8; + // let expected_amount = normalized_amount::from_raw(234567890, decimals); + // let expected_token_address = external_address::from_address(@0xbeef); + // let expected_token_chain = 1; + // let expected_recipient = external_address::from_address(@0xcafe); + // let expected_recipient_chain = 7; + // let expected_relayer_fee = + // normalized_amount::from_raw(123456789, decimals); + x"01000000000000000000000000000000000000000000000000000000000dfb38d2000000000000000000000000000000000000000000000000000000000000beef0001000000000000000000000000000000000000000000000000000000000000cafe000700000000000000000000000000000000000000000000000000000000075bcd15" + } + + public fun encoded_transfer_with_payload(): vector { + // let expected_amount = normalized_amount::from_raw(234567890, 8); + // let expected_token_address = external_address::from_address(@0xbeef); + // let expected_token_chain = 1; + // let expected_recipient = external_address::from_address(@0xcafe); + // let expected_recipient_chain = 7; + // let expected_sender = external_address::from_address(@0xdeadbeef); + // let expected_payload = b"All your base are belong to us."; + x"03000000000000000000000000000000000000000000000000000000000dfb38d2000000000000000000000000000000000000000000000000000000000000beef0001000000000000000000000000000000000000000000000000000000000000cafe0007381dd9078c322a4663c392761a0211b527c127b29583851217f948d62131f409416c6c20796f75722062617365206172652062656c6f6e6720746f2075732e" + } + + public fun encoded_transfer_vaa_native_with_fee(): vector { + // emitterChain: 2, + // emitterAddress: '0x00000000000000000000000000000000000000000000000000000000deadbeef', + // amount: 3000n, + // tokenAddress: '0x0000000000000000000000000000000000000000000000000000000000000001', + // tokenChain: 21, + // toAddress: '0x000000000000000000000000000000000000000000000000000000000000b0b1', + // chain: 21, + // fee: 1000n + x"01000000000100bce07d9dce4e16f564788b0885fa31fa6c5c1bb7ee1f7d0948b8f2c2ae9e87ea4eccfc86affb8b7cf8bfcc774effe0fa7a54066d8a4310a4bb0350fd3097ab25000000000000000000000200000000000000000000000000000000000000000000000000000000deadbeef00000000000000010f010000000000000000000000000000000000000000000000000000000000000bb80bc9c77af025eb7f73940ad00c9d6f06d45253339a110b0f9ff03b822e5877d30015000000000000000000000000000000000000000000000000000000000000b0b1001500000000000000000000000000000000000000000000000000000000000003e8" + } + + public fun encoded_transfer_with_payload_vaa_native(): vector { + // emitterChain: 2, + // emitterAddress: '0x00000000000000000000000000000000000000000000000000000000deadbeef', + // amount: 3000n, + // tokenAddress: '0x0000000000000000000000000000000000000000000000000000000000000001', + // tokenChain: 21, + // toAddress: '0x381dd9078c322a4663c392761a0211b527c127b29583851217f948d62131f409', + // chain: 21, + // fromAddress: '0x000000000000000000000000000000000000000000000000000000000badc0de', + // payload: 'All your base are belong to us.' + x"010000000001003aced6a481653aa534b2f679122e0179de056dbef47442b8c3a1a810dbdfa71049f53cab6e82362800c1558d44993fa6e958a75bd6e6a3472dd278e900041e29010000000000000000000200000000000000000000000000000000000000000000000000000000deadbeef00000000000000010f030000000000000000000000000000000000000000000000000000000000000bb80bc9c77af025eb7f73940ad00c9d6f06d45253339a110b0f9ff03b822e5877d30015381dd9078c322a4663c392761a0211b527c127b29583851217f948d62131f4090015000000000000000000000000000000000000000000000000000000000badc0de416c6c20796f75722062617365206172652062656c6f6e6720746f2075732e" + } + + public fun encoded_transfer_vaa_wrapped_12_with_fee(): vector { + // emitterChain: 2, + // emitterAddress: '0x00000000000000000000000000000000000000000000000000000000deadbeef', + // amount: 3000n, + // tokenAddress: '0x00000000000000000000000000000000000000000000000000000000beefface', + // tokenChain: 2, + // toAddress: '0x000000000000000000000000000000000000000000000000000000000000b0b1', + // chain: 21, + // fee: 1000n + x"010000000001005537ca9a981a62823f57a706f3ceab648391fd99a11631296f798aa394ba6aff73540afefad8634ed573c73c5aa9a16e68906321fa6a4c8a488611b933b1f5b1000000000000000000000200000000000000000000000000000000000000000000000000000000deadbeef00000000000000010f010000000000000000000000000000000000000000000000000000000000000bb800000000000000000000000000000000000000000000000000000000beefface0002000000000000000000000000000000000000000000000000000000000000b0b1001500000000000000000000000000000000000000000000000000000000000003e8" + } + + public fun encoded_transfer_vaa_wrapped_12_without_fee(): vector { + // emitterChain: 2, + // emitterAddress: '0x00000000000000000000000000000000000000000000000000000000deadbeef', + // amount: 3000n, + // tokenAddress: '0x00000000000000000000000000000000000000000000000000000000beefface', + // tokenChain: 2, + // toAddress: '0x000000000000000000000000000000000000000000000000000000000000b0b1', + // chain: 21, + // fee: 0n + x"01000000000100e5558a2955f94fdb174d7868c9f643700174949ac72b90f803bdbea00453ed4c426c055b956060c905189cb710b97916af6a77cd3168f83eca9c66b6366c85c4000000000000000000000200000000000000000000000000000000000000000000000000000000deadbeef00000000000000010f010000000000000000000000000000000000000000000000000000000000000bb800000000000000000000000000000000000000000000000000000000beefface0002000000000000000000000000000000000000000000000000000000000000b0b100150000000000000000000000000000000000000000000000000000000000000000" + } + + public fun encoded_transfer_with_payload_wrapped_12(): vector { + // emitterChain: 2, + // emitterAddress: '0x00000000000000000000000000000000000000000000000000000000deadbeef', + // amount: 3000n, + // tokenAddress: '0x00000000000000000000000000000000000000000000000000000000beefface', + // tokenChain: 2, + // toAddress: '0x381dd9078c322a4663c392761a0211b527c127b29583851217f948d62131f409', + // chain: 21, + // fromAddress: '0x000000000000000000000000000000000000000000000000000000000badc0de', + // payload: 'All your base are belong to us.' + x"0100000000010054968c9be4059d7dc373fff8e80dfc9083c485663517534807d61d11abec64896c4185a2bdd71e3caa713d082c78f5d8b1586c56bd5042dfaba1de0ca0d978a0010000000000000000000200000000000000000000000000000000000000000000000000000000deadbeef00000000000000010f030000000000000000000000000000000000000000000000000000000000000bb800000000000000000000000000000000000000000000000000000000beefface0002381dd9078c322a4663c392761a0211b527c127b29583851217f948d62131f4090015000000000000000000000000000000000000000000000000000000000badc0de416c6c20796f75722062617365206172652062656c6f6e6720746f2075732e" + } + + public fun encoded_transfer_vaa_wrapped_7_with_fee(): vector { + // emitterChain: 2, + // emitterAddress: '0x00000000000000000000000000000000000000000000000000000000deadbeef', + // amount: 3000n, + // tokenAddress: '0x00000000000000000000000000000000000000000000000000000000deafface', + // tokenChain: 2, + // toAddress: '0x000000000000000000000000000000000000000000000000000000000000b0b1', + // chain: 21, + // fee: 1000n + x"01000000000100b9dc34e110e4268ac1e0ef729513083d45b59e0c2cbee8f9fd7d7d2ed900c8ad2a5ca55310fb3741bf3ff8c611e37a2fee2852e09feb491261edf53fcc956edf010000000000000000000200000000000000000000000000000000000000000000000000000000deadbeef00000000000000010f010000000000000000000000000000000000000000000000000000000000000bb800000000000000000000000000000000000000000000000000000000deafface0002000000000000000000000000000000000000000000000000000000000000b0b1001500000000000000000000000000000000000000000000000000000000000003e8" + } + + public fun encoded_transfer_vaa_wrapped_7_without_fee(): vector { + // emitterChain: 2, + // emitterAddress: '0x00000000000000000000000000000000000000000000000000000000deadbeef', + // amount: 3000n, + // tokenAddress: '0x00000000000000000000000000000000000000000000000000000000deafface', + // tokenChain: 2, + // toAddress: '0x000000000000000000000000000000000000000000000000000000000000b0b1', + // chain: 21, + // fee: 0n + x"01000000000100389f0544dc2d3f7095d4e9543ae9f6cb5c9dd6a561e95ed896c870907fe85a94373a455acac8d2ad66154df1cb19ba4ae6c583a1c2839971e6760ecaa1d9fca7000000000000000000000200000000000000000000000000000000000000000000000000000000deadbeef00000000000000010f010000000000000000000000000000000000000000000000000000000000000bb800000000000000000000000000000000000000000000000000000000deafface0002000000000000000000000000000000000000000000000000000000000000b0b100150000000000000000000000000000000000000000000000000000000000000000" + } + + public fun encoded_transfer_vaa_wrapped_12_invalid_target_chain(): vector { + // emitterChain: 2, + // emitterAddress: '0x00000000000000000000000000000000000000000000000000000000deadbeef', + // amount: 3000n, + // tokenAddress: '0x00000000000000000000000000000000000000000000000000000000beefface', + // tokenChain: 2, + // toAddress: '0x000000000000000000000000000000000000000000000000000000000000b0b1', + // chain: 69, + // fee: 0n + x"010000000001009c0b89b21622bde003f8e775daffe343e65d6a537719bc977c85b0b18c26751c7bff61077e74711dfe865d935fa840a7352d7a1ccbcec4be77bfc591cd265a48000000000000000000000200000000000000000000000000000000000000000000000000000000deadbeef00000000000000010f010000000000000000000000000000000000000000000000000000000000000bb800000000000000000000000000000000000000000000000000000000beefface0002000000000000000000000000000000000000000000000000000000000000b0b1004500000000000000000000000000000000000000000000000000000000000003e8" + } + + public fun encoded_transfer_with_payload_wrapped_12_invalid_target_chain(): vector { + // emitterChain: 2, + // emitterAddress: '0x00000000000000000000000000000000000000000000000000000000deadbeef', + // amount: 3000n, + // tokenAddress: '0x00000000000000000000000000000000000000000000000000000000beefface', + // tokenChain: 2, + // toAddress: '0x381dd9078c322a4663c392761a0211b527c127b29583851217f948d62131f409', + // chain: 21, + // fromAddress: '0x000000000000000000000000000000000000000000000000000000000badc0de', + // payload: 'All your base are belong to us.' + x"01000000000100b139a7dbb747b04509ae4f511080a9cb080e423d8db086d5c7553baed2d6151e3fbdd00e691d82662b8d1ed49ec374dba5f82e82df20921151da4b948ddce41e000000000000000000000200000000000000000000000000000000000000000000000000000000deadbeef00000000000000010f030000000000000000000000000000000000000000000000000000000000000bb800000000000000000000000000000000000000000000000000000000beefface0002381dd9078c322a4663c392761a0211b527c127b29583851217f948d62131f4090045000000000000000000000000000000000000000000000000000000000badc0de416c6c20796f75722062617365206172652062656c6f6e6720746f2075732e" + } + + public fun encoded_register_chain_2(): vector { + x"0100000000010015d405c74be6d93c3c33ed6b48d8db70dfb31e0981f8098b2a6c7583083e0c3343d4a1abeb3fc1559674fa067b0c0e2e9de2fafeaecdfeae132de2c33c9d27cc0100000001000000010001000000000000000000000000000000000000000000000000000000000000000400000000016911ae00000000000000000000000000000000000000000000546f6b656e427269646765010000000200000000000000000000000000000000000000000000000000000000deadbeef" + } + + public fun encoded_asset_meta_vaa_foreign_12(): vector { + x"0100000000010080366065746148420220f25a6275097370e8db40984529a6676b7a5fc9feb11755ec49ca626b858ddfde88d15601f85ab7683c5f161413b0412143241c700aff010000000100000001000200000000000000000000000000000000000000000000000000000000deadbeef000000000150eb23000200000000000000000000000000000000000000000000000000000000beefface00020c424545460000000000000000000000000000000000000000000000000000000042656566206661636520546f6b656e0000000000000000000000000000000000" + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/token_bridge_scenario.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/token_bridge_scenario.move new file mode 100644 index 0000000000..d7921ee809 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/token_bridge_scenario.move @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: Apache 2 + +#[test_only] +module token_bridge::token_bridge_scenario { + use std::vector::{Self}; + use sui::balance::{Self}; + use sui::package::{UpgradeCap}; + use sui::test_scenario::{Self, Scenario}; + use wormhole::external_address::{Self}; + use wormhole::wormhole_scenario::{ + deployer, + return_state as return_wormhole_state, + set_up_wormhole, + take_state as take_wormhole_state + }; + + use token_bridge::native_asset::{Self}; + use token_bridge::setup::{Self, DeployerCap}; + use token_bridge::state::{Self, State}; + use token_bridge::token_registry::{Self}; + + public fun set_up_wormhole_and_token_bridge( + scenario: &mut Scenario, + wormhole_fee: u64 + ) { + // init and share wormhole core bridge + set_up_wormhole(scenario, wormhole_fee); + + // Ignore effects. + test_scenario::next_tx(scenario, deployer()); + + // Publish Token Bridge. + setup::init_test_only(test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, deployer()); + + let wormhole_state = take_wormhole_state(scenario); + + let upgrade_cap = + test_scenario::take_from_sender(scenario); + let emitter_cap = + wormhole::emitter::new( + &wormhole_state, + test_scenario::ctx(scenario) + ); + let governance_chain = 1; + let governance_contract = + x"0000000000000000000000000000000000000000000000000000000000000004"; + + // Finally share `State`. + setup::complete( + test_scenario::take_from_sender(scenario), + upgrade_cap, + emitter_cap, + governance_chain, + governance_contract, + test_scenario::ctx(scenario) + ); + + // Clean up. + return_wormhole_state(wormhole_state); + } + + /// Perform an upgrade (which just upticks the current version of what the + /// `State` believes is true). + public fun upgrade_token_bridge(scenario: &mut Scenario) { + // Clean up from activity prior. + test_scenario::next_tx(scenario, person()); + + let token_bridge_state = take_state(scenario); + state::test_upgrade(&mut token_bridge_state); + + // Clean up. + return_state(token_bridge_state); + } + + /// Register arbitrary chain ID with the same emitter address (0xdeadbeef). + public fun register_dummy_emitter(scenario: &mut Scenario, chain: u16) { + // Ignore effects. + test_scenario::next_tx(scenario, person()); + + let token_bridge_state = take_state(scenario); + token_bridge::register_chain::register_new_emitter_test_only( + &mut token_bridge_state, + chain, + external_address::from_address(@0xdeadbeef) + ); + + // Clean up. + return_state(token_bridge_state); + } + + /// Register 0xdeadbeef for multiple chains. + public fun register_dummy_emitters( + scenario: &mut Scenario, + chains: vector + ) { + while (!vector::is_empty(&chains)) { + register_dummy_emitter(scenario, vector::pop_back(&mut chains)); + }; + vector::destroy_empty(chains); + } + + public fun deposit_native( + token_bridge_state: &mut State, + deposit_amount: u64 + ) { + native_asset::deposit_test_only( + token_registry::borrow_mut_native_test_only( + state::borrow_mut_token_registry_test_only(token_bridge_state) + ), + balance::create_for_testing(deposit_amount) + ) + } + + public fun person(): address { + wormhole::wormhole_scenario::person() + } + + public fun two_people(): (address, address) { + wormhole::wormhole_scenario::two_people() + } + + public fun three_people(): (address, address, address) { + wormhole::wormhole_scenario::three_people() + } + + public fun take_state(scenario: &Scenario): State { + test_scenario::take_shared(scenario) + } + + public fun return_state(token_bridge_state: State) { + test_scenario::return_shared(token_bridge_state); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens.move new file mode 100644 index 0000000000..60f1aa2301 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens.move @@ -0,0 +1,1053 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements three methods: `prepare_transfer` and +/// `transfer_tokens`, which are meant to work together. +/// +/// `prepare_transfer` allows a contract to pack token transfer parameters in +/// preparation to bridge these assets to another network. Anyone can call this +/// method to create `TransferTicket`. +/// +/// `transfer_tokens` unpacks the `TransferTicket` and constructs a +/// `MessageTicket`, which will be used by Wormhole's `publish_message` +/// module. +/// +/// The purpose of splitting this token transferring into two steps is in case +/// Token Bridge needs to be upgraded and there is a breaking change for this +/// module, an integrator would not be left broken. It is discouraged to put +/// `transfer_tokens` in an integrator's package logic. Otherwise, this +/// integrator needs to be prepared to upgrade his contract to handle the latest +/// version of `transfer_tokens`. +/// +/// Instead, an integrator is encouraged to execute a transaction block, which +/// executes `transfer_tokens` using the latest Token Bridge package ID and to +/// implement `prepare_transfer` in his contract to produce `PrepareTransfer`. +/// +/// NOTE: Only assets that exist in the `TokenRegistry` can be bridged out, +/// which are native Sui assets that have been attested for via `attest_token` +/// and wrapped foreign assets that have been created using foreign asset +/// metadata via the `create_wrapped` module. +/// +/// See `transfer` module for serialization and deserialization of Wormhole +/// message payload. +module token_bridge::transfer_tokens { + use sui::balance::{Self, Balance}; + use sui::coin::{Self, Coin}; + use wormhole::bytes32::{Self}; + use wormhole::external_address::{Self, ExternalAddress}; + use wormhole::publish_message::{MessageTicket}; + + use token_bridge::native_asset::{Self}; + use token_bridge::normalized_amount::{Self, NormalizedAmount}; + use token_bridge::state::{Self, State, LatestOnly}; + use token_bridge::token_registry::{Self, VerifiedAsset}; + use token_bridge::transfer::{Self}; + use token_bridge::wrapped_asset::{Self}; + + friend token_bridge::transfer_tokens_with_payload; + + /// Relayer fee exceeds `Coin` object's value. + const E_RELAYER_FEE_EXCEEDS_AMOUNT: u64 = 0; + + /// This type represents transfer data for a recipient on a foreign chain. + /// The only way to destroy this type is calling `transfer_tokens`. + /// + /// NOTE: An integrator that expects to bridge assets between his contracts + /// should probably use the `transfer_tokens_with_payload` module, which + /// expects a specific redeemer to complete the transfer (transfers sent + /// using `transfer_tokens` can be redeemed by anyone on behalf of the + /// encoded recipient). + struct TransferTicket { + asset_info: VerifiedAsset, + bridged_in: Balance, + norm_amount: NormalizedAmount, + recipient_chain: u16, + recipient: vector, + relayer_fee: u64, + nonce: u32 + } + + /// `prepare_transfer` constructs token transfer parameters. Any remaining + /// amount (A.K.A. dust) from the funds provided will be returned along with + /// the `TransferTicket` type. The returned coin object is the same object + /// moved into this method. + /// + /// NOTE: Integrators of Token Bridge should be calling only this method + /// from their contracts. This method is not guarded by version control + /// (thus not requiring a reference to the Token Bridge `State` object), so + /// it is intended to work for any package version. + public fun prepare_transfer( + asset_info: VerifiedAsset, + funded: Coin, + recipient_chain: u16, + recipient: vector, + relayer_fee: u64, + nonce: u32 + ): ( + TransferTicket, + Coin + ) { + let ( + bridged_in, + norm_amount + ) = take_truncated_amount(&asset_info, &mut funded); + + let ticket = + TransferTicket { + asset_info, + bridged_in, + norm_amount, + relayer_fee, + recipient_chain, + recipient, + nonce + }; + + // The remaining amount of funded may have dust depending on the + // decimals of this asset. + (ticket, funded) + } + + /// `transfer_tokens` is the only method that can unpack the members of + /// `TransferTicket`. This method takes the balance from this type and + /// bridges this asset out of Sui by either joining its balance in the Token + /// Bridge's custody for native assets or burning its balance for wrapped + /// assets. + /// + /// A `relayer_fee` of some value less than or equal to the bridged balance + /// can be specified to incentivize someone to redeem this transfer on + /// behalf of the `recipient`. + /// + /// This method returns the prepared Wormhole message (which should be + /// consumed by calling `publish_message` in a transaction block). + /// + /// NOTE: This method is guarded by a minimum build version check. This + /// method could break backward compatibility on an upgrade. + /// + /// It is important for integrators to refrain from calling this method + /// within their contracts. This method is meant to be called in a + /// transaction block after receiving a `TransferTicket` from calling + /// `prepare_transfer` within a contract. If in a circumstance where this + /// module has a breaking change in an upgrade, `prepare_transfer` will not + /// be affected by this change. + public fun transfer_tokens( + token_bridge_state: &mut State, + ticket: TransferTicket + ): MessageTicket { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(token_bridge_state); + + let ( + nonce, + encoded_transfer + ) = + bridge_in_and_serialize_transfer( + &latest_only, + token_bridge_state, + ticket + ); + + // Prepare Wormhole message with encoded `Transfer`. + state::prepare_wormhole_message( + &latest_only, + token_bridge_state, + nonce, + encoded_transfer + ) + } + + /// Modify coin based on the decimals of a given coin type, which may + /// leave some amount if the decimals lead to truncating the coin's balance. + /// This method returns the extracted balance (which will be bridged out of + /// Sui) and the normalized amount, which will be encoded in the token + /// transfer payload. + /// + /// NOTE: This is a privileged method, which only this and the + /// `transfer_tokens_with_payload` modules can use. + public(friend) fun take_truncated_amount( + asset_info: &VerifiedAsset, + funded: &mut Coin + ): ( + Balance, + NormalizedAmount + ) { + // Calculate dust. If there is any, `bridged_in` will have remaining + // value after split. `norm_amount` is copied since it is denormalized + // at this step. + let decimals = token_registry::coin_decimals(asset_info); + let norm_amount = + normalized_amount::from_raw(coin::value(funded), decimals); + + // Split the `bridged_in` coin object to return any dust remaining on + // that object. Only bridge in the adjusted amount after de-normalizing + // the normalized amount. + let truncated = + balance::split( + coin::balance_mut(funded), + normalized_amount::to_raw(norm_amount, decimals) + ); + + (truncated, norm_amount) + } + + /// For a given coin type, either burn Token Bridge wrapped assets or + /// deposit coin into Token Bridge's custody. This method returns the + /// canonical token info (chain ID and address), which will be encoded in + /// the token transfer. + /// + /// NOTE: This is a privileged method, which only this and the + /// `transfer_tokens_with_payload` modules can use. + public(friend) fun burn_or_deposit_funds( + latest_only: &LatestOnly, + token_bridge_state: &mut State, + asset_info: &VerifiedAsset, + bridged_in: Balance + ): ( + u16, + ExternalAddress + ) { + // Either burn or deposit depending on `CoinType`. + let registry = + state::borrow_mut_token_registry(latest_only, token_bridge_state); + if (token_registry::is_wrapped(asset_info)) { + wrapped_asset::burn( + token_registry::borrow_mut_wrapped(registry), + bridged_in + ); + } else { + native_asset::deposit( + token_registry::borrow_mut_native(registry), + bridged_in + ); + }; + + // Return canonical token info. + ( + token_registry::token_chain(asset_info), + token_registry::token_address(asset_info) + ) + } + + fun bridge_in_and_serialize_transfer( + latest_only: &LatestOnly, + token_bridge_state: &mut State, + ticket: TransferTicket + ): ( + u32, + vector + ) { + let TransferTicket { + asset_info, + bridged_in, + norm_amount, + recipient_chain, + recipient, + relayer_fee, + nonce + } = ticket; + + // Disallow `relayer_fee` to be greater than the `Coin` object's value. + // Keep in mind that the relayer fee is evaluated against the truncated + // amount. + let amount = sui::balance::value(&bridged_in); + assert!(relayer_fee <= amount, E_RELAYER_FEE_EXCEEDS_AMOUNT); + + // Handle funds and get canonical token info for encoded transfer. + let ( + token_chain, + token_address + ) = burn_or_deposit_funds( + latest_only, + token_bridge_state, + &asset_info, bridged_in + ); + + // Ensure that the recipient is a 32-byte address. + let recipient = external_address::new(bytes32::from_bytes(recipient)); + + // Finally encode `Transfer`. + let encoded = + transfer::serialize( + transfer::new( + norm_amount, + token_address, + token_chain, + recipient, + recipient_chain, + normalized_amount::from_raw( + relayer_fee, + token_registry::coin_decimals(&asset_info) + ) + ) + ); + + (nonce, encoded) + } + + #[test_only] + public fun bridge_in_and_serialize_transfer_test_only( + token_bridge_state: &mut State, + ticket: TransferTicket + ): ( + u32, + vector + ) { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(token_bridge_state); + + bridge_in_and_serialize_transfer( + &latest_only, + token_bridge_state, + ticket + ) + } +} + +#[test_only] +module token_bridge::transfer_token_tests { + use sui::coin::{Self}; + use sui::test_scenario::{Self}; + use wormhole::bytes32::{Self}; + use wormhole::external_address::{Self}; + use wormhole::publish_message::{Self}; + use wormhole::state::{chain_id}; + + use token_bridge::coin_native_10::{Self, COIN_NATIVE_10}; + use token_bridge::coin_wrapped_7::{Self, COIN_WRAPPED_7}; + use token_bridge::native_asset::{Self}; + use token_bridge::normalized_amount::{Self}; + use token_bridge::state::{Self}; + use token_bridge::token_bridge_scenario::{ + set_up_wormhole_and_token_bridge, + register_dummy_emitter, + return_state, + take_state, + person + }; + use token_bridge::token_registry::{Self}; + use token_bridge::transfer::{Self}; + use token_bridge::transfer_tokens::{Self}; + use token_bridge::wrapped_asset::{Self}; + + /// Test consts. + const TEST_TARGET_RECIPIENT: vector = x"beef4269"; + const TEST_TARGET_CHAIN: u16 = 2; + const TEST_NONCE: u32 = 0; + const TEST_COIN_NATIVE_10_DECIMALS: u8 = 10; + const TEST_COIN_WRAPPED_7_DECIMALS: u8 = 7; + + #[test] + fun test_transfer_tokens_native_10() { + use token_bridge::transfer_tokens::{prepare_transfer, transfer_tokens}; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Register and mint coins. + let transfer_amount = 6942000; + let coin_10_balance = + coin_native_10::init_register_and_mint( + scenario, + sender, + transfer_amount + ); + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + // Define the relayer fee. + let relayer_fee = 100000; + + // Balance check the Token Bridge before executing the transfer. The + // initial balance should be zero for COIN_NATIVE_10. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_native(registry); + assert!(native_asset::custody(asset) == 0, 0); + }; + + let asset_info = state::verified_asset(&token_bridge_state); + let ( + ticket, + dust + ) = + prepare_transfer( + asset_info, + coin::from_balance( + coin_10_balance, + test_scenario::ctx(scenario) + ), + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + relayer_fee, + TEST_NONCE, + ); + coin::destroy_zero(dust); + + // Call `transfer_tokens`. + let prepared_msg = + transfer_tokens(&mut token_bridge_state, ticket); + + // Balance check the Token Bridge after executing the transfer. The + // balance should now reflect the `transfer_amount` defined in this + // test. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_native(registry); + assert!(native_asset::custody(asset) == transfer_amount, 0); + }; + + // Clean up. + publish_message::destroy(prepared_msg); + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_transfer_tokens_native_10_with_dust_refund() { + use token_bridge::transfer_tokens::{prepare_transfer, transfer_tokens}; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Register and mint coins. + let transfer_amount = 1000069; + let coin_10_balance = + coin_native_10::init_register_and_mint( + scenario, + sender, + transfer_amount + ); + + // This value will be used later. The contract should return dust + // to the caller since COIN_NATIVE_10 has 10 decimals. + let expected_dust = 69; + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + // Define the relayer fee. + let relayer_fee = 100000; + + // Balance check the Token Bridge before executing the transfer. The + // initial balance should be zero for COIN_NATIVE_10. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_native(registry); + assert!(native_asset::custody(asset) == 0, 0); + }; + + let asset_info = state::verified_asset(&token_bridge_state); + let ( + ticket, + dust + ) = + prepare_transfer( + asset_info, + coin::from_balance( + coin_10_balance, + test_scenario::ctx(scenario) + ), + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + relayer_fee, + TEST_NONCE, + ); + assert!(coin::value(&dust) == expected_dust, 0); + + // Call `transfer_tokens`. + let prepared_msg = + transfer_tokens(&mut token_bridge_state, ticket); + + // Balance check the Token Bridge after executing the transfer. The + // balance should now reflect the `transfer_amount` less `expected_dust` + // defined in this test. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_native(registry); + assert!( + native_asset::custody(asset) == transfer_amount - expected_dust, + 0 + ); + }; + + // Clean up. + publish_message::destroy(prepared_msg); + coin::burn_for_testing(dust); + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_serialize_transfer_tokens_native_10() { + use token_bridge::transfer_tokens::{ + bridge_in_and_serialize_transfer_test_only, + prepare_transfer + }; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Register and mint coins. + let transfer_amount = 6942000; + let bridged_coin_10 = + coin::from_balance( + coin_native_10::init_register_and_mint( + scenario, + sender, + transfer_amount + ), + test_scenario::ctx(scenario) + ); + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + // Define the relayer fee. + let relayer_fee = 100000; + + let asset_info = state::verified_asset(&token_bridge_state); + let expected_token_address = token_registry::token_address(&asset_info); + + let ( + ticket, + dust + ) = + prepare_transfer( + asset_info, + bridged_coin_10, + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + relayer_fee, + TEST_NONCE, + ); + coin::destroy_zero(dust); + + // Call `transfer_tokens`. + let ( + nonce, + payload + ) = + bridge_in_and_serialize_transfer_test_only( + &mut token_bridge_state, + ticket + ); + assert!(nonce == TEST_NONCE, 0); + + // Construct expected payload from scratch and confirm that the + // `transfer_tokens` call produces the same payload. + let expected_amount = + normalized_amount::from_raw( + transfer_amount, + TEST_COIN_NATIVE_10_DECIMALS + ); + let expected_relayer_fee = + normalized_amount::from_raw( + relayer_fee, + TEST_COIN_NATIVE_10_DECIMALS + ); + + let expected_payload = + transfer::new_test_only( + expected_amount, + expected_token_address, + chain_id(), + external_address::new( + bytes32::from_bytes(TEST_TARGET_RECIPIENT) + ), + TEST_TARGET_CHAIN, + expected_relayer_fee + ); + assert!(transfer::serialize_test_only(expected_payload) == payload, 0); + + // Clean up. + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_transfer_tokens_wrapped_7() { + use token_bridge::transfer_tokens::{prepare_transfer, transfer_tokens}; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Register and mint coins. + let transfer_amount = 42069000; + let coin_7_balance = + coin_wrapped_7::init_register_and_mint( + scenario, + sender, + transfer_amount + ); + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + // Define the relayer fee. + let relayer_fee = 100000; + + // Balance check the Token Bridge before executing the transfer. The + // initial balance should be the `transfer_amount` for COIN_WRAPPED_7. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = + token_registry::borrow_wrapped(registry); + assert!(wrapped_asset::total_supply(asset) == transfer_amount, 0); + }; + + let asset_info = state::verified_asset(&token_bridge_state); + let ( + ticket, + dust + ) = + prepare_transfer( + asset_info, + coin::from_balance( + coin_7_balance, + test_scenario::ctx(scenario) + ), + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + relayer_fee, + TEST_NONCE, + ); + coin::destroy_zero(dust); + + // Call `transfer_tokens`. + let prepared_msg = + transfer_tokens(&mut token_bridge_state, ticket); + + // Balance check the Token Bridge after executing the transfer. The + // balance should be zero, since tokens are burned when an outbound + // wrapped token transfer occurs. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_wrapped(registry); + assert!(wrapped_asset::total_supply(asset) == 0, 0); + }; + + // Clean up. + publish_message::destroy(prepared_msg); + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_serialize_transfer_tokens_wrapped_7() { + use token_bridge::transfer_tokens::{ + bridge_in_and_serialize_transfer_test_only, + prepare_transfer + }; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Register and mint coins. + let transfer_amount = 6942000; + let bridged_coin_7 = + coin::from_balance( + coin_wrapped_7::init_register_and_mint( + scenario, + sender, + transfer_amount + ), + test_scenario::ctx(scenario) + ); + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + // Define the relayer fee. + let relayer_fee = 100000; + + let asset_info = state::verified_asset(&token_bridge_state); + let expected_token_address = token_registry::token_address(&asset_info); + let expected_token_chain = token_registry::token_chain(&asset_info); + + let ( + ticket, + dust + ) = + prepare_transfer( + asset_info, + bridged_coin_7, + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + relayer_fee, + TEST_NONCE, + ); + coin::destroy_zero(dust); + + // Call `transfer_tokens`. + let ( + nonce, + payload + ) = + bridge_in_and_serialize_transfer_test_only( + &mut token_bridge_state, + ticket + ); + assert!(nonce == TEST_NONCE, 0); + + // Construct expected payload from scratch and confirm that the + // `transfer_tokens` call produces the same payload. + let expected_amount = + normalized_amount::from_raw( + transfer_amount, + TEST_COIN_WRAPPED_7_DECIMALS + ); + let expected_relayer_fee = + normalized_amount::from_raw( + relayer_fee, + TEST_COIN_WRAPPED_7_DECIMALS + ); + + let expected_payload = + transfer::new_test_only( + expected_amount, + expected_token_address, + expected_token_chain, + external_address::new( + bytes32::from_bytes(TEST_TARGET_RECIPIENT) + ), + TEST_TARGET_CHAIN, + expected_relayer_fee + ); + assert!(transfer::serialize_test_only(expected_payload) == payload, 0); + + // Clean up. + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = token_registry::E_UNREGISTERED)] + fun test_cannot_transfer_tokens_native_not_registered() { + use token_bridge::transfer_tokens::{prepare_transfer, transfer_tokens}; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Initialize COIN_NATIVE_10 (but don't register it). + coin_native_10::init_test_only(test_scenario::ctx(scenario)); + + // NOTE: This test purposely doesn't `attest` COIN_NATIVE_10. + let transfer_amount = 6942000; + let test_coins = + coin::mint_for_testing( + transfer_amount, + test_scenario::ctx(scenario) + ); + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + // Define the relayer fee. + let relayer_fee = 100000; + + let asset_info = state::verified_asset(&token_bridge_state); + let ( + ticket, + dust + ) = + prepare_transfer( + asset_info, + test_coins, + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + relayer_fee, + TEST_NONCE, + ); + coin::destroy_zero(dust); + + // You shall not pass! + let prepared_msg = + transfer_tokens(&mut token_bridge_state, ticket); + + // Clean up. + publish_message::destroy(prepared_msg); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = token_registry::E_UNREGISTERED)] + fun test_cannot_transfer_tokens_wrapped_not_registered() { + use token_bridge::transfer_tokens::{prepare_transfer, transfer_tokens}; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Initialize COIN_WRAPPED_7 (but don't register it). + coin_native_10::init_test_only(test_scenario::ctx(scenario)); + + let treasury_cap = + coin_wrapped_7::init_and_take_treasury_cap( + scenario, + sender + ); + sui::test_utils::destroy(treasury_cap); + + // NOTE: This test purposely doesn't `attest` COIN_WRAPPED_7. + let transfer_amount = 42069; + let test_coins = + coin::mint_for_testing( + transfer_amount, + test_scenario::ctx(scenario) + ); + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + // Define the relayer fee. + let relayer_fee = 1000; + + let asset_info = state::verified_asset(&token_bridge_state); + let ( + ticket, + dust + ) = + prepare_transfer( + asset_info, + test_coins, + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + relayer_fee, + TEST_NONCE, + ); + coin::destroy_zero(dust); + + // You shall not pass! + let prepared_msg = + transfer_tokens(&mut token_bridge_state, ticket); + + // Clean up. + publish_message::destroy(prepared_msg); + + abort 42 + } + + #[test] + #[expected_failure( + abort_code = transfer_tokens::E_RELAYER_FEE_EXCEEDS_AMOUNT + )] + fun test_cannot_transfer_tokens_fee_exceeds_amount() { + use token_bridge::transfer_tokens::{prepare_transfer, transfer_tokens}; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // NOTE: The `relayer_fee` is intentionally set to a higher number + // than the `transfer_amount`. + let relayer_fee = 100001; + let transfer_amount = 100000; + let coin_10_balance = + coin_native_10::init_register_and_mint( + scenario, + sender, + transfer_amount + ); + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + let asset_info = state::verified_asset(&token_bridge_state); + let ( + ticket, + dust + ) = + prepare_transfer( + asset_info, + coin::from_balance( + coin_10_balance, + test_scenario::ctx(scenario) + ), + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + relayer_fee, + TEST_NONCE, + ); + coin::destroy_zero(dust); + + // You shall not pass! + let prepared_msg = + transfer_tokens(&mut token_bridge_state, ticket); + + // Done. + publish_message::destroy(prepared_msg); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_transfer_tokens_outdated_version() { + use token_bridge::transfer_tokens::{prepare_transfer, transfer_tokens}; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Register and mint coins. + let transfer_amount = 6942000; + let coin_10_balance = + coin_native_10::init_register_and_mint( + scenario, + sender, + transfer_amount + ); + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + let asset_info = state::verified_asset(&token_bridge_state); + + let relayer_fee = 0; + + let ( + ticket, + dust + ) = + prepare_transfer( + asset_info, + coin::from_balance( + coin_10_balance, + test_scenario::ctx(scenario) + ), + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + relayer_fee, + TEST_NONCE, + ); + coin::destroy_zero(dust); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut token_bridge_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut token_bridge_state, + token_bridge::version_control::previous_version_test_only(), + token_bridge::version_control::next_version() + ); + + // You shall not pass! + let prepared_msg = + transfer_tokens(&mut token_bridge_state, ticket); + + // Clean up. + publish_message::destroy(prepared_msg); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens_with_payload.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens_with_payload.move new file mode 100644 index 0000000000..0ed204bfb7 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens_with_payload.move @@ -0,0 +1,812 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements three methods: `prepare_transfer` and +/// `transfer_tokens_with_payload`, which are meant to work together. +/// +/// `prepare_transfer` allows a contract to pack token transfer parameters with +/// an arbitrary payload in preparation to bridge these assets to another +/// network. Only an `EmitterCap` has the capability to create +/// `TransferTicket`. The `EmitterCap` object ID is encoded as the +/// sender. +/// +/// `transfer_tokens_with_payload` unpacks the `TransferTicket` and +/// constructs a `MessageTicket`, which will be used by Wormhole's +/// `publish_message` module. +/// +/// The purpose of splitting this token transferring into two steps is in case +/// Token Bridge needs to be upgraded and there is a breaking change for this +/// module, an integrator would not be left broken. It is discouraged to put +/// `transfer_tokens_with_payload` in an integrator's package logic. Otherwise, +/// this integrator needs to be prepared to upgrade his contract to handle the +/// latest version of `transfer_tokens_with_payload`. +/// +/// Instead, an integrator is encouraged to execute a transaction block, which +/// executes `transfer_tokens_with_payload` using the latest Token Bridge +/// package ID and to implement `prepare_transfer` in his contract to produce +/// `PrepareTransferWithPayload`. +/// +/// NOTE: Only assets that exist in the `TokenRegistry` can be bridged out, +/// which are native Sui assets that have been attested for via `attest_token` +/// and wrapped foreign assets that have been created using foreign asset +/// metadata via the `create_wrapped` module. +/// +/// See `transfer_with_payload` module for serialization and deserialization of +/// Wormhole message payload. +module token_bridge::transfer_tokens_with_payload { + use sui::balance::{Balance}; + use sui::coin::{Coin}; + use sui::object::{Self, ID}; + use wormhole::bytes32::{Self}; + use wormhole::emitter::{EmitterCap}; + use wormhole::external_address::{Self}; + use wormhole::publish_message::{MessageTicket}; + + use token_bridge::normalized_amount::{NormalizedAmount}; + use token_bridge::state::{Self, State, LatestOnly}; + use token_bridge::token_registry::{VerifiedAsset}; + use token_bridge::transfer_with_payload::{Self}; + + /// This type represents transfer data for a specific redeemer contract on a + /// foreign chain. The only way to destroy this type is calling + /// `transfer_tokens_with_payload`. Only the owner of an `EmitterCap` has + /// the capability of generating `TransferTicket`. This emitter + /// cap will usually live in an integrator's contract storage object. + struct TransferTicket { + asset_info: VerifiedAsset, + bridged_in: Balance, + norm_amount: NormalizedAmount, + sender: ID, + redeemer_chain: u16, + redeemer: vector, + payload: vector, + nonce: u32 + } + + /// `prepare_transfer` constructs token transfer parameters. Any remaining + /// amount (A.K.A. dust) from the funds provided will be returned along with + /// the `TransferTicket` type. The returned coin object is the + /// same object moved into this method. + /// + /// NOTE: Integrators of Token Bridge should be calling only this method + /// from their contracts. This method is not guarded by version control + /// (thus not requiring a reference to the Token Bridge `State` object), so + /// it is intended to work for any package version. + public fun prepare_transfer( + emitter_cap: &EmitterCap, + asset_info: VerifiedAsset, + funded: Coin, + redeemer_chain: u16, + redeemer: vector, + payload: vector, + nonce: u32 + ): ( + TransferTicket, + Coin + ) { + use token_bridge::transfer_tokens::{take_truncated_amount}; + + let ( + bridged_in, + norm_amount + ) = take_truncated_amount(&asset_info, &mut funded); + + let prepared_transfer = + TransferTicket { + asset_info, + bridged_in, + norm_amount, + sender: object::id(emitter_cap), + redeemer_chain, + redeemer, + payload, + nonce + }; + + // The remaining amount of funded may have dust depending on the + // decimals of this asset. + (prepared_transfer, funded) + } + + /// `transfer_tokens_with_payload` is the only method that can unpack the + /// members of `TransferTicket`. This method takes the balance + /// from this type and bridges this asset out of Sui by either joining its + /// balance in the Token Bridge's custody for native assets or burning its + /// balance for wrapped assets. + /// + /// The unpacked sender ID comes from an `EmitterCap`. It is encoded as the + /// sender of these assets. And associated with this transfer is an + /// arbitrary payload, which can be consumed by the specified redeemer and + /// used as instructions for a contract composing with Token Bridge. + /// + /// This method returns the prepared Wormhole message (which should be + /// consumed by calling `publish_message` in a transaction block). + /// + /// NOTE: This method is guarded by a minimum build version check. This + /// method could break backward compatibility on an upgrade. + /// + /// It is important for integrators to refrain from calling this method + /// within their contracts. This method is meant to be called in a + /// transaction block after receiving a `TransferTicket` from calling + /// `prepare_transfer` within a contract. If in a circumstance where this + /// module has a breaking change in an upgrade, `prepare_transfer` will not + /// be affected by this change. + public fun transfer_tokens_with_payload( + token_bridge_state: &mut State, + prepared_transfer: TransferTicket + ): MessageTicket { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(token_bridge_state); + + // Encode Wormhole message payload. + let ( + nonce, + encoded_transfer_with_payload + ) = + bridge_in_and_serialize_transfer( + &latest_only, + token_bridge_state, + prepared_transfer + ); + + // Prepare Wormhole message with encoded `TransferWithPayload`. + state::prepare_wormhole_message( + &latest_only, + token_bridge_state, + nonce, + encoded_transfer_with_payload + ) + } + + fun bridge_in_and_serialize_transfer( + latest_only: &LatestOnly, + token_bridge_state: &mut State, + prepared_transfer: TransferTicket + ): ( + u32, + vector + ) { + use token_bridge::transfer_tokens::{burn_or_deposit_funds}; + + let TransferTicket { + asset_info, + bridged_in, + norm_amount, + sender, + redeemer_chain, + redeemer, + payload, + nonce + } = prepared_transfer; + + let ( + token_chain, + token_address + ) = + burn_or_deposit_funds( + latest_only, + token_bridge_state, + &asset_info, + bridged_in + ); + + let redeemer = external_address::new(bytes32::from_bytes(redeemer)); + + let encoded = + transfer_with_payload::serialize( + transfer_with_payload::new( + sender, + norm_amount, + token_address, + token_chain, + redeemer, + redeemer_chain, + payload + ) + ); + + (nonce, encoded) + } + + #[test_only] + public fun bridge_in_and_serialize_transfer_test_only( + token_bridge_state: &mut State, + prepared_transfer: TransferTicket + ): ( + u32, + vector + ) { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(token_bridge_state); + + bridge_in_and_serialize_transfer( + &latest_only, + token_bridge_state, + prepared_transfer + ) + } +} + +#[test_only] +module token_bridge::transfer_tokens_with_payload_tests { + use sui::coin::{Self}; + use sui::object::{Self}; + use sui::test_scenario::{Self}; + use wormhole::bytes32::{Self}; + use wormhole::emitter::{Self}; + use wormhole::external_address::{Self}; + use wormhole::publish_message::{Self}; + use wormhole::state::{chain_id}; + + use token_bridge::coin_wrapped_7::{Self, COIN_WRAPPED_7}; + use token_bridge::coin_native_10::{Self, COIN_NATIVE_10}; + use token_bridge::native_asset::{Self}; + use token_bridge::normalized_amount::{Self}; + use token_bridge::state::{Self}; + use token_bridge::token_bridge_scenario::{ + set_up_wormhole_and_token_bridge, + register_dummy_emitter, + return_state, + take_state, + person + }; + use token_bridge::token_registry::{Self}; + use token_bridge::transfer_with_payload::{Self}; + use token_bridge::wrapped_asset::{Self}; + + /// Test consts. + const TEST_TARGET_RECIPIENT: vector = x"beef4269"; + const TEST_TARGET_CHAIN: u16 = 2; + const TEST_NONCE: u32 = 0; + const TEST_COIN_NATIVE_10_DECIMALS: u8 = 10; + const TEST_COIN_WRAPPED_7_DECIMALS: u8 = 7; + const TEST_MESSAGE_PAYLOAD: vector = x"deadbeefdeadbeef"; + + #[test] + fun test_transfer_tokens_with_payload_native_10() { + use token_bridge::transfer_tokens_with_payload::{ + prepare_transfer, + transfer_tokens_with_payload + }; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Register and mint coins. + let transfer_amount = 6942000; + let coin_10_balance = + coin_native_10::init_register_and_mint( + scenario, + sender, + transfer_amount + ); + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + // Balance check the Token Bridge before executing the transfer. The + // initial balance should be zero for COIN_NATIVE_10. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_native(registry); + assert!(native_asset::custody(asset) == 0, 0); + }; + + // Register and obtain a new wormhole emitter cap. + let emitter_cap = emitter::dummy(); + + let asset_info = state::verified_asset(&token_bridge_state); + let ( + prepared_transfer, + dust + ) = + prepare_transfer( + &emitter_cap, + asset_info, + coin::from_balance( + coin_10_balance, + test_scenario::ctx(scenario) + ), + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + TEST_MESSAGE_PAYLOAD, + TEST_NONCE, + ); + coin::destroy_zero(dust); + + // Call `transfer_tokens_with_payload`. + let prepared_msg = + transfer_tokens_with_payload( + &mut token_bridge_state, + prepared_transfer + ); + + // Balance check the Token Bridge after executing the transfer. The + // balance should now reflect the `transfer_amount` defined in this + // test. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_native(registry); + assert!(native_asset::custody(asset) == transfer_amount, 0); + }; + + // Clean up. + publish_message::destroy(prepared_msg); + return_state(token_bridge_state); + emitter::destroy_test_only(emitter_cap); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_transfer_tokens_native_10_with_dust_refund() { + use token_bridge::transfer_tokens_with_payload::{ + prepare_transfer, + transfer_tokens_with_payload + }; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Register and mint coins. + let transfer_amount = 1000069; + let coin_10_balance = coin_native_10::init_register_and_mint( + scenario, + sender, + transfer_amount + ); + + // This value will be used later. The contract should return dust + // to the caller since COIN_NATIVE_10 has 10 decimals. + let expected_dust = 69; + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + // Balance check the Token Bridge before executing the transfer. The + // initial balance should be zero for COIN_NATIVE_10. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_native(registry); + assert!(native_asset::custody(asset) == 0, 0); + }; + + // Register and obtain a new wormhole emitter cap. + let emitter_cap = emitter::dummy(); + + let asset_info = state::verified_asset(&token_bridge_state); + let ( + prepared_transfer, + dust + ) = + prepare_transfer( + &emitter_cap, + asset_info, + coin::from_balance( + coin_10_balance, + test_scenario::ctx(scenario) + ), + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + TEST_MESSAGE_PAYLOAD, + TEST_NONCE, + ); + assert!(coin::value(&dust) == expected_dust, 0); + + // Call `transfer_tokens`. + let prepared_msg = + transfer_tokens_with_payload( + &mut token_bridge_state, + prepared_transfer + ); + + // Balance check the Token Bridge after executing the transfer. The + // balance should now reflect the `transfer_amount` less `expected_dust` + // defined in this test. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_native(registry); + assert!( + native_asset::custody(asset) == transfer_amount - expected_dust, + 0 + ); + }; + + // Clean up. + publish_message::destroy(prepared_msg); + coin::burn_for_testing(dust); + emitter::destroy_test_only(emitter_cap); + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_serialize_transfer_tokens_native_10() { + use token_bridge::transfer_tokens_with_payload::{ + bridge_in_and_serialize_transfer_test_only, + prepare_transfer + }; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Register and mint coins. + let transfer_amount = 6942000; + let bridge_coin_10 = + coin::from_balance( + coin_native_10::init_register_and_mint( + scenario, + sender, + transfer_amount + ), + test_scenario::ctx(scenario) + ); + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + // Register and obtain a new wormhole emitter cap. + let emitter_cap = emitter::dummy(); + + let asset_info = state::verified_asset(&token_bridge_state); + let expected_token_address = token_registry::token_address(&asset_info); + + let ( + prepared_transfer, + dust + ) = + prepare_transfer( + &emitter_cap, + asset_info, + bridge_coin_10, + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + TEST_MESSAGE_PAYLOAD, + TEST_NONCE, + ); + coin::destroy_zero(dust); + + // Serialize the payload. + let ( + nonce, + payload + ) = + bridge_in_and_serialize_transfer_test_only( + &mut token_bridge_state, + prepared_transfer + ); + assert!(nonce == TEST_NONCE, 0); + + // Construct expected payload from scratch and confirm that the + // `transfer_tokens` call produces the same payload. + let expected_amount = normalized_amount::from_raw( + transfer_amount, + TEST_COIN_NATIVE_10_DECIMALS + ); + + let expected_payload = + transfer_with_payload::new_test_only( + object::id(&emitter_cap), + expected_amount, + expected_token_address, + chain_id(), + external_address::new(bytes32::from_bytes(TEST_TARGET_RECIPIENT)), + TEST_TARGET_CHAIN, + TEST_MESSAGE_PAYLOAD + ); + assert!( + transfer_with_payload::serialize(expected_payload) == payload, + 0 + ); + + // Clean up. + return_state(token_bridge_state); + emitter::destroy_test_only(emitter_cap); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_transfer_tokens_with_payload_wrapped_7() { + use token_bridge::transfer_tokens_with_payload::{ + prepare_transfer, + transfer_tokens_with_payload + }; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Register and mint coins. + let transfer_amount = 6942000; + let coin_7_balance = + coin_wrapped_7::init_register_and_mint( + scenario, + sender, + transfer_amount + ); + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + // Balance check the Token Bridge before executing the transfer. The + // initial balance should be the `transfer_amount` for COIN_WRAPPED_7. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_wrapped(registry); + assert!(wrapped_asset::total_supply(asset) == transfer_amount, 0); + }; + + // Register and obtain a new wormhole emitter cap. + let emitter_cap = emitter::dummy(); + + let asset_info = state::verified_asset(&token_bridge_state); + let ( + prepared_transfer, + dust + ) = + prepare_transfer( + &emitter_cap, + asset_info, + coin::from_balance( + coin_7_balance, + test_scenario::ctx(scenario) + ), + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + TEST_MESSAGE_PAYLOAD, + TEST_NONCE, + ); + coin::destroy_zero(dust); + + // Call `transfer_tokens_with_payload`. + let prepared_msg = + transfer_tokens_with_payload( + &mut token_bridge_state, + prepared_transfer + ); + + // Balance check the Token Bridge after executing the transfer. The + // balance should be zero, since tokens are burned when an outbound + // wrapped token transfer occurs. + { + let registry = state::borrow_token_registry(&token_bridge_state); + let asset = token_registry::borrow_wrapped(registry); + assert!(wrapped_asset::total_supply(asset) == 0, 0); + }; + + // Clean up. + publish_message::destroy(prepared_msg); + emitter::destroy_test_only(emitter_cap); + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_serialize_transfer_tokens_wrapped_7() { + use token_bridge::transfer_tokens_with_payload::{ + bridge_in_and_serialize_transfer_test_only, + prepare_transfer + }; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Register and mint coins. + let transfer_amount = 6942000; + let bridged_coin_7 = + coin::from_balance( + coin_wrapped_7::init_register_and_mint( + scenario, + sender, + transfer_amount + ), + test_scenario::ctx(scenario) + ); + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + // Register and obtain a new wormhole emitter cap. + let emitter_cap = emitter::dummy(); + + let asset_info = state::verified_asset(&token_bridge_state); + let expected_token_address = token_registry::token_address(&asset_info); + let expected_token_chain = token_registry::token_chain(&asset_info); + + let ( + prepared_transfer, + dust + ) = + prepare_transfer( + &emitter_cap, + asset_info, + bridged_coin_7, + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + TEST_MESSAGE_PAYLOAD, + TEST_NONCE, + ); + coin::destroy_zero(dust); + + // Serialize the payload. + let ( + nonce, + payload + ) = + bridge_in_and_serialize_transfer_test_only( + &mut token_bridge_state, + prepared_transfer + ); + assert!(nonce == TEST_NONCE, 0); + + // Construct expected payload from scratch and confirm that the + // `transfer_tokens` call produces the same payload. + let expected_amount = normalized_amount::from_raw( + transfer_amount, + TEST_COIN_WRAPPED_7_DECIMALS + ); + + let expected_payload = + transfer_with_payload::new_test_only( + object::id(&emitter_cap), + expected_amount, + expected_token_address, + expected_token_chain, + external_address::new(bytes32::from_bytes(TEST_TARGET_RECIPIENT)), + TEST_TARGET_CHAIN, + TEST_MESSAGE_PAYLOAD + ); + assert!( + transfer_with_payload::serialize(expected_payload) == payload, + 0 + ); + + // Clean up. + emitter::destroy_test_only(emitter_cap); + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_transfer_tokens_with_payload_outdated_version() { + use token_bridge::transfer_tokens_with_payload::{ + prepare_transfer, + transfer_tokens_with_payload + }; + + let sender = person(); + let my_scenario = test_scenario::begin(sender); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter on chain ID == 2. + register_dummy_emitter(scenario, TEST_TARGET_CHAIN); + + // Register and mint coins. + let transfer_amount = 6942000; + let coin_10_balance = + coin_native_10::init_register_and_mint( + scenario, + sender, + transfer_amount + ); + + // Ignore effects. + test_scenario::next_tx(scenario, sender); + + // Fetch objects necessary for sending the transfer. + let token_bridge_state = take_state(scenario); + + // Register and obtain a new wormhole emitter cap. + let emitter_cap = emitter::dummy(); + + let asset_info = state::verified_asset(&token_bridge_state); + let ( + prepared_transfer, + dust + ) = + prepare_transfer( + &emitter_cap, + asset_info, + coin::from_balance( + coin_10_balance, + test_scenario::ctx(scenario) + ), + TEST_TARGET_CHAIN, + TEST_TARGET_RECIPIENT, + TEST_MESSAGE_PAYLOAD, + TEST_NONCE, + ); + coin::destroy_zero(dust); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut token_bridge_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut token_bridge_state, + token_bridge::version_control::previous_version_test_only(), + token_bridge::version_control::next_version() + ); + + // You shall not pass! + let prepared_msg = + transfer_tokens_with_payload( + &mut token_bridge_state, + prepared_transfer + ); + + // Clean up. + publish_message::destroy(prepared_msg); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/coin_utils.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/coin_utils.move new file mode 100644 index 0000000000..80d351f933 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/coin_utils.move @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements utilities helpful for outbound token transfers. These +/// utility methods should also help avoid having to work around conversions +/// between `Coin` and `Balance` avoiding unnecessary object creation and +/// destruction. +module token_bridge::coin_utils { + use sui::balance::{Self, Balance}; + use sui::coin::{Self, Coin}; + use sui::tx_context::{TxContext}; + + /// Method similar to `coin::take` where an amount is split from a `Coin` + /// object's inner balance. + public fun take_balance( + coin_mut: &mut Coin, + amount: u64 + ): Balance { + balance::split(coin::balance_mut(coin_mut), amount) + } + + /// Method out of convenience to take the full balance value out of a `Coin` + /// object while preserving that object. This method is used to avoid + /// calling `coin::into_balance` which destroys the object. + public fun take_full_balance(coin_mut: &mut Coin): Balance { + let amount = coin::value(coin_mut); + take_balance(coin_mut, amount) + } + + /// Method similar to `coin::put` where an outside balance is joined with + /// an existing `Coin` object. + public fun put_balance( + coin_mut: &mut Coin, + the_balance: Balance + ): u64 { + balance::join(coin::balance_mut(coin_mut), the_balance) + } + + /// Method for those integrators that use `Coin` objects, where `the_coin` + /// will be destroyed if the value is zero. Otherwise it will be returned + /// back to the transaction sender. + public fun return_nonzero(the_coin: Coin, ctx: &TxContext) { + if (coin::value(&the_coin) == 0) { + coin::destroy_zero(the_coin); + } else { + sui::pay::keep(the_coin, ctx) + } + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/string_utils.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/string_utils.move new file mode 100644 index 0000000000..868fa74927 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/string_utils.move @@ -0,0 +1,97 @@ +module token_bridge::string_utils { + use std::ascii::{Self}; + use std::string::{Self, String}; + use std::vector::{Self}; + + const QUESTION_MARK: u8 = 63; + // Recall that UTF-8 characters have variable-length encoding and can have + // 1, 2, 3, or 4 bytes. + // The first byte of the 2, 3, and 4-byte UTF-8 characters have a special + // form indicating how many more bytes follow in the same character + // representation. Specifically, it can have the forms + // - 110xxxxx // 11000000 is 192 (base 10) + // - 1110xxxx // 11100000 is 224 (base 10) + // - or 11110xxx // 11110000 is 240 (base 10) + // + // We can tell the length the a hex UTF-8 character in bytes by looking + // at the first byte and counting the leading 1's, or alternatively + // seeing whether it falls in the range + // [11000000, 11100000) or [11100000, 11110000) or [11110000, 11111111], + // + // The following constants demarcate those ranges and are used in the + // string32::to_ascii function. + const UTF8_LENGTH_2_FIRST_BYTE_LOWER_BOUND: u8 = 192; + const UTF8_LENGTH_3_FIRST_BYTE_LOWER_BOUND: u8 = 224; + const UTF8_LENGTH_4_FIRST_BYTE_LOWER_BOUND: u8 = 240; + + /// Converts a String32 to an ascii string if possible, otherwise errors + /// out at `ascii::string(bytes)`. For input strings that contain non-ascii + /// characters, we will swap the non-ascii character with `?`. + /// + /// Note that while the Sui spec limits symbols to only use ascii + /// characters, the token bridge spec does allow utf8 symbols. + public fun to_ascii(s: &String): ascii::String { + let buf = *string::bytes(s); + // keep dropping the last character while it's 0 + while ( + !vector::is_empty(&buf) && + *vector::borrow(&buf, vector::length(&buf) - 1) == 0 + ) { + vector::pop_back(&mut buf); + }; + + // Run through `buf` to convert any non-ascii character to `?`. + let asciified = vector::empty(); + let (i, n) = (0, vector::length(&buf)); + while (i < n) { + let b = *vector::borrow(&buf, i); + // If it is a valid ascii character, keep it. + if (ascii::is_valid_char(b)) { + vector::push_back(&mut asciified, b); + i = i + 1; + } else { + // Since UTF-8 characters have variable-length encoding (they are + // represented using 1-4 bytes, unlike ASCII characters, which + // are represented using 1 byte), we don't want to transform + // every byte in a UTF-8 string that does not represent an ASCII + // character to the question mark symbol "?". This would result + // in having too many "?" symbols. + // + // Instead, we want a single "?" for each character. Note that + // the 1-byte UTF-8 characters correspond to valid ASCII + // characters and have the form 0xxxxxxx. + // The 2, 3, and 4-byte UTF-8 characters have first byte equal + // to: + // - 110xxxxx // 192 + // - 1110xxxx // 224 + // - or 11110xxx // 240 + // + // and remaining bytes of the form: + // - 10xxxxxx + // + // To ensure a one-to-one mapping of a multi-byte UTF-8 character + // to a "?", we detect the first byte of a new UTF-8 character + // in a multi-byte representation by checking if it is + // >= 11000000 (base 2) or 192 (base 10) and convert it to a "?" + // and skip the remaining bytes in the same representation. + // + // + // Reference: https://en.wikipedia.org/wiki/UTF-8 + if (b >= UTF8_LENGTH_2_FIRST_BYTE_LOWER_BOUND){ + vector::push_back(&mut asciified, QUESTION_MARK); + if (b >= UTF8_LENGTH_4_FIRST_BYTE_LOWER_BOUND){ + // The UTF-8 char has a 4-byte hex representation. + i = i + 4; + } else if (b >= UTF8_LENGTH_3_FIRST_BYTE_LOWER_BOUND){ + // The UTF-8 char has a 3-byte hex representation. + i = i + 3; + } else { + // The UTF-8 char has a 2-byte hex representation. + i = i + 2; + } + } + }; + }; + ascii::string(asciified) + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/vaa.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/vaa.move new file mode 100644 index 0000000000..c2e9a26096 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/vaa.move @@ -0,0 +1,351 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module builds on Wormhole's `vaa::parse_and_verify` method by adding +/// emitter verification and replay protection. +/// +/// Token Bridge only cares about other Token Bridge messages, so the emitter +/// address must be a registered Token Bridge emitter according to the VAA's +/// emitter chain ID. +/// +/// Token Bridge does not allow replaying any of its VAAs, so its hash is stored +/// in its `State`. If the encoded VAA passes through `parse_and_verify` again, +/// it will abort. +module token_bridge::vaa { + use sui::table::{Self}; + use wormhole::external_address::{ExternalAddress}; + use wormhole::vaa::{Self, VAA}; + + use token_bridge::state::{Self, State}; + + friend token_bridge::create_wrapped; + friend token_bridge::complete_transfer; + friend token_bridge::complete_transfer_with_payload; + + /// For a given chain ID, Token Bridge is non-existent. + const E_UNREGISTERED_EMITTER: u64 = 0; + /// Encoded emitter address does not match registered Token Bridge. + const E_EMITTER_ADDRESS_MISMATCH: u64 = 1; + + /// This type represents VAA data whose emitter is a registered Token Bridge + /// emitter. This message is also representative of a VAA that cannot be + /// replayed. + struct TokenBridgeMessage { + /// Wormhole chain ID from which network the message originated from. + emitter_chain: u16, + /// Address of Token Bridge (standardized to 32 bytes) that produced + /// this message. + emitter_address: ExternalAddress, + /// Sequence number of Token Bridge's Wormhole message. + sequence: u64, + /// Token Bridge payload. + payload: vector + } + + /// Parses and verifies encoded VAA. Because Token Bridge does not allow + /// VAAs to be replayed, the VAA hash is stored in a set, which is checked + /// against the next time the same VAA is used to make sure it cannot be + /// used again. + /// + /// In its verification, this method checks whether the emitter is a + /// registered Token Bridge contract on another network. + /// + /// NOTE: It is important for integrators to refrain from calling this + /// method within their contracts. This method is meant to be called within + /// a transaction block, passing the `TokenBridgeMessage` to one of the + /// Token Bridge methods that consumes this type. If in a circumstance where + /// this module has a breaking change in an upgrade, another method (e.g. + /// `complete_transfer_with_payload`) will not be affected by this change. + public fun verify_only_once( + token_bridge_state: &mut State, + verified_vaa: VAA + ): TokenBridgeMessage { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(token_bridge_state); + + // First parse and verify VAA using Wormhole. This also consumes the VAA + // hash to prevent replay. + vaa::consume( + state::borrow_mut_consumed_vaas(&latest_only, token_bridge_state), + &verified_vaa + ); + + // Does the emitter agree with a registered Token Bridge? + assert_registered_emitter(token_bridge_state, &verified_vaa); + + // Take emitter info, sequence and payload. + let sequence = vaa::sequence(&verified_vaa); + let ( + emitter_chain, + emitter_address, + payload + ) = vaa::take_emitter_info_and_payload(verified_vaa); + + TokenBridgeMessage { + emitter_chain, + emitter_address, + sequence, + payload + } + } + + public fun emitter_chain(self: &TokenBridgeMessage): u16 { + self.emitter_chain + } + + public fun emitter_address(self: &TokenBridgeMessage): ExternalAddress { + self.emitter_address + } + + public fun sequence(self: &TokenBridgeMessage): u64 { + self.sequence + } + + /// Destroy `TokenBridgeMessage` and extract payload, which is the same + /// payload in the `VAA`. + /// + /// NOTE: This is a privileged method, which only friends within the Token + /// Bridge package can use. This guarantees that no other package can redeem + /// a VAA intended for Token Bridge as a denial-of-service by calling + /// `verify_only_once` and then destroying it by calling it this method. + public(friend) fun take_payload(msg: TokenBridgeMessage): vector { + let TokenBridgeMessage { + emitter_chain: _, + emitter_address: _, + sequence: _, + payload + } = msg; + + payload + } + + /// Assert that a given emitter equals one that is registered as a foreign + /// Token Bridge. + fun assert_registered_emitter( + token_bridge_state: &State, + verified_vaa: &VAA + ) { + let chain = vaa::emitter_chain(verified_vaa); + let registry = state::borrow_emitter_registry(token_bridge_state); + assert!(table::contains(registry, chain), E_UNREGISTERED_EMITTER); + + let registered = table::borrow(registry, chain); + let emitter_addr = vaa::emitter_address(verified_vaa); + assert!(*registered == emitter_addr, E_EMITTER_ADDRESS_MISMATCH); + } + + #[test_only] + public fun destroy(msg: TokenBridgeMessage) { + take_payload(msg); + } +} + +#[test_only] +module token_bridge::vaa_tests { + use sui::test_scenario::{Self}; + use wormhole::external_address::{Self}; + use wormhole::wormhole_scenario::{parse_and_verify_vaa}; + + use token_bridge::state::{Self}; + use token_bridge::token_bridge_scenario::{ + person, + register_dummy_emitter, + return_state, + set_up_wormhole_and_token_bridge, + take_state + }; + use token_bridge::vaa::{Self}; + + /// VAA sent from the ethereum token bridge 0xdeadbeef. + const VAA: vector = + x"01000000000100102d399190fa61daccb11c2ea4f7a3db3a9365e5936bcda4cded87c1b9eeb095173514f226256d5579af71d4089eb89496befb998075ba94cd1d4460c5c57b84000000000100000001000200000000000000000000000000000000000000000000000000000000deadbeef0000000002634973000200000000000000000000000000000000000000000000000000000000beefface00020c0000000000000000000000000000000000000000000000000000000042454546000000000000000000000000000000000042656566206661636520546f6b656e"; + + #[test] + #[expected_failure(abort_code = vaa::E_UNREGISTERED_EMITTER)] + fun test_cannot_verify_only_once_unregistered_chain() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + + let verified_vaa = parse_and_verify_vaa(scenario, VAA); + // You shall not pass! + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Clean up. + vaa::destroy(msg); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = vaa::E_EMITTER_ADDRESS_MISMATCH)] + fun test_cannot_verify_only_once_emitter_address_mismatch() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + + // First register emitter. + let emitter_chain = 2; + let emitter_addr = external_address::from_address(@0xdeafbeef); + token_bridge::register_chain::register_new_emitter_test_only( + &mut token_bridge_state, + emitter_chain, + emitter_addr + ); + + // Confirm that encoded emitter disagrees with registered emitter. + let verified_vaa = parse_and_verify_vaa(scenario, VAA); + assert!( + wormhole::vaa::emitter_address(&verified_vaa) != emitter_addr, + 0 + ); + + // You shall not pass! + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Clean up. + vaa::destroy(msg); + + abort 42 + } + + #[test] + fun test_verify_only_once() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + + // Confirm VAA originated from where we expect. + let verified_vaa = parse_and_verify_vaa(scenario, VAA); + assert!( + wormhole::vaa::emitter_chain(&verified_vaa) == expected_source_chain, + 0 + ); + + // Finally verify. + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Clean up. + vaa::destroy(msg); + return_state(token_bridge_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = wormhole::set::E_KEY_ALREADY_EXISTS)] + fun test_cannot_verify_only_once_again() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + + // Confirm VAA originated from where we expect. + let verified_vaa = parse_and_verify_vaa(scenario, VAA); + assert!( + wormhole::vaa::emitter_chain(&verified_vaa) == expected_source_chain, + 0 + ); + + // Finally verify. + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + vaa::destroy(msg); + + let verified_vaa = parse_and_verify_vaa(scenario, VAA); + // You shall not pass! + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Clean up. + vaa::destroy(msg); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_verify_only_once_outdated_version() { + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Set up contracts. + let wormhole_fee = 350; + set_up_wormhole_and_token_bridge(scenario, wormhole_fee); + + // Register foreign emitter. + let expected_source_chain = 2; + register_dummy_emitter(scenario, expected_source_chain); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let token_bridge_state = take_state(scenario); + + // Verify VAA. + let verified_vaa = parse_and_verify_vaa(scenario, VAA); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut token_bridge_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut token_bridge_state, + token_bridge::version_control::previous_version_test_only(), + token_bridge::version_control::next_version() + ); + + // You shall not pass! + let msg = vaa::verify_only_once(&mut token_bridge_state, verified_vaa); + + // Clean up. + vaa::destroy(msg); + + abort 42 + } + +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/version_control.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/version_control.move new file mode 100644 index 0000000000..caee75794c --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/version_control.move @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements dynamic field keys as empty structs. These keys are +/// used to determine the latest version for this build. If the current version +/// is not this build's, then paths through the `state` module will abort. +/// +/// See `token_bridge::state` and `wormhole::package_utils` for more info. +module token_bridge::version_control { + //////////////////////////////////////////////////////////////////////////// + // + // Hard-coded Version Control + // + // Before upgrading, please set the types for `current_version` and + // `previous_version` to match the correct types (current being the latest + // version reflecting this build). + // + //////////////////////////////////////////////////////////////////////////// + + public(friend) fun current_version(): V__0_2_0 { + V__0_2_0 {} + } + + #[test_only] + public fun current_version_test_only(): V__0_2_0 { + current_version() + } + + public(friend) fun previous_version(): V__DUMMY { + V__DUMMY {} + } + + #[test_only] + public fun previous_version_test_only(): V__DUMMY { + previous_version() + } + + //////////////////////////////////////////////////////////////////////////// + // + // Change Log + // + // Please write release notes as doc strings for each version struct. These + // notes will be our attempt at tracking upgrades. Wish us luck. + // + //////////////////////////////////////////////////////////////////////////// + + /// First published package on Sui mainnet. + struct V__0_2_0 has store, drop, copy {} + + // Dummy. + struct V__DUMMY has store, drop, copy {} + + //////////////////////////////////////////////////////////////////////////// + // + // Implementation and Test-Only Methods + // + //////////////////////////////////////////////////////////////////////////// + + friend token_bridge::state; + + #[test_only] + public fun dummy(): V__DUMMY { + V__DUMMY {} + } + + #[test_only] + struct V__MIGRATED has store, drop, copy {} + + #[test_only] + public fun next_version(): V__MIGRATED { + V__MIGRATED {} + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/.gitignore b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/.gitignore new file mode 100644 index 0000000000..378eac25d3 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/.gitignore @@ -0,0 +1 @@ +build diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Makefile b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Makefile new file mode 100644 index 0000000000..1f9d45f8af --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Makefile @@ -0,0 +1,18 @@ +-include ../../Makefile.help + +VERSION = $(shell grep -Po "version = \"\K[^\"]*" Move.toml | sed "s/\./_/g") + +.PHONY: clean +clean: + rm -rf build + +.PHONY: check +## Build contract +check: + sui move build -d + +.PHONY: test +## Run tests +test: check + grep "public(friend) fun current_version(): V__${VERSION} {" sources/version_control.move + sui move test -d -t 1 diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.devnet.toml b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.devnet.toml new file mode 100644 index 0000000000..ba3f5e54aa --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.devnet.toml @@ -0,0 +1,11 @@ +[package] +name = "Wormhole" +version = "0.2.0" + +[dependencies.Sui] +git = "https://github.com/MystenLabs/sui.git" +subdir = "crates/sui-framework/packages/sui-framework" +rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" + +[addresses] +wormhole = "_" diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.lock b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.lock new file mode 100644 index 0000000000..570d17a100 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.lock @@ -0,0 +1,27 @@ +# @generated by Move, please check-in and do not edit manually. + +[move] +version = 0 +manifest_digest = "E8C411A83F4F7EF268B73C732B9B6F49F7B204F0C9C2530765365C38B76EF646" +deps_digest = "F8BBB0CCB2491CA29A3DF03D6F92277A4F3574266507ACD77214D37ECA3F3082" + +dependencies = [ + { name = "Sui" }, +] + +[[move.package]] +name = "MoveStdlib" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "041c5f2bae2fe52079e44b70514333532d69f4e6", subdir = "crates/sui-framework/packages/move-stdlib" } + +[[move.package]] +name = "Sui" +source = { git = "https://github.com/MystenLabs/sui.git", rev = "041c5f2bae2fe52079e44b70514333532d69f4e6", subdir = "crates/sui-framework/packages/sui-framework" } + +dependencies = [ + { name = "MoveStdlib" }, +] + +[move.toolchain-version] +compiler-version = "1.19.0" +edition = "legacy" +flavor = "sui" diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.mainnet.toml b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.mainnet.toml new file mode 100644 index 0000000000..7465390ebb --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.mainnet.toml @@ -0,0 +1,12 @@ +[package] +name = "Wormhole" +version = "0.2.0" +published-at = "0x5306f64e312b581766351c07af79c72fcb1cd25147157fdc2f8ad76de9a3fb6a" + +[dependencies.Sui] +git = "https://github.com/MystenLabs/sui.git" +subdir = "crates/sui-framework/packages/sui-framework" +rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" + +[addresses] +wormhole = "0x5306f64e312b581766351c07af79c72fcb1cd25147157fdc2f8ad76de9a3fb6a" diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.testnet.toml b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.testnet.toml new file mode 100644 index 0000000000..ed34ccb472 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.testnet.toml @@ -0,0 +1,12 @@ +[package] +name = "Wormhole" +version = "0.2.0" +published-at = "0xf47329f4344f3bf0f8e436e2f7b485466cff300f12a166563995d3888c296a94" + +[dependencies.Sui] +git = "https://github.com/MystenLabs/sui.git" +subdir = "crates/sui-framework/packages/sui-framework" +rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" + +[addresses] +wormhole = "0xf47329f4344f3bf0f8e436e2f7b485466cff300f12a166563995d3888c296a94" diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.toml b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.toml new file mode 100644 index 0000000000..42fe952451 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.toml @@ -0,0 +1,12 @@ +[package] +name = "Wormhole" +version = "0.2.0" +published-at = "0x23a373b70e6e23a39e4846fa6896fa12beb08da061b3d4ec856bc8ead54f1e22" + +[dependencies.Sui] +git = "https://github.com/MystenLabs/sui.git" +subdir = "crates/sui-framework/packages/sui-framework" +rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" + +[addresses] +wormhole = "0x23a373b70e6e23a39e4846fa6896fa12beb08da061b3d4ec856bc8ead54f1e22" diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/README.md b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/README.md new file mode 100644 index 0000000000..62a62a67b1 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/README.md @@ -0,0 +1,25 @@ +# Sui Wormhole Core Bridge Design + +## State + +The `State` object is created exactly once during the initialisation of the +contract. Normally, run-once functionality is implemented in the special `init` +function of a module (this code runs once, when the module is first deployed), +but this function takes no arguments, while our initialisation code does (to +ease deployment to different environments without recompiling the contract). + +To allow configuring the state with arguments, it's initialised in the +`init_and_share_state` function, which also shares the state object. To ensure +this function can only be called once, it consumes a `DeployerCap` object +which in turn is created and transferred to the deployer in the `init` function. +Since `init_and_share_state` consumes this object, it won't be possible to call +it again. + +## Dynamic fields + +TODO: up to date notes on where and how we use dynamic fields. + +## Epoch Timestamp + +Sui currently does not have fine-grained timestamps, so we use +`tx_context::epoch(ctx)` in place of on-chain time in seconds. diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes20.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes20.move new file mode 100644 index 0000000000..3c097dec69 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes20.move @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a custom type representing a fixed-size array of +/// length 20. +module wormhole::bytes20 { + use std::vector::{Self}; + + use wormhole::bytes::{Self}; + use wormhole::cursor::{Cursor}; + + /// Invalid vector length to create `Bytes20`. + const E_INVALID_BYTES20: u64 = 0; + /// Found non-zero bytes when attempting to trim `vector`. + const E_CANNOT_TRIM_NONZERO: u64 = 1; + + /// 20. + const LEN: u64 = 20; + + /// Container for `vector`, which has length == 20. + struct Bytes20 has copy, drop, store { + data: vector + } + + public fun length(): u64 { + LEN + } + + /// Create new `Bytes20`, which checks the length of input `data`. + public fun new(data: vector): Bytes20 { + assert!(is_valid(&data), E_INVALID_BYTES20); + Bytes20 { data } + } + + /// Create new `Bytes20` of all zeros. + public fun default(): Bytes20 { + let data = vector::empty(); + let i = 0; + while (i < LEN) { + vector::push_back(&mut data, 0); + i = i + 1; + }; + new(data) + } + + /// Retrieve underlying `data`. + public fun data(self: &Bytes20): vector { + self.data + } + + /// Either trim or pad (depending on length of the input `vector`) to 20 + /// bytes. + public fun from_bytes(buf: vector): Bytes20 { + let len = vector::length(&buf); + if (len > LEN) { + trim_nonzero_left(&mut buf); + new(buf) + } else { + new(pad_left(&buf, false)) + } + } + + /// Destroy `Bytes20` for its underlying data. + public fun to_bytes(value: Bytes20): vector { + let Bytes20 { data } = value; + data + } + + /// Drain 20 elements of `Cursor` to create `Bytes20`. + public fun take(cur: &mut Cursor): Bytes20 { + new(bytes::take_bytes(cur, LEN)) + } + + /// Validate that any of the bytes in underlying data is non-zero. + public fun is_nonzero(self: &Bytes20): bool { + let i = 0; + while (i < LEN) { + if (*vector::borrow(&self.data, i) > 0) { + return true + }; + i = i + 1; + }; + + false + } + + /// Check that the input data is correct length. + fun is_valid(data: &vector): bool { + vector::length(data) == LEN + } + + /// For vector size less than 20, add zeros to the left. + fun pad_left(data: &vector, data_reversed: bool): vector { + let out = vector::empty(); + let len = vector::length(data); + let i = len; + while (i < LEN) { + vector::push_back(&mut out, 0); + i = i + 1; + }; + if (data_reversed) { + let i = 0; + while (i < len) { + vector::push_back( + &mut out, + *vector::borrow(data, len - i - 1) + ); + i = i + 1; + }; + } else { + vector::append(&mut out, *data); + }; + + out + } + + /// Trim bytes from the left if they are zero. If any of these bytes + /// are non-zero, abort. + fun trim_nonzero_left(data: &mut vector) { + vector::reverse(data); + let (i, n) = (0, vector::length(data) - LEN); + while (i < n) { + assert!(vector::pop_back(data) == 0, E_CANNOT_TRIM_NONZERO); + i = i + 1; + }; + vector::reverse(data); + } +} + +#[test_only] +module wormhole::bytes20_tests { + use std::vector::{Self}; + + use wormhole::bytes20::{Self}; + + #[test] + public fun new() { + let data = x"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + assert!(vector::length(&data) == 20, 0); + let actual = bytes20::new(data); + + assert!(bytes20::data(&actual) == data, 0); + } + + #[test] + public fun default() { + let actual = bytes20::default(); + let expected = x"0000000000000000000000000000000000000000"; + assert!(bytes20::data(&actual) == expected, 0); + } + + #[test] + public fun from_bytes() { + let actual = bytes20::from_bytes(x"deadbeef"); + let expected = x"00000000000000000000000000000000deadbeef"; + assert!(bytes20::data(&actual) == expected, 0); + } + + #[test] + public fun is_nonzero() { + let data = x"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + let actual = bytes20::new(data); + assert!(bytes20::is_nonzero(&actual), 0); + + let zeros = bytes20::default(); + assert!(!bytes20::is_nonzero(&zeros), 0); + } + + #[test] + #[expected_failure(abort_code = bytes20::E_INVALID_BYTES20)] + public fun cannot_new_non_20_byte_vector() { + let data = + x"deadbeefdeadbeefdeadbeefdeadbeefdeadbe"; + assert!(vector::length(&data) != 20, 0); + bytes20::new(data); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes32.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes32.move new file mode 100644 index 0000000000..ab713f6f25 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes32.move @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a custom type representing a fixed-size array of +/// length 32. +module wormhole::bytes32 { + use std::option::{Self}; + use std::string::{Self, String}; + use std::vector::{Self}; + use sui::bcs::{Self}; + + use wormhole::bytes::{Self}; + use wormhole::cursor::{Self, Cursor}; + + /// Invalid vector length to create `Bytes32`. + const E_INVALID_BYTES32: u64 = 0; + /// Found non-zero bytes when attempting to trim `vector`. + const E_CANNOT_TRIM_NONZERO: u64 = 1; + /// Value of deserialized 32-byte array data overflows u64 max. + const E_U64_OVERFLOW: u64 = 2; + + /// 32. + const LEN: u64 = 32; + + /// Container for `vector`, which has length == 32. + struct Bytes32 has copy, drop, store { + data: vector, + } + + public fun length(): u64 { + LEN + } + + /// Create new `Bytes32`, which checks the length of input `data`. + public fun new(data: vector): Bytes32 { + assert!(is_valid(&data), E_INVALID_BYTES32); + Bytes32 { data } + } + + /// Create new `Bytes20` of all zeros. + public fun default(): Bytes32 { + let data = vector::empty(); + let i = 0; + while (i < LEN) { + vector::push_back(&mut data, 0); + i = i + 1; + }; + new(data) + } + + /// Retrieve underlying `data`. + public fun data(self: &Bytes32): vector { + self.data + } + + /// Serialize `u256` as big-endian format in zero-padded `Bytes32`. + public fun from_u256_be(value: u256): Bytes32 { + let buf = bcs::to_bytes(&value); + vector::reverse(&mut buf); + new(buf) + } + + /// Deserialize from big-endian `u256`. + public fun to_u256_be(value: Bytes32): u256 { + let cur = cursor::new(to_bytes(value)); + let out = bytes::take_u256_be(&mut cur); + cursor::destroy_empty(cur); + + out + } + + /// Serialize `u64` as big-endian format in zero-padded `Bytes32`. + public fun from_u64_be(value: u64): Bytes32 { + from_u256_be((value as u256)) + } + + /// Deserialize from big-endian `u64` as long as the data does not + /// overflow. + public fun to_u64_be(value: Bytes32): u64 { + let num = to_u256_be(value); + assert!(num < (1u256 << 64), E_U64_OVERFLOW); + (num as u64) + } + + /// Either trim or pad (depending on length of the input `vector`) to 32 + /// bytes. + public fun from_bytes(buf: vector): Bytes32 { + let len = vector::length(&buf); + if (len > LEN) { + trim_nonzero_left(&mut buf); + new(buf) + } else { + new(pad_left(&buf, false)) + } + } + + /// Destroy `Bytes32` for its underlying data. + public fun to_bytes(value: Bytes32): vector { + let Bytes32 { data } = value; + data + } + + /// Drain 32 elements of `Cursor` to create `Bytes32`. + public fun take_bytes(cur: &mut Cursor): Bytes32 { + new(bytes::take_bytes(cur, LEN)) + } + + /// Destroy `Bytes32` to represent its underlying data as `address`. + public fun to_address(value: Bytes32): address { + sui::address::from_bytes(to_bytes(value)) + } + + /// Create `Bytes32` from `address`. + public fun from_address(addr: address): Bytes32 { + new(sui::address::to_bytes(addr)) + } + + public fun from_utf8(str: String): Bytes32 { + let data = *string::bytes(&str); + let len = vector::length(&data); + if (len > LEN) { + // Trim from end. + let i = len; + while (i > LEN) { + vector::pop_back(&mut data); + i = i - 1; + } + } else { + // Pad right to `LEN`. + let i = len; + while (i < LEN) { + vector::push_back(&mut data, 0); + i = i + 1; + } + }; + + new(data) + } + + /// Even if the input is valid utf8, the result might be shorter than 32 + /// bytes, because the original string might have a multi-byte utf8 + /// character at the 32 byte boundary, which, when split, results in an + /// invalid code point, so we remove it. + public fun to_utf8(value: Bytes32): String { + let data = to_bytes(value); + + let utf8 = string::try_utf8(data); + while (option::is_none(&utf8)) { + vector::pop_back(&mut data); + utf8 = string::try_utf8(data); + }; + + let buf = *string::bytes(&option::extract(&mut utf8)); + + // Now trim zeros from the right. + while ( + *vector::borrow(&buf, vector::length(&buf) - 1) == 0 + ) { + vector::pop_back(&mut buf); + }; + + string::utf8(buf) + } + + /// Validate that any of the bytes in underlying data is non-zero. + public fun is_nonzero(self: &Bytes32): bool { + let i = 0; + while (i < LEN) { + if (*vector::borrow(&self.data, i) > 0) { + return true + }; + i = i + 1; + }; + + false + } + + /// Check that the input data is correct length. + fun is_valid(data: &vector): bool { + vector::length(data) == LEN + } + + /// For vector size less than 32, add zeros to the left. + fun pad_left(data: &vector, data_reversed: bool): vector { + let out = vector::empty(); + let len = vector::length(data); + let i = len; + while (i < LEN) { + vector::push_back(&mut out, 0); + i = i + 1; + }; + if (data_reversed) { + let i = 0; + while (i < len) { + vector::push_back( + &mut out, + *vector::borrow(data, len - i - 1) + ); + i = i + 1; + }; + } else { + vector::append(&mut out, *data); + }; + + out + } + + /// Trim bytes from the left if they are zero. If any of these bytes + /// are non-zero, abort. + fun trim_nonzero_left(data: &mut vector) { + vector::reverse(data); + let (i, n) = (0, vector::length(data) - LEN); + while (i < n) { + assert!(vector::pop_back(data) == 0, E_CANNOT_TRIM_NONZERO); + i = i + 1; + }; + vector::reverse(data); + } +} + +#[test_only] +module wormhole::bytes32_tests { + use std::vector::{Self}; + + use wormhole::bytes32::{Self}; + + #[test] + public fun new() { + let data = + x"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + assert!(vector::length(&data) == 32, 0); + let actual = bytes32::new(data); + + assert!(bytes32::data(&actual) == data, 0); + } + + #[test] + public fun default() { + let actual = bytes32::default(); + let expected = + x"0000000000000000000000000000000000000000000000000000000000000000"; + assert!(bytes32::data(&actual) == expected, 0); + } + + #[test] + public fun from_u256_be() { + let actual = bytes32::from_u256_be(1 << 32); + let expected = + x"0000000000000000000000000000000000000000000000000000000100000000"; + assert!(bytes32::data(&actual) == expected, 0); + } + + #[test] + public fun to_u256_be() { + let actual = bytes32::new( + x"0000000000000000000000000000000000000000000000000000000100000000" + ); + assert!(bytes32::to_u256_be(actual) == (1 << 32), 0); + } + + #[test] + public fun from_bytes() { + let actual = bytes32::from_bytes(x"deadbeef"); + let expected = + x"00000000000000000000000000000000000000000000000000000000deadbeef"; + assert!(bytes32::data(&actual) == expected, 0); + } + + #[test] + public fun is_nonzero() { + let data = + x"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + let actual = bytes32::new(data); + assert!(bytes32::is_nonzero(&actual), 0); + + let zeros = bytes32::default(); + assert!(!bytes32::is_nonzero(&zeros), 0); + } + + #[test] + #[expected_failure(abort_code = bytes32::E_INVALID_BYTES32)] + public fun cannot_new_non_32_byte_vector() { + let data = + x"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbe"; + assert!(vector::length(&data) != 32, 0); + bytes32::new(data); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/external_address.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/external_address.move new file mode 100644 index 0000000000..b2134caf35 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/external_address.move @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a custom type for a 32-byte standardized address, +/// which is meant to represent an address from any other network. +module wormhole::external_address { + use sui::object::{Self, ID}; + use wormhole::bytes32::{Self, Bytes32}; + use wormhole::cursor::{Cursor}; + + /// Underlying data is all zeros. + const E_ZERO_ADDRESS: u64 = 0; + + /// Container for `Bytes32`. + struct ExternalAddress has copy, drop, store { + value: Bytes32, + } + + /// Create `ExternalAddress`. + public fun new(value: Bytes32): ExternalAddress { + ExternalAddress { value } + } + + /// Create `ExternalAddress` of all zeros.` + public fun default(): ExternalAddress { + new(bytes32::default()) + } + + /// Create `ExternalAddress` ensuring that not all bytes are zero. + public fun new_nonzero(value: Bytes32): ExternalAddress { + assert!(bytes32::is_nonzero(&value), E_ZERO_ADDRESS); + new(value) + } + + /// Destroy `ExternalAddress` for underlying bytes as `vector`. + public fun to_bytes(ext: ExternalAddress): vector { + bytes32::to_bytes(to_bytes32(ext)) + } + + /// Destroy 'ExternalAddress` for underlying data. + public fun to_bytes32(ext: ExternalAddress): Bytes32 { + let ExternalAddress { value } = ext; + value + } + + /// Drain 32 elements of `Cursor` to create `ExternalAddress`. + public fun take_bytes(cur: &mut Cursor): ExternalAddress { + new(bytes32::take_bytes(cur)) + } + + /// Drain 32 elements of `Cursor` to create `ExternalAddress` ensuring + /// that not all bytes are zero. + public fun take_nonzero(cur: &mut Cursor): ExternalAddress { + new_nonzero(bytes32::take_bytes(cur)) + } + + /// Destroy `ExternalAddress` to represent its underlying data as `address`. + public fun to_address(ext: ExternalAddress): address { + sui::address::from_bytes(to_bytes(ext)) + } + + /// Create `ExternalAddress` from `address`. + public fun from_address(addr: address): ExternalAddress { + new(bytes32::from_address(addr)) + } + + /// Create `ExternalAddress` from `ID`. + public fun from_id(id: ID): ExternalAddress { + new(bytes32::from_bytes(object::id_to_bytes(&id))) + } + + /// Check whether underlying data is not all zeros. + public fun is_nonzero(self: &ExternalAddress): bool { + bytes32::is_nonzero(&self.value) + } +} + +#[test_only] +module wormhole::external_address_tests { + use wormhole::bytes32::{Self}; + use wormhole::external_address::{Self}; + + #[test] + public fun test_bytes() { + let data = + bytes32::new( + x"1234567891234567891234567891234512345678912345678912345678912345" + ); + let addr = external_address::new(data); + assert!(external_address::to_bytes(addr) == bytes32::to_bytes(data), 0); + } + + #[test] + public fun test_address() { + let data = + bytes32::new( + x"0000000000000000000000000000000000000000000000000000000000001234" + ); + let addr = external_address::new(data); + assert!(external_address::to_address(addr) == @0x1234, 0); + assert!(addr == external_address::from_address(@0x1234), 0); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/guardian_signature.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/guardian_signature.move new file mode 100644 index 0000000000..58698d51a6 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/guardian_signature.move @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a custom type representing a Guardian's signature +/// with recovery ID of a particular hashed VAA message body. The components of +/// `GuardianSignature` are used to perform public key recovery using ECDSA. +module wormhole::guardian_signature { + use std::vector::{Self}; + + use wormhole::bytes32::{Self, Bytes32}; + + /// Container for elliptic curve signature parameters and Guardian index. + struct GuardianSignature has store, drop { + r: Bytes32, + s: Bytes32, + recovery_id: u8, + index: u8, + } + + /// Create new `GuardianSignature`. + public fun new( + r: Bytes32, + s: Bytes32, + recovery_id: u8, + index: u8 + ): GuardianSignature { + GuardianSignature { r, s, recovery_id, index } + } + + /// 32-byte signature parameter R. + public fun r(self: &GuardianSignature): Bytes32 { + self.r + } + + /// 32-byte signature parameter S. + public fun s(self: &GuardianSignature): Bytes32 { + self.s + } + + /// Signature recovery ID. + public fun recovery_id(self: &GuardianSignature): u8 { + self.recovery_id + } + + /// Guardian index. + public fun index(self: &GuardianSignature): u8 { + self.index + } + + /// Guardian index as u64. + public fun index_as_u64(self: &GuardianSignature): u64 { + (self.index as u64) + } + + /// Serialize elliptic curve paramters as `vector` of length == 65 to be + /// consumed by `ecdsa_k1` for public key recovery. + public fun to_rsv(gs: GuardianSignature): vector { + let GuardianSignature { r, s, recovery_id, index: _ } = gs; + let out = vector::empty(); + vector::append(&mut out, bytes32::to_bytes(r)); + vector::append(&mut out, bytes32::to_bytes(s)); + vector::push_back(&mut out, recovery_id); + out + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/emitter.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/emitter.move new file mode 100644 index 0000000000..5c5f2dbe73 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/emitter.move @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a capability (`EmitterCap`), which allows one to send +/// Wormhole messages. Its external address is determined by the capability's +/// `id`, which is a 32-byte vector. +module wormhole::emitter { + use sui::object::{Self, ID, UID}; + use sui::tx_context::{TxContext}; + + use wormhole::state::{Self, State}; + + friend wormhole::publish_message; + + /// Event reflecting when `new` is called. + struct EmitterCreated has drop, copy { + emitter_cap: ID + } + + /// Event reflecting when `destroy` is called. + struct EmitterDestroyed has drop, copy { + emitter_cap: ID + } + + /// `EmitterCap` is a Sui object that gives a user or smart contract the + /// capability to send Wormhole messages. For every Wormhole message + /// emitted, a unique `sequence` is used. + struct EmitterCap has key, store { + id: UID, + + /// Sequence number of the next wormhole message. + sequence: u64 + } + + /// Generate a new `EmitterCap`. + public fun new(wormhole_state: &State, ctx: &mut TxContext): EmitterCap { + state::assert_latest_only(wormhole_state); + + let cap = + EmitterCap { + id: object::new(ctx), + sequence: 0 + }; + + sui::event::emit( + EmitterCreated { emitter_cap: object::id(&cap)} + ); + + cap + } + + /// Returns current sequence (which will be used in the next Wormhole + /// message emitted). + public fun sequence(self: &EmitterCap): u64 { + self.sequence + } + + /// Once a Wormhole message is emitted, an `EmitterCap` upticks its + /// internal `sequence` for the next message. + public(friend) fun use_sequence(self: &mut EmitterCap): u64 { + let sequence = self.sequence; + self.sequence = sequence + 1; + sequence + } + + /// Destroys an `EmitterCap`. + /// + /// Note that this operation removes the ability to send messages using the + /// emitter id, and is irreversible. + public fun destroy(wormhole_state: &State, cap: EmitterCap) { + state::assert_latest_only(wormhole_state); + + sui::event::emit( + EmitterDestroyed { emitter_cap: object::id(&cap) } + ); + + let EmitterCap { id, sequence: _ } = cap; + object::delete(id); + } + + #[test_only] + public fun destroy_test_only(cap: EmitterCap) { + let EmitterCap { id, sequence: _ } = cap; + object::delete(id); + } + + #[test_only] + public fun dummy(): EmitterCap { + EmitterCap { + id: object::new(&mut sui::tx_context::dummy()), + sequence: 0 + } + } +} + +#[test_only] +module wormhole::emitter_tests { + use sui::object::{Self}; + use sui::test_scenario::{Self}; + + use wormhole::emitter::{Self}; + use wormhole::state::{Self}; + use wormhole::version_control::{Self}; + use wormhole::wormhole_scenario::{ + person, + return_state, + set_up_wormhole, + take_state + }; + + #[test] + fun test_emitter() { + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + + let dummy_cap = emitter::dummy(); + let expected = + @0x381dd9078c322a4663c392761a0211b527c127b29583851217f948d62131f409; + assert!(object::id_to_address(&object::id(&dummy_cap)) == expected, 0); + + // Generate new emitter. + let cap = emitter::new(&worm_state, test_scenario::ctx(scenario)); + + // And check emitter cap's address. + let expected = + @0x75c3360eb19fd2c20fbba5e2da8cf1a39cdb1ee913af3802ba330b852e459e05; + assert!(object::id_to_address(&object::id(&cap)) == expected, 0); + + // Clean up. + emitter::destroy(&worm_state, dummy_cap); + emitter::destroy(&worm_state, cap); + return_state(worm_state); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_new_emitter_outdated_version() { + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut worm_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut worm_state, + version_control::previous_version_test_only(), + version_control::next_version() + ); + + // You shall not pass! + let cap = emitter::new(&worm_state, test_scenario::ctx(scenario)); + + // Clean up. + emitter::destroy(&worm_state, cap); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/set_fee.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/set_fee.move new file mode 100644 index 0000000000..ab3431b7ca --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/set_fee.move @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements handling a governance VAA to enact setting the +/// Wormhole message fee to another amount. +module wormhole::set_fee { + use wormhole::bytes32::{Self}; + use wormhole::cursor::{Self}; + use wormhole::governance_message::{Self, DecreeTicket, DecreeReceipt}; + use wormhole::state::{Self, State}; + + /// Specific governance payload ID (action) for setting Wormhole fee. + const ACTION_SET_FEE: u8 = 3; + + struct GovernanceWitness has drop {} + + struct SetFee { + amount: u64 + } + + public fun authorize_governance( + wormhole_state: &State + ): DecreeTicket { + governance_message::authorize_verify_local( + GovernanceWitness {}, + state::governance_chain(wormhole_state), + state::governance_contract(wormhole_state), + state::governance_module(), + ACTION_SET_FEE + ) + } + + /// Redeem governance VAA to configure Wormhole message fee amount in SUI + /// denomination. This governance message is only relevant for Sui because + /// fee administration is only relevant to one particular network (in this + /// case Sui). + /// + /// NOTE: This method is guarded by a minimum build version check. This + /// method could break backward compatibility on an upgrade. + public fun set_fee( + wormhole_state: &mut State, + receipt: DecreeReceipt + ): u64 { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(wormhole_state); + + let payload = + governance_message::take_payload( + state::borrow_mut_consumed_vaas(&latest_only, wormhole_state), + receipt + ); + + // Deserialize the payload as amount to change the Wormhole fee. + let SetFee { amount } = deserialize(payload); + + state::set_message_fee(&latest_only, wormhole_state, amount); + + amount + } + + fun deserialize(payload: vector): SetFee { + let cur = cursor::new(payload); + + // This amount cannot be greater than max u64. + let amount = bytes32::to_u64_be(bytes32::take_bytes(&mut cur)); + + cursor::destroy_empty(cur); + + SetFee { amount: (amount as u64) } + } + + #[test_only] + public fun action(): u8 { + ACTION_SET_FEE + } +} + +#[test_only] +module wormhole::set_fee_tests { + use sui::balance::{Self}; + use sui::test_scenario::{Self}; + + use wormhole::bytes::{Self}; + use wormhole::cursor::{Self}; + use wormhole::governance_message::{Self}; + use wormhole::set_fee::{Self}; + use wormhole::state::{Self}; + use wormhole::vaa::{Self}; + use wormhole::version_control::{Self}; + use wormhole::wormhole_scenario::{ + person, + return_clock, + return_state, + set_up_wormhole, + take_clock, + take_state, + upgrade_wormhole + }; + + const VAA_SET_FEE_1: vector = + x"01000000000100181aa27fd44f3060fad0ae72895d42f97c45f7a5d34aa294102911370695e91e17ae82caa59f779edde2356d95cd46c2c381cdeba7a8165901a562374f212d750000bc614e000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000010100000000000000000000000000000000000000000000000000000000436f7265030015000000000000000000000000000000000000000000000000000000000000015e"; + const VAA_SET_FEE_MAX: vector = + x"01000000000100b0697fd31572e11b2256cf46d5934f38fbb90e6265e999bee50950846bf9f94d5b86f247cce20e3cc158163be7b5ae21ebaaf67e20d597229ca04d505fd4bc1c0000bc614e000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000010100000000000000000000000000000000000000000000000000000000436f7265030015000000000000000000000000000000000000000000000000ffffffffffffffff"; + const VAA_SET_FEE_OVERFLOW: vector = + x"01000000000100950a509a797c9b40a678a5d6297f5b74e1ce1794b3c012dad5774c395e65e8b0773cf160113f571f1452ee98d10aa61273b6bc8aefa74a3c8f7e2c9c89fb25fa0000bc614e000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000010100000000000000000000000000000000000000000000000000000000436f72650300150000000000000000000000000000000000000000000000010000000000000000"; + + #[test] + fun test_set_fee() { + // Testing this method. + use wormhole::set_fee::{set_fee}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 420; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `set_fee`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Double-check current fee (from setup). + assert!(state::message_fee(&worm_state) == wormhole_fee, 0); + + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_SET_FEE_1, &the_clock); + let ticket = set_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + let fee_amount = set_fee(&mut worm_state, receipt); + assert!(wormhole_fee != fee_amount, 0); + + // Confirm the fee changed. + assert!(state::message_fee(&worm_state) == fee_amount, 0); + + // And confirm that we can deposit the new fee amount. + state::deposit_fee_test_only( + &mut worm_state, + balance::create_for_testing(fee_amount) + ); + + // Finally set the fee again to max u64 (this will effectively pause + // Wormhole message publishing until the fee gets adjusted back to a + // reasonable level again). + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_SET_FEE_MAX, &the_clock); + let ticket = set_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + let fee_amount = set_fee(&mut worm_state, receipt); + + // Confirm. + assert!(state::message_fee(&worm_state) == fee_amount, 0); + + // Clean up. + return_state(worm_state); + return_clock(the_clock); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_set_fee_after_upgrade() { + // Testing this method. + use wormhole::set_fee::{set_fee}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 420; + set_up_wormhole(scenario, wormhole_fee); + + // Upgrade. + upgrade_wormhole(scenario); + + // Prepare test to execute `set_fee`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Double-check current fee (from setup). + assert!(state::message_fee(&worm_state) == wormhole_fee, 0); + + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_SET_FEE_1, &the_clock); + let ticket = set_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + let fee_amount = set_fee(&mut worm_state, receipt); + + // Confirm the fee changed. + assert!(state::message_fee(&worm_state) == fee_amount, 0); + + // Clean up. + return_state(worm_state); + return_clock(the_clock); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = wormhole::set::E_KEY_ALREADY_EXISTS)] + fun test_cannot_set_fee_with_same_vaa() { + // Testing this method. + use wormhole::set_fee::{set_fee}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 420; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `set_fee`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Set once. + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_SET_FEE_1, &the_clock); + let ticket = set_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + set_fee(&mut worm_state, receipt); + + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_SET_FEE_1, &the_clock); + let ticket = set_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + + // You shall not pass! + set_fee(&mut worm_state, receipt); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::bytes32::E_U64_OVERFLOW)] + fun test_cannot_set_fee_with_overflow() { + // Testing this method. + use wormhole::set_fee::{set_fee}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 420; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `set_fee`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Show that the encoded fee is greater than u64 max. + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_SET_FEE_OVERFLOW, + &the_clock + ); + let payload = + governance_message::take_decree(vaa::payload(&verified_vaa)); + let cur = cursor::new(payload); + + let fee_amount = bytes::take_u256_be(&mut cur); + assert!(fee_amount > 0xffffffffffffffff, 0); + + cursor::destroy_empty(cur); + + let ticket = set_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + + // You shall not pass! + set_fee(&mut worm_state, receipt); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_set_fee_outdated_version() { + // Testing this method. + use wormhole::set_fee::{set_fee}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 420; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `set_fee`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut worm_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut worm_state, + version_control::previous_version_test_only(), + version_control::next_version() + ); + + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_SET_FEE_1, + &the_clock + ); + + let ticket = set_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + + // You shall not pass! + set_fee(&mut worm_state, receipt); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/transfer_fee.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/transfer_fee.move new file mode 100644 index 0000000000..f31274b8e0 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/transfer_fee.move @@ -0,0 +1,529 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements handling a governance VAA to enact transferring some +/// amount of collected fees to an intended recipient. +module wormhole::transfer_fee { + use sui::coin::{Self}; + use sui::transfer::{Self}; + use sui::tx_context::{TxContext}; + + use wormhole::bytes32::{Self}; + use wormhole::cursor::{Self}; + use wormhole::external_address::{Self}; + use wormhole::governance_message::{Self, DecreeTicket, DecreeReceipt}; + use wormhole::state::{Self, State, LatestOnly}; + + /// Specific governance payload ID (action) for setting Wormhole fee. + const ACTION_TRANSFER_FEE: u8 = 4; + + struct GovernanceWitness has drop {} + + struct TransferFee { + amount: u64, + recipient: address + } + + public fun authorize_governance( + wormhole_state: &State + ): DecreeTicket { + governance_message::authorize_verify_local( + GovernanceWitness {}, + state::governance_chain(wormhole_state), + state::governance_contract(wormhole_state), + state::governance_module(), + ACTION_TRANSFER_FEE + ) + } + + /// Redeem governance VAA to transfer collected Wormhole fees to the + /// recipient encoded in its Wormhole governance message. This governance + /// message is only relevant for Sui because fee administration is only + /// relevant to one particular network (in this case Sui). + /// + /// NOTE: This method is guarded by a minimum build version check. This + /// method could break backward compatibility on an upgrade. + public fun transfer_fee( + wormhole_state: &mut State, + receipt: DecreeReceipt, + ctx: &mut TxContext + ): u64 { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(wormhole_state); + + let payload = + governance_message::take_payload( + state::borrow_mut_consumed_vaas(&latest_only, wormhole_state), + receipt + ); + + // Proceed with setting the new message fee. + handle_transfer_fee(&latest_only, wormhole_state, payload, ctx) + } + + fun handle_transfer_fee( + latest_only: &LatestOnly, + wormhole_state: &mut State, + governance_payload: vector, + ctx: &mut TxContext + ): u64 { + // Deserialize the payload as amount to withdraw and to whom SUI should + // be sent. + let TransferFee { amount, recipient } = deserialize(governance_payload); + + transfer::public_transfer( + coin::from_balance( + state::withdraw_fee(latest_only, wormhole_state, amount), + ctx + ), + recipient + ); + + amount + } + + fun deserialize(payload: vector): TransferFee { + let cur = cursor::new(payload); + + // This amount cannot be greater than max u64. + let amount = bytes32::to_u64_be(bytes32::take_bytes(&mut cur)); + + // Recipient must be non-zero address. + let recipient = external_address::take_nonzero(&mut cur); + + cursor::destroy_empty(cur); + + TransferFee { + amount: (amount as u64), + recipient: external_address::to_address(recipient) + } + } + + #[test_only] + public fun action(): u8 { + ACTION_TRANSFER_FEE + } +} + +#[test_only] +module wormhole::transfer_fee_tests { + use sui::balance::{Self}; + use sui::coin::{Self, Coin}; + use sui::sui::{SUI}; + use sui::test_scenario::{Self}; + + use wormhole::bytes::{Self}; + use wormhole::bytes32::{Self}; + use wormhole::cursor::{Self}; + use wormhole::external_address::{Self}; + use wormhole::governance_message::{Self}; + use wormhole::state::{Self}; + use wormhole::transfer_fee::{Self}; + use wormhole::vaa::{Self}; + use wormhole::version_control::{Self}; + use wormhole::wormhole_scenario::{ + person, + return_clock, + return_state, + set_up_wormhole, + take_clock, + take_state, + two_people, + upgrade_wormhole + }; + + const VAA_TRANSFER_FEE_1: vector = + x"01000000000100a96aee105d7683266d98c9b274eddb20391378adddcefbc7a5266b4be78bc6eb582797741b65617d796c6c613ae7a4dad52a8b4aa4659842dcc4c9b3891549820100bc614e000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000010100000000000000000000000000000000000000000000000000000000436f726504001500000000000000000000000000000000000000000000000000000000000004b0000000000000000000000000000000000000000000000000000000000000b0b2"; + const VAA_TRANSFER_FEE_OVERFLOW: vector = + x"01000000000100529b407a673f8917ccb9bb6f8d46d0f729c1ff845b0068ef5e0a3de464670b2e379a8994b15362785e52d73e01c880dbcdf432ef3702782d17d352fb07ed86830100bc614e000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000010100000000000000000000000000000000000000000000000000000000436f72650400150000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000b0b2"; + const VAA_TRANSFER_FEE_ZERO_ADDRESS: vector = + x"0100000000010032b2ab65a690ae4af8c85903d7b22239fc272183eefdd5a4fa784664f82aa64b381380cc03859156e88623949ce4da4435199aaac1cb09e52a09d6915725a5e70100bc614e000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000010100000000000000000000000000000000000000000000000000000000436f726504001500000000000000000000000000000000000000000000000000000000000004b00000000000000000000000000000000000000000000000000000000000000000"; + + #[test] + fun test_transfer_fee() { + // Testing this method. + use wormhole::transfer_fee::{transfer_fee}; + + // Set up. + let (caller, recipient) = two_people(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `transfer_fee`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Double-check current fee (from setup). + assert!(state::message_fee(&worm_state) == wormhole_fee, 0); + + // Deposit fee several times. + let (i, n) = (0, 8); + while (i < n) { + state::deposit_fee_test_only( + &mut worm_state, + balance::create_for_testing(wormhole_fee) + ); + i = i + 1; + }; + + // Double-check balance. + let total_deposited = n * wormhole_fee; + assert!(state::fees_collected(&worm_state) == total_deposited, 0); + + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_TRANSFER_FEE_1, &the_clock); + let ticket = transfer_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + let withdrawn = + transfer_fee( + &mut worm_state, + receipt, + test_scenario::ctx(scenario) + ); + assert!(withdrawn == 1200, 0); + + // Ignore effects. + test_scenario::next_tx(scenario, caller); + + // Verify that the recipient received the withdrawal. + let withdrawn_coin = + test_scenario::take_from_address>(scenario, recipient); + assert!(coin::value(&withdrawn_coin) == withdrawn, 0); + + // And there is still a balance on Wormhole's fee collector. + let remaining = total_deposited - withdrawn; + assert!(state::fees_collected(&worm_state) == remaining, 0); + + // Clean up. + coin::burn_for_testing(withdrawn_coin); + return_state(worm_state); + return_clock(the_clock); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_transfer_fee_after_upgrade() { + // Testing this method. + use wormhole::transfer_fee::{transfer_fee}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Upgrade. + upgrade_wormhole(scenario); + + // Prepare test to execute `transfer_fee`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Double-check current fee (from setup). + assert!(state::message_fee(&worm_state) == wormhole_fee, 0); + + // Deposit fee several times. + let (i, n) = (0, 8); + while (i < n) { + state::deposit_fee_test_only( + &mut worm_state, + balance::create_for_testing(wormhole_fee) + ); + i = i + 1; + }; + + // Double-check balance. + let total_deposited = n * wormhole_fee; + assert!(state::fees_collected(&worm_state) == total_deposited, 0); + + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_TRANSFER_FEE_1, &the_clock); + let ticket = transfer_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + let withdrawn = + transfer_fee( + &mut worm_state, + receipt, + test_scenario::ctx(scenario) + ); + assert!(withdrawn == 1200, 0); + + // Clean up. + return_state(worm_state); + return_clock(the_clock); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = wormhole::set::E_KEY_ALREADY_EXISTS)] + fun test_cannot_transfer_fee_with_same_vaa() { + // Testing this method. + use wormhole::transfer_fee::{transfer_fee}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `transfer_fee`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Double-check current fee (from setup). + assert!(state::message_fee(&worm_state) == wormhole_fee, 0); + + // Deposit fee several times. + let (i, n) = (0, 8); + while (i < n) { + state::deposit_fee_test_only( + &mut worm_state, + balance::create_for_testing(wormhole_fee) + ); + i = i + 1; + }; + + // Transfer once. + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_TRANSFER_FEE_1, &the_clock); + let ticket = transfer_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + transfer_fee(&mut worm_state, receipt, test_scenario::ctx(scenario)); + + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_TRANSFER_FEE_1, &the_clock); + let ticket = transfer_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + // You shall not pass! + transfer_fee(&mut worm_state, receipt, test_scenario::ctx(scenario)); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = sui::balance::ENotEnough)] + fun test_cannot_transfer_fee_insufficient_balance() { + // Testing this method. + use wormhole::transfer_fee::{transfer_fee}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `transfer_fee`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Show balance is zero. + assert!(state::fees_collected(&worm_state) == 0, 0); + + // Show that the encoded fee is greater than zero. + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_TRANSFER_FEE_1, &the_clock); + let payload = + governance_message::take_decree(vaa::payload(&verified_vaa)); + let cur = cursor::new(payload); + + let amount = bytes::take_u256_be(&mut cur); + assert!(amount > 0, 0); + cursor::take_rest(cur); + + let ticket = transfer_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + // You shall not pass! + transfer_fee(&mut worm_state, receipt, test_scenario::ctx(scenario)); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = external_address::E_ZERO_ADDRESS)] + fun test_cannot_transfer_fee_recipient_zero_address() { + // Testing this method. + use wormhole::transfer_fee::{transfer_fee}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `transfer_fee`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Show balance is zero. + assert!(state::fees_collected(&worm_state) == 0, 0); + + // Show that the encoded fee is greater than zero. + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_TRANSFER_FEE_ZERO_ADDRESS, + &the_clock + ); + let payload = + governance_message::take_decree(vaa::payload(&verified_vaa)); + let cur = cursor::new(payload); + + bytes::take_u256_be(&mut cur); + + // Confirm recipient is zero address. + let addr = bytes32::take_bytes(&mut cur); + assert!(!bytes32::is_nonzero(&addr), 0); + cursor::destroy_empty(cur); + + let ticket = transfer_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + // You shall not pass! + transfer_fee(&mut worm_state, receipt, test_scenario::ctx(scenario)); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::bytes32::E_U64_OVERFLOW)] + fun test_cannot_transfer_fee_withdraw_amount_overflow() { + // Testing this method. + use wormhole::transfer_fee::{transfer_fee}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `transfer_fee`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Show balance is zero. + assert!(state::fees_collected(&worm_state) == 0, 0); + + // Show that the encoded fee is greater than zero. + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_TRANSFER_FEE_OVERFLOW, + &the_clock + ); + let payload = + governance_message::take_decree(vaa::payload(&verified_vaa)); + let cur = cursor::new(payload); + + let amount = bytes::take_u256_be(&mut cur); + assert!(amount > 0xffffffffffffffff, 0); + cursor::take_rest(cur); + + let ticket = transfer_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + // You shall not pass! + transfer_fee(&mut worm_state, receipt, test_scenario::ctx(scenario)); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_set_fee_outdated_version() { + // Testing this method. + use wormhole::transfer_fee::{transfer_fee}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `transfer_fee`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Double-check current fee (from setup). + assert!(state::message_fee(&worm_state) == wormhole_fee, 0); + + // Deposit fee several times. + let (i, n) = (0, 8); + while (i < n) { + state::deposit_fee_test_only( + &mut worm_state, + balance::create_for_testing(wormhole_fee) + ); + i = i + 1; + }; + + // Double-check balance. + let total_deposited = n * wormhole_fee; + assert!(state::fees_collected(&worm_state) == total_deposited, 0); + + // Prepare test to execute `transfer_fee`. + test_scenario::next_tx(scenario, caller); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut worm_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut worm_state, + version_control::previous_version_test_only(), + version_control::next_version() + ); + + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_TRANSFER_FEE_1, + &the_clock + ); + let ticket = transfer_fee::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + // You shall not pass! + transfer_fee(&mut worm_state, receipt, test_scenario::ctx(scenario)); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/update_guardian_set.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/update_guardian_set.move new file mode 100644 index 0000000000..15bfb1953b --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/update_guardian_set.move @@ -0,0 +1,471 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements handling a governance VAA to enact updating the +/// current guardian set to be a new set of guardian public keys. As a part of +/// this process, the previous guardian set's expiration time is set. Keep in +/// mind that the current guardian set has no expiration. +module wormhole::update_guardian_set { + use std::vector::{Self}; + use sui::clock::{Clock}; + + use wormhole::bytes::{Self}; + use wormhole::cursor::{Self}; + use wormhole::governance_message::{Self, DecreeTicket, DecreeReceipt}; + use wormhole::guardian::{Self, Guardian}; + use wormhole::guardian_set::{Self}; + use wormhole::state::{Self, State, LatestOnly}; + + /// No guardians public keys found in VAA. + const E_NO_GUARDIANS: u64 = 0; + /// Guardian set index is not incremented from last known guardian set. + const E_NON_INCREMENTAL_GUARDIAN_SETS: u64 = 1; + + /// Specific governance payload ID (action) for updating the guardian set. + const ACTION_UPDATE_GUARDIAN_SET: u8 = 2; + + struct GovernanceWitness has drop {} + + /// Event reflecting a Guardian Set update. + struct GuardianSetAdded has drop, copy { + new_index: u32 + } + + struct UpdateGuardianSet { + new_index: u32, + guardians: vector, + } + + public fun authorize_governance( + wormhole_state: &State + ): DecreeTicket { + governance_message::authorize_verify_global( + GovernanceWitness {}, + state::governance_chain(wormhole_state), + state::governance_contract(wormhole_state), + state::governance_module(), + ACTION_UPDATE_GUARDIAN_SET + ) + } + + /// Redeem governance VAA to update the current Guardian set with a new + /// set of Guardian public keys. This governance action is applied globally + /// across all networks. + /// + /// NOTE: This method is guarded by a minimum build version check. This + /// method could break backward compatibility on an upgrade. + public fun update_guardian_set( + wormhole_state: &mut State, + receipt: DecreeReceipt, + the_clock: &Clock + ): u32 { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(wormhole_state); + + // Even though this disallows the VAA to be replayed, it may be + // impossible to redeem the same VAA again because `governance_message` + // requires new governance VAAs being signed by the most recent guardian + // set). + let payload = + governance_message::take_payload( + state::borrow_mut_consumed_vaas(&latest_only, wormhole_state), + receipt + ); + + // Proceed with the update. + handle_update_guardian_set(&latest_only, wormhole_state, payload, the_clock) + } + + fun handle_update_guardian_set( + latest_only: &LatestOnly, + wormhole_state: &mut State, + governance_payload: vector, + the_clock: &Clock + ): u32 { + // Deserialize the payload as the updated guardian set. + let UpdateGuardianSet { + new_index, + guardians + } = deserialize(governance_payload); + + // Every new guardian set index must be incremental from the last known + // guardian set. + assert!( + new_index == state::guardian_set_index(wormhole_state) + 1, + E_NON_INCREMENTAL_GUARDIAN_SETS + ); + + // Expire the existing guardian set. + state::expire_guardian_set(latest_only, wormhole_state, the_clock); + + // And store the new one. + state::add_new_guardian_set( + latest_only, + wormhole_state, + guardian_set::new(new_index, guardians) + ); + + sui::event::emit(GuardianSetAdded { new_index }); + + new_index + } + + fun deserialize(payload: vector): UpdateGuardianSet { + let cur = cursor::new(payload); + let new_index = bytes::take_u32_be(&mut cur); + let num_guardians = bytes::take_u8(&mut cur); + assert!(num_guardians > 0, E_NO_GUARDIANS); + + let guardians = vector::empty(); + let i = 0; + while (i < num_guardians) { + let key = bytes::take_bytes(&mut cur, 20); + vector::push_back(&mut guardians, guardian::new(key)); + i = i + 1; + }; + cursor::destroy_empty(cur); + + UpdateGuardianSet { new_index, guardians } + } + + #[test_only] + public fun action(): u8 { + ACTION_UPDATE_GUARDIAN_SET + } +} + +#[test_only] +module wormhole::update_guardian_set_tests { + use std::vector::{Self}; + use sui::clock::{Self}; + use sui::test_scenario::{Self}; + + use wormhole::bytes::{Self}; + use wormhole::cursor::{Self}; + use wormhole::governance_message::{Self}; + use wormhole::guardian::{Self}; + use wormhole::guardian_set::{Self}; + use wormhole::state::{Self}; + use wormhole::update_guardian_set::{Self}; + use wormhole::vaa::{Self}; + use wormhole::version_control::{Self}; + use wormhole::wormhole_scenario::{ + person, + return_clock, + return_state, + set_up_wormhole, + take_clock, + take_state, + upgrade_wormhole + }; + + const VAA_UPDATE_GUARDIAN_SET_1: vector = + x"010000000001004f74e9596bd8246ef456918594ae16e81365b52c0cf4490b2a029fb101b058311f4a5592baeac014dc58215faad36453467a85a4c3e1c6cf5166e80f6e4dc50b0100bc614e000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000010100000000000000000000000000000000000000000000000000000000436f72650200000000000113befa429d57cd18b7f8a4d91a2da9ab4af05d0fbe88d7d8b32a9105d228100e72dffe2fae0705d31c58076f561cc62a47087b567c86f986426dfcd000bd6e9833490f8fa87c733a183cd076a6cbd29074b853fcf0a5c78c1b56d15fce7a154e6ebe9ed7a2af3503dbd2e37518ab04d7ce78b630f98b15b78a785632dea5609064803b1c8ea8bb2c77a6004bd109a281a698c0f5ba31f158585b41f4f33659e54d3178443ab76a60e21690dbfb17f7f59f09ae3ea1647ec26ae49b14060660504f4da1c2059e1c5ab6810ac3d8e1258bd2f004a94ca0cd4c68fc1c061180610e96d645b12f47ae5cf4546b18538739e90f2edb0d8530e31a218e72b9480202acbaeb06178da78858e5e5c4705cdd4b668ffe3be5bae4867c9d5efe3a05efc62d60e1d19faeb56a80223cdd3472d791b7d32c05abb1cc00b6381fa0c4928f0c56fc14bc029b8809069093d712a3fd4dfab31963597e246ab29fc6ebedf2d392a51ab2dc5c59d0902a03132a84dfd920b35a3d0ba5f7a0635df298f9033e"; + const VAA_UPDATE_GUARDIAN_SET_2A: vector = + x"010000000001005fb17d5e0e736e3014756bf7e7335722c4fe3ad18b5b1b566e8e61e562cc44555f30b298bc6a21ea4b192a6f1877a5e638ecf90a77b0b028f297a3a70d93614d0100bc614e000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000010100000000000000000000000000000000000000000000000000000000436f72650200000000000101befa429d57cd18b7f8a4d91a2da9ab4af05d0fbe"; + const VAA_UPDATE_GUARDIAN_SET_2B: vector = + x"01000000010100195f37abd29438c74db6e57bf527646b36fa96e36392221e869debe0e911f2f319abc0fd5c5a454da76fc0ffdd23a71a60bca40aa4289a841ad07f2964cde9290000bc614e000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000020100000000000000000000000000000000000000000000000000000000436f72650200000000000201befa429d57cd18b7f8a4d91a2da9ab4af05d0fbe"; + const VAA_UPDATE_GUARDIAN_SET_EMPTY: vector = + x"0100000000010098f9e45f836661d2932def9c74c587168f4f75d0282201ee6f5a98557e6212ff19b0f8881c2750646250f60dd5d565530779ecbf9442aa5ffc2d6afd7303aaa40000bc614e000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000010100000000000000000000000000000000000000000000000000000000436f72650200000000000100"; + + #[test] + fun test_update_guardian_set() { + // Testing this method. + use wormhole::update_guardian_set::{update_guardian_set}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `update_guardian_set`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_UPDATE_GUARDIAN_SET_1, + &the_clock + ); + let ticket = update_guardian_set::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + let new_index = + update_guardian_set(&mut worm_state, receipt, &the_clock); + assert!(new_index == 1, 0); + + let new_guardian_set = + state::guardian_set_at(&worm_state, new_index); + + // Verify new guardian set index. + assert!(state::guardian_set_index(&worm_state) == new_index, 0); + assert!( + guardian_set::index(new_guardian_set) == state::guardian_set_index(&worm_state), + 0 + ); + + // Check that the guardians agree with what we expect. + let guardians = guardian_set::guardians(new_guardian_set); + let expected = vector[ + guardian::new(x"befa429d57cd18b7f8a4d91a2da9ab4af05d0fbe"), + guardian::new(x"88d7d8b32a9105d228100e72dffe2fae0705d31c"), + guardian::new(x"58076f561cc62a47087b567c86f986426dfcd000"), + guardian::new(x"bd6e9833490f8fa87c733a183cd076a6cbd29074"), + guardian::new(x"b853fcf0a5c78c1b56d15fce7a154e6ebe9ed7a2"), + guardian::new(x"af3503dbd2e37518ab04d7ce78b630f98b15b78a"), + guardian::new(x"785632dea5609064803b1c8ea8bb2c77a6004bd1"), + guardian::new(x"09a281a698c0f5ba31f158585b41f4f33659e54d"), + guardian::new(x"3178443ab76a60e21690dbfb17f7f59f09ae3ea1"), + guardian::new(x"647ec26ae49b14060660504f4da1c2059e1c5ab6"), + guardian::new(x"810ac3d8e1258bd2f004a94ca0cd4c68fc1c0611"), + guardian::new(x"80610e96d645b12f47ae5cf4546b18538739e90f"), + guardian::new(x"2edb0d8530e31a218e72b9480202acbaeb06178d"), + guardian::new(x"a78858e5e5c4705cdd4b668ffe3be5bae4867c9d"), + guardian::new(x"5efe3a05efc62d60e1d19faeb56a80223cdd3472"), + guardian::new(x"d791b7d32c05abb1cc00b6381fa0c4928f0c56fc"), + guardian::new(x"14bc029b8809069093d712a3fd4dfab31963597e"), + guardian::new(x"246ab29fc6ebedf2d392a51ab2dc5c59d0902a03"), + guardian::new(x"132a84dfd920b35a3d0ba5f7a0635df298f9033e"), + ]; + assert!(vector::length(&expected) == vector::length(guardians), 0); + + let cur = cursor::new(expected); + let i = 0; + while (!cursor::is_empty(&cur)) { + let left = guardian::as_bytes(vector::borrow(guardians, i)); + let right = guardian::to_bytes(cursor::poke(&mut cur)); + assert!(left == right, 0); + i = i + 1; + }; + cursor::destroy_empty(cur); + + // Make sure old guardian set is still active. + let old_guardian_set = + state::guardian_set_at(&worm_state, new_index - 1); + assert!(guardian_set::is_active(old_guardian_set, &the_clock), 0); + + // Fast forward time beyond expiration by + // `guardian_set_seconds_to_live`. + let tick_ms = + (state::guardian_set_seconds_to_live(&worm_state) as u64) * 1000; + clock::increment_for_testing(&mut the_clock, tick_ms + 1); + + // Now the old guardian set should be expired (because in the test setup + // time to live is set to 2 epochs). + assert!(!guardian_set::is_active(old_guardian_set, &the_clock), 0); + + // Clean up. + return_state(worm_state); + return_clock(the_clock); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_update_guardian_set_after_upgrade() { + // Testing this method. + use wormhole::update_guardian_set::{update_guardian_set}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Upgrade. + upgrade_wormhole(scenario); + + // Prepare test to execute `update_guardian_set`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_UPDATE_GUARDIAN_SET_1, + &the_clock + ); + let ticket = update_guardian_set::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + let new_index = + update_guardian_set(&mut worm_state, receipt, &the_clock); + assert!(new_index == 1, 0); + + // Clean up. + return_state(worm_state); + return_clock(the_clock); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure( + abort_code = governance_message::E_OLD_GUARDIAN_SET_GOVERNANCE + )] + fun test_cannot_update_guardian_set_again_with_same_vaa() { + // Testing this method. + use wormhole::update_guardian_set::{update_guardian_set}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `update_guardian_set`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_UPDATE_GUARDIAN_SET_2A, + &the_clock + ); + let ticket = update_guardian_set::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + update_guardian_set(&mut worm_state, receipt, &the_clock); + + // Update guardian set again with new VAA. + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_UPDATE_GUARDIAN_SET_2B, + &the_clock + ); + let ticket = update_guardian_set::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + let new_index = + update_guardian_set(&mut worm_state, receipt, &the_clock); + assert!(new_index == 2, 0); + assert!(state::guardian_set_index(&worm_state) == 2, 0); + + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_UPDATE_GUARDIAN_SET_2A, + &the_clock + ); + let ticket = update_guardian_set::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + // You shall not pass! + update_guardian_set(&mut worm_state, receipt, &the_clock); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = update_guardian_set::E_NO_GUARDIANS)] + fun test_cannot_update_guardian_set_with_no_guardians() { + // Testing this method. + use wormhole::update_guardian_set::{update_guardian_set}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `update_guardian_set`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + + // Show that the encoded number of guardians is zero. + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_UPDATE_GUARDIAN_SET_EMPTY, + &the_clock + ); + let payload = + governance_message::take_decree(vaa::payload(&verified_vaa)); + let cur = cursor::new(payload); + + let new_guardian_set_index = bytes::take_u32_be(&mut cur); + assert!(new_guardian_set_index == 1, 0); + + let num_guardians = bytes::take_u8(&mut cur); + assert!(num_guardians == 0, 0); + + cursor::destroy_empty(cur); + + let ticket = update_guardian_set::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + // You shall not pass! + update_guardian_set(&mut worm_state, receipt, &the_clock); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_set_fee_outdated_version() { + // Testing this method. + use wormhole::update_guardian_set::{update_guardian_set}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test to execute `update_guardian_set`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut worm_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut worm_state, + version_control::previous_version_test_only(), + version_control::next_version() + ); + + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_UPDATE_GUARDIAN_SET_1, + &the_clock + ); + let ticket = update_guardian_set::authorize_governance(&worm_state); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + // You shall not pass! + update_guardian_set(&mut worm_state, receipt, &the_clock); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/upgrade_contract.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/upgrade_contract.move new file mode 100644 index 0000000000..018511e784 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/upgrade_contract.move @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements handling a governance VAA to enact upgrading the +/// Wormhole contract to a new build. The procedure to upgrade this contract +/// requires a Programmable Transaction, which includes the following procedure: +/// 1. Load new build. +/// 2. Authorize upgrade. +/// 3. Upgrade. +/// 4. Commit upgrade. +module wormhole::upgrade_contract { + use sui::object::{ID}; + use sui::package::{UpgradeReceipt, UpgradeTicket}; + + use wormhole::bytes32::{Self, Bytes32}; + use wormhole::cursor::{Self}; + use wormhole::governance_message::{Self, DecreeTicket, DecreeReceipt}; + use wormhole::state::{Self, State}; + + friend wormhole::migrate; + + /// Digest is all zeros. + const E_DIGEST_ZERO_BYTES: u64 = 0; + + /// Specific governance payload ID (action) to complete upgrading the + /// contract. + const ACTION_UPGRADE_CONTRACT: u8 = 1; + + struct GovernanceWitness has drop {} + + // Event reflecting package upgrade. + struct ContractUpgraded has drop, copy { + old_contract: ID, + new_contract: ID + } + + struct UpgradeContract { + digest: Bytes32 + } + + public fun authorize_governance( + wormhole_state: &State + ): DecreeTicket { + governance_message::authorize_verify_local( + GovernanceWitness {}, + state::governance_chain(wormhole_state), + state::governance_contract(wormhole_state), + state::governance_module(), + ACTION_UPGRADE_CONTRACT + ) + } + + /// Redeem governance VAA to issue an `UpgradeTicket` for the upgrade given + /// a contract upgrade VAA. This governance message is only relevant for Sui + /// because a contract upgrade is only relevant to one particular network + /// (in this case Sui), whose build digest is encoded in this message. + public fun authorize_upgrade( + wormhole_state: &mut State, + receipt: DecreeReceipt + ): UpgradeTicket { + // NOTE: This is the only governance method that does not enforce + // current package checking when consuming VAA hashes. This is because + // upgrades are protected by the Sui VM, enforcing the latest package + // is the one performing the upgrade. + let consumed = + state::borrow_mut_consumed_vaas_unchecked(wormhole_state); + + // And consume. + let payload = governance_message::take_payload(consumed, receipt); + + // Proceed with processing new implementation version. + handle_upgrade_contract(wormhole_state, payload) + } + + /// Finalize the upgrade that ran to produce the given `receipt`. This + /// method invokes `state::commit_upgrade` which interacts with + /// `sui::package`. + public fun commit_upgrade( + self: &mut State, + receipt: UpgradeReceipt, + ) { + let (old_contract, new_contract) = state::commit_upgrade(self, receipt); + + // Emit an event reflecting package ID change. + sui::event::emit(ContractUpgraded { old_contract, new_contract }); + } + + /// Privileged method only to be used by this module and `migrate` module. + /// + /// During migration, we make sure that the digest equals what we expect by + /// passing in the same VAA used to upgrade the package. + public(friend) fun take_digest(governance_payload: vector): Bytes32 { + // Deserialize the payload as the build digest. + let UpgradeContract { digest } = deserialize(governance_payload); + + digest + } + + fun handle_upgrade_contract( + wormhole_state: &mut State, + payload: vector + ): UpgradeTicket { + state::authorize_upgrade(wormhole_state, take_digest(payload)) + } + + fun deserialize(payload: vector): UpgradeContract { + let cur = cursor::new(payload); + + // This amount cannot be greater than max u64. + let digest = bytes32::take_bytes(&mut cur); + assert!(bytes32::is_nonzero(&digest), E_DIGEST_ZERO_BYTES); + + cursor::destroy_empty(cur); + + UpgradeContract { digest } + } + + #[test_only] + public fun action(): u8 { + ACTION_UPGRADE_CONTRACT + } +} + +#[test_only] +module wormhole::upgrade_contract_tests { + // TODO +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance_message.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance_message.move new file mode 100644 index 0000000000..20d4cfee5f --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance_message.move @@ -0,0 +1,694 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a custom type representing a Guardian governance +/// action. Each governance action has an associated module name, relevant chain +/// and payload encoding instructions/data used to perform an administrative +/// change on a contract. +module wormhole::governance_message { + use wormhole::bytes::{Self}; + use wormhole::bytes32::{Self, Bytes32}; + use wormhole::consumed_vaas::{Self, ConsumedVAAs}; + use wormhole::cursor::{Self}; + use wormhole::external_address::{ExternalAddress}; + use wormhole::state::{Self, State, chain_id}; + use wormhole::vaa::{Self, VAA}; + + /// Guardian set used to sign VAA did not use current Guardian set. + const E_OLD_GUARDIAN_SET_GOVERNANCE: u64 = 0; + /// Governance chain does not match. + const E_INVALID_GOVERNANCE_CHAIN: u64 = 1; + /// Governance emitter address does not match. + const E_INVALID_GOVERNANCE_EMITTER: u64 = 2; + /// Governance module name does not match. + const E_INVALID_GOVERNANCE_MODULE: u64 = 4; + /// Governance action does not match. + const E_INVALID_GOVERNANCE_ACTION: u64 = 5; + /// Governance target chain not indicative of global action. + const E_GOVERNANCE_TARGET_CHAIN_NONZERO: u64 = 6; + /// Governance target chain not indicative of actino specifically for Sui + /// Wormhole contract. + const E_GOVERNANCE_TARGET_CHAIN_NOT_SUI: u64 = 7; + + /// The public constructors for `DecreeTicket` (`authorize_verify_global` + /// and `authorize_verify_local`) require a witness of type `T`. This is to + /// ensure that `DecreeTicket`s cannot be mixed up between modules + /// maliciously. + struct DecreeTicket { + governance_chain: u16, + governance_contract: ExternalAddress, + module_name: Bytes32, + action: u8, + global: bool + } + + struct DecreeReceipt { + payload: vector, + digest: Bytes32, + sequence: u64 + } + + /// This method prepares `DecreeTicket` for global governance action. This + /// means the VAA encodes target chain ID == 0. + public fun authorize_verify_global( + _witness: T, + governance_chain: u16, + governance_contract: ExternalAddress, + module_name: Bytes32, + action: u8 + ): DecreeTicket { + DecreeTicket { + governance_chain, + governance_contract, + module_name, + action, + global: true + } + } + + /// This method prepares `DecreeTicket` for local governance action. This + /// means the VAA encodes target chain ID == 21 (Sui's). + public fun authorize_verify_local( + _witness: T, + governance_chain: u16, + governance_contract: ExternalAddress, + module_name: Bytes32, + action: u8 + ): DecreeTicket { + DecreeTicket { + governance_chain, + governance_contract, + module_name, + action, + global: false + } + } + + public fun sequence(receipt: &DecreeReceipt): u64 { + receipt.sequence + } + + /// This method unpacks `DecreeReceipt` and puts the VAA digest into a + /// `ConsumedVAAs` container. Then it returns the governance payload. + public fun take_payload( + consumed: &mut ConsumedVAAs, + receipt: DecreeReceipt + ): vector { + let DecreeReceipt { payload, digest, sequence: _ } = receipt; + + consumed_vaas::consume(consumed, digest); + + payload + } + + /// Method to peek into the payload in `DecreeReceipt`. + public fun payload(receipt: &DecreeReceipt): vector { + receipt.payload + } + + /// Destroy the receipt. + public fun destroy(receipt: DecreeReceipt) { + let DecreeReceipt { payload: _, digest: _, sequence: _ } = receipt; + } + + /// This method unpacks a `DecreeTicket` to validate its members to make + /// sure that the parameters match what was encoded in the VAA. + public fun verify_vaa( + wormhole_state: &State, + verified_vaa: VAA, + ticket: DecreeTicket + ): DecreeReceipt { + state::assert_latest_only(wormhole_state); + + let DecreeTicket { + governance_chain, + governance_contract, + module_name, + action, + global + } = ticket; + + // Protect against governance actions enacted using an old guardian set. + // This is not a protection found in the other Wormhole contracts. + assert!( + vaa::guardian_set_index(&verified_vaa) == state::guardian_set_index(wormhole_state), + E_OLD_GUARDIAN_SET_GOVERNANCE + ); + + // Both the emitter chain and address must equal. + assert!( + vaa::emitter_chain(&verified_vaa) == governance_chain, + E_INVALID_GOVERNANCE_CHAIN + ); + assert!( + vaa::emitter_address(&verified_vaa) == governance_contract, + E_INVALID_GOVERNANCE_EMITTER + ); + + // Cache VAA digest. + let digest = vaa::digest(&verified_vaa); + + // Get the VAA sequence number. + let sequence = vaa::sequence(&verified_vaa); + + // Finally deserialize Wormhole payload as governance message. + let ( + parsed_module_name, + parsed_action, + chain, + payload + ) = deserialize(vaa::take_payload(verified_vaa)); + + assert!(module_name == parsed_module_name, E_INVALID_GOVERNANCE_MODULE); + assert!(action == parsed_action, E_INVALID_GOVERNANCE_ACTION); + + // Target chain, which determines whether the governance VAA applies to + // all chains or Sui. + if (global) { + assert!(chain == 0, E_GOVERNANCE_TARGET_CHAIN_NONZERO); + } else { + assert!(chain == chain_id(), E_GOVERNANCE_TARGET_CHAIN_NOT_SUI); + }; + + DecreeReceipt { payload, digest, sequence } + } + + fun deserialize(buf: vector): (Bytes32, u8, u16, vector) { + let cur = cursor::new(buf); + + ( + bytes32::take_bytes(&mut cur), + bytes::take_u8(&mut cur), + bytes::take_u16_be(&mut cur), + cursor::take_rest(cur) + ) + } + + #[test_only] + public fun deserialize_test_only( + buf: vector + ): ( + Bytes32, + u8, + u16, + vector + ) { + deserialize(buf) + } + + #[test_only] + public fun take_decree(buf: vector): vector { + let (_, _, _, payload) = deserialize(buf); + payload + } +} + +#[test_only] +module wormhole::governance_message_tests { + use sui::test_scenario::{Self}; + use sui::tx_context::{Self}; + + use wormhole::bytes32::{Self}; + use wormhole::consumed_vaas::{Self}; + use wormhole::external_address::{Self}; + use wormhole::governance_message::{Self}; + use wormhole::state::{Self}; + use wormhole::vaa::{Self}; + use wormhole::version_control::{Self}; + use wormhole::wormhole_scenario::{ + set_up_wormhole, + person, + return_clock, + return_state, + take_clock, + take_state + }; + + struct GovernanceWitness has drop {} + + const VAA_UPDATE_GUARDIAN_SET_1: vector = + x"010000000001004f74e9596bd8246ef456918594ae16e81365b52c0cf4490b2a029fb101b058311f4a5592baeac014dc58215faad36453467a85a4c3e1c6cf5166e80f6e4dc50b0100bc614e000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000010100000000000000000000000000000000000000000000000000000000436f72650200000000000113befa429d57cd18b7f8a4d91a2da9ab4af05d0fbe88d7d8b32a9105d228100e72dffe2fae0705d31c58076f561cc62a47087b567c86f986426dfcd000bd6e9833490f8fa87c733a183cd076a6cbd29074b853fcf0a5c78c1b56d15fce7a154e6ebe9ed7a2af3503dbd2e37518ab04d7ce78b630f98b15b78a785632dea5609064803b1c8ea8bb2c77a6004bd109a281a698c0f5ba31f158585b41f4f33659e54d3178443ab76a60e21690dbfb17f7f59f09ae3ea1647ec26ae49b14060660504f4da1c2059e1c5ab6810ac3d8e1258bd2f004a94ca0cd4c68fc1c061180610e96d645b12f47ae5cf4546b18538739e90f2edb0d8530e31a218e72b9480202acbaeb06178da78858e5e5c4705cdd4b668ffe3be5bae4867c9d5efe3a05efc62d60e1d19faeb56a80223cdd3472d791b7d32c05abb1cc00b6381fa0c4928f0c56fc14bc029b8809069093d712a3fd4dfab31963597e246ab29fc6ebedf2d392a51ab2dc5c59d0902a03132a84dfd920b35a3d0ba5f7a0635df298f9033e"; + const VAA_SET_FEE_1: vector = + x"01000000000100181aa27fd44f3060fad0ae72895d42f97c45f7a5d34aa294102911370695e91e17ae82caa59f779edde2356d95cd46c2c381cdeba7a8165901a562374f212d750000bc614e000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000010100000000000000000000000000000000000000000000000000000000436f7265030015000000000000000000000000000000000000000000000000000000000000015e"; + + #[test] + fun test_global_action() { + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test setting sender to `caller`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_UPDATE_GUARDIAN_SET_1, + &the_clock + ); + let ( + _, + _, + _, + expected_payload + ) = governance_message::deserialize_test_only( + vaa::payload(&verified_vaa) + ); + + let ticket = + governance_message::authorize_verify_global( + GovernanceWitness {}, + state::governance_chain(&worm_state), + state::governance_contract(&worm_state), + state::governance_module(), + 2 // update guadian set + ); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + + let consumed = consumed_vaas::new(&mut tx_context::dummy()); + let payload = governance_message::take_payload(&mut consumed, receipt); + assert!(payload == expected_payload, 0); + + // Clean up. + consumed_vaas::destroy(consumed); + return_state(worm_state); + return_clock(the_clock); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_local_action() { + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test setting sender to `caller`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_SET_FEE_1, &the_clock); + let ( + _, + _, + _, + expected_payload + ) = governance_message::deserialize_test_only( + vaa::payload(&verified_vaa) + ); + + let ticket = + governance_message::authorize_verify_local( + GovernanceWitness {}, + state::governance_chain(&worm_state), + state::governance_contract(&worm_state), + state::governance_module(), + 3 // set fee + ); + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + + let consumed = consumed_vaas::new(&mut tx_context::dummy()); + let payload = governance_message::take_payload(&mut consumed, receipt); + assert!(payload == expected_payload, 0); + + // Clean up. + consumed_vaas::destroy(consumed); + return_state(worm_state); + return_clock(the_clock); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure( + abort_code = governance_message::E_INVALID_GOVERNANCE_CHAIN + )] + fun test_cannot_verify_vaa_invalid_governance_chain() { + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test setting sender to `caller`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_SET_FEE_1, &the_clock); + + // Show that this emitter chain ID does not equal the encoded one. + let invalid_chain = 0xffff; + assert!(invalid_chain != vaa::emitter_chain(&verified_vaa), 0); + + let ticket = + governance_message::authorize_verify_local( + GovernanceWitness {}, + invalid_chain, + state::governance_contract(&worm_state), + state::governance_module(), + 3 // set fee + ); + + // You shall not pass! + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + + // Clean up. + governance_message::destroy(receipt); + + abort 42 + } + + #[test] + #[expected_failure( + abort_code = governance_message::E_INVALID_GOVERNANCE_EMITTER + )] + fun test_cannot_verify_vaa_invalid_governance_emitter() { + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test setting sender to `caller`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_SET_FEE_1, &the_clock); + + // Show that this emitter address does not equal the encoded one. + let invalid_emitter = external_address::new(bytes32::default()); + assert!(invalid_emitter != vaa::emitter_address(&verified_vaa), 0); + + let ticket = + governance_message::authorize_verify_global( + GovernanceWitness {}, + state::governance_chain(&worm_state), + invalid_emitter, + state::governance_module(), + 3 // set fee + ); + + // You shall not pass! + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + + // Clean up. + governance_message::destroy(receipt); + + abort 42 + } + + #[test] + #[expected_failure( + abort_code = governance_message::E_INVALID_GOVERNANCE_MODULE + )] + fun test_cannot_verify_vaa_invalid_governance_module() { + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test setting sender to `caller`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_SET_FEE_1, &the_clock); + let ( + expected_module, + _, + _, + _ + ) = governance_message::deserialize_test_only( + vaa::payload(&verified_vaa) + ); + + // Show that this module does not equal the encoded one. + let invalid_module = bytes32::from_bytes(b"Not Wormhole"); + assert!(invalid_module != expected_module, 0); + + let ticket = + governance_message::authorize_verify_local( + GovernanceWitness {}, + state::governance_chain(&worm_state), + state::governance_contract(&worm_state), + invalid_module, + 3 // set fee + ); + + // You shall not pass! + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + + // Clean up. + governance_message::destroy(receipt); + + abort 42 + } + + #[test] + #[expected_failure( + abort_code = governance_message::E_INVALID_GOVERNANCE_ACTION + )] + fun test_cannot_verify_vaa_invalid_governance_action() { + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test setting sender to `caller`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_SET_FEE_1, &the_clock); + let ( + _, + expected_action, + _, + _ + ) = governance_message::deserialize_test_only( + vaa::payload(&verified_vaa) + ); + + // Show that this action does not equal the encoded one. + let invalid_action = 0xff; + assert!(invalid_action != expected_action, 0); + + let ticket = + governance_message::authorize_verify_local( + GovernanceWitness {}, + state::governance_chain(&worm_state), + state::governance_contract(&worm_state), + state::governance_module(), + invalid_action + ); + + // You shall not pass! + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + + // Clean up. + governance_message::destroy(receipt); + + abort 42 + } + + #[test] + #[expected_failure( + abort_code = governance_message::E_GOVERNANCE_TARGET_CHAIN_NONZERO + )] + fun test_cannot_verify_vaa_governance_target_chain_nonzero() { + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test setting sender to `caller`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_SET_FEE_1, &the_clock); + let ( + _, + _, + expected_target_chain, + _ + ) = governance_message::deserialize_test_only( + vaa::payload(&verified_vaa) + ); + + // Show that this target chain ID does reflect a global action. + let not_global = expected_target_chain != 0; + assert!(not_global, 0); + + let ticket = + governance_message::authorize_verify_global( + GovernanceWitness {}, + state::governance_chain(&worm_state), + state::governance_contract(&worm_state), + state::governance_module(), + 3 // set fee + ); + + // You shall not pass! + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + + // Clean up. + governance_message::destroy(receipt); + + abort 42 + } + + #[test] + #[expected_failure( + abort_code = governance_message::E_GOVERNANCE_TARGET_CHAIN_NOT_SUI + )] + fun test_cannot_verify_vaa_governance_target_chain_not_sui() { + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test setting sender to `caller`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let verified_vaa = + vaa::parse_and_verify( + &worm_state, + VAA_UPDATE_GUARDIAN_SET_1, + &the_clock + ); + let ( + _, + _, + expected_target_chain, + _ + ) = governance_message::deserialize_test_only( + vaa::payload(&verified_vaa) + ); + + // Show that this target chain ID does reflect a global action. + let global = expected_target_chain == 0; + assert!(global, 0); + + let ticket = + governance_message::authorize_verify_local( + GovernanceWitness {}, + state::governance_chain(&worm_state), + state::governance_contract(&worm_state), + state::governance_module(), + 2 // update guardian set + ); + + // You shall not pass! + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + + // Clean up. + governance_message::destroy(receipt); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_verify_vaa_outdated_version() { + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + let wormhole_fee = 350; + set_up_wormhole(scenario, wormhole_fee); + + // Prepare test setting sender to `caller`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let verified_vaa = + vaa::parse_and_verify(&worm_state, VAA_SET_FEE_1, &the_clock); + let ticket = + governance_message::authorize_verify_local( + GovernanceWitness {}, + state::governance_chain(&worm_state), + state::governance_contract(&worm_state), + state::governance_module(), + 3 // set fee + ); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut worm_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut worm_state, + version_control::previous_version_test_only(), + version_control::next_version() + ); + + // You shall not pass! + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + + // Clean up. + governance_message::destroy(receipt); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/migrate.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/migrate.move new file mode 100644 index 0000000000..958a0b12f8 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/migrate.move @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a public method intended to be called after an +/// upgrade has been committed. The purpose is to add one-off migration logic +/// that would alter Wormhole `State`. +/// +/// Included in migration is the ability to ensure that breaking changes for +/// any of Wormhole's methods by enforcing the current build version as their +/// required minimum version. +module wormhole::migrate { + use sui::clock::{Clock}; + use sui::object::{ID}; + + use wormhole::governance_message::{Self}; + use wormhole::state::{Self, State}; + use wormhole::upgrade_contract::{Self}; + use wormhole::vaa::{Self}; + + /// Event reflecting when `migrate` is successfully executed. + struct MigrateComplete has drop, copy { + package: ID + } + + /// Execute migration logic. See `wormhole::migrate` description for more + /// info. + public fun migrate( + wormhole_state: &mut State, + upgrade_vaa_buf: vector, + the_clock: &Clock + ) { + state::migrate__v__0_2_0(wormhole_state); + + // Perform standard migrate. + handle_migrate(wormhole_state, upgrade_vaa_buf, the_clock); + + //////////////////////////////////////////////////////////////////////// + // + // NOTE: Put any one-off migration logic here. + // + // Most upgrades likely won't need to do anything, in which case the + // rest of this function's body may be empty. Make sure to delete it + // after the migration has gone through successfully. + // + // WARNING: The migration does *not* proceed atomically with the + // upgrade (as they are done in separate transactions). + // If the nature of this migration absolutely requires the migration to + // happen before certain other functionality is available, then guard + // that functionality with the `assert!` from above. + // + //////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + } + + fun handle_migrate( + wormhole_state: &mut State, + upgrade_vaa_buf: vector, + the_clock: &Clock + ) { + // Update the version first. + // + // See `version_control` module for hard-coded configuration. + state::migrate_version(wormhole_state); + + // This VAA needs to have been used for upgrading this package. + // + // NOTE: All of the following methods have protections to make sure that + // the current build is used. Given that we officially migrated the + // version as the first call of `migrate`, these should be successful. + + // First we need to check that `parse_and_verify` still works. + let verified_vaa = + vaa::parse_and_verify(wormhole_state, upgrade_vaa_buf, the_clock); + + // And governance methods. + let ticket = upgrade_contract::authorize_governance(wormhole_state); + let receipt = + governance_message::verify_vaa( + wormhole_state, + verified_vaa, + ticket + ); + + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(wormhole_state); + + // Check if build digest is the current one. + let digest = + upgrade_contract::take_digest( + governance_message::payload(&receipt) + ); + state::assert_authorized_digest(&latest_only, wormhole_state, digest); + governance_message::destroy(receipt); + + // Finally emit an event reflecting a successful migrate. + let package = state::current_package(&latest_only, wormhole_state); + sui::event::emit(MigrateComplete { package }); + } + + #[test_only] + public fun set_up_migrate(wormhole_state: &mut State) { + state::reverse_migrate__v__dummy(wormhole_state); + } +} + +#[test_only] +module wormhole::migrate_tests { + use sui::test_scenario::{Self}; + + use wormhole::state::{Self}; + use wormhole::wormhole_scenario::{ + person, + return_clock, + return_state, + set_up_wormhole, + take_clock, + take_state, + upgrade_wormhole + }; + + const UPGRADE_VAA: vector = + x"01000000000100db695668c0c91f4df6e4106dcb912d9062898fd976d631ff1c1b4109ccd203b43cd2419c7d9a191f8d42a780419e63307aacc93080d8629c6c03061c52becf1d0100bc614e000000000001000000000000000000000000000000000000000000000000000000000000000400000000000000010100000000000000000000000000000000000000000000000000000000436f726501001500000000000000000000000000000000000000000000006e6577206275696c64"; + + #[test] + fun test_migrate() { + use wormhole::migrate::{migrate}; + + let user = person(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Initialize Wormhole. + let wormhole_message_fee = 350; + set_up_wormhole(scenario, wormhole_message_fee); + + // Next transaction should be conducted as an ordinary user. + test_scenario::next_tx(scenario, user); + + // Upgrade (digest is just b"new build") for testing purposes. + upgrade_wormhole(scenario); + + // Ignore effects. + test_scenario::next_tx(scenario, user); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Set up migrate (which prepares this package to be the same state as + // a previous release). + wormhole::migrate::set_up_migrate(&mut worm_state); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut worm_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + migrate(&mut worm_state, UPGRADE_VAA, &the_clock); + + // Make sure we emitted an event. + let effects = test_scenario::next_tx(scenario, user); + assert!(test_scenario::num_user_events(&effects) == 1, 0); + + // Clean up. + return_state(worm_state); + return_clock(the_clock); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_INCORRECT_OLD_VERSION)] + /// ^ This expected error may change depending on the migration. In most + /// cases, this will abort with `wormhole::package_utils::E_INCORRECT_OLD_VERSION`. + fun test_cannot_migrate_again() { + use wormhole::migrate::{migrate}; + + let user = person(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + // Initialize Wormhole. + let wormhole_message_fee = 350; + set_up_wormhole(scenario, wormhole_message_fee); + + // Next transaction should be conducted as an ordinary user. + test_scenario::next_tx(scenario, user); + + // Upgrade (digest is just b"new build") for testing purposes. + upgrade_wormhole(scenario); + + // Ignore effects. + test_scenario::next_tx(scenario, user); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Set up migrate (which prepares this package to be the same state as + // a previous release). + wormhole::migrate::set_up_migrate(&mut worm_state); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut worm_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + migrate(&mut worm_state, UPGRADE_VAA, &the_clock); + + // Make sure we emitted an event. + let effects = test_scenario::next_tx(scenario, user); + assert!(test_scenario::num_user_events(&effects) == 1, 0); + + // You shall not pass! + migrate(&mut worm_state, UPGRADE_VAA, &the_clock); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/publish_message.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/publish_message.move new file mode 100644 index 0000000000..3255fee5ba --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/publish_message.move @@ -0,0 +1,426 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements two methods: `prepare_message` and `publish_message`, +/// which are to be executed in a transaction block in this order. +/// +/// `prepare_message` allows a contract to pack Wormhole message info (payload +/// that has meaning to an integrator plus nonce) in preparation to publish a +/// `WormholeMessage` event via `publish_message`. Only the owner of an +/// `EmitterCap` has the capability of creating this `MessageTicket`. +/// +/// `publish_message` unpacks the `MessageTicket` and emits a +/// `WormholeMessage` with this message info and timestamp. This event is +/// observed by the Guardian network. +/// +/// The purpose of splitting this message publishing into two steps is in case +/// Wormhole needs to be upgraded and there is a breaking change for this +/// module, an integrator would not be left broken. It is discouraged to put +/// `publish_message` in an integrator's package logic. Otherwise, this +/// integrator needs to be prepared to upgrade his contract to handle the latest +/// version of `publish_message`. +/// +/// Instead, an integtrator is encouraged to execute a transaction block, which +/// executes `publish_message` using the latest Wormhole package ID and to +/// implement `prepare_message` in his contract to produce `MessageTicket`, +/// which `publish_message` consumes. +module wormhole::publish_message { + use sui::coin::{Self, Coin}; + use sui::clock::{Self, Clock}; + use sui::object::{Self, ID}; + use sui::sui::{SUI}; + + use wormhole::emitter::{Self, EmitterCap}; + use wormhole::state::{Self, State}; + + /// This type is emitted via `sui::event` module. Guardians pick up this + /// observation and attest to its existence. + struct WormholeMessage has drop, copy { + /// `EmitterCap` object ID. + sender: ID, + /// From `EmitterCap`. + sequence: u64, + /// A.K.A. Batch ID. + nonce: u32, + /// Arbitrary message data relevant to integrator. + payload: vector, + /// This will always be `0`. + consistency_level: u8, + /// `Clock` timestamp. + timestamp: u64 + } + + /// This type represents Wormhole message data. The sender is the object ID + /// of an `EmitterCap`, who acts as the capability of creating this type. + /// The only way to destroy this type is calling `publish_message` with + /// a fee to emit a `WormholeMessage` with the unpacked members of this + /// struct. + struct MessageTicket { + /// `EmitterCap` object ID. + sender: ID, + /// From `EmitterCap`. + sequence: u64, + /// A.K.A. Batch ID. + nonce: u32, + /// Arbitrary message data relevant to integrator. + payload: vector + } + + /// `prepare_message` constructs Wormhole message parameters. An + /// `EmitterCap` provides the capability to send an arbitrary payload. + /// + /// NOTE: Integrators of Wormhole should be calling only this method from + /// their contracts. This method is not guarded by version control (thus not + /// requiring a reference to the Wormhole `State` object), so it is intended + /// to work for any package version. + public fun prepare_message( + emitter_cap: &mut EmitterCap, + nonce: u32, + payload: vector + ): MessageTicket { + // Produce sequence number for this message. This will also be the + // return value for this method. + let sequence = emitter::use_sequence(emitter_cap); + + MessageTicket { + sender: object::id(emitter_cap), + sequence, + nonce, + payload + } + } + + /// `publish_message` emits a message as a Sui event. This method uses the + /// input `EmitterCap` as the registered sender of the + /// `WormholeMessage`. It also produces a new sequence for this emitter. + /// + /// NOTE: This method is guarded by a minimum build version check. This + /// method could break backward compatibility on an upgrade. + /// + /// It is important for integrators to refrain from calling this method + /// within their contracts. This method is meant to be called in a + /// transaction block after receiving a `MessageTicket` from calling + /// `prepare_message` within a contract. If in a circumstance where this + /// module has a breaking change in an upgrade, `prepare_message` will not + /// be affected by this change. + /// + /// See `prepare_message` for more details. + public fun publish_message( + wormhole_state: &mut State, + message_fee: Coin, + prepared_msg: MessageTicket, + the_clock: &Clock + ): u64 { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(wormhole_state); + + // Deposit `message_fee`. This method interacts with the `FeeCollector`, + // which will abort if `message_fee` does not equal the collector's + // expected fee amount. + state::deposit_fee( + &latest_only, + wormhole_state, + coin::into_balance(message_fee) + ); + + let MessageTicket { + sender, + sequence, + nonce, + payload + } = prepared_msg; + + // Truncate to seconds. + let timestamp = clock::timestamp_ms(the_clock) / 1000; + + // Sui is an instant finality chain, so we don't need confirmations. + let consistency_level = 0; + + // Emit Sui event with `WormholeMessage`. + sui::event::emit( + WormholeMessage { + sender, + sequence, + nonce, + payload, + consistency_level, + timestamp + } + ); + + // Done. + sequence + } + + #[test_only] + public fun destroy(prepared_msg: MessageTicket) { + let MessageTicket { + sender: _, + sequence: _, + nonce: _, + payload: _ + } = prepared_msg; + } +} + +#[test_only] +module wormhole::publish_message_tests { + use sui::coin::{Self}; + use sui::test_scenario::{Self}; + + use wormhole::emitter::{Self, EmitterCap}; + use wormhole::fee_collector::{Self}; + use wormhole::state::{Self}; + use wormhole::version_control::{Self}; + use wormhole::wormhole_scenario::{ + person, + return_clock, + return_state, + set_up_wormhole, + take_clock, + take_state, + upgrade_wormhole + }; + + #[test] + /// This test verifies that `publish_message` is successfully called when + /// the specified message fee is used. + fun test_publish_message() { + use wormhole::publish_message::{prepare_message, publish_message}; + + let user = person(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + let wormhole_message_fee = 100000000; + + // Initialize Wormhole. + set_up_wormhole(scenario, wormhole_message_fee); + + // Next transaction should be conducted as an ordinary user. + test_scenario::next_tx(scenario, user); + + { + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // User needs an `EmitterCap` so he can send a message. + let emitter_cap = + wormhole::emitter::new( + &worm_state, + test_scenario::ctx(scenario) + ); + + // Check for event corresponding to new emitter. + let effects = test_scenario::next_tx(scenario, user); + assert!(test_scenario::num_user_events(&effects) == 1, 0); + + // Prepare message. + let msg = + prepare_message( + &mut emitter_cap, + 0, // nonce + b"Hello World" + ); + + // Finally publish Wormhole message. + let sequence = + publish_message( + &mut worm_state, + coin::mint_for_testing( + wormhole_message_fee, + test_scenario::ctx(scenario) + ), + msg, + &the_clock + ); + assert!(sequence == 0, 0); + + // Prepare another message. + let msg = + prepare_message( + &mut emitter_cap, + 0, // nonce + b"Hello World... again" + ); + + // Publish again to check sequence uptick. + let another_sequence = + publish_message( + &mut worm_state, + coin::mint_for_testing( + wormhole_message_fee, + test_scenario::ctx(scenario) + ), + msg, + &the_clock + ); + assert!(another_sequence == 1, 0); + + // Clean up. + return_state(worm_state); + return_clock(the_clock); + sui::transfer::public_transfer(emitter_cap, user); + }; + + // Grab the `TransactionEffects` of the previous transaction. + let effects = test_scenario::next_tx(scenario, user); + + // We expect two events (the Wormhole messages). `test_scenario` does + // not give us an in-depth view of the event specifically. But we can + // check that there was an event associated with the previous + // transaction. + assert!(test_scenario::num_user_events(&effects) == 2, 0); + + // Simulate upgrade and confirm that publish message still works. + { + upgrade_wormhole(scenario); + + // Ignore effects from upgrade. + test_scenario::next_tx(scenario, user); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + let emitter_cap = + test_scenario::take_from_sender(scenario); + + let msg = + prepare_message( + &mut emitter_cap, + 0, // nonce + b"Hello?" + ); + + let sequence = + publish_message( + &mut worm_state, + coin::mint_for_testing( + wormhole_message_fee, + test_scenario::ctx(scenario) + ), + msg, + &the_clock + ); + assert!(sequence == 2, 0); + + // Clean up. + test_scenario::return_to_sender(scenario, emitter_cap); + return_state(worm_state); + return_clock(the_clock); + }; + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = fee_collector::E_INCORRECT_FEE)] + /// This test verifies that `publish_message` fails when the fee is not the + /// correct amount. `FeeCollector` will be the reason for this abort. + fun test_cannot_publish_message_with_incorrect_fee() { + use wormhole::publish_message::{prepare_message, publish_message}; + + let user = person(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + let wormhole_message_fee = 100000000; + let wrong_fee_amount = wormhole_message_fee - 1; + + // Initialize Wormhole. + set_up_wormhole(scenario, wormhole_message_fee); + + // Next transaction should be conducted as an ordinary user. + test_scenario::next_tx(scenario, user); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // User needs an `EmitterCap` so he can send a message. + let emitter_cap = + emitter::new(&worm_state, test_scenario::ctx(scenario)); + + let msg = + prepare_message( + &mut emitter_cap, + 0, // nonce + b"Hello World" + ); + // You shall not pass! + publish_message( + &mut worm_state, + coin::mint_for_testing( + wrong_fee_amount, + test_scenario::ctx(scenario) + ), + msg, + &the_clock + ); + + // Clean up. + emitter::destroy_test_only(emitter_cap); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + /// This test verifies that `publish_message` will fail if the minimum + /// required version is greater than the current build's. + fun test_cannot_publish_message_outdated_version() { + use wormhole::publish_message::{prepare_message, publish_message}; + + let user = person(); + let my_scenario = test_scenario::begin(user); + let scenario = &mut my_scenario; + + let wormhole_message_fee = 100000000; + + // Initialize Wormhole. + set_up_wormhole(scenario, wormhole_message_fee); + + // Next transaction should be conducted as an ordinary user. + test_scenario::next_tx(scenario, user); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // User needs an `EmitterCap` so he can send a message. + let emitter_cap = + emitter::new(&worm_state, test_scenario::ctx(scenario)); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut worm_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut worm_state, + version_control::previous_version_test_only(), + version_control::next_version() + ); + + let msg = + prepare_message( + &mut emitter_cap, + 0, // nonce + b"Hello World", + ); + + // You shall not pass! + publish_message( + &mut worm_state, + coin::mint_for_testing( + wormhole_message_fee, + test_scenario::ctx(scenario) + ), + msg, + &the_clock + ); + + // Clean up. + emitter::destroy_test_only(emitter_cap); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/consumed_vaas.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/consumed_vaas.move new file mode 100644 index 0000000000..a09327cf7e --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/consumed_vaas.move @@ -0,0 +1,29 @@ +module wormhole::consumed_vaas { + use sui::tx_context::{TxContext}; + + use wormhole::bytes32::{Bytes32}; + use wormhole::set::{Self, Set}; + + /// Container storing VAA hashes (digests). This will be checked against in + /// `parse_verify_and_consume` so a particular VAA cannot be replayed. It + /// is up to the integrator to have this container live in his contract + /// in order to take advantage of this no-replay protection. Or an + /// integrator can implement his own method to prevent replay. + struct ConsumedVAAs has store { + hashes: Set + } + + public fun new(ctx: &mut TxContext): ConsumedVAAs { + ConsumedVAAs { hashes: set::new(ctx) } + } + + public fun consume(self: &mut ConsumedVAAs, digest: Bytes32) { + set::add(&mut self.hashes, digest); + } + + #[test_only] + public fun destroy(consumed: ConsumedVAAs) { + let ConsumedVAAs { hashes } = consumed; + set::destroy(hashes); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/fee_collector.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/fee_collector.move new file mode 100644 index 0000000000..f75ab49719 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/fee_collector.move @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a container that collects fees in SUI denomination. +/// The `FeeCollector` requires that the fee deposited is exactly equal to the +/// `fee_amount` configured. +module wormhole::fee_collector { + use sui::balance::{Self, Balance}; + use sui::coin::{Self, Coin}; + use sui::sui::{SUI}; + use sui::tx_context::{TxContext}; + + /// Amount deposited is not exactly the amount configured. + const E_INCORRECT_FEE: u64 = 0; + + /// Container for configured `fee_amount` and `balance` of SUI collected. + struct FeeCollector has store { + fee_amount: u64, + balance: Balance + } + + /// Create new `FeeCollector` with specified amount to collect. + public fun new(fee_amount: u64): FeeCollector { + FeeCollector { fee_amount, balance: balance::zero() } + } + + /// Retrieve configured amount to collect. + public fun fee_amount(self: &FeeCollector): u64 { + self.fee_amount + } + + /// Retrieve current SUI balance. + public fun balance_value(self: &FeeCollector): u64 { + balance::value(&self.balance) + } + + /// Take `Balance` and add it to current collected balance. + public fun deposit_balance(self: &mut FeeCollector, fee: Balance) { + assert!(balance::value(&fee) == self.fee_amount, E_INCORRECT_FEE); + balance::join(&mut self.balance, fee); + } + + /// Take `Coin` and add it to current collected balance. + public fun deposit(self: &mut FeeCollector, fee: Coin) { + deposit_balance(self, coin::into_balance(fee)) + } + + /// Create `Balance` of some `amount` by taking from collected balance. + public fun withdraw_balance( + self: &mut FeeCollector, + amount: u64 + ): Balance { + // This will trigger `sui::balance::ENotEnough` if amount > balance. + balance::split(&mut self.balance, amount) + } + + /// Create `Coin` of some `amount` by taking from collected balance. + public fun withdraw( + self: &mut FeeCollector, + amount: u64, + ctx: &mut TxContext + ): Coin { + coin::from_balance(withdraw_balance(self, amount), ctx) + } + + /// Re-configure current `fee_amount`. + public fun change_fee(self: &mut FeeCollector, new_amount: u64) { + self.fee_amount = new_amount; + } + + #[test_only] + public fun destroy(collector: FeeCollector) { + let FeeCollector { fee_amount: _, balance: bal } = collector; + balance::destroy_for_testing(bal); + } +} + +#[test_only] +module wormhole::fee_collector_tests { + use sui::coin::{Self}; + use sui::tx_context::{Self}; + + use wormhole::fee_collector::{Self}; + + #[test] + public fun test_fee_collector() { + let ctx = &mut tx_context::dummy(); + + let fee_amount = 350; + let collector = fee_collector::new(fee_amount); + + // We expect the fee_amount to be the same as what we specified and + // no balance on `FeeCollector` yet. + assert!(fee_collector::fee_amount(&collector) == fee_amount, 0); + assert!(fee_collector::balance_value(&collector) == 0, 0); + + // Deposit fee once. + let fee = coin::mint_for_testing(fee_amount, ctx); + fee_collector::deposit(&mut collector, fee); + assert!(fee_collector::balance_value(&collector) == fee_amount, 0); + + // Now deposit nine more times and check the aggregate balance. + let i = 0; + while (i < 9) { + let fee = coin::mint_for_testing(fee_amount, ctx); + fee_collector::deposit(&mut collector, fee); + i = i + 1; + }; + let total = fee_collector::balance_value(&collector); + assert!(total == 10 * fee_amount, 0); + + // Withdraw a fifth. + let withdraw_amount = total / 5; + let withdrawn = + fee_collector::withdraw(&mut collector, withdraw_amount, ctx); + assert!(coin::value(&withdrawn) == withdraw_amount, 0); + coin::burn_for_testing(withdrawn); + + let remaining = fee_collector::balance_value(&collector); + assert!(remaining == total - withdraw_amount, 0); + + // Withdraw remaining. + let withdrawn = fee_collector::withdraw(&mut collector, remaining, ctx); + assert!(coin::value(&withdrawn) == remaining, 0); + coin::burn_for_testing(withdrawn); + + // There shouldn't be anything left in `FeeCollector`. + assert!(fee_collector::balance_value(&collector) == 0, 0); + + // Done. + fee_collector::destroy(collector); + } + + #[test] + #[expected_failure(abort_code = fee_collector::E_INCORRECT_FEE)] + public fun test_cannot_deposit_incorrect_fee() { + let ctx = &mut tx_context::dummy(); + + let fee_amount = 350; + let collector = fee_collector::new(fee_amount); + + // You shall not pass! + let fee = coin::mint_for_testing(fee_amount + 1, ctx); + fee_collector::deposit(&mut collector, fee); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = sui::balance::ENotEnough)] + public fun test_cannot_withdraw_more_than_balance() { + let ctx = &mut tx_context::dummy(); + + let fee_amount = 350; + let collector = fee_collector::new(fee_amount); + + // Deposit once. + let fee = coin::mint_for_testing(fee_amount, ctx); + fee_collector::deposit(&mut collector, fee); + + // Attempt to withdraw more than the balance. + let bal = fee_collector::balance_value(&collector); + let withdrawn = + fee_collector::withdraw(&mut collector, bal + 1, ctx); + + // Shouldn't get here. But we need to clean up anyway. + coin::burn_for_testing(withdrawn); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian.move new file mode 100644 index 0000000000..84c6a48eb1 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian.move @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a `Guardian` that warehouses a 20-byte public key. +module wormhole::guardian { + use std::vector::{Self}; + use sui::hash::{Self}; + use sui::ecdsa_k1::{Self}; + + use wormhole::bytes20::{Self, Bytes20}; + use wormhole::guardian_signature::{Self, GuardianSignature}; + + /// Guardian public key is all zeros. + const E_ZERO_ADDRESS: u64 = 1; + + /// Container for 20-byte Guardian public key. + struct Guardian has store { + pubkey: Bytes20 + } + + /// Create new `Guardian` ensuring that the input is not all zeros. + public fun new(pubkey: vector): Guardian { + let data = bytes20::new(pubkey); + assert!(bytes20::is_nonzero(&data), E_ZERO_ADDRESS); + Guardian { pubkey: data } + } + + /// Retrieve underlying 20-byte public key. + public fun pubkey(self: &Guardian): Bytes20 { + self.pubkey + } + + /// Retrieve underlying 20-byte public key as `vector`. + public fun as_bytes(self: &Guardian): vector { + bytes20::data(&self.pubkey) + } + + /// Verify that the recovered public key (using `ecrecover`) equals the one + /// that exists for this Guardian with an elliptic curve signature and raw + /// message that was signed. + public fun verify( + self: &Guardian, + signature: GuardianSignature, + message_hash: vector + ): bool { + let sig = guardian_signature::to_rsv(signature); + as_bytes(self) == ecrecover(message_hash, sig) + } + + /// Same as 'ecrecover' in EVM. + fun ecrecover(message: vector, sig: vector): vector { + let pubkey = + ecdsa_k1::decompress_pubkey(&ecdsa_k1::secp256k1_ecrecover(&sig, &message, 0)); + + // `decompress_pubkey` returns 65 bytes. The last 64 bytes are what we + // need to compute the Guardian's public key. + vector::remove(&mut pubkey, 0); + + let hash = hash::keccak256(&pubkey); + let guardian_pubkey = vector::empty(); + let (i, n) = (0, bytes20::length()); + while (i < n) { + vector::push_back( + &mut guardian_pubkey, + vector::pop_back(&mut hash) + ); + i = i + 1; + }; + vector::reverse(&mut guardian_pubkey); + + guardian_pubkey + } + + #[test_only] + public fun destroy(g: Guardian) { + let Guardian { pubkey: _ } = g; + } + + #[test_only] + public fun to_bytes(value: Guardian): vector { + let Guardian { pubkey } = value; + bytes20::to_bytes(pubkey) + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian_set.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian_set.move new file mode 100644 index 0000000000..e55ccd3758 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian_set.move @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a container that keeps track of a list of Guardian +/// public keys and which Guardian set index this list of Guardians represents. +/// Each guardian set is unique and there should be no two sets that have the +/// same Guardian set index (which requirement is handled in `wormhole::state`). +/// +/// If the current Guardian set is not the latest one, its `expiration_time` is +/// configured, which defines how long the past Guardian set can be active. +module wormhole::guardian_set { + use std::vector::{Self}; + use sui::clock::{Self, Clock}; + + use wormhole::guardian::{Self, Guardian}; + + // Needs `set_expiration`. + friend wormhole::state; + + /// Found duplicate public key. + const E_DUPLICATE_GUARDIAN: u64 = 0; + + /// Container for the list of Guardian public keys, its index value and at + /// what point in time the Guardian set is configured to expire. + struct GuardianSet has store { + /// A.K.A. Guardian set index. + index: u32, + + /// List of Guardians. This order should not change. + guardians: vector, + + /// At what point in time the Guardian set is no longer active (in ms). + expiration_timestamp_ms: u64, + } + + /// Create new `GuardianSet`. + public fun new(index: u32, guardians: vector): GuardianSet { + // Ensure that there are no duplicate guardians. + let (i, n) = (0, vector::length(&guardians)); + while (i < n - 1) { + let left = guardian::pubkey(vector::borrow(&guardians, i)); + let j = i + 1; + while (j < n) { + let right = guardian::pubkey(vector::borrow(&guardians, j)); + assert!(left != right, E_DUPLICATE_GUARDIAN); + j = j + 1; + }; + i = i + 1; + }; + + GuardianSet { index, guardians, expiration_timestamp_ms: 0 } + } + + /// Retrieve the Guardian set index. + public fun index(self: &GuardianSet): u32 { + self.index + } + + /// Retrieve the Guardian set index as `u64` (for convenience when used to + /// compare to indices for iterations, which are natively `u64`). + public fun index_as_u64(self: &GuardianSet): u64 { + (self.index as u64) + } + + /// Retrieve list of Guardians. + public fun guardians(self: &GuardianSet): &vector { + &self.guardians + } + + /// Retrieve specific Guardian by index (in the array representing the set). + public fun guardian_at(self: &GuardianSet, index: u64): &Guardian { + vector::borrow(&self.guardians, index) + } + + /// Retrieve when the Guardian set is no longer active. + public fun expiration_timestamp_ms(self: &GuardianSet): u64 { + self.expiration_timestamp_ms + } + + /// Retrieve whether this Guardian set is still active by checking the + /// current time. + public fun is_active(self: &GuardianSet, clock: &Clock): bool { + ( + self.expiration_timestamp_ms == 0 || + self.expiration_timestamp_ms > clock::timestamp_ms(clock) + ) + } + + /// Retrieve how many guardians exist in the Guardian set. + public fun num_guardians(self: &GuardianSet): u64 { + vector::length(&self.guardians) + } + + /// Returns the minimum number of signatures required for a VAA to be valid. + public fun quorum(self: &GuardianSet): u64 { + (num_guardians(self) * 2) / 3 + 1 + } + + /// Configure this Guardian set to expire from some amount of time based on + /// what time it is right now. + /// + /// NOTE: `time_to_live` is in units of seconds while `Clock` uses + /// milliseconds. + public(friend) fun set_expiration( + self: &mut GuardianSet, + seconds_to_live: u32, + the_clock: &Clock + ) { + let ttl_ms = (seconds_to_live as u64) * 1000; + self.expiration_timestamp_ms = clock::timestamp_ms(the_clock) + ttl_ms; + } + + #[test_only] + public fun destroy(set: GuardianSet) { + use wormhole::guardian::{Self}; + + let GuardianSet { + index: _, + guardians, + expiration_timestamp_ms: _ + } = set; + while (!vector::is_empty(&guardians)) { + guardian::destroy(vector::pop_back(&mut guardians)); + }; + + vector::destroy_empty(guardians); + } +} + +#[test_only] +module wormhole::guardian_set_tests { + use std::vector::{Self}; + + use wormhole::guardian::{Self}; + use wormhole::guardian_set::{Self}; + + #[test] + fun test_new() { + let guardians = vector::empty(); + + let pubkeys = vector[ + x"8888888888888888888888888888888888888888", + x"9999999999999999999999999999999999999999", + x"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + x"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + x"cccccccccccccccccccccccccccccccccccccccc", + x"dddddddddddddddddddddddddddddddddddddddd", + x"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + x"ffffffffffffffffffffffffffffffffffffffff" + ]; + while (!vector::is_empty(&pubkeys)) { + vector::push_back( + &mut guardians, + guardian::new(vector::pop_back(&mut pubkeys)) + ); + }; + + let set = guardian_set::new(69, guardians); + + // Clean up. + guardian_set::destroy(set); + } + + #[test] + #[expected_failure(abort_code = guardian_set::E_DUPLICATE_GUARDIAN)] + fun test_cannot_new_duplicate_guardian() { + let guardians = vector::empty(); + + let pubkeys = vector[ + x"8888888888888888888888888888888888888888", + x"9999999999999999999999999999999999999999", + x"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + x"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", + x"cccccccccccccccccccccccccccccccccccccccc", + x"dddddddddddddddddddddddddddddddddddddddd", + x"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + x"ffffffffffffffffffffffffffffffffffffffff", + x"cccccccccccccccccccccccccccccccccccccccc", + ]; + while (!vector::is_empty(&pubkeys)) { + vector::push_back( + &mut guardians, + guardian::new(vector::pop_back(&mut pubkeys)) + ); + }; + + let set = guardian_set::new(69, guardians); + + // Clean up. + guardian_set::destroy(set); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/set.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/set.move new file mode 100644 index 0000000000..bb52326489 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/set.move @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a custom type that resembles the set data structure. +/// `Set` leverages `sui::table` to store unique keys of the same type. +/// +/// NOTE: Items added to this data structure cannot be removed. +module wormhole::set { + use sui::table::{Self, Table}; + use sui::tx_context::{TxContext}; + + /// Explicit error if key already exists in `Set`. + const E_KEY_ALREADY_EXISTS: u64 = 0; + /// Explicit error if key does not exist in `Set`. + const E_KEY_NONEXISTENT: u64 = 1; + + /// Empty struct. Used as the value type in mappings to encode a set + struct Empty has store, drop {} + + /// A set containing elements of type `T` with support for membership + /// checking. + struct Set has store { + items: Table + } + + /// Create a new Set. + public fun new(ctx: &mut TxContext): Set { + Set { items: table::new(ctx) } + } + + /// Add a new element to the set. + /// Aborts if the element already exists + public fun add(self: &mut Set, key: T) { + assert!(!contains(self, key), E_KEY_ALREADY_EXISTS); + table::add(&mut self.items, key, Empty {}) + } + + /// Returns true iff `set` contains an entry for `key`. + public fun contains(self: &Set, key: T): bool { + table::contains(&self.items, key) + } + + public fun remove(self: &mut Set, key: T) { + assert!(contains(self, key), E_KEY_NONEXISTENT); + table::remove(&mut self.items, key); + } + + #[test_only] + public fun destroy(set: Set) { + let Set { items } = set; + table::drop(items); + } + +} + +#[test_only] +module wormhole::set_tests { + use sui::tx_context::{Self}; + + use wormhole::set::{Self}; + + #[test] + public fun test_add_and_contains() { + let ctx = &mut tx_context::dummy(); + + let my_set = set::new(ctx); + + let (i, n) = (0, 256); + while (i < n) { + set::add(&mut my_set, i); + i = i + 1; + }; + + // Check that the set has the values just added. + let i = 0; + while (i < n) { + assert!(set::contains(&my_set, i), 0); + i = i + 1; + }; + + // Check that these values that were not added are not in the set. + while (i < 2 * n) { + assert!(!set::contains(&my_set, i), 0); + i = i + 1; + }; + + set::destroy(my_set); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/setup.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/setup.move new file mode 100644 index 0000000000..c7f233e102 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/setup.move @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements the mechanism to publish the Wormhole contract and +/// initialize `State` as a shared object. +module wormhole::setup { + use std::vector::{Self}; + use sui::object::{Self, UID}; + use sui::package::{Self, UpgradeCap}; + use sui::transfer::{Self}; + use sui::tx_context::{Self, TxContext}; + + use wormhole::cursor::{Self}; + use wormhole::state::{Self}; + + /// Capability created at `init`, which will be destroyed once + /// `init_and_share_state` is called. This ensures only the deployer can + /// create the shared `State`. + struct DeployerCap has key, store { + id: UID + } + + /// Called automatically when module is first published. Transfers + /// `DeployerCap` to sender. + /// + /// Only `setup::init_and_share_state` requires `DeployerCap`. + fun init(ctx: &mut TxContext) { + let deployer = DeployerCap { id: object::new(ctx) }; + transfer::transfer(deployer, tx_context::sender(ctx)); + } + + #[test_only] + public fun init_test_only(ctx: &mut TxContext) { + init(ctx); + + // This will be created and sent to the transaction sender + // automatically when the contract is published. + transfer::public_transfer( + sui::package::test_publish(object::id_from_address(@wormhole), ctx), + tx_context::sender(ctx) + ); + } + + #[allow(lint(share_owned))] + /// Only the owner of the `DeployerCap` can call this method. This + /// method destroys the capability and shares the `State` object. + public fun complete( + deployer: DeployerCap, + upgrade_cap: UpgradeCap, + governance_chain: u16, + governance_contract: vector, + guardian_set_index: u32, + initial_guardians: vector>, + guardian_set_seconds_to_live: u32, + message_fee: u64, + ctx: &mut TxContext + ) { + wormhole::package_utils::assert_package_upgrade_cap( + &upgrade_cap, + package::compatible_policy(), + 1 + ); + + // Destroy deployer cap. + let DeployerCap { id } = deployer; + object::delete(id); + + let guardians = { + let out = vector::empty(); + let cur = cursor::new(initial_guardians); + while (!cursor::is_empty(&cur)) { + vector::push_back( + &mut out, + wormhole::guardian::new(cursor::poke(&mut cur)) + ); + }; + cursor::destroy_empty(cur); + out + }; + + // Share new state. + transfer::public_share_object( + state::new( + upgrade_cap, + governance_chain, + wormhole::external_address::new_nonzero( + wormhole::bytes32::from_bytes(governance_contract) + ), + guardian_set_index, + guardians, + guardian_set_seconds_to_live, + message_fee, + ctx + ) + ); + } +} + +#[test_only] +module wormhole::setup_tests { + use std::option::{Self}; + use std::vector::{Self}; + use sui::package::{Self}; + use sui::object::{Self}; + use sui::test_scenario::{Self}; + + use wormhole::bytes32::{Self}; + use wormhole::cursor::{Self}; + use wormhole::external_address::{Self}; + use wormhole::guardian::{Self}; + use wormhole::guardian_set::{Self}; + use wormhole::setup::{Self, DeployerCap}; + use wormhole::state::{Self, State}; + use wormhole::wormhole_scenario::{person}; + + #[test] + fun test_init() { + let deployer = person(); + let my_scenario = test_scenario::begin(deployer); + let scenario = &mut my_scenario; + + // Initialize Wormhole smart contract. + setup::init_test_only(test_scenario::ctx(scenario)); + + // Process effects of `init`. + let effects = test_scenario::next_tx(scenario, deployer); + + // We expect two objects to be created: `DeployerCap` and `UpgradeCap`. + assert!(vector::length(&test_scenario::created(&effects)) == 2, 0); + + // We should be able to take the `DeployerCap` from the sender + // of the transaction. + let cap = + test_scenario::take_from_address( + scenario, + deployer + ); + + // The above should succeed, so we will return to `deployer`. + test_scenario::return_to_address(deployer, cap); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + fun test_complete() { + let deployer = person(); + let my_scenario = test_scenario::begin(deployer); + let scenario = &mut my_scenario; + + // Initialize Wormhole smart contract. + setup::init_test_only(test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, deployer); + + let governance_chain = 1234; + let governance_contract = + x"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + let guardian_set_index = 0; + let initial_guardians = + vector[ + x"1337133713371337133713371337133713371337", + x"c0dec0dec0dec0dec0dec0dec0dec0dec0dec0de", + x"ba5edba5edba5edba5edba5edba5edba5edba5ed" + ]; + let guardian_set_seconds_to_live = 5678; + let message_fee = 350; + + // Take the `DeployerCap` and move it to `init_and_share_state`. + let deployer_cap = + test_scenario::take_from_address( + scenario, + deployer + ); + let deployer_cap_id = object::id(&deployer_cap); + + // This will be created and sent to the transaction sender automatically + // when the contract is published. This exists in place of grabbing + // it from the sender. + let upgrade_cap = + package::test_publish( + object::id_from_address(@wormhole), + test_scenario::ctx(scenario) + ); + + setup::complete( + deployer_cap, + upgrade_cap, + governance_chain, + governance_contract, + guardian_set_index, + initial_guardians, + guardian_set_seconds_to_live, + message_fee, + test_scenario::ctx(scenario) + ); + + // Process effects. + let effects = test_scenario::next_tx(scenario, deployer); + + // We expect one object to be created: `State`. And it is shared. + let created = test_scenario::created(&effects); + let shared = test_scenario::shared(&effects); + assert!(vector::length(&created) == 1, 0); + assert!(vector::length(&shared) == 1, 0); + assert!( + vector::borrow(&created, 0) == vector::borrow(&shared, 0), + 0 + ); + + // Verify `State`. Ideally we compare structs, but we will check each + // element. + let worm_state = test_scenario::take_shared(scenario); + + assert!(state::governance_chain(&worm_state) == governance_chain, 0); + + let expected_governance_contract = + external_address::new_nonzero( + bytes32::from_bytes(governance_contract) + ); + assert!( + state::governance_contract(&worm_state) == expected_governance_contract, + 0 + ); + + assert!(state::guardian_set_index(&worm_state) == 0, 0); + assert!( + state::guardian_set_seconds_to_live(&worm_state) == guardian_set_seconds_to_live, + 0 + ); + + let guardians = + guardian_set::guardians( + state::guardian_set_at(&worm_state, 0) + ); + let num_guardians = vector::length(guardians); + assert!(num_guardians == vector::length(&initial_guardians), 0); + + let i = 0; + while (i < num_guardians) { + let left = guardian::as_bytes(vector::borrow(guardians, i)); + let right = *vector::borrow(&initial_guardians, i); + assert!(left == right, 0); + i = i + 1; + }; + + assert!(state::message_fee(&worm_state) == message_fee, 0); + + // Clean up. + test_scenario::return_shared(worm_state); + + // We expect `DeployerCap` to be destroyed. There are other + // objects deleted, but we only care about the deployer cap for this + // test. + let deleted = cursor::new(test_scenario::deleted(&effects)); + let found = option::none(); + while (!cursor::is_empty(&deleted)) { + let id = cursor::poke(&mut deleted); + if (id == deployer_cap_id) { + found = option::some(id); + } + }; + cursor::destroy_empty(deleted); + + // If we found the deployer cap, `found` will have the ID. + assert!(!option::is_none(&found), 0); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure( + abort_code = wormhole::package_utils::E_INVALID_UPGRADE_CAP + )] + fun test_cannot_complete_invalid_upgrade_cap() { + let deployer = person(); + let my_scenario = test_scenario::begin(deployer); + let scenario = &mut my_scenario; + + // Initialize Wormhole smart contract. + setup::init_test_only(test_scenario::ctx(scenario)); + + // Ignore effects. + test_scenario::next_tx(scenario, deployer); + + let governance_chain = 1234; + let governance_contract = + x"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + let guardian_set_index = 0; + let initial_guardians = + vector[x"1337133713371337133713371337133713371337"]; + let guardian_set_seconds_to_live = 5678; + let message_fee = 350; + + // Take the `DeployerCap` and move it to `init_and_share_state`. + let deployer_cap = + test_scenario::take_from_address( + scenario, + deployer + ); + + // This will be created and sent to the transaction sender automatically + // when the contract is published. This exists in place of grabbing + // it from the sender. + let upgrade_cap = + package::test_publish( + object::id_from_address(@0xbadc0de), + test_scenario::ctx(scenario) + ); + + setup::complete( + deployer_cap, + upgrade_cap, + governance_chain, + governance_contract, + guardian_set_index, + initial_guardians, + guardian_set_seconds_to_live, + message_fee, + test_scenario::ctx(scenario) + ); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/state.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/state.move new file mode 100644 index 0000000000..d3b1f0c94b --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/state.move @@ -0,0 +1,468 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements the global state variables for Wormhole as a shared +/// object. The `State` object is used to perform anything that requires access +/// to data that defines the Wormhole contract. Examples of which are publishing +/// Wormhole messages (requires depositing a message fee), verifying `VAA` by +/// checking signatures versus an existing Guardian set, and generating new +/// emitters for Wormhole integrators. +module wormhole::state { + use std::vector::{Self}; + use sui::balance::{Balance}; + use sui::clock::{Clock}; + use sui::object::{Self, ID, UID}; + use sui::package::{UpgradeCap, UpgradeReceipt, UpgradeTicket}; + use sui::sui::{SUI}; + use sui::table::{Self, Table}; + use sui::tx_context::{TxContext}; + + use wormhole::bytes32::{Self, Bytes32}; + use wormhole::consumed_vaas::{Self, ConsumedVAAs}; + use wormhole::external_address::{ExternalAddress}; + use wormhole::fee_collector::{Self, FeeCollector}; + use wormhole::guardian::{Guardian}; + use wormhole::guardian_set::{Self, GuardianSet}; + use wormhole::package_utils::{Self}; + use wormhole::version_control::{Self}; + + friend wormhole::emitter; + friend wormhole::governance_message; + friend wormhole::migrate; + friend wormhole::publish_message; + friend wormhole::set_fee; + friend wormhole::setup; + friend wormhole::transfer_fee; + friend wormhole::update_guardian_set; + friend wormhole::upgrade_contract; + friend wormhole::vaa; + + /// Cannot initialize state with zero guardians. + const E_ZERO_GUARDIANS: u64 = 0; + /// Build digest does not agree with current implementation. + const E_INVALID_BUILD_DIGEST: u64 = 1; + + /// Sui's chain ID is hard-coded to one value. + const CHAIN_ID: u16 = 50076; + + /// Capability reflecting that the current build version is used to invoke + /// state methods. + struct LatestOnly has drop {} + + /// Container for all state variables for Wormhole. + struct State has key, store { + id: UID, + + /// Governance chain ID. + governance_chain: u16, + + /// Governance contract address. + governance_contract: ExternalAddress, + + /// Current active guardian set index. + guardian_set_index: u32, + + /// All guardian sets (including expired ones). + guardian_sets: Table, + + /// Period for which a guardian set stays active after it has been + /// replaced. + /// + /// NOTE: `Clock` timestamp is in units of ms while this value is in + /// terms of seconds. See `guardian_set` module for more info. + guardian_set_seconds_to_live: u32, + + /// Consumed VAA hashes to protect against replay. VAAs relevant to + /// Wormhole are just governance VAAs. + consumed_vaas: ConsumedVAAs, + + /// Wormhole fee collector. + fee_collector: FeeCollector, + + /// Upgrade capability. + upgrade_cap: UpgradeCap + } + + /// Create new `State`. This is only executed using the `setup` module. + public(friend) fun new( + upgrade_cap: UpgradeCap, + governance_chain: u16, + governance_contract: ExternalAddress, + guardian_set_index: u32, + initial_guardians: vector, + guardian_set_seconds_to_live: u32, + message_fee: u64, + ctx: &mut TxContext + ): State { + // We need at least one guardian. + assert!(vector::length(&initial_guardians) > 0, E_ZERO_GUARDIANS); + + let state = State { + id: object::new(ctx), + governance_chain, + governance_contract, + guardian_set_index, + guardian_sets: table::new(ctx), + guardian_set_seconds_to_live, + consumed_vaas: consumed_vaas::new(ctx), + fee_collector: fee_collector::new(message_fee), + upgrade_cap + }; + + // Set first version and initialize package info. This will be used for + // emitting information of successful migrations. + let upgrade_cap = &state.upgrade_cap; + package_utils::init_package_info( + &mut state.id, + version_control::current_version(), + upgrade_cap + ); + + // Store the initial guardian set. + add_new_guardian_set( + &assert_latest_only(&state), + &mut state, + guardian_set::new(guardian_set_index, initial_guardians) + ); + + state + } + + //////////////////////////////////////////////////////////////////////////// + // + // Simple Getters + // + // These methods do not require `LatestOnly` for access. Anyone is free to + // access these values. + // + //////////////////////////////////////////////////////////////////////////// + + /// Convenience method to get hard-coded Wormhole chain ID (recognized by + /// the Wormhole network). + public fun chain_id(): u16 { + CHAIN_ID + } + + /// Retrieve governance module name. + public fun governance_module(): Bytes32 { + // A.K.A. "Core". + bytes32::new( + x"00000000000000000000000000000000000000000000000000000000436f7265" + ) + } + + /// Retrieve governance chain ID, which is governance's emitter chain ID. + public fun governance_chain(self: &State): u16 { + self.governance_chain + } + + /// Retrieve governance emitter address. + public fun governance_contract(self: &State): ExternalAddress { + self.governance_contract + } + + /// Retrieve current Guardian set index. This value is important for + /// verifying VAA signatures and especially important for governance VAAs. + public fun guardian_set_index(self: &State): u32 { + self.guardian_set_index + } + + /// Retrieve how long after a Guardian set can live for in terms of Sui + /// timestamp (in seconds). + public fun guardian_set_seconds_to_live(self: &State): u32 { + self.guardian_set_seconds_to_live + } + + /// Retrieve a particular Guardian set by its Guardian set index. This + /// method is used when verifying a VAA. + /// + /// See `wormhole::vaa` for more info. + public fun guardian_set_at( + self: &State, + index: u32 + ): &GuardianSet { + table::borrow(&self.guardian_sets, index) + } + + /// Retrieve current fee to send Wormhole message. + public fun message_fee(self: &State): u64 { + fee_collector::fee_amount(&self.fee_collector) + } + + #[test_only] + public fun fees_collected(self: &State): u64 { + fee_collector::balance_value(&self.fee_collector) + } + + #[test_only] + public fun cache_latest_only_test_only(self: &State): LatestOnly { + assert_latest_only(self) + } + + #[test_only] + public fun deposit_fee_test_only(self: &mut State, fee: Balance) { + deposit_fee(&assert_latest_only(self), self, fee) + } + + #[test_only] + public fun migrate_version_test_only( + self: &mut State, + old_version: Old, + new_version: New + ) { + package_utils::update_version_type_test_only( + &mut self.id, + old_version, + new_version + ); + } + + #[test_only] + public fun test_upgrade(self: &mut State) { + let test_digest = bytes32::from_bytes(b"new build"); + let ticket = authorize_upgrade(self, test_digest); + let receipt = sui::package::test_upgrade(ticket); + commit_upgrade(self, receipt); + } + + #[test_only] + public fun reverse_migrate_version(self: &mut State) { + package_utils::update_version_type_test_only( + &mut self.id, + version_control::current_version(), + version_control::previous_version() + ); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Privileged `State` Access + // + // This section of methods require a `LatestOnly`, which can only be created + // within the Wormhole package. This capability allows special access to + // the `State` object. + // + // NOTE: A lot of these methods are still marked as `(friend)` as a safety + // precaution. When a package is upgraded, friend modifiers can be + // removed. + // + //////////////////////////////////////////////////////////////////////////// + + /// Obtain a capability to interact with `State` methods. This method checks + /// that we are running the current build. + /// + /// NOTE: This method allows caching the current version check so we avoid + /// multiple checks to dynamic fields. + public(friend) fun assert_latest_only(self: &State): LatestOnly { + package_utils::assert_version( + &self.id, + version_control::current_version() + ); + + LatestOnly {} + } + + /// Deposit fee when sending Wormhole message. This method does not + /// necessarily have to be a `friend` to `wormhole::publish_message`. But + /// we also do not want an integrator to mistakenly deposit fees outside + /// of calling `publish_message`. + /// + /// See `wormhole::publish_message` for more info. + public(friend) fun deposit_fee( + _: &LatestOnly, + self: &mut State, + fee: Balance + ) { + fee_collector::deposit_balance(&mut self.fee_collector, fee); + } + + /// Withdraw collected fees when governance action to transfer fees to a + /// particular recipient. + /// + /// See `wormhole::transfer_fee` for more info. + public(friend) fun withdraw_fee( + _: &LatestOnly, + self: &mut State, + amount: u64 + ): Balance { + fee_collector::withdraw_balance(&mut self.fee_collector, amount) + } + + /// Store `VAA` hash as a way to claim a VAA. This method prevents a VAA + /// from being replayed. For Wormhole, the only VAAs that it cares about + /// being replayed are its governance actions. + public(friend) fun borrow_mut_consumed_vaas( + _: &LatestOnly, + self: &mut State + ): &mut ConsumedVAAs { + borrow_mut_consumed_vaas_unchecked(self) + } + + /// Store `VAA` hash as a way to claim a VAA. This method prevents a VAA + /// from being replayed. For Wormhole, the only VAAs that it cares about + /// being replayed are its governance actions. + /// + /// NOTE: This method does not require `LatestOnly`. Only methods in the + /// `upgrade_contract` module requires this to be unprotected to prevent + /// a corrupted upgraded contract from bricking upgradability. + public(friend) fun borrow_mut_consumed_vaas_unchecked( + self: &mut State + ): &mut ConsumedVAAs { + &mut self.consumed_vaas + } + + /// When a new guardian set is added to `State`, part of the process + /// involves setting the last known Guardian set's expiration time based + /// on how long a Guardian set can live for. + /// + /// See `guardian_set_epochs_to_live` for the parameter that determines how + /// long a Guardian set can live for. + /// + /// See `wormhole::update_guardian_set` for more info. + public(friend) fun expire_guardian_set( + _: &LatestOnly, + self: &mut State, + the_clock: &Clock + ) { + guardian_set::set_expiration( + table::borrow_mut(&mut self.guardian_sets, self.guardian_set_index), + self.guardian_set_seconds_to_live, + the_clock + ); + } + + /// Add the latest Guardian set from the governance action to update the + /// current guardian set. + /// + /// See `wormhole::update_guardian_set` for more info. + public(friend) fun add_new_guardian_set( + _: &LatestOnly, + self: &mut State, + new_guardian_set: GuardianSet + ) { + self.guardian_set_index = guardian_set::index(&new_guardian_set); + table::add( + &mut self.guardian_sets, + self.guardian_set_index, + new_guardian_set + ); + } + + /// Modify the cost to send a Wormhole message via governance. + /// + /// See `wormhole::set_fee` for more info. + public(friend) fun set_message_fee( + _: &LatestOnly, + self: &mut State, + amount: u64 + ) { + fee_collector::change_fee(&mut self.fee_collector, amount); + } + + public(friend) fun current_package(_: &LatestOnly, self: &State): ID { + package_utils::current_package(&self.id) + } + + //////////////////////////////////////////////////////////////////////////// + // + // Upgradability + // + // A special space that controls upgrade logic. These methods are invoked + // via the `upgrade_contract` module. + // + // Also in this section is managing contract migrations, which uses the + // `migrate` module to officially roll state access to the latest build. + // Only those methods that require `LatestOnly` will be affected by an + // upgrade. + // + //////////////////////////////////////////////////////////////////////////// + + /// Issue an `UpgradeTicket` for the upgrade. + /// + /// NOTE: The Sui VM performs a check that this method is executed from the + /// latest published package. If someone were to try to execute this using + /// a stale build, the transaction will revert with `PackageUpgradeError`, + /// specifically `PackageIDDoesNotMatch`. + public(friend) fun authorize_upgrade( + self: &mut State, + package_digest: Bytes32 + ): UpgradeTicket { + let cap = &mut self.upgrade_cap; + package_utils::authorize_upgrade(&mut self.id, cap, package_digest) + } + + /// Finalize the upgrade that ran to produce the given `receipt`. + /// + /// NOTE: The Sui VM performs a check that this method is executed from the + /// latest published package. If someone were to try to execute this using + /// a stale build, the transaction will revert with `PackageUpgradeError`, + /// specifically `PackageIDDoesNotMatch`. + public(friend) fun commit_upgrade( + self: &mut State, + receipt: UpgradeReceipt + ): (ID, ID) { + let cap = &mut self.upgrade_cap; + package_utils::commit_upgrade(&mut self.id, cap, receipt) + } + + /// Method executed by the `migrate` module to roll access from one package + /// to another. This method will be called from the upgraded package. + public(friend) fun migrate_version(self: &mut State) { + package_utils::migrate_version( + &mut self.id, + version_control::previous_version(), + version_control::current_version() + ); + } + + /// As a part of the migration, we verify that the upgrade contract VAA's + /// encoded package digest used in `migrate` equals the one used to conduct + /// the upgrade. + public(friend) fun assert_authorized_digest( + _: &LatestOnly, + self: &State, + digest: Bytes32 + ) { + let authorized = package_utils::authorized_digest(&self.id); + assert!(digest == authorized, E_INVALID_BUILD_DIGEST); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Special State Interaction via Migrate + // + // A VERY special space that manipulates `State` via calling `migrate`. + // + // PLEASE KEEP ANY METHODS HERE AS FRIENDS. We want the ability to remove + // these for future builds. + // + //////////////////////////////////////////////////////////////////////////// + + /// This method is used to make modifications to `State` when `migrate` is + /// called. This method name should change reflecting which version this + /// contract is migrating to. + /// + /// NOTE: Please keep this method as public(friend) because we never want + /// to expose this method as a public method. + public(friend) fun migrate__v__0_2_0(_self: &mut State) { + // Intentionally do nothing. + } + + #[test_only] + /// Bloody hack. + /// + /// This method is used to set up tests where we migrate to a new version, + /// which is meant to test that modules protected by version control will + /// break. + public fun reverse_migrate__v__dummy(_self: &mut State) { + // Intentionally do nothing. + } + + //////////////////////////////////////////////////////////////////////////// + // + // Deprecated + // + // Dumping grounds for old structs and methods. These things should not + // be used in future builds. + // + //////////////////////////////////////////////////////////////////////////// +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/test/wormhole_scenario.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/test/wormhole_scenario.move new file mode 100644 index 0000000000..f587778e4c --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/test/wormhole_scenario.move @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: Apache 2 + +#[test_only] +/// This module implements ways to initialize Wormhole in a test scenario. This +/// module includes a default method (`set_up_wormhole`) with only one of the +/// devnet (Tilt) Guardians. The private key for this Guardian is known (see the +/// main Wormhole repository at https://github.com/wormhole-foundation/wormhole +/// for the key), which allows an integrator to generate his own VAAs and +/// validate them with this test-only Wormhole instance. +module wormhole::wormhole_scenario { + use std::vector::{Self}; + use sui::clock::{Self, Clock}; + use sui::package::{UpgradeCap}; + use sui::test_scenario::{Self, Scenario}; + + use wormhole::emitter::{EmitterCap}; + use wormhole::governance_message::{Self, DecreeTicket, DecreeReceipt}; + use wormhole::setup::{Self, DeployerCap}; + use wormhole::state::{Self, State}; + use wormhole::vaa::{Self, VAA}; + + const DEPLOYER: address = @0xDEADBEEF; + const WALLET_1: address = @0xB0B1; + const WALLET_2: address = @0xB0B2; + const WALLET_3: address = @0xB0B3; + const VAA_VERIFIER: address = @0xD00D; + const EMITTER_MAKER: address = @0xFEED; + + /// Set up Wormhole with any guardian pubkeys. For most testing purposes, + /// please use `set_up_wormhole` which only uses one guardian. + /// + /// NOTE: This also creates `Clock` for testing. + public fun set_up_wormhole_with_guardians( + scenario: &mut Scenario, + message_fee: u64, + initial_guardians: vector>, + ) { + // Process effects prior. `init_test_only` will be executed as the + // Wormhole contract deployer. + test_scenario::next_tx(scenario, DEPLOYER); + + // `init` Wormhole contract as if it were published. + wormhole::setup::init_test_only(test_scenario::ctx(scenario)); + + // `init_and_share_state` will also be executed as the Wormhole deployer + // to destroy the `DeployerCap` to create a sharable `State`. + test_scenario::next_tx(scenario, DEPLOYER); + + // Parameters for Wormhole's `State` are common in the Wormhole testing + // environment aside from the `guardian_set_epochs_to_live`, which at + // the moment needs to be discussed on how to configure. As of now, + // there is no clock with unix timestamp to expire guardian sets in + // terms of human-interpretable time. + { + // This will be created and sent to the transaction sender + // automatically when the contract is published. This exists in + // place of grabbing it from the sender. + let upgrade_cap = + test_scenario::take_from_sender(scenario); + + let governance_chain = 1; + let governance_contract = + x"0000000000000000000000000000000000000000000000000000000000000004"; + let guardian_set_index = 0; + let guardian_set_seconds_to_live = 420; + + // Share `State`. + setup::complete( + test_scenario::take_from_address( + scenario, DEPLOYER + ), + upgrade_cap, + governance_chain, + governance_contract, + guardian_set_index, + initial_guardians, + guardian_set_seconds_to_live, + message_fee, + test_scenario::ctx(scenario) + ); + }; + + // Done. + } + + /// Set up Wormhole with only the first devnet guardian. + public fun set_up_wormhole(scenario: &mut Scenario, message_fee: u64) { + let initial_guardians = vector::empty(); + vector::push_back( + &mut initial_guardians, + *vector::borrow(&guardians(), 0) + ); + + set_up_wormhole_with_guardians(scenario, message_fee, initial_guardians) + } + + /// Perform an upgrade (which just upticks the current version of what the + /// `State` believes is true). + public fun upgrade_wormhole(scenario: &mut Scenario) { + // Clean up from activity prior. + test_scenario::next_tx(scenario, person()); + + let worm_state = take_state(scenario); + state::test_upgrade(&mut worm_state); + + // Clean up. + return_state(worm_state); + } + + /// Address of wallet that published Wormhole contract. + public fun deployer(): address { + DEPLOYER + } + + public fun person(): address { + WALLET_1 + } + + public fun two_people(): (address, address) { + (WALLET_1, WALLET_2) + } + + public fun three_people(): (address, address, address) { + (WALLET_1, WALLET_2, WALLET_3) + } + + /// All guardians that exist in devnet (Tilt) environment. + public fun guardians(): vector> { + vector[ + x"befa429d57cd18b7f8a4d91a2da9ab4af05d0fbe", + x"88d7d8b32a9105d228100e72dffe2fae0705d31c", + x"58076f561cc62a47087b567c86f986426dfcd000", + x"bd6e9833490f8fa87c733a183cd076a6cbd29074", + x"b853fcf0a5c78c1b56d15fce7a154e6ebe9ed7a2", + x"af3503dbd2e37518ab04d7ce78b630f98b15b78a", + x"785632dea5609064803b1c8ea8bb2c77a6004bd1", + x"09a281a698c0f5ba31f158585b41f4f33659e54d", + x"3178443ab76a60e21690dbfb17f7f59f09ae3ea1", + x"647ec26ae49b14060660504f4da1c2059e1c5ab6", + x"810ac3d8e1258bd2f004a94ca0cd4c68fc1c0611", + x"80610e96d645b12f47ae5cf4546b18538739e90f", + x"2edb0d8530e31a218e72b9480202acbaeb06178d", + x"a78858e5e5c4705cdd4b668ffe3be5bae4867c9d", + x"5efe3a05efc62d60e1d19faeb56a80223cdd3472", + x"d791b7d32c05abb1cc00b6381fa0c4928f0c56fc", + x"14bc029b8809069093d712a3fd4dfab31963597e", + x"246ab29fc6ebedf2d392a51ab2dc5c59d0902a03", + x"132a84dfd920b35a3d0ba5f7a0635df298f9033e", + ] + } + + public fun take_state(scenario: &Scenario): State { + test_scenario::take_shared(scenario) + } + + public fun return_state(wormhole_state: State) { + test_scenario::return_shared(wormhole_state); + } + + public fun parse_and_verify_vaa( + scenario: &mut Scenario, + vaa_buf: vector + ): VAA { + test_scenario::next_tx(scenario, VAA_VERIFIER); + + let the_clock = take_clock(scenario); + let worm_state = take_state(scenario); + + let out = + vaa::parse_and_verify( + &worm_state, + vaa_buf, + &the_clock + ); + + // Clean up. + return_state(worm_state); + return_clock(the_clock); + + out + } + + public fun verify_governance_vaa( + scenario: &mut Scenario, + verified_vaa: VAA, + ticket: DecreeTicket + ): DecreeReceipt { + test_scenario::next_tx(scenario, VAA_VERIFIER); + + let worm_state = take_state(scenario); + + let receipt = + governance_message::verify_vaa(&worm_state, verified_vaa, ticket); + + // Clean up. + return_state(worm_state); + + receipt + } + + public fun new_emitter( + scenario: &mut Scenario + ): EmitterCap { + test_scenario::next_tx(scenario, EMITTER_MAKER); + + let worm_state = take_state(scenario); + + let emitter = + wormhole::emitter::new(&worm_state, test_scenario::ctx(scenario)); + + // Clean up. + return_state(worm_state); + + emitter + } + + public fun take_clock(scenario: &mut Scenario): Clock { + clock::create_for_testing(test_scenario::ctx(scenario)) + } + + public fun return_clock(the_clock: Clock) { + clock::destroy_for_testing(the_clock) + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/bytes.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/bytes.move new file mode 100644 index 0000000000..0d181f6775 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/bytes.move @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a library that serializes and deserializes specific +/// types into a buffer (i.e. `vector`). For serialization, the first +/// argument will be of `&mut vector`. For deserialization, the first +/// argument will be of `&mut Cursor` (see `wormhole::cursor` for more +/// details). +module wormhole::bytes { + use std::vector::{Self}; + use std::bcs::{Self}; + use wormhole::cursor::{Self, Cursor}; + + public fun push_u8(buf: &mut vector, v: u8) { + vector::push_back(buf, v); + } + + public fun push_u16_be(buf: &mut vector, value: u16) { + push_reverse(buf, value); + } + + public fun push_u32_be(buf: &mut vector, value: u32) { + push_reverse(buf, value); + } + + public fun push_u64_be(buf: &mut vector, value: u64) { + push_reverse(buf, value); + } + + public fun push_u128_be(buf: &mut vector, value: u128) { + push_reverse(buf, value); + } + + public fun push_u256_be(buf: &mut vector, value: u256) { + push_reverse(buf, value); + } + + public fun take_u8(cur: &mut Cursor): u8 { + cursor::poke(cur) + } + + public fun take_u16_be(cur: &mut Cursor): u16 { + let out = 0; + let i = 0; + while (i < 2) { + out = (out << 8) + (cursor::poke(cur) as u16); + i = i + 1; + }; + out + } + + public fun take_u32_be(cur: &mut Cursor): u32 { + let out = 0; + let i = 0; + while (i < 4) { + out = (out << 8) + (cursor::poke(cur) as u32); + i = i + 1; + }; + out + } + + public fun take_u64_be(cur: &mut Cursor): u64 { + let out = 0; + let i = 0; + while (i < 8) { + out = (out << 8) + (cursor::poke(cur) as u64); + i = i + 1; + }; + out + } + + public fun take_u128_be(cur: &mut Cursor): u128 { + let out = 0; + let i = 0; + while (i < 16) { + out = (out << 8) + (cursor::poke(cur) as u128); + i = i + 1; + }; + out + } + + public fun take_u256_be(cur: &mut Cursor): u256 { + let out = 0; + let i = 0; + while (i < 32) { + out = (out << 8) + (cursor::poke(cur) as u256); + i = i + 1; + }; + out + } + + public fun take_bytes(cur: &mut Cursor, num_bytes: u64): vector { + let out = vector::empty(); + let i = 0; + while (i < num_bytes) { + vector::push_back(&mut out, cursor::poke(cur)); + i = i + 1; + }; + out + } + + fun push_reverse(buf: &mut vector, v: T) { + let data = bcs::to_bytes(&v); + vector::reverse(&mut data); + vector::append(buf, data); + } +} + +#[test_only] +module wormhole::bytes_tests { + use std::vector::{Self}; + use wormhole::bytes::{Self}; + use wormhole::cursor::{Self}; + + #[test] + fun test_push_u8(){ + let u = 0x12; + let s = vector::empty(); + bytes::push_u8(&mut s, u); + let cur = cursor::new(s); + let p = bytes::take_u8(&mut cur); + cursor::destroy_empty(cur); + assert!(p==u, 0); + } + + #[test] + fun test_push_u16_be(){ + let u = 0x1234; + let s = vector::empty(); + bytes::push_u16_be(&mut s, u); + let cur = cursor::new(s); + let p = bytes::take_u16_be(&mut cur); + cursor::destroy_empty(cur); + assert!(p==u, 0); + } + + #[test] + fun test_push_u32_be(){ + let u = 0x12345678; + let s = vector::empty(); + bytes::push_u32_be(&mut s, u); + let cur = cursor::new(s); + let p = bytes::take_u32_be(&mut cur); + cursor::destroy_empty(cur); + assert!(p==u, 0); + } + + #[test] + fun test_push_u64_be(){ + let u = 0x1234567812345678; + let s = vector::empty(); + bytes::push_u64_be(&mut s, u); + let cur = cursor::new(s); + let p = bytes::take_u64_be(&mut cur); + cursor::destroy_empty(cur); + assert!(p==u, 0); + } + + #[test] + fun test_push_u128_be(){ + let u = 0x12345678123456781234567812345678; + let s = vector::empty(); + bytes::push_u128_be(&mut s, u); + let cur = cursor::new(s); + let p = bytes::take_u128_be(&mut cur); + cursor::destroy_empty(cur); + assert!(p==u, 0); + } + + #[test] + fun test_push_u256_be(){ + let u = + 0x4738691759099793746170047375612500000000000000000000000000009876; + let s = vector::empty(); + bytes::push_u256_be(&mut s, u); + assert!( + s == x"4738691759099793746170047375612500000000000000000000000000009876", + 0 + ); + } + + #[test] + fun test_take_u8() { + let cursor = cursor::new(x"99"); + let byte = bytes::take_u8(&mut cursor); + assert!(byte==0x99, 0); + cursor::destroy_empty(cursor); + } + + #[test] + fun test_take_u16_be() { + let cursor = cursor::new(x"9987"); + let u = bytes::take_u16_be(&mut cursor); + assert!(u == 0x9987, 0); + cursor::destroy_empty(cursor); + } + + #[test] + fun test_take_u32_be() { + let cursor = cursor::new(x"99876543"); + let u = bytes::take_u32_be(&mut cursor); + assert!(u == 0x99876543, 0); + cursor::destroy_empty(cursor); + } + + #[test] + fun test_take_u64_be() { + let cursor = cursor::new(x"1300000025000001"); + let u = bytes::take_u64_be(&mut cursor); + assert!(u == 0x1300000025000001, 0); + cursor::destroy_empty(cursor); + } + + #[test] + fun test_take_u128_be() { + let cursor = cursor::new(x"130209AB2500FA0113CD00AE25000001"); + let u = bytes::take_u128_be(&mut cursor); + assert!(u == 0x130209AB2500FA0113CD00AE25000001, 0); + cursor::destroy_empty(cursor); + } + + #[test] + fun test_to_bytes() { + let cursor = cursor::new(b"hello world"); + let hello = bytes::take_bytes(&mut cursor, 5); + bytes::take_u8(&mut cursor); + let world = bytes::take_bytes(&mut cursor, 5); + assert!(hello == b"hello", 0); + assert!(world == b"world", 0); + cursor::destroy_empty(cursor); + } + +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/cursor.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/cursor.move new file mode 100644 index 0000000000..73a96ebea0 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/cursor.move @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a custom type that allows consuming a vector +/// incrementally for parsing operations. It has no drop ability, and the only +/// way to deallocate it is by calling the `destroy_empty` method, which will +/// fail if the whole input hasn't been consumed. +/// +/// This setup statically guarantees that the parsing methods consume the full +/// input. +module wormhole::cursor { + use std::vector::{Self}; + + /// Container for the underlying `vector` data to be consumed. + struct Cursor { + data: vector, + } + + /// Initialises a cursor from a vector. + public fun new(data: vector): Cursor { + // reverse the array so we have access to the first element easily + vector::reverse(&mut data); + Cursor { data } + } + + /// Retrieve underlying data. + public fun data(self: &Cursor): &vector { + &self.data + } + + /// Check whether the underlying data is empty. This method is useful for + /// iterating over a `Cursor` to exhaust its contents. + public fun is_empty(self: &Cursor): bool { + vector::is_empty(&self.data) + } + + /// Destroys an empty cursor. This method aborts if the cursor is not empty. + public fun destroy_empty(cursor: Cursor) { + let Cursor { data } = cursor; + vector::destroy_empty(data); + } + + /// Consumes the rest of the cursor (thus destroying it) and returns the + /// remaining bytes. + /// + /// NOTE: Only use this function if you intend to consume the rest of the + /// bytes. Since the result is a vector, which can be dropped, it is not + /// possible to statically guarantee that the rest will be used. + public fun take_rest(cursor: Cursor): vector { + let Cursor { data } = cursor; + // Because the data was reversed in initialization, we need to reverse + // again so it is in the same order as the original input. + vector::reverse(&mut data); + data + } + + /// Retrieve the first element of the cursor and advances it. + public fun poke(self: &mut Cursor): T { + vector::pop_back(&mut self.data) + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/package_utils.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/package_utils.move new file mode 100644 index 0000000000..12dcfde91a --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/package_utils.move @@ -0,0 +1,422 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements utilities that supplement those methods implemented +/// in `sui::package`. +module wormhole::package_utils { + use std::type_name::{Self, TypeName}; + use sui::dynamic_field::{Self as field}; + use sui::object::{Self, ID, UID}; + use sui::package::{Self, UpgradeCap, UpgradeTicket, UpgradeReceipt}; + + use wormhole::bytes32::{Self, Bytes32}; + + /// `UpgradeCap` is not from the same package as `T`. + const E_INVALID_UPGRADE_CAP: u64 = 0; + /// Build is not current. + const E_NOT_CURRENT_VERSION: u64 = 1; + /// Old version to update from is wrong. + const E_INCORRECT_OLD_VERSION: u64 = 2; + /// Old and new are the same version. + const E_SAME_VERSION: u64 = 3; + /// Version types must come from this module. + const E_TYPE_NOT_ALLOWED: u64 = 4; + + /// Key for version dynamic fields. + struct CurrentVersion has store, drop, copy {} + + /// Key for dynamic field reflecting current package info. Its value is + /// `PackageInfo`. + struct CurrentPackage has store, drop, copy {} + struct PendingPackage has store, drop, copy {} + + struct PackageInfo has store, drop, copy { + package: ID, + digest: Bytes32 + } + + /// Retrieve current package ID, which should be the only one that anyone is + /// allowed to interact with. + public fun current_package(id: &UID): ID { + let info: &PackageInfo = field::borrow(id, CurrentPackage {}); + info.package + } + + /// Retrieve the build digest reflecting the current build. + public fun current_digest(id: &UID): Bytes32 { + let info: &PackageInfo = field::borrow(id, CurrentPackage {}); + info.digest + } + + /// Retrieve the upgraded package ID, which was taken from `UpgradeCap` + /// during `commit_upgrade`. + public fun committed_package(id: &UID): ID { + let info: &PackageInfo = field::borrow(id, PendingPackage {}); + info.package + } + + /// Retrieve the build digest of the latest upgrade, which was the same + /// digest used when `authorize_upgrade` is called. + public fun authorized_digest(id: &UID): Bytes32 { + let info: &PackageInfo = field::borrow(id, PendingPackage {}); + info.digest + } + + /// Convenience method that can be used with any package that requires + /// `UpgradeCap` to have certain preconditions before it is considered + /// belonging to `T` object's package. + public fun assert_package_upgrade_cap( + cap: &UpgradeCap, + expected_policy: u8, + expected_version: u64 + ) { + let expected_package = + sui::address::from_bytes( + sui::hex::decode( + std::ascii::into_bytes( + std::type_name::get_address( + &std::type_name::get() + ) + ) + ) + ); + let cap_package = + object::id_to_address(&package::upgrade_package(cap)); + assert!( + ( + cap_package == expected_package && + package::upgrade_policy(cap) == expected_policy && + package::version(cap) == expected_version + ), + E_INVALID_UPGRADE_CAP + ); + } + + /// Assert that the version type passed into this method is what exists + /// as the current version. + public fun assert_version( + id: &UID, + _version: Version + ) { + assert!( + field::exists_with_type( + id, + CurrentVersion {} + ), + E_NOT_CURRENT_VERSION + ) + } + + // Retrieve the `TypeName` of a given version. + public fun type_of_version(_version: Version): TypeName { + type_name::get() + } + + /// Initialize package info and set the initial version. This should be done + /// when a contract's state/storage shared object is created. + public fun init_package_info( + id: &mut UID, + version: InitialVersion, + upgrade_cap: &UpgradeCap + ) { + let package = package::upgrade_package(upgrade_cap); + field::add( + id, + CurrentPackage {}, + PackageInfo { package, digest: bytes32::default() } + ); + + // Set placeholders for pending package. We don't ever plan on removing + // this field. + field::add( + id, + PendingPackage {}, + PackageInfo { package, digest: bytes32::default() } + ); + + // Set the initial version. + field::add(id, CurrentVersion {}, version); + } + + /// Perform the version switchover and copy package info from pending to + /// current. This method should be executed after an upgrade (via a migrate + /// method) from the upgraded package. + /// + /// NOTE: This method can only be called once with the same version type + /// arguments. + public fun migrate_version< + Old: store + drop, + New: store + drop + >( + id: &mut UID, + old_version: Old, + new_version: New + ) { + update_version_type(id, old_version, new_version); + + update_package_info_from_pending(id); + } + + /// Helper for `sui::package::authorize_upgrade` to modify pending package + /// info by updating its digest. + /// + /// NOTE: This digest will be copied over when `migrate_version` is called. + public fun authorize_upgrade( + id: &mut UID, + upgrade_cap: &mut UpgradeCap, + package_digest: Bytes32 + ): UpgradeTicket { + let policy = package::upgrade_policy(upgrade_cap); + + // Manage saving the current digest. + set_authorized_digest(id, package_digest); + + // Finally authorize upgrade. + package::authorize_upgrade( + upgrade_cap, + policy, + bytes32::to_bytes(package_digest), + ) + } + + /// Helper for `sui::package::commit_upgrade` to modify pending package info + /// by updating its package ID with from what exists in the `UpgradeCap`. + /// This method returns the last package and the upgraded package IDs. + /// + /// NOTE: This package ID (second return value) will be copied over when + /// `migrate_version` is called. + public fun commit_upgrade( + id: &mut UID, + upgrade_cap: &mut UpgradeCap, + receipt: UpgradeReceipt + ): (ID, ID) { + // Uptick the upgrade cap version number using this receipt. + package::commit_upgrade(upgrade_cap, receipt); + + // Take the last pending package and replace it with the one now in + // the upgrade cap. + let previous_package = committed_package(id); + set_commited_package(id, upgrade_cap); + + // Return the package IDs. + (previous_package, committed_package(id)) + } + + fun set_commited_package(id: &mut UID, upgrade_cap: &UpgradeCap) { + let info: &mut PackageInfo = field::borrow_mut(id, PendingPackage {}); + info.package = package::upgrade_package(upgrade_cap); + } + + fun set_authorized_digest(id: &mut UID, digest: Bytes32) { + let info: &mut PackageInfo = field::borrow_mut(id, PendingPackage {}); + info.digest = digest; + } + + fun update_package_info_from_pending(id: &mut UID) { + let pending: PackageInfo = *field::borrow(id, PendingPackage {}); + *field::borrow_mut(id, CurrentPackage {}) = pending; + } + + /// Update from version n to n+1. We enforce that the versions be kept in + /// a module called "version_control". + fun update_version_type< + Old: store + drop, + New: store + drop + >( + id: &mut UID, + _old_version: Old, + new_version: New + ) { + use std::ascii::{into_bytes}; + + assert!( + field::exists_with_type(id, CurrentVersion {}), + E_INCORRECT_OLD_VERSION + ); + let _: Old = field::remove(id, CurrentVersion {}); + + let new_type = type_name::get(); + // Make sure the new type does not equal the old type, which means there + // is no protection against either build. + assert!(new_type != type_name::get(), E_SAME_VERSION); + + // Also make sure `New` originates from this module. + let module_name = into_bytes(type_name::get_module(&new_type)); + assert!(module_name == b"version_control", E_TYPE_NOT_ALLOWED); + + // Finally add the new version. + field::add(id, CurrentVersion {}, new_version); + } + + #[test_only] + public fun remove_package_info(id: &mut UID) { + let _: PackageInfo = field::remove(id, CurrentPackage {}); + let _: PackageInfo = field::remove(id, PendingPackage {}); + } + + #[test_only] + public fun init_version( + id: &mut UID, + version: Version + ) { + field::add(id, CurrentVersion {}, version); + } + + #[test_only] + public fun update_version_type_test_only< + Old: store + drop, + New: store + drop + >( + id: &mut UID, + old_version: Old, + new_version: New + ) { + update_version_type(id, old_version, new_version) + } +} + +#[test_only] +module wormhole::package_utils_tests { + use sui::object::{Self, UID}; + use sui::tx_context::{Self}; + + use wormhole::package_utils::{Self}; + use wormhole::version_control::{Self}; + + struct State has key { + id: UID + } + + struct V_DUMMY has store, drop, copy {} + + #[test] + fun test_assert_current() { + // Create dummy state. + let state = State { id: object::new(&mut tx_context::dummy()) }; + package_utils::init_version( + &mut state.id, + version_control::current_version() + ); + + package_utils::assert_version( + &state.id, + version_control::current_version() + ); + + // Clean up. + let State { id } = state; + object::delete(id); + } + + #[test] + #[expected_failure(abort_code = package_utils::E_INCORRECT_OLD_VERSION)] + fun test_cannot_update_incorrect_old_version() { + // Create dummy state. + let state = State { id: object::new(&mut tx_context::dummy()) }; + package_utils::init_version( + &mut state.id, + version_control::current_version() + ); + + package_utils::assert_version( + &state.id, + version_control::current_version() + ); + + // You shall not pass! + package_utils::update_version_type_test_only( + &mut state.id, + version_control::next_version(), + version_control::next_version() + ); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = package_utils::E_SAME_VERSION)] + fun test_cannot_update_same_version() { + // Create dummy state. + let state = State { id: object::new(&mut tx_context::dummy()) }; + package_utils::init_version( + &mut state.id, + version_control::current_version() + ); + + package_utils::assert_version( + &state.id, + version_control::current_version() + ); + + // You shall not pass! + package_utils::update_version_type_test_only( + &mut state.id, + version_control::current_version(), + version_control::current_version() + ); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_assert_current_outdated_version() { + // Create dummy state. + let state = State { id: object::new(&mut tx_context::dummy()) }; + package_utils::init_version( + &mut state.id, + version_control::current_version() + ); + + package_utils::assert_version( + &state.id, + version_control::current_version() + ); + + // Valid update. + package_utils::update_version_type_test_only( + &mut state.id, + version_control::current_version(), + version_control::next_version() + ); + + // You shall not pass! + package_utils::assert_version( + &state.id, + version_control::current_version() + ); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = package_utils::E_TYPE_NOT_ALLOWED)] + fun test_cannot_update_type_not_allowed() { + // Create dummy state. + let state = State { id: object::new(&mut tx_context::dummy()) }; + package_utils::init_version( + &mut state.id, + version_control::current_version() + ); + + package_utils::assert_version( + &state.id, + version_control::current_version() + ); + + // You shall not pass! + package_utils::update_version_type_test_only( + &mut state.id, + version_control::current_version(), + V_DUMMY {} + ); + + abort 42 + } + + #[test] + fun test_latest_version_different_from_previous() { + let prev = version_control::previous_version(); + let curr = version_control::current_version(); + assert!(package_utils::type_of_version(prev) != package_utils::type_of_version(curr), 0); + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/vaa.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/vaa.move new file mode 100644 index 0000000000..03528351d4 --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/vaa.move @@ -0,0 +1,782 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements a mechanism to parse and verify VAAs, which are +/// verified Wormhole messages (messages with Guardian signatures attesting to +/// its observation). Signatures on VAA are checked against an existing Guardian +/// set that exists in the `State` (see `wormhole::state`). +/// +/// A Wormhole integrator is discouraged from integrating `parse_and_verify` in +/// his contract. If there is a breaking change to the `vaa` module, Wormhole +/// will be upgraded to prevent previous build versions of this module to work. +/// If an integrator happened to use `parse_and_verify` in his contract, he will +/// need to be prepared to upgrade his contract to take the change (by building +/// with the latest package implementation). +/// +/// Instead, an integrator is encouraged to execute a transaction block, which +/// executes `parse_and_verify` from the latest Wormhole package ID and to +/// implement his methods that require redeeming a VAA to take `VAA` as an +/// argument. +/// +/// A good example of how this methodology is implemented is how the Token +/// Bridge contract redeems its VAAs. +module wormhole::vaa { + use std::option::{Self}; + use std::vector::{Self}; + use sui::clock::{Clock}; + use sui::hash::{keccak256}; + + use wormhole::bytes::{Self}; + use wormhole::bytes32::{Self, Bytes32}; + use wormhole::consumed_vaas::{Self, ConsumedVAAs}; + use wormhole::cursor::{Self}; + use wormhole::external_address::{Self, ExternalAddress}; + use wormhole::guardian::{Self}; + use wormhole::guardian_set::{Self, GuardianSet}; + use wormhole::guardian_signature::{Self, GuardianSignature}; + use wormhole::state::{Self, State}; + + /// Incorrect VAA version. + const E_WRONG_VERSION: u64 = 0; + /// Not enough guardians attested to this Wormhole observation. + const E_NO_QUORUM: u64 = 1; + /// Signature does not match expected Guardian public key. + const E_INVALID_SIGNATURE: u64 = 2; + /// Prior guardian set is no longer valid. + const E_GUARDIAN_SET_EXPIRED: u64 = 3; + /// Guardian signature is encoded out of sequence. + const E_NON_INCREASING_SIGNERS: u64 = 4; + + const VERSION_VAA: u8 = 1; + + /// Container storing verified Wormhole message info. This struct also + /// caches the digest, which is a double Keccak256 hash of the message body. + struct VAA { + /// Guardian set index of Guardians that attested to observing the + /// Wormhole message. + guardian_set_index: u32, + /// Time when Wormhole message was emitted or observed. + timestamp: u32, + /// A.K.A. Batch ID. + nonce: u32, + /// Wormhole chain ID from which network the message originated from. + emitter_chain: u16, + /// Address of contract (standardized to 32 bytes) that produced the + /// message. + emitter_address: ExternalAddress, + /// Sequence number of emitter's Wormhole message. + sequence: u64, + /// A.K.A. Finality. + consistency_level: u8, + /// Arbitrary payload encoding data relevant to receiver. + payload: vector, + + /// Double Keccak256 hash of message body. + digest: Bytes32 + } + + public fun guardian_set_index(self: &VAA): u32 { + self.guardian_set_index + } + + public fun timestamp(self: &VAA): u32 { + self.timestamp + } + + public fun nonce(self: &VAA): u32 { + self.nonce + } + + public fun batch_id(self: &VAA): u32 { + nonce(self) + } + + public fun payload(self: &VAA): vector { + self.payload + } + + public fun digest(self: &VAA): Bytes32 { + self.digest + } + + public fun emitter_chain(self: &VAA): u16 { + self.emitter_chain + } + + public fun emitter_address(self: &VAA): ExternalAddress { + self.emitter_address + } + + public fun emitter_info(self: &VAA): (u16, ExternalAddress, u64) { + (self.emitter_chain, self.emitter_address, self.sequence) + } + + public fun sequence(self: &VAA): u64 { + self.sequence + } + + public fun consistency_level(self: &VAA): u8 { + self.consistency_level + } + + public fun finality(self: &VAA): u8 { + consistency_level(self) + } + + /// Destroy the `VAA` and take the Wormhole message payload. + public fun take_payload(vaa: VAA): vector { + let (_, _, payload) = take_emitter_info_and_payload(vaa); + + payload + } + + /// Destroy the `VAA` and take emitter info (chain and address) and Wormhole + /// message payload. + public fun take_emitter_info_and_payload( + vaa: VAA + ): (u16, ExternalAddress, vector) { + let VAA { + guardian_set_index: _, + timestamp: _, + nonce: _, + emitter_chain, + emitter_address, + sequence: _, + consistency_level: _, + digest: _, + payload, + } = vaa; + (emitter_chain, emitter_address, payload) + } + + /// Parses and verifies the signatures of a VAA. + /// + /// NOTE: This is the only public function that returns a VAA, and it should + /// be kept that way. This ensures that if an external module receives a + /// `VAA`, it has been verified. + public fun parse_and_verify( + wormhole_state: &State, + buf: vector, + the_clock: &Clock + ): VAA { + state::assert_latest_only(wormhole_state); + + // Deserialize VAA buffer (and return `VAA` after verifying signatures). + let (signatures, vaa) = parse(buf); + + // Fetch the guardian set which this VAA was supposedly signed with and + // verify signatures using guardian set. + verify_signatures( + state::guardian_set_at( + wormhole_state, + vaa.guardian_set_index + ), + signatures, + bytes32::to_bytes(compute_message_hash(&vaa)), + the_clock + ); + + // Done. + vaa + } + + public fun consume(consumed: &mut ConsumedVAAs, parsed: &VAA) { + consumed_vaas::consume(consumed, digest(parsed)) + } + + public fun compute_message_hash(parsed: &VAA): Bytes32 { + let buf = vector::empty(); + + bytes::push_u32_be(&mut buf, parsed.timestamp); + bytes::push_u32_be(&mut buf, parsed.nonce); + bytes::push_u16_be(&mut buf, parsed.emitter_chain); + vector::append( + &mut buf, + external_address::to_bytes(parsed.emitter_address) + ); + bytes::push_u64_be(&mut buf, parsed.sequence); + bytes::push_u8(&mut buf, parsed.consistency_level); + vector::append(&mut buf, parsed.payload); + + // Return hash. + bytes32::new(keccak256(&buf)) + } + + /// Parses a VAA. + /// + /// NOTE: This method does NOT perform any verification. This ensures the + /// invariant that if an external module receives a `VAA` object, its + /// signatures must have been verified, because the only public function + /// that returns a `VAA` is `parse_and_verify`. + fun parse(buf: vector): (vector, VAA) { + let cur = cursor::new(buf); + + // Check VAA version. + assert!( + bytes::take_u8(&mut cur) == VERSION_VAA, + E_WRONG_VERSION + ); + + let guardian_set_index = bytes::take_u32_be(&mut cur); + + // Deserialize guardian signatures. + let num_signatures = bytes::take_u8(&mut cur); + let signatures = vector::empty(); + let i = 0; + while (i < num_signatures) { + let guardian_index = bytes::take_u8(&mut cur); + let r = bytes32::take_bytes(&mut cur); + let s = bytes32::take_bytes(&mut cur); + let recovery_id = bytes::take_u8(&mut cur); + vector::push_back( + &mut signatures, + guardian_signature::new(r, s, recovery_id, guardian_index) + ); + i = i + 1; + }; + + // Deserialize message body. + let body_buf = cursor::take_rest(cur); + + let cur = cursor::new(body_buf); + let timestamp = bytes::take_u32_be(&mut cur); + let nonce = bytes::take_u32_be(&mut cur); + let emitter_chain = bytes::take_u16_be(&mut cur); + let emitter_address = external_address::take_bytes(&mut cur); + let sequence = bytes::take_u64_be(&mut cur); + let consistency_level = bytes::take_u8(&mut cur); + let payload = cursor::take_rest(cur); + + let parsed = VAA { + guardian_set_index, + timestamp, + nonce, + emitter_chain, + emitter_address, + sequence, + consistency_level, + digest: double_keccak256(body_buf), + payload, + }; + + (signatures, parsed) + } + + fun double_keccak256(buf: vector): Bytes32 { + use sui::hash::{keccak256}; + + bytes32::new(keccak256(&keccak256(&buf))) + } + + /// Using the Guardian signatures deserialized from VAA, verify that all of + /// the Guardian public keys are recovered using these signatures and the + /// VAA message body as the message used to produce these signatures. + /// + /// We are careful to only allow `wormhole:vaa` to control the hash that + /// gets used in the `ecdsa_k1` module by computing the hash after + /// deserializing the VAA message body. Even though `ecdsa_k1` hashes a + /// raw message (as of version 0.28), the "raw message" in this case is a + /// single keccak256 hash of the VAA message body. + fun verify_signatures( + set: &GuardianSet, + signatures: vector, + message_hash: vector, + the_clock: &Clock + ) { + // Guardian set must be active (not expired). + assert!( + guardian_set::is_active(set, the_clock), + E_GUARDIAN_SET_EXPIRED + ); + + // Number of signatures must be at least quorum. + assert!( + vector::length(&signatures) >= guardian_set::quorum(set), + E_NO_QUORUM + ); + + // Drain `Cursor` by checking each signature. + let cur = cursor::new(signatures); + let last_guardian_index = option::none(); + while (!cursor::is_empty(&cur)) { + let signature = cursor::poke(&mut cur); + let guardian_index = guardian_signature::index_as_u64(&signature); + + // Ensure that the provided signatures are strictly increasing. + // This check makes sure that no duplicate signers occur. The + // increasing order is guaranteed by the guardians, or can always be + // reordered by the client. + assert!( + ( + option::is_none(&last_guardian_index) || + guardian_index > *option::borrow(&last_guardian_index) + ), + E_NON_INCREASING_SIGNERS + ); + + // If the guardian pubkey cannot be recovered using the signature + // and message hash, revert. + assert!( + guardian::verify( + guardian_set::guardian_at(set, guardian_index), + signature, + message_hash + ), + E_INVALID_SIGNATURE + ); + + // Continue. + option::swap_or_fill(&mut last_guardian_index, guardian_index); + }; + + // Done. + cursor::destroy_empty(cur); + } + + #[test_only] + public fun parse_test_only( + buf: vector + ): (vector, VAA) { + parse(buf) + } + + #[test_only] + public fun destroy(vaa: VAA) { + take_payload(vaa); + } + + #[test_only] + public fun peel_payload_from_vaa(buf: &vector): vector { + // Just make sure that we are passing version 1 VAAs to this method. + assert!(*vector::borrow(buf, 0) == VERSION_VAA, E_WRONG_VERSION); + + // Find the location of the payload. + let num_signatures = (*vector::borrow(buf, 5) as u64); + let i = 57 + num_signatures * 66; + + // Push the payload bytes to `out` and return. + let out = vector::empty(); + let len = vector::length(buf); + while (i < len) { + vector::push_back(&mut out, *vector::borrow(buf, i)); + i = i + 1; + }; + + // Return the payload. + out + } +} + +#[test_only] +module wormhole::vaa_tests { + use std::vector::{Self}; + use sui::test_scenario::{Self}; + + use wormhole::bytes32::{Self}; + use wormhole::cursor::{Self}; + use wormhole::external_address::{Self}; + use wormhole::guardian_signature::{Self}; + use wormhole::state::{Self}; + use wormhole::vaa::{Self}; + use wormhole::version_control::{Self}; + use wormhole::wormhole_scenario::{ + guardians, + person, + return_clock, + return_state, + set_up_wormhole_with_guardians, + take_clock, + take_state + //upgrade_wormhole + }; + + const VAA_1: vector = + x"01000000000d009bafff633087a9587d9afb6d29bd74a3483b7a8d5619323a416fe9ca43b482cd5526fabe953157cfd42eea9ffa544babc0f3a025a8a6159217b96fc9ff586d560002c9367884940a43a1a4d86531ea33ccb41e52bd7d1679c106fdff756f3da2ca743f7c181fcf40d19151d0a8397335c1b71709279b6e4fa97b6e3de90824e841c801035a493b65bf12ab9b98aa4db3bfcb73df20ab854d8e5998a1552f3b3e57ea7cd3546187c62cd450d12d430cae0fb48124ae68034dae602fa3e2232b55257961f90104758e265101353923661f6df67cec3c38528ed1b68825099b5bb2ce3fb2e735c5073d90223bebd00cc10406a60413a6089b5fb9acee0a1b04a63a8d7db24c0bbc000587777306dd174e266c313f711e881086355b6ce66cf2bf1f5da58273a10be77813b5ffcafc1ba6b83645e326a7c1a3751496f279ba307a6cd554f2709c2f1eda0108ed23ba8264146c3e3cc0601c93260c25058bcdd25213a7834e51679afdc4b50104e3f3a3079ba45115e703096c7e0700354cd48348bbf686dcbc58be896c35a20009c2352cb46ef1d2ef9e185764650403aee87a1be071555b31cdcee0c346991da858defb8d5e164a293ce4377b54fc74b65e3acbdedcbb53c2bcc2688a0b5bd1c9010ae470b1573989f387f7c54a86325cc05978bbcbc13267e90e2fa2efb0e18bccb772252bd6d13ebf908f7f3f2caf20a45c17dec7168122a2535ea93d300fae7063000ba0e8770298d4e3567488f455455a33f1e723e1e629ba4f87928016aeaa5875561ec38bde5d934389dc657d80a927cd9d06a9d9c7ce910c98d77a576e3f31735c000eeeedc956cff4489ac55b52ca38233cdc11e88767e5cc82f664bd1d7c28dfb5a12d7d17620725aae08e499b021200919f42c50c05916cf425dcd6e59f24b4b233000f18d447c9608a076c066b30ee770910e3c133087d33e329ad0101f08f88d88e142623df87aa3842edcf34e10fd36271b49f7af73ff2a7bcf4a65a4306d59586f20111905fc99dc650d9b1b33c313e9b31dfdbc85ce57e9f31abc4841d5791a239f20e5f28e4e612db96aee2f49ae712f724466007aaf27309d0385005fe0264d33dd100127b46f2fbbbf12efb10c2e662b4449de404f6a408ad7f38c7ea40a46300930e9a3b1e02ce00b97e33fa8a87221c1fd9064ce966dc4772658b98f2ec1e28d13e7400000023280000000c002adeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef000000000000092a20416c6c20796f75722062617365206172652062656c6f6e6720746f207573"; + const VAA_DOUBLE_SIGNED: vector = + x"01000000000d009bafff633087a9587d9afb6d29bd74a3483b7a8d5619323a416fe9ca43b482cd5526fabe953157cfd42eea9ffa544babc0f3a025a8a6159217b96fc9ff586d560002c9367884940a43a1a4d86531ea33ccb41e52bd7d1679c106fdff756f3da2ca743f7c181fcf40d19151d0a8397335c1b71709279b6e4fa97b6e3de90824e841c80102c9367884940a43a1a4d86531ea33ccb41e52bd7d1679c106fdff756f3da2ca743f7c181fcf40d19151d0a8397335c1b71709279b6e4fa97b6e3de90824e841c801035a493b65bf12ab9b98aa4db3bfcb73df20ab854d8e5998a1552f3b3e57ea7cd3546187c62cd450d12d430cae0fb48124ae68034dae602fa3e2232b55257961f90104758e265101353923661f6df67cec3c38528ed1b68825099b5bb2ce3fb2e735c5073d90223bebd00cc10406a60413a6089b5fb9acee0a1b04a63a8d7db24c0bbc000587777306dd174e266c313f711e881086355b6ce66cf2bf1f5da58273a10be77813b5ffcafc1ba6b83645e326a7c1a3751496f279ba307a6cd554f2709c2f1eda0108ed23ba8264146c3e3cc0601c93260c25058bcdd25213a7834e51679afdc4b50104e3f3a3079ba45115e703096c7e0700354cd48348bbf686dcbc58be896c35a20009c2352cb46ef1d2ef9e185764650403aee87a1be071555b31cdcee0c346991da858defb8d5e164a293ce4377b54fc74b65e3acbdedcbb53c2bcc2688a0b5bd1c9010ae470b1573989f387f7c54a86325cc05978bbcbc13267e90e2fa2efb0e18bccb772252bd6d13ebf908f7f3f2caf20a45c17dec7168122a2535ea93d300fae7063000ba0e8770298d4e3567488f455455a33f1e723e1e629ba4f87928016aeaa5875561ec38bde5d934389dc657d80a927cd9d06a9d9c7ce910c98d77a576e3f31735c000f18d447c9608a076c066b30ee770910e3c133087d33e329ad0101f08f88d88e142623df87aa3842edcf34e10fd36271b49f7af73ff2a7bcf4a65a4306d59586f20111905fc99dc650d9b1b33c313e9b31dfdbc85ce57e9f31abc4841d5791a239f20e5f28e4e612db96aee2f49ae712f724466007aaf27309d0385005fe0264d33dd100127b46f2fbbbf12efb10c2e662b4449de404f6a408ad7f38c7ea40a46300930e9a3b1e02ce00b97e33fa8a87221c1fd9064ce966dc4772658b98f2ec1e28d13e7400000023280000000c002adeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef000000000000092a20416c6c20796f75722062617365206172652062656c6f6e6720746f207573"; + const VAA_NO_QUORUM: vector = + x"01000000000c009bafff633087a9587d9afb6d29bd74a3483b7a8d5619323a416fe9ca43b482cd5526fabe953157cfd42eea9ffa544babc0f3a025a8a6159217b96fc9ff586d560002c9367884940a43a1a4d86531ea33ccb41e52bd7d1679c106fdff756f3da2ca743f7c181fcf40d19151d0a8397335c1b71709279b6e4fa97b6e3de90824e841c801035a493b65bf12ab9b98aa4db3bfcb73df20ab854d8e5998a1552f3b3e57ea7cd3546187c62cd450d12d430cae0fb48124ae68034dae602fa3e2232b55257961f90104758e265101353923661f6df67cec3c38528ed1b68825099b5bb2ce3fb2e735c5073d90223bebd00cc10406a60413a6089b5fb9acee0a1b04a63a8d7db24c0bbc000587777306dd174e266c313f711e881086355b6ce66cf2bf1f5da58273a10be77813b5ffcafc1ba6b83645e326a7c1a3751496f279ba307a6cd554f2709c2f1eda0108ed23ba8264146c3e3cc0601c93260c25058bcdd25213a7834e51679afdc4b50104e3f3a3079ba45115e703096c7e0700354cd48348bbf686dcbc58be896c35a20009c2352cb46ef1d2ef9e185764650403aee87a1be071555b31cdcee0c346991da858defb8d5e164a293ce4377b54fc74b65e3acbdedcbb53c2bcc2688a0b5bd1c9010ae470b1573989f387f7c54a86325cc05978bbcbc13267e90e2fa2efb0e18bccb772252bd6d13ebf908f7f3f2caf20a45c17dec7168122a2535ea93d300fae7063000ba0e8770298d4e3567488f455455a33f1e723e1e629ba4f87928016aeaa5875561ec38bde5d934389dc657d80a927cd9d06a9d9c7ce910c98d77a576e3f31735c000f18d447c9608a076c066b30ee770910e3c133087d33e329ad0101f08f88d88e142623df87aa3842edcf34e10fd36271b49f7af73ff2a7bcf4a65a4306d59586f20111905fc99dc650d9b1b33c313e9b31dfdbc85ce57e9f31abc4841d5791a239f20e5f28e4e612db96aee2f49ae712f724466007aaf27309d0385005fe0264d33dd100127b46f2fbbbf12efb10c2e662b4449de404f6a408ad7f38c7ea40a46300930e9a3b1e02ce00b97e33fa8a87221c1fd9064ce966dc4772658b98f2ec1e28d13e7400000023280000000c002adeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef000000000000092a20416c6c20796f75722062617365206172652062656c6f6e6720746f207573"; + + #[test] + fun test_parse() { + let (signatures, parsed) = vaa::parse_test_only(VAA_1); + + let expected_signatures = + vector[ + guardian_signature::new( + bytes32::new( + x"9bafff633087a9587d9afb6d29bd74a3483b7a8d5619323a416fe9ca43b482cd" + ), // r + bytes32::new( + x"5526fabe953157cfd42eea9ffa544babc0f3a025a8a6159217b96fc9ff586d56" + ), // s + 0, // recovery_id + 0 // index + ), + guardian_signature::new( + bytes32::new( + x"c9367884940a43a1a4d86531ea33ccb41e52bd7d1679c106fdff756f3da2ca74" + ), // r + bytes32::new( + x"3f7c181fcf40d19151d0a8397335c1b71709279b6e4fa97b6e3de90824e841c8" + ), // s + 1, // recovery_id + 2 // index + ), + guardian_signature::new( + bytes32::new( + x"5a493b65bf12ab9b98aa4db3bfcb73df20ab854d8e5998a1552f3b3e57ea7cd3" + ), // r + bytes32::new( + x"546187c62cd450d12d430cae0fb48124ae68034dae602fa3e2232b55257961f9" + ), // s + 1, // recovery_id + 3 // index + ), + guardian_signature::new( + bytes32::new( + x"758e265101353923661f6df67cec3c38528ed1b68825099b5bb2ce3fb2e735c5" + ), // r + bytes32::new( + x"073d90223bebd00cc10406a60413a6089b5fb9acee0a1b04a63a8d7db24c0bbc" + ), // s + 0, // recovery_id + 4 // index + ), + guardian_signature::new( + bytes32::new( + x"87777306dd174e266c313f711e881086355b6ce66cf2bf1f5da58273a10be778" + ), // r + bytes32::new( + x"13b5ffcafc1ba6b83645e326a7c1a3751496f279ba307a6cd554f2709c2f1eda" + ), // s + 1, // recovery_id + 5 // index + ), + guardian_signature::new( + bytes32::new( + x"ed23ba8264146c3e3cc0601c93260c25058bcdd25213a7834e51679afdc4b501" + ), // r + bytes32::new( + x"04e3f3a3079ba45115e703096c7e0700354cd48348bbf686dcbc58be896c35a2" + ), // s + 0, // recovery_id + 8 // index + ), + guardian_signature::new( + bytes32::new( + x"c2352cb46ef1d2ef9e185764650403aee87a1be071555b31cdcee0c346991da8" + ), // r + bytes32::new( + x"58defb8d5e164a293ce4377b54fc74b65e3acbdedcbb53c2bcc2688a0b5bd1c9" + ), // s + 1, // recovery_id + 9 // index + ), + guardian_signature::new( + bytes32::new( + x"e470b1573989f387f7c54a86325cc05978bbcbc13267e90e2fa2efb0e18bccb7" + ), // r + bytes32::new( + x"72252bd6d13ebf908f7f3f2caf20a45c17dec7168122a2535ea93d300fae7063" + ), // s + 0, // recovery_id + 10 // index + ), + guardian_signature::new( + bytes32::new( + x"a0e8770298d4e3567488f455455a33f1e723e1e629ba4f87928016aeaa587556" + ), // r + bytes32::new( + x"1ec38bde5d934389dc657d80a927cd9d06a9d9c7ce910c98d77a576e3f31735c" + ), // s + 0, // recovery_id + 11 // index + ), + guardian_signature::new( + bytes32::new( + x"eeedc956cff4489ac55b52ca38233cdc11e88767e5cc82f664bd1d7c28dfb5a1" + ), // r + bytes32::new( + x"2d7d17620725aae08e499b021200919f42c50c05916cf425dcd6e59f24b4b233" + ), // s + 0, // recovery_id + 14 // index + ), + guardian_signature::new( + bytes32::new( + x"18d447c9608a076c066b30ee770910e3c133087d33e329ad0101f08f88d88e14" + ), // r + bytes32::new( + x"2623df87aa3842edcf34e10fd36271b49f7af73ff2a7bcf4a65a4306d59586f2" + ), // s + 1, // recovery_id + 15 // index + ), + guardian_signature::new( + bytes32::new( + x"905fc99dc650d9b1b33c313e9b31dfdbc85ce57e9f31abc4841d5791a239f20e" + ), // r + bytes32::new( + x"5f28e4e612db96aee2f49ae712f724466007aaf27309d0385005fe0264d33dd1" + ), // s + 0, // recovery_id + 17 // index + ), + guardian_signature::new( + bytes32::new( + x"7b46f2fbbbf12efb10c2e662b4449de404f6a408ad7f38c7ea40a46300930e9a" + ), // r + bytes32::new( + x"3b1e02ce00b97e33fa8a87221c1fd9064ce966dc4772658b98f2ec1e28d13e74" + ), // s + 0, // recovery_id + 18 // index + ) + ]; + assert!( + vector::length(&signatures) == vector::length(&expected_signatures), + 0 + ); + let left = cursor::new(signatures); + let right = cursor::new(expected_signatures); + while (!cursor::is_empty(&left)) { + assert!(cursor::poke(&mut left) == cursor::poke(&mut right), 0); + }; + cursor::destroy_empty(left); + cursor::destroy_empty(right); + + assert!(vaa::guardian_set_index(&parsed) == 0, 0); + assert!(vaa::timestamp(&parsed) == 9000, 0); + + let expected_batch_id = 12; + assert!(vaa::batch_id(&parsed) == expected_batch_id, 0); + assert!(vaa::nonce(&parsed) == expected_batch_id, 0); + + assert!(vaa::emitter_chain(&parsed) == 42, 0); + + let expected_emitter_address = + external_address::from_address( + @0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef + ); + assert!(vaa::emitter_address(&parsed) == expected_emitter_address, 0); + assert!(vaa::sequence(&parsed) == 2346, 0); + + let expected_finality = 32; + assert!(vaa::finality(&parsed) == expected_finality, 0); + assert!(vaa::consistency_level(&parsed) == expected_finality, 0); + + // The message Wormhole guardians sign is a hash of the actual message + // body. So the hash we need to check against is keccak256 of this + // message. + let body_buf = { + use wormhole::bytes::{Self}; + + let buf = vector::empty(); + bytes::push_u32_be(&mut buf, vaa::timestamp(&parsed)); + bytes::push_u32_be(&mut buf, vaa::nonce(&parsed)); + bytes::push_u16_be(&mut buf, vaa::emitter_chain(&parsed)); + vector::append( + &mut buf, + external_address::to_bytes(vaa::emitter_address(&parsed)) + ); + bytes::push_u64_be(&mut buf, vaa::sequence(&parsed)); + bytes::push_u8(&mut buf, vaa::consistency_level(&parsed)); + vector::append(&mut buf, vaa::payload(&parsed)); + + buf + }; + + let expected_message_hash = + bytes32::new(sui::hash::keccak256(&body_buf)); + assert!(vaa::compute_message_hash(&parsed) == expected_message_hash, 0); + + let expected_digest = + bytes32::new( + sui::hash::keccak256(&sui::hash::keccak256(&body_buf)) + ); + assert!(vaa::digest(&parsed) == expected_digest, 0); + + assert!( + vaa::take_payload(parsed) == b"All your base are belong to us", + 0 + ); + } + + #[test] + fun test_parse_and_verify() { + // Testing this method. + use wormhole::vaa::{parse_and_verify}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Initialize Wormhole with 19 guardians. + let wormhole_fee = 350; + set_up_wormhole_with_guardians(scenario, wormhole_fee, guardians()); + + // Prepare test to execute `parse_and_verify`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + let verified_vaa = parse_and_verify(&worm_state, VAA_1, &the_clock); + + // We verified all parsed output in `test_parse`. But in destroying the + // parsed VAA, we will check the payload for the heck of it. + assert!( + vaa::take_payload(verified_vaa) == b"All your base are belong to us", + 0 + ); + + // Clean up. + return_state(worm_state); + return_clock(the_clock); + + // Done. + test_scenario::end(my_scenario); + } + + #[test] + #[expected_failure(abort_code = vaa::E_NO_QUORUM)] + fun test_cannot_parse_and_verify_without_quorum() { + // Testing this method. + use wormhole::vaa::{parse_and_verify}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Initialize Wormhole with 19 guardians. + let wormhole_fee = 350; + set_up_wormhole_with_guardians(scenario, wormhole_fee, guardians()); + + // Prepare test to execute `parse_and_verify`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // You shall not pass! + let verified_vaa = parse_and_verify(&worm_state, VAA_NO_QUORUM, &the_clock); + + // Clean up. + vaa::destroy(verified_vaa); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = vaa::E_NON_INCREASING_SIGNERS)] + fun test_cannot_parse_and_verify_non_increasing() { + // Testing this method. + use wormhole::vaa::{parse_and_verify}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Initialize Wormhole with 19 guardians. + let wormhole_fee = 350; + set_up_wormhole_with_guardians(scenario, wormhole_fee, guardians()); + + // Prepare test to execute `parse_and_verify`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // You shall not pass! + let verified_vaa = + parse_and_verify(&worm_state, VAA_DOUBLE_SIGNED, &the_clock); + + // Clean up. + vaa::destroy(verified_vaa); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = vaa::E_INVALID_SIGNATURE)] + fun test_cannot_parse_and_verify_invalid_signature() { + // Testing this method. + use wormhole::vaa::{parse_and_verify}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Initialize Wormhole with 19 guardians. But reverse the order so the + // signatures will not match. + let initial_guardians = guardians(); + std::vector::reverse(&mut initial_guardians); + + let wormhole_fee = 350; + set_up_wormhole_with_guardians( + scenario, + wormhole_fee, + initial_guardians + ); + + // Prepare test to execute `parse_and_verify`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // You shall not pass! + let verified_vaa = parse_and_verify(&worm_state, VAA_1, &the_clock); + + // Clean up. + vaa::destroy(verified_vaa); + + abort 42 + } + + #[test] + #[expected_failure(abort_code = wormhole::package_utils::E_NOT_CURRENT_VERSION)] + fun test_cannot_parse_and_verify_outdated_version() { + // Testing this method. + use wormhole::vaa::{parse_and_verify}; + + // Set up. + let caller = person(); + let my_scenario = test_scenario::begin(caller); + let scenario = &mut my_scenario; + + // Initialize Wormhole with 19 guardians. + let wormhole_fee = 350; + set_up_wormhole_with_guardians(scenario, wormhole_fee, guardians()); + + // Prepare test to execute `parse_and_verify`. + test_scenario::next_tx(scenario, caller); + + let worm_state = take_state(scenario); + let the_clock = take_clock(scenario); + + // Conveniently roll version back. + state::reverse_migrate_version(&mut worm_state); + + // Simulate executing with an outdated build by upticking the minimum + // required version for `publish_message` to something greater than + // this build. + state::migrate_version_test_only( + &mut worm_state, + version_control::previous_version_test_only(), + version_control::next_version() + ); + + // You shall not pass! + let verified_vaa = parse_and_verify(&worm_state, VAA_1, &the_clock); + + // Clean up. + vaa::destroy(verified_vaa); + + abort 42 + } +} diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/version_control.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/version_control.move new file mode 100644 index 0000000000..13ee6106df --- /dev/null +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/version_control.move @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements dynamic field keys as empty structs. These keys are +/// used to determine the latest version for this build. If the current version +/// is not this build's, then paths through the `state` module will abort. +/// +/// See `wormhole::state` and `wormhole::package_utils` for more info. +module wormhole::version_control { + //////////////////////////////////////////////////////////////////////////// + // + // Hard-coded Version Control + // + // Before upgrading, please set the types for `current_version` and + // `previous_version` to match the correct types (current being the latest + // version reflecting this build). + // + //////////////////////////////////////////////////////////////////////////// + + public(friend) fun current_version(): V__0_2_0 { + V__0_2_0 {} + } + + public(friend) fun previous_version(): V__DUMMY { + V__DUMMY {} + } + + #[test_only] + public fun previous_version_test_only(): V__DUMMY { + previous_version() + } + + //////////////////////////////////////////////////////////////////////////// + // + // Change Log + // + // Please write release notes as doc strings for each version struct. These + // notes will be our attempt at tracking upgrades. Wish us luck. + // + //////////////////////////////////////////////////////////////////////////// + + /// First published package on Sui mainnet. + struct V__0_2_0 has store, drop, copy {} + + // Dummy. + struct V__DUMMY has store, drop, copy {} + + //////////////////////////////////////////////////////////////////////////// + // + // Implementation and Test-Only Methods + // + //////////////////////////////////////////////////////////////////////////// + + friend wormhole::state; + + #[test_only] + friend wormhole::package_utils_tests; + + #[test_only] + public fun dummy(): V__DUMMY { + V__DUMMY {} + } + + #[test_only] + struct V__MIGRATED has store, drop, copy {} + + #[test_only] + public fun next_version(): V__MIGRATED { + V__MIGRATED {} + } +} From 13f4a88cf05b578d29d4fa6bdce51ce09cc2481f Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Mon, 24 Feb 2025 14:10:27 +0100 Subject: [PATCH 02/10] chore(target_chains/sui): add new chain ids for new networks --- governance/xc_admin/packages/xc_admin_common/src/chains.ts | 1 + target_chains/sui/vendor/README.md | 6 +++--- .../wormhole_iota_testnet/wormhole/sources/state.move | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/governance/xc_admin/packages/xc_admin_common/src/chains.ts b/governance/xc_admin/packages/xc_admin_common/src/chains.ts index d774a55bc2..dbb451b8e1 100644 --- a/governance/xc_admin/packages/xc_admin_common/src/chains.ts +++ b/governance/xc_admin/packages/xc_admin_common/src/chains.ts @@ -232,6 +232,7 @@ export const RECEIVER_CHAINS = { bittensor_testnet: 50115, monad_devnet: 50116, monad_testnet: 50117, + iota_sui_testnet: 50118, }; // If there is any overlapping value the receiver chain will replace the wormhole diff --git a/target_chains/sui/vendor/README.md b/target_chains/sui/vendor/README.md index 6d8b177323..589fb36c17 100644 --- a/target_chains/sui/vendor/README.md +++ b/target_chains/sui/vendor/README.md @@ -1,8 +1,8 @@ # Vendored dependencies for SUI contract -This directory contains the wormhole dependencies used for deploying Pyth contracts on the chains -that Wormhole is not officially deployed on. For each network, a slightly different variant of the -code should be used that has the `CHAIN_ID` and `Move.toml` modified. Therefore, we are storing +This directory contains the wormhole dependencies used for deploying Pyth contracts on the chains that Wormhole is not +officially deployed on. For each network, a slightly different variant of the code should be used that has the +`CHAIN_ID` constant (in `wormhole/sources/state.move`) and `Move.toml` modified. Therefore, we are storing each of them in a separate directory. The Wormhole contract is taken out of commit diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/state.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/state.move index d3b1f0c94b..1db91f87fe 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/state.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/state.move @@ -42,7 +42,7 @@ module wormhole::state { const E_INVALID_BUILD_DIGEST: u64 = 1; /// Sui's chain ID is hard-coded to one value. - const CHAIN_ID: u16 = 50076; + const CHAIN_ID: u16 = 50118; /// Capability reflecting that the current build version is used to invoke /// state methods. From 5fbca9460002d4143c73a808c3bc7db6bf5ce7e2 Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Wed, 26 Feb 2025 18:56:58 +0100 Subject: [PATCH 03/10] chore(target_chains/sui): add iota cli and sdk packages with no change --- contract_manager/src/contracts/iota.ts | 568 ++++++++++++++++++ pnpm-lock.yaml | 434 ++++++------- pnpm-workspace.yaml | 2 + target_chains/sui/cli-iota/.gitignore | 1 + target_chains/sui/cli-iota/README.md | 69 +++ target_chains/sui/cli-iota/package.json | 27 + target_chains/sui/cli-iota/src/cli.ts | 297 +++++++++ target_chains/sui/cli-iota/src/pyth_deploy.ts | 169 ++++++ .../sui/cli-iota/src/upgrade_pyth.ts | 111 ++++ target_chains/sui/cli-iota/tsconfig.json | 9 + target_chains/sui/sdk/js-iota/.eslintrc.js | 9 + target_chains/sui/sdk/js-iota/.gitignore | 1 + target_chains/sui/sdk/js-iota/README.md | 154 +++++ target_chains/sui/sdk/js-iota/jest.config.js | 5 + target_chains/sui/sdk/js-iota/package.json | 59 ++ .../js-iota/src/SuiPriceServiceConnection.ts | 21 + target_chains/sui/sdk/js-iota/src/client.ts | 304 ++++++++++ .../sui/sdk/js-iota/src/examples/SuiRelay.ts | 97 +++ target_chains/sui/sdk/js-iota/src/index.ts | 11 + target_chains/sui/sdk/js-iota/tsconfig.json | 14 + 20 files changed, 2145 insertions(+), 217 deletions(-) create mode 100644 contract_manager/src/contracts/iota.ts create mode 100644 target_chains/sui/cli-iota/.gitignore create mode 100644 target_chains/sui/cli-iota/README.md create mode 100644 target_chains/sui/cli-iota/package.json create mode 100644 target_chains/sui/cli-iota/src/cli.ts create mode 100644 target_chains/sui/cli-iota/src/pyth_deploy.ts create mode 100644 target_chains/sui/cli-iota/src/upgrade_pyth.ts create mode 100644 target_chains/sui/cli-iota/tsconfig.json create mode 100644 target_chains/sui/sdk/js-iota/.eslintrc.js create mode 100644 target_chains/sui/sdk/js-iota/.gitignore create mode 100644 target_chains/sui/sdk/js-iota/README.md create mode 100644 target_chains/sui/sdk/js-iota/jest.config.js create mode 100644 target_chains/sui/sdk/js-iota/package.json create mode 100644 target_chains/sui/sdk/js-iota/src/SuiPriceServiceConnection.ts create mode 100644 target_chains/sui/sdk/js-iota/src/client.ts create mode 100644 target_chains/sui/sdk/js-iota/src/examples/SuiRelay.ts create mode 100644 target_chains/sui/sdk/js-iota/src/index.ts create mode 100644 target_chains/sui/sdk/js-iota/tsconfig.json diff --git a/contract_manager/src/contracts/iota.ts b/contract_manager/src/contracts/iota.ts new file mode 100644 index 0000000000..af8f7d17a6 --- /dev/null +++ b/contract_manager/src/contracts/iota.ts @@ -0,0 +1,568 @@ +import { Chain, SuiChain } from "../chains"; +import { DataSource } from "@pythnetwork/xc-admin-common"; +import { WormholeContract } from "./wormhole"; +import { PriceFeedContract, PrivateKey, TxResult } from "../base"; +import { SuiPythClient } from "@pythnetwork/pyth-sui-js"; +import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui/utils"; +import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519"; +import { Transaction } from "@mysten/sui/transactions"; +import { uint8ArrayToBCS } from "@certusone/wormhole-sdk/lib/cjs/sui"; + +type ObjectId = string; + +export class SuiPriceFeedContract extends PriceFeedContract { + static type = "SuiPriceFeedContract"; + private client: SuiPythClient; + + /** + * Given the ids of the pyth state and wormhole state, create a new SuiPriceFeedContract + * The package ids are derived based on the state ids + * + * @param chain the chain which this contract is deployed on + * @param stateId id of the pyth state for the deployed contract + * @param wormholeStateId id of the wormhole state for the wormhole contract that pyth binds to + */ + constructor( + public chain: SuiChain, + public stateId: string, + public wormholeStateId: string + ) { + super(); + this.client = new SuiPythClient( + this.getProvider(), + this.stateId, + this.wormholeStateId + ); + } + + static fromJson( + chain: Chain, + parsed: { type: string; stateId: string; wormholeStateId: string } + ): SuiPriceFeedContract { + if (parsed.type !== SuiPriceFeedContract.type) + throw new Error("Invalid type"); + if (!(chain instanceof SuiChain)) + throw new Error(`Wrong chain type ${chain}`); + return new SuiPriceFeedContract( + chain, + parsed.stateId, + parsed.wormholeStateId + ); + } + + getType(): string { + return SuiPriceFeedContract.type; + } + + getChain(): SuiChain { + return this.chain; + } + + toJson() { + return { + chain: this.chain.getId(), + stateId: this.stateId, + wormholeStateId: this.wormholeStateId, + type: SuiPriceFeedContract.type, + }; + } + + /** + * Given a objectId, returns the id for the package that the object belongs to. + * @param objectId + */ + async getPackageId(objectId: ObjectId): Promise { + return this.client.getPackageId(objectId); + } + + async getPythPackageId(): Promise { + return await this.getPackageId(this.stateId); + } + + async getWormholePackageId(): Promise { + return await this.getPackageId(this.wormholeStateId); + } + + getId(): string { + return `${this.chain.getId()}_${this.stateId}`; + } + + private async parsePrice(priceInfo: { + type: string; + fields: { + expo: { fields: { magnitude: string; negative: boolean } }; + price: { fields: { magnitude: string; negative: boolean } }; + conf: string; + timestamp: string; + }; + }) { + let expo = priceInfo.fields.expo.fields.magnitude; + if (priceInfo.fields.expo.fields.negative) expo = "-" + expo; + let price = priceInfo.fields.price.fields.magnitude; + if (priceInfo.fields.price.fields.negative) price = "-" + price; + return { + conf: priceInfo.fields.conf, + publishTime: priceInfo.fields.timestamp, + expo, + price, + }; + } + + async getPriceFeed(feedId: string) { + const provider = this.getProvider(); + const priceInfoObjectId = await this.client.getPriceFeedObjectId(feedId); + if (!priceInfoObjectId) return undefined; + const priceInfo = await provider.getObject({ + id: priceInfoObjectId, + options: { showContent: true }, + }); + if (!priceInfo.data || !priceInfo.data.content) { + throw new Error( + `Price feed ID ${priceInfoObjectId} in price table but object not found!!` + ); + } + if (priceInfo.data.content.dataType !== "moveObject") { + throw new Error( + `Expected ${priceInfoObjectId} to be a moveObject (PriceInfoObject)` + ); + } + return { + emaPrice: await this.parsePrice( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + priceInfo.data.content.fields.price_info.fields.price_feed.fields + .ema_price + ), + price: await this.parsePrice( + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + priceInfo.data.content.fields.price_info.fields.price_feed.fields.price + ), + }; + } + + /** + * Given a signed VAA, execute the migration instruction on the pyth contract. + * The payload of the VAA can be obtained from the `getUpgradePackagePayload` method. + * @param vaa + * @param keypair used to sign the transaction + */ + async executeMigrateInstruction(vaa: Buffer, keypair: Ed25519Keypair) { + const tx = new Transaction(); + const packageId = await this.getPythPackageId(); + const verificationReceipt = await this.getVaaVerificationReceipt( + tx, + packageId, + vaa + ); + + tx.moveCall({ + target: `${packageId}::migrate::migrate`, + arguments: [tx.object(this.stateId), verificationReceipt], + }); + + return this.executeTransaction(tx, keypair); + } + + async executeUpdatePriceFeed(): Promise { + // We need the feed ids to be able to execute the transaction + // it may be possible to get them from the VAA but in batch transactions, + // it is also possible to hava fewer feeds that user wants to update compared to + // what exists in the VAA. + throw new Error("Use executeUpdatePriceFeedWithFeeds instead"); + } + + async executeUpdatePriceFeedWithFeeds( + senderPrivateKey: string, + vaas: Buffer[], + feedIds: string[] + ): Promise { + const tx = new Transaction(); + await this.client.updatePriceFeeds(tx, vaas, feedIds); + const keypair = Ed25519Keypair.fromSecretKey( + Buffer.from(senderPrivateKey, "hex") + ); + const result = await this.executeTransaction(tx, keypair); + return { id: result.digest, info: result }; + } + async executeCreatePriceFeed( + senderPrivateKey: string, + vaas: Buffer[] + ): Promise { + const tx = new Transaction(); + await this.client.createPriceFeed(tx, vaas); + const keypair = Ed25519Keypair.fromSecretKey( + Buffer.from(senderPrivateKey, "hex") + ); + + const result = await this.executeTransaction(tx, keypair); + return { id: result.digest, info: result }; + } + + async executeGovernanceInstruction( + senderPrivateKey: PrivateKey, + vaa: Buffer + ): Promise { + const keypair = Ed25519Keypair.fromSecretKey( + Buffer.from(senderPrivateKey, "hex") + ); + const tx = new Transaction(); + const packageId = await this.getPythPackageId(); + const verificationReceipt = await this.getVaaVerificationReceipt( + tx, + packageId, + vaa + ); + + tx.moveCall({ + target: `${packageId}::governance::execute_governance_instruction`, + arguments: [tx.object(this.stateId), verificationReceipt], + }); + + const result = await this.executeTransaction(tx, keypair); + return { id: result.digest, info: result }; + } + + async executeUpgradeInstruction( + vaa: Buffer, + keypair: Ed25519Keypair, + modules: number[][], + dependencies: string[] + ) { + const tx = new Transaction(); + const packageId = await this.getPythPackageId(); + const verificationReceipt = await this.getVaaVerificationReceipt( + tx, + packageId, + vaa + ); + + const [upgradeTicket] = tx.moveCall({ + target: `${packageId}::contract_upgrade::authorize_upgrade`, + arguments: [tx.object(this.stateId), verificationReceipt], + }); + + const [upgradeReceipt] = tx.upgrade({ + modules, + dependencies, + package: packageId, + ticket: upgradeTicket, + }); + + tx.moveCall({ + target: `${packageId}::contract_upgrade::commit_upgrade`, + arguments: [tx.object(this.stateId), upgradeReceipt], + }); + const result = await this.executeTransaction(tx, keypair); + return { id: result.digest, info: result }; + } + + /** + * Utility function to get the verification receipt object for a VAA that can be + * used to authorize a governance instruction. + * @param tx + * @param packageId pyth package id + * @param vaa + * @private + */ + async getVaaVerificationReceipt( + tx: Transaction, + packageId: string, + vaa: Buffer + ) { + const wormholePackageId = await this.getWormholePackageId(); + + const [verifiedVAA] = tx.moveCall({ + target: `${wormholePackageId}::vaa::parse_and_verify`, + arguments: [ + tx.object(this.wormholeStateId), + tx.pure.arguments(Array.from(vaa)), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + }); + + const [verificationReceipt] = tx.moveCall({ + target: `${packageId}::governance::verify_vaa`, + arguments: [tx.object(this.stateId), verifiedVAA], + }); + return verificationReceipt; + } + + /** + * Given a transaction block and a keypair, sign and execute it + * Sets the gas budget to 2x the estimated gas cost + * @param tx + * @param keypair + * @private + */ + private async executeTransaction(tx: Transaction, keypair: Ed25519Keypair) { + const provider = this.getProvider(); + tx.setSender(keypair.toSuiAddress()); + const dryRun = await provider.dryRunTransactionBlock({ + transactionBlock: await tx.build({ client: provider }), + }); + tx.setGasBudget(BigInt(dryRun.input.gasData.budget.toString()) * BigInt(2)); + return provider.signAndExecuteTransaction({ + signer: keypair, + transaction: tx, + options: { + showEffects: true, + showEvents: true, + }, + }); + } + + async getValidTimePeriod() { + const fields = await this.getStateFields(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return Number(fields.stale_price_threshold); + } + + async getDataSources(): Promise { + const provider = this.getProvider(); + const result = await provider.getDynamicFieldObject({ + parentId: this.stateId, + name: { + type: "vector", + value: "data_sources", + }, + }); + if (!result.data || !result.data.content) { + throw new Error( + "Data Sources not found, contract may not be initialized" + ); + } + if (result.data.content.dataType !== "moveObject") { + throw new Error("Data Sources type mismatch"); + } + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return result.data.content.fields.value.fields.keys.map( + ({ + fields, + }: { + fields: { + emitter_address: { fields: { value: { fields: { data: string } } } }; + emitter_chain: string; + }; + }) => { + return { + emitterChain: Number(fields.emitter_chain), + emitterAddress: Buffer.from( + fields.emitter_address.fields.value.fields.data + ).toString("hex"), + }; + } + ); + } + + async getGovernanceDataSource(): Promise { + const fields = await this.getStateFields(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const governanceFields = fields.governance_data_source.fields; + const chainId = governanceFields.emitter_chain; + const emitterAddress = + governanceFields.emitter_address.fields.value.fields.data; + return { + emitterChain: Number(chainId), + emitterAddress: Buffer.from(emitterAddress).toString("hex"), + }; + } + + async getBaseUpdateFee() { + const fields = await this.getStateFields(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return { amount: fields.base_update_fee }; + } + + async getLastExecutedGovernanceSequence() { + const fields = await this.getStateFields(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return Number(fields.last_executed_governance_sequence); + } + + getProvider() { + return this.chain.getProvider(); + } + + private async getStateFields() { + const provider = this.getProvider(); + const result = await provider.getObject({ + id: this.stateId, + options: { showContent: true }, + }); + if ( + !result.data || + !result.data.content || + result.data.content.dataType !== "moveObject" + ) + throw new Error("Unable to fetch pyth state object"); + return result.data.content.fields; + } +} + +export class SuiWormholeContract extends WormholeContract { + public static type = "SuiWormholeContract"; + private client: SuiPythClient; + + getId(): string { + return `${this.chain.getId()}_${this.address}`; + } + + getType(): string { + return SuiWormholeContract.type; + } + + toJson() { + return { + chain: this.chain.getId(), + address: this.address, + type: SuiWormholeContract.type, + }; + } + + static fromJson( + chain: Chain, + parsed: { + type: string; + address: string; + stateId: string; + } + ): SuiWormholeContract { + if (parsed.type !== SuiWormholeContract.type) + throw new Error("Invalid type"); + if (!(chain instanceof SuiChain)) + throw new Error(`Wrong chain type ${chain}`); + return new SuiWormholeContract(chain, parsed.address, parsed.stateId); + } + + constructor( + public chain: SuiChain, + public address: string, + public stateId: string + ) { + super(); + this.client = new SuiPythClient( + this.chain.getProvider(), + // HACK: + // We're using the SuiPythClient to work with the Wormhole contract + // so there is no Pyth contract here, passing empty string to type- + // check. + "", + this.stateId + ); + } + + async getCurrentGuardianSetIndex(): Promise { + const data = await this.getStateFields(); + return Number(data.guardian_set_index); + } + + // There doesn't seem to be a way to get a value out of any function call + // via a Sui transaction due to the linear nature of the language, this is + // enforced at the TransactionBlock level by only allowing you to receive + // receipts. + async getChainId(): Promise { + return this.chain.getWormholeChainId(); + } + + // NOTE: There's no way to getChain() on the main interface, should update + // that interface. + public getChain(): SuiChain { + return this.chain; + } + + async getGuardianSet(): Promise { + const data = await this.getStateFields(); + const guardian_sets = data.guardian_sets; + return guardian_sets; + } + + async upgradeGuardianSets( + senderPrivateKey: PrivateKey, + vaa: Buffer + ): Promise { + const tx = new Transaction(); + const coreObjectId = this.stateId; + const corePackageId = await this.client.getWormholePackageId(); + const [verifiedVaa] = tx.moveCall({ + target: `${corePackageId}::vaa::parse_and_verify`, + arguments: [ + tx.object(coreObjectId), + tx.pure(uint8ArrayToBCS(vaa)), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + }); + + const [decreeTicket] = tx.moveCall({ + target: `${corePackageId}::update_guardian_set::authorize_governance`, + arguments: [tx.object(coreObjectId)], + }); + + const [decreeReceipt] = tx.moveCall({ + target: `${corePackageId}::governance_message::verify_vaa`, + arguments: [tx.object(coreObjectId), verifiedVaa, decreeTicket], + typeArguments: [ + `${corePackageId}::update_guardian_set::GovernanceWitness`, + ], + }); + + tx.moveCall({ + target: `${corePackageId}::update_guardian_set::update_guardian_set`, + arguments: [ + tx.object(coreObjectId), + decreeReceipt, + tx.object(SUI_CLOCK_OBJECT_ID), + ], + }); + + const keypair = Ed25519Keypair.fromSecretKey( + Buffer.from(senderPrivateKey, "hex") + ); + const result = await this.executeTransaction(tx, keypair); + return { id: result.digest, info: result }; + } + + private async getStateFields(): Promise { + const provider = this.chain.getProvider(); + const result = await provider.getObject({ + id: this.stateId, + options: { showContent: true }, + }); + if ( + !result.data || + !result.data.content || + result.data.content.dataType !== "moveObject" + ) + throw new Error("Unable to fetch pyth state object"); + return result.data.content.fields; + } + + /** + * Given a transaction block and a keypair, sign and execute it + * Sets the gas budget to 2x the estimated gas cost + * @param tx + * @param keypair + * @private + */ + private async executeTransaction(tx: Transaction, keypair: Ed25519Keypair) { + const provider = this.chain.getProvider(); + tx.setSender(keypair.toSuiAddress()); + const dryRun = await provider.dryRunTransactionBlock({ + transactionBlock: await tx.build({ client: provider }), + }); + tx.setGasBudget(BigInt(dryRun.input.gasData.budget.toString()) * BigInt(2)); + return provider.signAndExecuteTransaction({ + signer: keypair, + transaction: tx, + options: { + showEffects: true, + showEvents: true, + }, + }); + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 55c7175ac3..7770c006f0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,195 +4,6 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false -catalogs: - default: - '@amplitude/analytics-browser': - specifier: 2.11.8 - version: 2.11.8 - '@amplitude/plugin-autocapture-browser': - specifier: 1.0.0 - version: 1.0.0 - '@axe-core/react': - specifier: 4.9.1 - version: 4.9.1 - '@clickhouse/client': - specifier: 1.8.0 - version: 1.8.0 - '@cprussin/eslint-config': - specifier: 3.0.0 - version: 3.0.0 - '@cprussin/jest-config': - specifier: 1.4.1 - version: 1.4.1 - '@cprussin/prettier-config': - specifier: 2.1.1 - version: 2.1.1 - '@cprussin/tsconfig': - specifier: 3.0.1 - version: 3.0.1 - '@next/third-parties': - specifier: 15.0.2 - version: 15.0.2 - '@phosphor-icons/react': - specifier: 2.1.7 - version: 2.1.7 - '@pythnetwork/client': - specifier: 2.22.1 - version: 2.22.1 - '@react-hookz/web': - specifier: 24.0.4 - version: 24.0.4 - '@solana/web3.js': - specifier: 1.95.4 - version: 1.95.4 - '@storybook/addon-essentials': - specifier: 8.3.5 - version: 8.3.5 - '@storybook/addon-styling-webpack': - specifier: 1.0.0 - version: 1.0.0 - '@storybook/addon-themes': - specifier: 8.3.5 - version: 8.3.5 - '@storybook/blocks': - specifier: 8.3.5 - version: 8.3.5 - '@storybook/nextjs': - specifier: 8.3.5 - version: 8.3.5 - '@storybook/react': - specifier: 8.3.5 - version: 8.3.5 - '@svgr/webpack': - specifier: 8.1.0 - version: 8.1.0 - '@types/jest': - specifier: 29.5.14 - version: 29.5.14 - '@types/node': - specifier: 22.8.2 - version: 22.8.2 - '@types/react': - specifier: 19.0.1 - version: 19.0.1 - '@types/react-dom': - specifier: 19.0.2 - version: 19.0.2 - autoprefixer: - specifier: 10.4.20 - version: 10.4.20 - bcp-47: - specifier: 2.1.0 - version: 2.1.0 - bs58: - specifier: 6.0.0 - version: 6.0.0 - clsx: - specifier: 2.1.1 - version: 2.1.1 - cryptocurrency-icons: - specifier: 0.18.1 - version: 0.18.1 - css-loader: - specifier: 7.1.2 - version: 7.1.2 - dnum: - specifier: 2.14.0 - version: 2.14.0 - eslint: - specifier: 9.13.0 - version: 9.13.0 - framer-motion: - specifier: 11.11.10 - version: 11.11.10 - jest: - specifier: 29.7.0 - version: 29.7.0 - lightweight-charts: - specifier: ^4.2.3 - version: 4.2.3 - modern-normalize: - specifier: 3.0.1 - version: 3.0.1 - motion: - specifier: 11.14.4 - version: 11.14.4 - next: - specifier: 15.1.2 - version: 15.1.2 - next-themes: - specifier: 0.3.0 - version: 0.3.0 - nuqs: - specifier: 2.1.2 - version: 2.1.2 - pino: - specifier: 9.5.0 - version: 9.5.0 - postcss: - specifier: 8.4.47 - version: 8.4.47 - postcss-loader: - specifier: 8.1.1 - version: 8.1.1 - prettier: - specifier: 3.3.3 - version: 3.3.3 - react: - specifier: 19.0.0 - version: 19.0.0 - react-aria: - specifier: 3.36.0 - version: 3.36.0 - react-aria-components: - specifier: 1.5.0 - version: 1.5.0 - react-dom: - specifier: 19.0.0 - version: 19.0.0 - recharts: - specifier: 2.14.1 - version: 2.14.1 - sass: - specifier: 1.80.7 - version: 1.80.7 - sass-loader: - specifier: 16.0.3 - version: 16.0.3 - storybook: - specifier: 8.3.5 - version: 8.3.5 - style-loader: - specifier: 4.0.0 - version: 4.0.0 - stylelint: - specifier: 16.10.0 - version: 16.10.0 - stylelint-config-standard-scss: - specifier: 13.1.0 - version: 13.1.0 - superjson: - specifier: 2.2.2 - version: 2.2.2 - swr: - specifier: 2.2.5 - version: 2.2.5 - tailwindcss: - specifier: 3.4.14 - version: 3.4.14 - typescript: - specifier: 5.6.3 - version: 5.6.3 - vercel: - specifier: 37.12.1 - version: 37.12.1 - zod: - specifier: 3.23.8 - version: 3.23.8 - zod-validation-error: - specifier: 3.4.0 - version: 3.4.0 - overrides: '@injectivelabs/sdk-ts@1.10.72>@injectivelabs/token-metadata': 1.10.42 eslint-config-next>@typescript-eslint/parser: ^7.0.0 @@ -2634,6 +2445,43 @@ importers: specifier: ^17.0.32 version: 17.0.33 + target_chains/sui/cli-iota: + dependencies: + '@certusone/wormhole-sdk': + specifier: ^0.9.12 + version: 0.9.24(bufferutil@4.0.8)(encoding@0.1.13)(google-protobuf@3.21.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@5.0.10) + '@iota/iota-sdk': + specifier: ^0.5.0 + version: 0.5.0(svelte@4.2.18)(typescript@5.6.3) + '@pythnetwork/contract-manager': + specifier: workspace:* + version: link:../../../contract_manager + '@pythnetwork/price-service-client': + specifier: ^1.4.0 + version: 1.9.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@pythnetwork/price-service-sdk': + specifier: ^1.2.0 + version: 1.7.1 + '@pythnetwork/xc-admin-common': + specifier: workspace:* + version: link:../../../governance/xc_admin/packages/xc_admin_common + prettier: + specifier: ^2.8.7 + version: 2.8.8 + ts-node: + specifier: ^10.9.1 + version: 10.9.2(@types/node@22.8.2)(typescript@5.6.3) + typescript: + specifier: ^5.0.4 + version: 5.6.3 + yargs: + specifier: ^17.7.2 + version: 17.7.2 + devDependencies: + '@types/yargs': + specifier: ^17.0.32 + version: 17.0.33 + target_chains/sui/sdk/js: dependencies: '@mysten/sui': @@ -5021,6 +4869,13 @@ packages: '@internationalized/string@3.2.5': resolution: {integrity: sha512-rKs71Zvl2OKOHM+mzAFMIyqR5hI1d1O6BBkMK2/lkfg3fkmVh9Eeg0awcA8W2WqYqDOv6a86DIOlFpggwLtbuw==} + '@iota/bcs@0.2.1': + resolution: {integrity: sha512-T+iv5gZhUZP7BiDY7+Ir4MA2rYmyGNZA2b+nxjv219Fp8klFt+l38OWA+1RgJXrCmzuZ+M4hbMAeHhHziURX6Q==} + + '@iota/iota-sdk@0.5.0': + resolution: {integrity: sha512-ZFg4C5EuHV55fHITKOO6Mg1dLqgojZqJsDsR3SRt8W9Ofzbjt8shlM2uLNRwDpiM7GzTb4UUFcKXpOt//5gEmQ==} + engines: {node: '>=20'} + '@ipld/dag-pb@2.1.18': resolution: {integrity: sha512-ZBnf2fuX9y3KccADURG5vb9FaOeMjFkCrNysB0PtftME/4iCTjxfaLoNq/IAh5fTqUOMXvryN6Jyka4ZGuMLIg==} @@ -21096,6 +20951,12 @@ snapshots: graphql: 16.9.0 typescript: 5.5.4 + '@0no-co/graphqlsp@1.12.12(graphql@16.9.0)(typescript@5.6.3)': + dependencies: + '@gql.tada/internal': 1.0.4(graphql@16.9.0)(typescript@5.6.3) + graphql: 16.9.0 + typescript: 5.6.3 + '@75lb/deep-merge@1.1.1': dependencies: lodash.assignwith: 4.2.0 @@ -22467,6 +22328,41 @@ snapshots: - subscriptions-transport-ws - utf-8-validate + '@certusone/wormhole-sdk@0.9.24(bufferutil@4.0.8)(encoding@0.1.13)(google-protobuf@3.21.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@5.0.10)': + dependencies: + '@certusone/wormhole-sdk-proto-web': 0.0.6(google-protobuf@3.21.4) + '@certusone/wormhole-sdk-wasm': 0.0.1 + '@coral-xyz/borsh': 0.2.6(@solana/web3.js@1.92.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) + '@mysten/sui.js': 0.32.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) + '@project-serum/anchor': 0.25.0(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@solana/spl-token': 0.3.7(@solana/web3.js@1.92.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.92.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@terra-money/terra.js': 3.1.9 + '@xpla/xpla.js': 0.2.3 + algosdk: 2.7.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.6 + js-base64: 3.7.5 + near-api-js: 1.1.0(encoding@0.1.13) + optionalDependencies: + '@injectivelabs/networks': 1.10.12(google-protobuf@3.21.4) + '@injectivelabs/sdk-ts': 1.10.72(bufferutil@4.0.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@5.0.10) + '@injectivelabs/utils': 1.10.12(google-protobuf@3.21.4) + transitivePeerDependencies: + - bufferutil + - debug + - encoding + - google-protobuf + - graphql-ws + - react + - react-dom + - subscriptions-transport-ws + - utf-8-validate + '@certusone/wormhole-sdk@0.9.24(bufferutil@4.0.8)(encoding@0.1.13)(google-protobuf@3.21.4)(utf-8-validate@5.0.10)': dependencies: '@certusone/wormhole-sdk-proto-web': 0.0.6(google-protobuf@3.21.4) @@ -22579,7 +22475,7 @@ snapshots: '@confio/ics23@0.6.8': dependencies: - '@noble/hashes': 1.6.1 + '@noble/hashes': 1.7.1 protobufjs: 6.11.4 '@coral-xyz/anchor-errors@0.30.1': {} @@ -22768,7 +22664,7 @@ snapshots: '@cosmjs/encoding': 0.30.1 '@cosmjs/math': 0.30.1 '@cosmjs/utils': 0.30.1 - '@noble/hashes': 1.6.1 + '@noble/hashes': 1.7.1 bn.js: 5.2.1 elliptic: 6.5.6 libsodium-wrappers: 0.7.10 @@ -23011,7 +22907,7 @@ snapshots: '@cosmjs/socket': 0.30.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@cosmjs/stream': 0.30.1 '@cosmjs/utils': 0.30.1 - axios: 0.21.4(debug@4.3.7) + axios: 0.21.4 readonly-date: 1.0.0 xstream: 11.14.0 transitivePeerDependencies: @@ -24519,12 +24415,30 @@ snapshots: transitivePeerDependencies: - svelte + '@gql.tada/cli-utils@1.5.1(@0no-co/graphqlsp@1.12.12(graphql@16.9.0)(typescript@5.6.3))(graphql@16.9.0)(svelte@4.2.18)(typescript@5.6.3)': + dependencies: + '@0no-co/graphqlsp': 1.12.12(graphql@16.9.0)(typescript@5.6.3) + '@gql.tada/internal': 1.0.4(graphql@16.9.0)(typescript@5.6.3) + '@vue/compiler-dom': 3.4.34 + '@vue/language-core': 2.0.29(typescript@5.6.3) + graphql: 16.9.0 + svelte2tsx: 0.7.13(svelte@4.2.18)(typescript@5.6.3) + typescript: 5.6.3 + transitivePeerDependencies: + - svelte + '@gql.tada/internal@1.0.4(graphql@16.9.0)(typescript@5.5.4)': dependencies: '@0no-co/graphql.web': 1.0.7(graphql@16.9.0) graphql: 16.9.0 typescript: 5.5.4 + '@gql.tada/internal@1.0.4(graphql@16.9.0)(typescript@5.6.3)': + dependencies: + '@0no-co/graphql.web': 1.0.7(graphql@16.9.0) + graphql: 16.9.0 + typescript: 5.6.3 + '@graphql-tools/batch-execute@8.5.1(graphql@15.9.0)': dependencies: '@graphql-tools/utils': 8.9.0(graphql@15.9.0) @@ -25035,7 +24949,7 @@ snapshots: '@injectivelabs/test-utils@1.14.4': dependencies: - axios: 0.21.4(debug@4.3.7) + axios: 0.21.4 bignumber.js: 9.1.2 link-module-alias: 1.2.0 shx: 0.3.4 @@ -25087,7 +25001,7 @@ snapshots: dependencies: '@injectivelabs/exceptions': 1.14.6(google-protobuf@3.21.4) '@injectivelabs/ts-types': 1.14.6 - axios: 0.21.4(debug@4.3.7) + axios: 0.21.4 bignumber.js: 9.1.2 http-status-codes: 2.2.0 link-module-alias: 1.2.0 @@ -25103,7 +25017,7 @@ snapshots: dependencies: '@injectivelabs/exceptions': 1.14.6(google-protobuf@3.21.4) '@injectivelabs/ts-types': 1.14.6 - axios: 0.21.4(debug@4.3.7) + axios: 0.21.4 bignumber.js: 9.1.2 http-status-codes: 2.2.0 link-module-alias: 1.2.0 @@ -25131,6 +25045,28 @@ snapshots: dependencies: '@swc/helpers': 0.5.15 + '@iota/bcs@0.2.1': + dependencies: + bs58: 6.0.0 + + '@iota/iota-sdk@0.5.0(svelte@4.2.18)(typescript@5.6.3)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0) + '@iota/bcs': 0.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + '@suchipi/femver': 1.0.0 + bech32: 2.0.0 + gql.tada: 1.8.2(graphql@16.9.0)(svelte@4.2.18)(typescript@5.6.3) + graphql: 16.9.0 + tweetnacl: 1.0.3 + valibot: 0.36.0 + transitivePeerDependencies: + - svelte + - typescript + '@ipld/dag-pb@2.1.18': dependencies: multiformats: 9.9.0 @@ -26358,10 +26294,10 @@ snapshots: '@mysten/sui.js@0.32.2(bufferutil@4.0.8)(utf-8-validate@5.0.10)': dependencies: '@mysten/bcs': 0.7.1 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 - '@scure/bip32': 1.6.0 - '@scure/bip39': 1.5.0 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 '@suchipi/femver': 1.0.0 jayson: 4.1.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) rpc-websockets: 7.5.1 @@ -27463,6 +27399,20 @@ snapshots: transitivePeerDependencies: - axios + '@pythnetwork/price-service-client@1.9.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': + dependencies: + '@pythnetwork/price-service-sdk': 1.7.1 + '@types/ws': 8.5.13 + axios: 1.7.7 + axios-retry: 3.9.1 + isomorphic-ws: 4.0.1(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) + ts-log: 2.2.7 + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - debug + - utf-8-validate + '@pythnetwork/price-service-client@1.9.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)': dependencies: '@pythnetwork/price-service-sdk': 1.7.1 @@ -30725,8 +30675,8 @@ snapshots: '@solana/web3.js@1.92.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.25.7 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 '@solana/buffer-layout': 4.0.1 agentkeepalive: 4.5.0 bigint-buffer: 1.1.5 @@ -30791,8 +30741,8 @@ snapshots: '@solana/web3.js@1.95.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.25.7 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 '@solana/buffer-layout': 4.0.1 agentkeepalive: 4.5.0 bigint-buffer: 1.1.5 @@ -32410,15 +32360,15 @@ snapshots: '@types/bn.js@4.11.6': dependencies: - '@types/node': 20.14.15 + '@types/node': 22.8.2 '@types/bn.js@5.1.1': dependencies: - '@types/node': 20.14.15 + '@types/node': 22.8.2 '@types/bn.js@5.1.6': dependencies: - '@types/node': 20.14.15 + '@types/node': 22.8.2 '@types/body-parser@1.19.2': dependencies: @@ -32442,7 +32392,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 20.14.15 + '@types/node': 22.8.2 '@types/cors@2.8.12': optional: true @@ -32661,7 +32611,7 @@ snapshots: '@types/pbkdf2@3.1.0': dependencies: - '@types/node': 20.14.15 + '@types/node': 22.8.2 '@types/pino@7.0.5': dependencies: @@ -32694,7 +32644,7 @@ snapshots: '@types/secp256k1@4.0.6': dependencies: - '@types/node': 20.14.15 + '@types/node': 22.8.2 '@types/seedrandom@3.0.1': {} @@ -32751,11 +32701,11 @@ snapshots: '@types/ws@7.4.7': dependencies: - '@types/node': 20.14.15 + '@types/node': 22.8.2 '@types/ws@8.5.13': dependencies: - '@types/node': 20.14.15 + '@types/node': 22.8.2 '@types/ws@8.5.3': dependencies: @@ -33865,6 +33815,19 @@ snapshots: optionalDependencies: typescript: 5.5.4 + '@vue/language-core@2.0.29(typescript@5.6.3)': + dependencies: + '@volar/language-core': 2.4.0-alpha.18 + '@vue/compiler-dom': 3.4.34 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.4.34 + computeds: 0.0.1 + minimatch: 9.0.5 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.6.3 + '@vue/shared@3.4.34': {} '@wagmi/connectors@5.0.16(s3vyldfg35276nhmgidicj7ffm)': @@ -35130,6 +35093,12 @@ snapshots: axios: 1.7.7(debug@4.3.7) is-retry-allowed: 2.2.0 + axios@0.21.4: + dependencies: + follow-redirects: 1.15.9 + transitivePeerDependencies: + - debug + axios@0.21.4(debug@4.3.7): dependencies: follow-redirects: 1.15.9(debug@4.3.7) @@ -35138,7 +35107,7 @@ snapshots: axios@0.24.0: dependencies: - follow-redirects: 1.15.9(debug@4.3.7) + follow-redirects: 1.15.9 transitivePeerDependencies: - debug @@ -35150,13 +35119,13 @@ snapshots: axios@0.26.1: dependencies: - follow-redirects: 1.15.9(debug@4.3.7) + follow-redirects: 1.15.9 transitivePeerDependencies: - debug axios@0.27.2: dependencies: - follow-redirects: 1.15.9(debug@4.3.7) + follow-redirects: 1.15.9 form-data: 4.0.1 transitivePeerDependencies: - debug @@ -35177,6 +35146,14 @@ snapshots: transitivePeerDependencies: - debug + axios@1.7.7: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.1 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + axios@1.7.7(debug@4.3.7): dependencies: follow-redirects: 1.15.9(debug@4.3.7) @@ -39257,6 +39234,8 @@ snapshots: flow-parser@0.250.0: {} + follow-redirects@1.15.9: {} + follow-redirects@1.15.9(debug@4.3.7): optionalDependencies: debug: 4.3.7(supports-color@8.1.1) @@ -39879,6 +39858,17 @@ snapshots: - graphql - svelte + gql.tada@1.8.2(graphql@16.9.0)(svelte@4.2.18)(typescript@5.6.3): + dependencies: + '@0no-co/graphql.web': 1.0.7(graphql@16.9.0) + '@0no-co/graphqlsp': 1.12.12(graphql@16.9.0)(typescript@5.6.3) + '@gql.tada/cli-utils': 1.5.1(@0no-co/graphqlsp@1.12.12(graphql@16.9.0)(typescript@5.6.3))(graphql@16.9.0)(svelte@4.2.18)(typescript@5.6.3) + '@gql.tada/internal': 1.0.4(graphql@16.9.0)(typescript@5.6.3) + typescript: 5.6.3 + transitivePeerDependencies: + - graphql + - svelte + graceful-fs@4.2.11: {} grapheme-splitter@1.0.4: {} @@ -40805,6 +40795,10 @@ snapshots: dependencies: ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@6.0.4) + isomorphic-ws@4.0.1(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)): + dependencies: + ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) + isomorphic-ws@4.0.1(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)): dependencies: ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) @@ -45373,7 +45367,7 @@ snapshots: '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 '@types/long': 4.0.2 - '@types/node': 20.14.15 + '@types/node': 22.8.2 long: 4.0.0 protobufjs@7.2.6: @@ -45388,7 +45382,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.14.15 + '@types/node': 22.8.2 long: 5.2.3 protocols@2.0.1: {} @@ -47688,6 +47682,13 @@ snapshots: svelte: 4.2.18 typescript: 5.5.4 + svelte2tsx@0.7.13(svelte@4.2.18)(typescript@5.6.3): + dependencies: + dedent-js: 1.0.1 + pascal-case: 3.1.2 + svelte: 4.2.18 + typescript: 5.6.3 + svelte@4.2.18: dependencies: '@ampproject/remapping': 2.3.0 @@ -48652,7 +48653,6 @@ snapshots: typescript: 5.6.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - optional: true ts-node@7.0.1: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a583a4b246..19dfbbacef 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -21,7 +21,9 @@ packages: - "target_chains/fuel/sdk/js" - "target_chains/starknet/sdk/js" - "target_chains/sui/sdk/js" + - "target_chains/sui/sdk/js-iota" - "target_chains/sui/cli" + - "target_chains/sui/cli-iota " - "target_chains/solana/sdk/js/solana_utils" - "target_chains/solana/sdk/js/pyth_solana_receiver" - "target_chains/ton/contracts" diff --git a/target_chains/sui/cli-iota/.gitignore b/target_chains/sui/cli-iota/.gitignore new file mode 100644 index 0000000000..a65b41774a --- /dev/null +++ b/target_chains/sui/cli-iota/.gitignore @@ -0,0 +1 @@ +lib diff --git a/target_chains/sui/cli-iota/README.md b/target_chains/sui/cli-iota/README.md new file mode 100644 index 0000000000..cf4a0275a6 --- /dev/null +++ b/target_chains/sui/cli-iota/README.md @@ -0,0 +1,69 @@ +# Pre-requisites + +Install move cli according to this [doc](../contracts/README.md) + +# Deploying from scratch + +Configure the `Move.toml` file accordingly. The wormhole address should be specified based on the target chain in the `Move.toml` and the pyth address should be `0x0`. +We can deploy the pyth oracle and initialize it with the following command: + +```bash +npm run cli -- deploy --private-key --chain [sui_mainnet|sui_testnet] +``` + +You can then add your sui contract configs to the contract manager store. + +You can also manually create all the price feeds available at the moment to make it easier for devs to test the oracle. + +```bash +npm run cli -- create-all --private-key --contract +``` + +# Updating price feeds: + +You can use the `create` and `update-feeds` commands to create and update price feeds respectively. + +```bash +npm run cli -- create --feed-id --private-key --contract +``` + +```bash +npm run cli -- update-feeds --feed-id --private-key --contract +``` + +# Upgrade process: + +The following steps are needed to upgrade our sui contracts: + +- Contract changes: + - Create a new struct for the new version and update `current_version` and `previous_version` functions in `version_control` module + - Implement any custom logic needed to migrate the data from the old struct to the new one in the `migrate` module + - Update dependency (e.g. wormhole) addresses if needed +- Generate the digest for the new contract build +- Create a governance proposal, proposing the sui package to be upgraded to this specific digest +- Approve and execute the governance proposal +- Run the upgrade transaction and publish the new package + +## Generating the new contract hash: + +Run the following command to generate the new hash, make sure the contract addresses are identical to the deployed ones: + +```bash +npm run cli -- generate-digest +``` + +## Upgrading the contract + +To upgrade the contract after the governance vaa was executed run: + +```bash +npm run cli -- upgrade --private-key --contract --vaa +``` + +The upgrade procedure consists of 2 transactions. The first one is to upgrade the contract (sui level) and the second one is to run the `migrate` function and upgrade the version (package level). +Since clients try to fetch the latest version of the package automatically, it's important to run the second transaction as soon as possible after the first one. + +### FAQ: + +- I'm seeing the error `Transaction has non recoverable errors from at least 1/3 of validators`. What should I do? + Make sure you have enough funding in the wallet and try again. Usually a more descriptive error message is available in the returned value of the transaction. diff --git a/target_chains/sui/cli-iota/package.json b/target_chains/sui/cli-iota/package.json new file mode 100644 index 0000000000..dea2f7daa0 --- /dev/null +++ b/target_chains/sui/cli-iota/package.json @@ -0,0 +1,27 @@ +{ + "name": "pyth-iota-cli", + "version": "0.1.0", + "description": "Pyth Sui Integration Cli tools", + "main": "index.js", + "license": "Apache-2.0", + "scripts": { + "cli": "ts-node src/cli.ts", + "build": "tsc" + }, + "private": "true", + "dependencies": { + "@certusone/wormhole-sdk": "^0.9.12", + "@iota/iota-sdk": "^0.5.0", + "@pythnetwork/contract-manager": "workspace:*", + "@pythnetwork/price-service-client": "^1.4.0", + "@pythnetwork/price-service-sdk": "^1.2.0", + "@pythnetwork/xc-admin-common": "workspace:*", + "prettier": "^2.8.7", + "ts-node": "^10.9.1", + "typescript": "^5.0.4", + "yargs": "^17.7.2" + }, + "devDependencies": { + "@types/yargs": "^17.0.32" + } +} diff --git a/target_chains/sui/cli-iota/src/cli.ts b/target_chains/sui/cli-iota/src/cli.ts new file mode 100644 index 0000000000..de2e33a943 --- /dev/null +++ b/target_chains/sui/cli-iota/src/cli.ts @@ -0,0 +1,297 @@ +import yargs from "yargs"; +import { hideBin } from "yargs/helpers"; +import { + DefaultStore, + getDefaultDeploymentConfig, + SuiChain, + SuiPriceFeedContract, +} from "@pythnetwork/contract-manager"; +import { PriceServiceConnection } from "@pythnetwork/price-service-client"; +import { execSync } from "child_process"; +import { initPyth, publishPackage } from "./pyth_deploy"; +import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519"; +import { resolve } from "path"; +import { + buildForBytecodeAndDigest, + migratePyth, + upgradePyth, +} from "./upgrade_pyth"; + +const OPTIONS = { + "private-key": { + type: "string", + demandOption: true, + desc: "Private key to use to sign transaction", + }, + contract: { + type: "string", + demandOption: true, + desc: "Contract to use for the command (e.g sui_testnet_0xe8c2ddcd5b10e8ed98e53b12fcf8f0f6fd9315f810ae61fa4001858851f21c88)", + }, + path: { + type: "string", + default: "../../contracts", + desc: "Path to the sui contracts, will use ../../contracts by default", + }, + endpoint: { + type: "string", + desc: "Price service endpoint to use, defaults to https://hermes.pyth.network for mainnet and https://hermes-beta.pyth.network for testnet", + }, + "feed-id": { + type: "array", + demandOption: true, + desc: "Price feed ids to create without the leading 0x (e.g f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b). Can be provided multiple times for multiple feed updates", + }, +} as const; + +function getContract(contractId: string): SuiPriceFeedContract { + const contract = DefaultStore.contracts[contractId] as SuiPriceFeedContract; + if (!contract) { + throw new Error(`Contract ${contractId} not found`); + } + return contract; +} + +function getPriceService( + contract: SuiPriceFeedContract, + endpointOverride: string | undefined +): PriceServiceConnection { + const defaultEndpoint = contract.getChain().isMainnet() + ? "https://hermes.pyth.network" + : "https://hermes-beta.pyth.network"; + return new PriceServiceConnection(endpointOverride || defaultEndpoint); +} + +yargs(hideBin(process.argv)) + .command( + "create", + "Create a new price feed", + (yargs) => { + return yargs + .options({ + contract: OPTIONS.contract, + "feed-id": OPTIONS["feed-id"], + "private-key": OPTIONS["private-key"], + endpoint: OPTIONS.endpoint, + }) + .usage( + "$0 create --contract --feed-id --private-key " + ); + }, + async (argv) => { + const contract = getContract(argv.contract); + const priceService = getPriceService(contract, argv.endpoint); + const feedIds = argv["feed-id"] as string[]; + const vaas = await priceService.getLatestVaas(feedIds); + const digest = await contract.executeCreatePriceFeed( + argv["private-key"], + vaas.map((vaa) => Buffer.from(vaa, "base64")) + ); + console.log("Transaction successful. Digest:", digest); + } + ) + .command( + "create-all", + "Create all price feeds for a contract", + (yargs) => { + return yargs + .options({ + contract: OPTIONS.contract, + "private-key": OPTIONS["private-key"], + endpoint: OPTIONS.endpoint, + }) + .usage( + "$0 create-all --contract --private-key " + ); + }, + async (argv) => { + const contract = getContract(argv.contract); + const priceService = getPriceService(contract, argv.endpoint); + const feedIds = await priceService.getPriceFeedIds(); + const BATCH_SIZE = 10; + for (let i = 0; i < feedIds.length; i += BATCH_SIZE) { + const batch = feedIds.slice(i, i + BATCH_SIZE); + const vaas = await priceService.getLatestVaas(batch); + const digest = await contract.executeCreatePriceFeed( + argv["private-key"], + vaas.map((vaa) => Buffer.from(vaa, "base64")) + ); + console.log("Transaction successful. Digest:", digest); + console.log(`Progress: ${i + BATCH_SIZE}/${feedIds.length}`); + } + } + ) + .command( + "generate-digest", + "Generate digest for a contract", + (yargs) => { + return yargs + .options({ + path: OPTIONS.path, + }) + .usage("$0 generate-digest --path "); + }, + async (argv) => { + const buildOutput: { + modules: string[]; + dependencies: string[]; + digest: number[]; + } = JSON.parse( + execSync( + `sui move build --dump-bytecode-as-base64 --path ${__dirname}/${argv.path} 2> /dev/null`, + { + encoding: "utf-8", + } + ) + ); + console.log("Contract digest:"); + console.log(Buffer.from(buildOutput.digest).toString("hex")); + } + ) + .command( + "deploy", + "Deploy a contract", + (yargs) => { + return yargs + .options({ + "private-key": OPTIONS["private-key"], + chain: { + type: "string", + demandOption: true, + desc: "Chain to deploy the code to. Can be sui_mainnet or sui_testnet", + }, + path: OPTIONS.path, + }) + .usage( + "$0 deploy --private-key --chain [sui_mainnet|sui_testnet] --path " + ); + }, + async (argv) => { + const walletPrivateKey = argv["private-key"]; + const chain = DefaultStore.chains[argv.chain] as SuiChain; + const keypair = Ed25519Keypair.fromSecretKey( + Buffer.from(walletPrivateKey, "hex") + ); + const result = await publishPackage( + keypair, + chain.getProvider(), + argv.path + ); + const deploymentType = chain.isMainnet() ? "stable" : "beta"; + const config = getDefaultDeploymentConfig(deploymentType); + await initPyth( + keypair, + chain.getProvider(), + result.packageId, + result.deployerCapId, + result.upgradeCapId, + config + ); + } + ) + .command( + "update-feeds", + "Update price feeds for a contract", + (yargs) => { + return yargs + .options({ + contract: OPTIONS.contract, + "feed-id": OPTIONS["feed-id"], + "private-key": OPTIONS["private-key"], + endpoint: OPTIONS.endpoint, + }) + .usage( + "$0 update-feeds --contract --feed-id --private-key " + ); + }, + async (argv) => { + const contract = getContract(argv.contract); + const priceService = getPriceService(contract, argv.endpoint); + const feedIds = argv["feed-id"] as string[]; + const vaas = await priceService.getLatestVaas(feedIds); + const digest = await contract.executeUpdatePriceFeedWithFeeds( + argv["private-key"], + vaas.map((vaa) => Buffer.from(vaa, "base64")), + feedIds + ); + console.log("Transaction successful. Digest:", digest); + } + ) + .command( + "upgrade", + "Upgrade a contract", + (yargs) => { + return yargs + .options({ + "private-key": OPTIONS["private-key"], + contract: OPTIONS.contract, + vaa: { + type: "string", + demandOption: true, + desc: "Signed Vaa for upgrading the package in hex format", + }, + path: OPTIONS.path, + }) + .usage( + "$0 upgrade --private-key --contract --vaa " + ); + }, + async (argv) => { + const contract = getContract(argv.contract); + const keypair = Ed25519Keypair.fromSecretKey( + Buffer.from(argv["private-key"], "hex") + ); + + const pythContractsPath = resolve(`${__dirname}/${argv.path}`); + + // Build for modules and dependencies + const { modules, dependencies, digest } = + buildForBytecodeAndDigest(pythContractsPath); + //Execute upgrade with signed governance VAA. + console.log("Digest is", digest.toString("hex")); + const pythPackageOld = await contract.getPackageId(contract.stateId); + console.log("Old package id:", pythPackageOld); + const signedVaa = Buffer.from(argv.vaa, "hex"); + const upgradeResults = await upgradePyth( + keypair, + contract.chain.getProvider(), + modules, + dependencies, + signedVaa, + contract + ); + console.log("Tx digest", upgradeResults.digest); + if ( + !upgradeResults.effects || + upgradeResults.effects.status.status !== "success" + ) { + throw new Error("Upgrade failed"); + } + + console.log( + "Upgrade successful, Executing the migrate function in a separate transaction..." + ); + + // We can not do the migration in the same transaction since the newly published package is not found + // on chain at the beginning of the transaction. + + const migrateResults = await migratePyth( + keypair, + contract.chain.getProvider(), + signedVaa, + contract, + pythPackageOld + ); + console.log("Tx digest", migrateResults.digest); + if ( + !migrateResults.effects || + migrateResults.effects.status.status !== "success" + ) { + throw new Error( + `Migrate failed. Old package id is ${pythPackageOld}. Please do the migration manually` + ); + } + console.log("Migrate successful"); + } + ) + .demandCommand().argv; diff --git a/target_chains/sui/cli-iota/src/pyth_deploy.ts b/target_chains/sui/cli-iota/src/pyth_deploy.ts new file mode 100644 index 0000000000..82518df0a9 --- /dev/null +++ b/target_chains/sui/cli-iota/src/pyth_deploy.ts @@ -0,0 +1,169 @@ +import { Transaction } from "@mysten/sui/transactions"; + +import { MIST_PER_SUI, normalizeSuiObjectId, fromB64 } from "@mysten/sui/utils"; + +import { Ed25519Keypair } from "@mysten/sui/dist/cjs/keypairs/ed25519"; +import { execSync } from "child_process"; +import { DataSource } from "@pythnetwork/xc-admin-common"; +import { SuiClient } from "@mysten/sui/client"; + +export async function publishPackage( + keypair: Ed25519Keypair, + provider: SuiClient, + packagePath: string +): Promise<{ packageId: string; upgradeCapId: string; deployerCapId: string }> { + // Build contracts + const buildOutput: { + modules: string[]; + dependencies: string[]; + } = JSON.parse( + execSync( + `sui move build --dump-bytecode-as-base64 --path ${__dirname}/${packagePath} 2> /dev/null`, + { + encoding: "utf-8", + } + ) + ); + + console.log("buildOutput: ", buildOutput); + + // Publish contracts + // const transactionBlock = new TransactionBlock(); + const txb = new Transaction(); + + txb.setGasBudget(MIST_PER_SUI / 2n); // 0.5 SUI + + const [upgradeCap] = txb.publish({ + modules: buildOutput.modules.map((m: string) => Array.from(fromB64(m))), + dependencies: buildOutput.dependencies.map((d: string) => + normalizeSuiObjectId(d) + ), + }); + + // Transfer upgrade capability to deployer + txb.transferObjects([upgradeCap], txb.pure.address(keypair.toSuiAddress())); + + // Execute transactions + const result = await provider.signAndExecuteTransaction({ + signer: keypair, + transaction: txb, + options: { + showInput: true, + showObjectChanges: true, + }, + }); + + const publishedChanges = result.objectChanges?.filter( + (change) => change.type === "published" + ); + + if ( + publishedChanges?.length !== 1 || + publishedChanges[0].type !== "published" + ) { + throw new Error( + "No publish event found in transaction:" + + JSON.stringify(result.objectChanges, null, 2) + ); + } + + const packageId = publishedChanges[0].packageId; + + console.log("Published with package id: ", packageId); + console.log("Tx digest", result.digest); + let upgradeCapId: string | undefined; + let deployerCapId: string | undefined; + for (const objectChange of result.objectChanges!) { + if (objectChange.type === "created") { + if (objectChange.objectType === "0x2::package::UpgradeCap") { + upgradeCapId = objectChange.objectId; + } + if (objectChange.objectType === `${packageId}::setup::DeployerCap`) { + deployerCapId = objectChange.objectId; + } + } + } + if (!upgradeCapId || !deployerCapId) { + throw new Error("Could not find upgrade cap or deployer cap"); + } + console.log("UpgradeCapId: ", upgradeCapId); + console.log("DeployerCapId: ", deployerCapId); + return { + packageId, + upgradeCapId: upgradeCapId, + deployerCapId: deployerCapId, + }; +} + +export async function initPyth( + keypair: Ed25519Keypair, + provider: SuiClient, + pythPackageId: string, + deployerCapId: string, + upgradeCapId: string, + config: { + dataSources: DataSource[]; + governanceDataSource: DataSource; + } +) { + const tx = new Transaction(); + + const baseUpdateFee = tx.pure.u64(1); + const dataSourceEmitterAddresses = tx.pure.arguments( + config.dataSources.map((dataSource) => [ + ...Buffer.from(dataSource.emitterAddress, "hex"), + ]) + ); + const dataSourceEmitterChainIds = tx.pure.arguments( + config.dataSources.map((dataSource) => dataSource.emitterChain) + ); + const governanceEmitterAddress = tx.pure.arguments([ + ...Buffer.from(config.governanceDataSource.emitterAddress, "hex"), + ]); + const governanceEmitterChainId = tx.pure.arguments( + config.governanceDataSource.emitterChain + ); + const stalePriceThreshold = tx.pure.u64(60); + tx.moveCall({ + target: `${pythPackageId}::pyth::init_pyth`, + arguments: [ + tx.object(deployerCapId), + tx.object(upgradeCapId), + stalePriceThreshold, + governanceEmitterChainId, + governanceEmitterAddress, + dataSourceEmitterChainIds, + dataSourceEmitterAddresses, + baseUpdateFee, + ], + }); + + tx.setGasBudget(MIST_PER_SUI / 10n); // 0.1 sui + + let result = await provider.signAndExecuteTransaction({ + signer: keypair, + transaction: tx, + options: { + showInput: true, + showEffects: true, + showEvents: true, + showObjectChanges: true, + showBalanceChanges: true, + }, + }); + if (!result.effects || !result.objectChanges) { + throw new Error("No effects or object changes found in transaction"); + } + if (result.effects.status.status === "success") { + console.log("Pyth init successful"); + console.log("Tx digest", result.digest); + } + for (const objectChange of result.objectChanges) { + if (objectChange.type === "created") { + if (objectChange.objectType === `${pythPackageId}::state::State`) { + console.log("Pyth state id: ", objectChange.objectId); + } + } + } + return result; +} diff --git a/target_chains/sui/cli-iota/src/upgrade_pyth.ts b/target_chains/sui/cli-iota/src/upgrade_pyth.ts new file mode 100644 index 0000000000..ee2b83cf92 --- /dev/null +++ b/target_chains/sui/cli-iota/src/upgrade_pyth.ts @@ -0,0 +1,111 @@ +import { Transaction } from "@mysten/sui/transactions"; +import { fromB64, MIST_PER_SUI, normalizeSuiObjectId } from "@mysten/sui/utils"; +import { SuiClient } from "@mysten/sui/client"; +import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519"; + +import { execSync } from "child_process"; +import { SuiPriceFeedContract } from "@pythnetwork/contract-manager"; + +export function buildForBytecodeAndDigest(packagePath: string) { + const buildOutput: { + modules: string[]; + dependencies: string[]; + digest: number[]; + } = JSON.parse( + execSync( + `sui move build --dump-bytecode-as-base64 -p ${packagePath} 2> /dev/null`, + { encoding: "utf-8" } + ) + ); + return { + modules: buildOutput.modules.map((m: string) => Array.from(fromB64(m))), + dependencies: buildOutput.dependencies.map((d: string) => + normalizeSuiObjectId(d) + ), + digest: Buffer.from(buildOutput.digest), + }; +} + +export async function upgradePyth( + keypair: Ed25519Keypair, + provider: SuiClient, + modules: number[][], + dependencies: string[], + signedVaa: Buffer, + contract: SuiPriceFeedContract +) { + const pythPackage = await contract.getPackageId(contract.stateId); + + const tx = new Transaction(); + + const verificationReceipt = await contract.getVaaVerificationReceipt( + tx as any, + pythPackage, + signedVaa + ); + + // Authorize upgrade. + const [upgradeTicket] = tx.moveCall({ + target: `${pythPackage}::contract_upgrade::authorize_upgrade`, + arguments: [tx.object(contract.stateId), verificationReceipt as any], + }); + + // Build and generate modules and dependencies for upgrade. + const [upgradeReceipt] = tx.upgrade({ + modules, + dependencies, + package: pythPackage, + ticket: upgradeTicket, + }); + + // Commit upgrade. + tx.moveCall({ + target: `${pythPackage}::contract_upgrade::commit_upgrade`, + arguments: [tx.object(contract.stateId), upgradeReceipt], + }); + + tx.setGasBudget(MIST_PER_SUI / 4n); // 0.25 SUI + + return provider.signAndExecuteTransaction({ + signer: keypair, + transaction: tx, + options: { + showEffects: true, + showEvents: true, + }, + }); +} + +export async function migratePyth( + keypair: Ed25519Keypair, + provider: SuiClient, + signedUpgradeVaa: Buffer, + contract: SuiPriceFeedContract, + pythPackageOld: string +) { + const pythPackage = await contract.getPackageId(contract.stateId); + const tx = new Transaction(); + // The pyth package version is not updated yet, therefore we can not get the verification receipts from the new + // package yet. We need to use the old package id to get the verification receipt in this transaction and then submit + // it to the migrate function in the new package! + const verificationReceipt = await contract.getVaaVerificationReceipt( + tx, + pythPackageOld, + signedUpgradeVaa + ); + tx.moveCall({ + target: `${pythPackage}::migrate::migrate`, + arguments: [tx.object(contract.stateId), verificationReceipt as any], + }); + + tx.setGasBudget(MIST_PER_SUI / 10n); //0.1 SUI + + return provider.signAndExecuteTransaction({ + signer: keypair, + transaction: tx, + options: { + showEffects: true, + showEvents: true, + }, + }); +} diff --git a/target_chains/sui/cli-iota/tsconfig.json b/target_chains/sui/cli-iota/tsconfig.json new file mode 100644 index 0000000000..be27cdded2 --- /dev/null +++ b/target_chains/sui/cli-iota/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../../tsconfig.base.json", + "include": ["src"], + "exclude": ["node_modules", "**/__tests__/*"], + "compilerOptions": { + "rootDir": "src/", + "outDir": "./lib" + } +} diff --git a/target_chains/sui/sdk/js-iota/.eslintrc.js b/target_chains/sui/sdk/js-iota/.eslintrc.js new file mode 100644 index 0000000000..8fa976bb6e --- /dev/null +++ b/target_chains/sui/sdk/js-iota/.eslintrc.js @@ -0,0 +1,9 @@ +module.exports = { + root: true, + parser: "@typescript-eslint/parser", + plugins: ["@typescript-eslint"], + extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + rules: { + "@typescript-eslint/no-explicit-any": "off", + }, +}; diff --git a/target_chains/sui/sdk/js-iota/.gitignore b/target_chains/sui/sdk/js-iota/.gitignore new file mode 100644 index 0000000000..a65b41774a --- /dev/null +++ b/target_chains/sui/sdk/js-iota/.gitignore @@ -0,0 +1 @@ +lib diff --git a/target_chains/sui/sdk/js-iota/README.md b/target_chains/sui/sdk/js-iota/README.md new file mode 100644 index 0000000000..d39cd67b8f --- /dev/null +++ b/target_chains/sui/sdk/js-iota/README.md @@ -0,0 +1,154 @@ +# Pyth Sui JS SDK + +[Pyth](https://pyth.network/) provides real-time pricing data in a variety of asset classes, including cryptocurrency, equities, FX and commodities. This library allows you to use these real-time prices on the [Sui network](https://sui.io/). + +## Installation + +### npm + +``` +$ npm install --save @pythnetwork/pyth-sui-js +``` + +### Yarn + +``` +$ yarn add @pythnetwork/pyth-sui-js +``` + +## Quickstart + +Pyth stores prices off-chain to minimize gas fees, which allows us to offer a wider selection of products and faster update times. +See [On-Demand Updates](https://docs.pyth.network/documentation/pythnet-price-feeds/on-demand) for more information about this approach. +Typically, to use Pyth prices on chain, +they must be fetched from an off-chain Hermes instance. The `SuiPriceServiceConnection` class can be used to interact with these services, +providing a way to fetch these prices directly in your code. The following example wraps an existing RPC provider and shows how to obtain +Pyth prices and submit them to the network: + +```typescript +const connection = new SuiPriceServiceConnection( + "https://hermes-beta.pyth.network" +); // See Hermes endpoints section below for other endpoints + +const priceIds = [ + // You can find the ids of prices at https://pyth.network/developers/price-feed-ids#sui-testnet + "0xf9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b", // BTC/USD price id in testnet + "0xca80ba6dc32e08d06f1aa886011eed1d77c77be9eb761cc10d72b7d0a2fd57a6", // ETH/USD price id in testnet +]; + +// In order to use Pyth prices in your protocol you need to submit the price update data to Pyth contract in your target +// chain. `getPriceUpdateData` creates the update data which can be submitted to your contract. + +const priceUpdateData = await connection.getPriceFeedsUpdateData(priceIds); +``` + +## On-chain prices + +### **_Important Note for Integrators_** + +Your Sui Move module **should NOT** have a hard-coded call to `pyth::update_single_price_feed`. In other words, the Sui Pyth `pyth::update_single_price_feed` entry point should never be called by a contract, instead it should be called directly from client code (e.g. Typescript or Rust). + +This is because when a Sui contract is [upgraded](https://docs.sui.io/build/package-upgrades), the new address is different from the original. If your module has a hard-coded call to `pyth::update_single_price_feed` living at a fixed call-site, it may eventually get bricked due to the way Pyth upgrades are implemented. (We only allows users to interact with the most recent package version for security reasons). + +Therefore, you should build a [Sui programmable transaction](https://docs.sui.io/build/prog-trans-ts-sdk) that first updates the price by calling `pyth::update_single_price_feed` at the latest call-site from the client-side and then call a function in your contract that invokes `pyth::get_price` on the `PriceInfoObject` to get the recently updated price. +You can use `SuiPythClient` to build such transactions. + +### Example + +```ts +import { SuiPythClient } from "@pythnetwork/pyth-sui-js"; +import { Transaction } from "@mysten/sui/transactions"; +import { SuiClient } from "@mysten/sui/client"; + +const priceUpdateData = await connection.getPriceFeedsUpdateData(priceIds); // see quickstart section + + +// It is either injected from browser or instantiated in backend via some private key +const wallet: SignerWithProvider = getWallet(); +// Get the state ids of the Pyth and Wormhole contracts from +// https://docs.pyth.network/documentation/pythnet-price-feeds/sui +const wormholeStateId = " 0xFILL_ME"; +const pythStateId = "0xFILL_ME"; + +const provider = new SuiClient({ url: "https://fill-sui-endpoint" }); +const client = new SuiPythClient(wallet.provider, pythStateId, wormholeStateId); +const tx = new Transaction(); +const priceInfoObjectIds = await client.updatePriceFeeds(tx, priceFeedUpdateData, priceIds); + +tx.moveCall({ + target: `YOUR_PACKAGE::YOUR_MODULE::use_pyth_for_defi`, + arguments: [ + ..., // other arguments needed for your contract + tx.object(pythStateId), + tx.object(priceInfoObjectIds[0]), + ], +}); + +const result = await provider.signAndExecuteTransaction({ + signer: wallet, + transaction: tx, + options: { + showEffects: true, + showEvents: true, + }, +}); +``` + +Now in your contract you can consume the price by calling `pyth::get_price` or other utility functions on the `PriceInfoObject`. + +### CLI Example + +[This example](./src/examples/SuiRelay.ts) shows how to update prices on an Sui network. It does the following: + +1. Fetches update data from Hermes for the given price feeds. +2. Calls the Pyth Sui contract with the update data. + +You can run this example with `npm run example-relay`. A full command that updates prices on Sui testnet looks like: + +```bash +export SUI_KEY=YOUR_PRIV_KEY; +npm run example-relay -- --feed-id "5a035d5440f5c163069af66062bac6c79377bf88396fa27e6067bfca8096d280" \ +--price-service "https://hermes-beta.pyth.network" \ +--full-node "https://fullnode.testnet.sui.io:443" \ +--pyth-state-id "0xd3e79c2c083b934e78b3bd58a490ec6b092561954da6e7322e1e2b3c8abfddc0" \ +--wormhole-state-id "0x31358d198147da50db32eda2562951d53973a0c0ad5ed738e9b17d88b213d790" +``` + +## Off-chain prices + +Many applications additionally need to display Pyth prices off-chain, for example, in their frontend application. +The `SuiPriceServiceConnection` provides two different ways to fetch the current Pyth price. +The code blocks below assume that the `connection` and `priceIds` objects have been initialized as shown above. +The first method is a single-shot query: + +```typescript +// `getLatestPriceFeeds` returns a `PriceFeed` for each price id. It contains all information about a price and has +// utility functions to get the current and exponentially-weighted moving average price, and other functionality. +const priceFeeds = await connection.getLatestPriceFeeds(priceIds); +// Get the price if it is not older than 60 seconds from the current time. +console.log(priceFeeds[0].getPriceNoOlderThan(60)); // Price { conf: '1234', expo: -8, price: '12345678' } +// Get the exponentially-weighted moving average price if it is not older than 60 seconds from the current time. +console.log(priceFeeds[1].getEmaPriceNoOlderThan(60)); +``` + +The object also supports a streaming websocket connection that allows you to subscribe to every new price update for a given feed. +This method is useful if you want to show continuously updating real-time prices in your frontend: + +```typescript +// Subscribe to the price feeds given by `priceId`. The callback will be invoked every time the requested feed +// gets a price update. +connection.subscribePriceFeedUpdates(priceIds, (priceFeed) => { + console.log( + `Received update for ${priceFeed.id}: ${priceFeed.getPriceNoOlderThan(60)}` + ); +}); + +// When using the subscription, make sure to close the websocket upon termination to finish the process gracefully. +setTimeout(() => { + connection.closeWebSocket(); +}, 60000); +``` + +## Hermes endpoints + +You can find the list of Hermes public endpoints [here](https://docs.pyth.network/documentation/pythnet-price-feeds/hermes#public-endpoints). diff --git a/target_chains/sui/sdk/js-iota/jest.config.js b/target_chains/sui/sdk/js-iota/jest.config.js new file mode 100644 index 0000000000..21a1e973ab --- /dev/null +++ b/target_chains/sui/sdk/js-iota/jest.config.js @@ -0,0 +1,5 @@ +/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */ +module.exports = { + preset: "ts-jest", + testEnvironment: "node", +}; diff --git a/target_chains/sui/sdk/js-iota/package.json b/target_chains/sui/sdk/js-iota/package.json new file mode 100644 index 0000000000..21f06182ae --- /dev/null +++ b/target_chains/sui/sdk/js-iota/package.json @@ -0,0 +1,59 @@ +{ + "name": "@pythnetwork/pyth-iota-js", + "version": "2.1.0", + "description": "Pyth Network Sui Utilities", + "homepage": "https://pyth.network", + "author": { + "name": "Pyth Data Association" + }, + "main": "lib/index.js", + "types": "lib/index.d.ts", + "files": [ + "lib/**/*" + ], + "repository": { + "type": "git", + "url": "https://github.com/pyth-network/pyth-crosschain", + "directory": "target_chains/sui/sdk/js" + }, + "publishConfig": { + "access": "public" + }, + "scripts": { + "build": "tsc", + "example-relay": "pnpm run build && node lib/examples/SuiRelay.js", + "format": "prettier --write \"src/**/*.ts\"", + "test:lint": "eslint src/", + "prepublishOnly": "pnpm run build && pnpm test:lint", + "preversion": "pnpm run test:lint", + "version": "pnpm run format && git add -A src" + }, + "keywords": [ + "pyth", + "oracle", + "sui" + ], + "license": "Apache-2.0", + "devDependencies": { + "@truffle/hdwallet-provider": "^2.1.5", + "@types/ethereum-protocol": "^1.0.2", + "@types/jest": "^29.4.0", + "@types/node": "^18.11.18", + "@types/web3-provider-engine": "^14.0.1", + "@types/yargs": "^17.0.20", + "@typescript-eslint/eslint-plugin": "^6.0.0", + "@typescript-eslint/parser": "^6.0.0", + "eslint": "^8.14.0", + "jest": "^29.4.1", + "prettier": "^2.6.2", + "ts-jest": "^29.0.5", + "typescript": "^5.3.3", + "web3": "^1.8.2", + "yargs": "^17.0.20" + }, + "dependencies": { + "@mysten/sui": "^1.3.0", + "@pythnetwork/price-service-client": "workspace:*", + "buffer": "^6.0.3" + } +} diff --git a/target_chains/sui/sdk/js-iota/src/SuiPriceServiceConnection.ts b/target_chains/sui/sdk/js-iota/src/SuiPriceServiceConnection.ts new file mode 100644 index 0000000000..6d520cf3e3 --- /dev/null +++ b/target_chains/sui/sdk/js-iota/src/SuiPriceServiceConnection.ts @@ -0,0 +1,21 @@ +import { + PriceServiceConnection, + HexString, +} from "@pythnetwork/price-service-client"; +import { Buffer } from "buffer"; + +export class SuiPriceServiceConnection extends PriceServiceConnection { + /** + * Gets price update data (either batch price attestation VAAs or accumulator messages, depending on the chosen endpoint), which then + * can be submitted to the Pyth contract to update the prices. This will throw an axios error if there is a network problem or + * the price service returns a non-ok response (e.g: Invalid price ids) + * + * @param priceIds Array of hex-encoded price ids. + * @returns Array of buffers containing the price update data. + */ + async getPriceFeedsUpdateData(priceIds: HexString[]): Promise { + // Fetch the latest price feed update VAAs from the price service + const latestVaas = await this.getLatestVaas(priceIds); + return latestVaas.map((vaa) => Buffer.from(vaa, "base64")); + } +} diff --git a/target_chains/sui/sdk/js-iota/src/client.ts b/target_chains/sui/sdk/js-iota/src/client.ts new file mode 100644 index 0000000000..f65e2e21aa --- /dev/null +++ b/target_chains/sui/sdk/js-iota/src/client.ts @@ -0,0 +1,304 @@ +import { SuiClient } from "@mysten/sui/client"; +import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui/utils"; +import { Transaction } from "@mysten/sui/transactions"; +import { bcs } from "@mysten/sui/bcs"; +import { HexString } from "@pythnetwork/price-service-client"; +import { Buffer } from "buffer"; + +const MAX_ARGUMENT_SIZE = 16 * 1024; +export type ObjectId = string; + +export class SuiPythClient { + private pythPackageId: ObjectId | undefined; + private wormholePackageId: ObjectId | undefined; + private priceTableInfo: { id: ObjectId; fieldType: ObjectId } | undefined; + private priceFeedObjectIdCache: Map = new Map(); + private baseUpdateFee: number | undefined; + constructor( + public provider: SuiClient, + public pythStateId: ObjectId, + public wormholeStateId: ObjectId + ) { + this.pythPackageId = undefined; + this.wormholePackageId = undefined; + } + + async getBaseUpdateFee(): Promise { + if (this.baseUpdateFee === undefined) { + const result = await this.provider.getObject({ + id: this.pythStateId, + options: { showContent: true }, + }); + if ( + !result.data || + !result.data.content || + result.data.content.dataType !== "moveObject" + ) + throw new Error("Unable to fetch pyth state object"); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + this.baseUpdateFee = result.data.content.fields.base_update_fee as number; + } + + return this.baseUpdateFee; + } + + /** + * getPackageId returns the latest package id that the object belongs to. Use this to + * fetch the latest package id for a given object id and handle package upgrades automatically. + * @param objectId + * @returns package id + */ + async getPackageId(objectId: ObjectId): Promise { + const state = await this.provider + .getObject({ + id: objectId, + options: { + showContent: true, + }, + }) + .then((result) => { + if (result.data?.content?.dataType == "moveObject") { + return result.data.content.fields; + } + console.log(result.data?.content); + + throw new Error(`Cannot fetch package id for object ${objectId}`); + }); + + if ("upgrade_cap" in state) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return state.upgrade_cap.fields.package; + } + + throw new Error("upgrade_cap not found"); + } + + /** + * Adds the commands for calling wormhole and verifying the vaas and returns the verified vaas. + * @param vaas array of vaas to verify + * @param tx transaction block to add commands to + */ + async verifyVaas(vaas: Buffer[], tx: Transaction) { + const wormholePackageId = await this.getWormholePackageId(); + const verifiedVaas = []; + for (const vaa of vaas) { + const [verifiedVaa] = tx.moveCall({ + target: `${wormholePackageId}::vaa::parse_and_verify`, + arguments: [ + tx.object(this.wormholeStateId), + tx.pure( + bcs + .vector(bcs.U8) + .serialize(Array.from(vaa), { + maxSize: MAX_ARGUMENT_SIZE, + }) + .toBytes() + ), + tx.object(SUI_CLOCK_OBJECT_ID), + ], + }); + verifiedVaas.push(verifiedVaa); + } + return verifiedVaas; + } + + /** + * Adds the necessary commands for updating the pyth price feeds to the transaction block. + * @param tx transaction block to add commands to + * @param updates array of price feed updates received from the price service + * @param feedIds array of feed ids to update (in hex format) + */ + async updatePriceFeeds( + tx: Transaction, + updates: Buffer[], + feedIds: HexString[] + ): Promise { + const packageId = await this.getPythPackageId(); + + let priceUpdatesHotPotato; + if (updates.length > 1) { + throw new Error( + "SDK does not support sending multiple accumulator messages in a single transaction" + ); + } + const vaa = this.extractVaaBytesFromAccumulatorMessage(updates[0]); + const verifiedVaas = await this.verifyVaas([vaa], tx); + [priceUpdatesHotPotato] = tx.moveCall({ + target: `${packageId}::pyth::create_authenticated_price_infos_using_accumulator`, + arguments: [ + tx.object(this.pythStateId), + tx.pure( + bcs + .vector(bcs.U8) + .serialize(Array.from(updates[0]), { + maxSize: MAX_ARGUMENT_SIZE, + }) + .toBytes() + ), + verifiedVaas[0], + tx.object(SUI_CLOCK_OBJECT_ID), + ], + }); + + const priceInfoObjects: ObjectId[] = []; + const baseUpdateFee = await this.getBaseUpdateFee(); + const coins = tx.splitCoins( + tx.gas, + feedIds.map(() => tx.pure.u64(baseUpdateFee)) + ); + let coinId = 0; + for (const feedId of feedIds) { + const priceInfoObjectId = await this.getPriceFeedObjectId(feedId); + if (!priceInfoObjectId) { + throw new Error( + `Price feed ${feedId} not found, please create it first` + ); + } + priceInfoObjects.push(priceInfoObjectId); + [priceUpdatesHotPotato] = tx.moveCall({ + target: `${packageId}::pyth::update_single_price_feed`, + arguments: [ + tx.object(this.pythStateId), + priceUpdatesHotPotato, + tx.object(priceInfoObjectId), + coins[coinId], + tx.object(SUI_CLOCK_OBJECT_ID), + ], + }); + coinId++; + } + tx.moveCall({ + target: `${packageId}::hot_potato_vector::destroy`, + arguments: [priceUpdatesHotPotato], + typeArguments: [`${packageId}::price_info::PriceInfo`], + }); + return priceInfoObjects; + } + async createPriceFeed(tx: Transaction, updates: Buffer[]) { + const packageId = await this.getPythPackageId(); + if (updates.length > 1) { + throw new Error( + "SDK does not support sending multiple accumulator messages in a single transaction" + ); + } + const vaa = this.extractVaaBytesFromAccumulatorMessage(updates[0]); + const verifiedVaas = await this.verifyVaas([vaa], tx); + tx.moveCall({ + target: `${packageId}::pyth::create_price_feeds_using_accumulator`, + arguments: [ + tx.object(this.pythStateId), + tx.pure( + bcs + .vector(bcs.U8) + .serialize(Array.from(updates[0]), { + maxSize: MAX_ARGUMENT_SIZE, + }) + .toBytes() + ), + verifiedVaas[0], + tx.object(SUI_CLOCK_OBJECT_ID), + ], + }); + } + + /** + * Get the packageId for the wormhole package if not already cached + */ + async getWormholePackageId() { + if (!this.wormholePackageId) { + this.wormholePackageId = await this.getPackageId(this.wormholeStateId); + } + return this.wormholePackageId; + } + + /** + * Get the packageId for the pyth package if not already cached + */ + async getPythPackageId() { + if (!this.pythPackageId) { + this.pythPackageId = await this.getPackageId(this.pythStateId); + } + return this.pythPackageId; + } + + /** + * Get the priceFeedObjectId for a given feedId if not already cached + * @param feedId + */ + async getPriceFeedObjectId(feedId: HexString): Promise { + const normalizedFeedId = feedId.replace("0x", ""); + if (!this.priceFeedObjectIdCache.has(normalizedFeedId)) { + const { id: tableId, fieldType } = await this.getPriceTableInfo(); + const result = await this.provider.getDynamicFieldObject({ + parentId: tableId, + name: { + type: `${fieldType}::price_identifier::PriceIdentifier`, + value: { + bytes: Array.from(Buffer.from(normalizedFeedId, "hex")), + }, + }, + }); + if (!result.data || !result.data.content) { + return undefined; + } + if (result.data.content.dataType !== "moveObject") { + throw new Error("Price feed type mismatch"); + } + this.priceFeedObjectIdCache.set( + normalizedFeedId, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + result.data.content.fields.value + ); + } + return this.priceFeedObjectIdCache.get(normalizedFeedId); + } + + /** + * Fetches the price table object id for the current state id if not cached + * @returns price table object id + */ + async getPriceTableInfo(): Promise<{ id: ObjectId; fieldType: ObjectId }> { + if (this.priceTableInfo === undefined) { + const result = await this.provider.getDynamicFieldObject({ + parentId: this.pythStateId, + name: { + type: "vector", + value: "price_info", + }, + }); + if (!result.data || !result.data.type) { + throw new Error( + "Price Table not found, contract may not be initialized" + ); + } + let type = result.data.type.replace("0x2::table::Table<", ""); + type = type.replace( + "::price_identifier::PriceIdentifier, 0x2::object::ID>", + "" + ); + this.priceTableInfo = { id: result.data.objectId, fieldType: type }; + } + return this.priceTableInfo; + } + + /** + * Obtains the vaa bytes embedded in an accumulator message. + * @param accumulatorMessage - the accumulator price update message + * @returns vaa bytes as a uint8 array + */ + extractVaaBytesFromAccumulatorMessage(accumulatorMessage: Buffer): Buffer { + // the first 6 bytes in the accumulator message encode the header, major, and minor bytes + // we ignore them, since we are only interested in the VAA bytes + const trailingPayloadSize = accumulatorMessage.readUint8(6); + const vaaSizeOffset = + 7 + // header bytes (header(4) + major(1) + minor(1) + trailing payload size(1)) + trailingPayloadSize + // trailing payload (variable number of bytes) + 1; // proof_type (1 byte) + const vaaSize = accumulatorMessage.readUint16BE(vaaSizeOffset); + const vaaOffset = vaaSizeOffset + 2; + return accumulatorMessage.subarray(vaaOffset, vaaOffset + vaaSize); + } +} diff --git a/target_chains/sui/sdk/js-iota/src/examples/SuiRelay.ts b/target_chains/sui/sdk/js-iota/src/examples/SuiRelay.ts new file mode 100644 index 0000000000..cb40f4b1e5 --- /dev/null +++ b/target_chains/sui/sdk/js-iota/src/examples/SuiRelay.ts @@ -0,0 +1,97 @@ +import yargs from "yargs"; +import { hideBin } from "yargs/helpers"; +import { SuiClient } from "@mysten/sui/client"; +import { Transaction } from "@mysten/sui/transactions"; +import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519"; + +import { Buffer } from "buffer"; +import { SuiPythClient } from "../client"; +import { SuiPriceServiceConnection } from "../index"; + +const argvPromise = yargs(hideBin(process.argv)) + .option("feed-id", { + description: + "Price feed ids to update without the leading 0x (e.g f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b). Can be provided multiple times for multiple feed updates", + type: "array", + demandOption: true, + }) + .option("hermes", { + description: "Endpoint URL for Hermes. e.g: https://hermes.pyth.network", + type: "string", + demandOption: true, + }) + .option("full-node", { + description: + "URL of the full Sui node RPC endpoint. e.g: https://fullnode.testnet.sui.io:443", + type: "string", + demandOption: true, + }) + .option("pyth-state-id", { + description: "Pyth state object id.", + type: "string", + demandOption: true, + }) + .option("wormhole-state-id", { + description: "Wormhole state object id.", + type: "string", + demandOption: true, + }).argv; + +export function getProvider(url: string) { + return new SuiClient({ url }); +} +async function run() { + if (process.env.SUI_KEY === undefined) { + throw new Error(`SUI_KEY environment variable should be set.`); + } + + const argv = await argvPromise; + + // Fetch the latest price feed update data from the Price Service + const connection = new SuiPriceServiceConnection(argv["hermes"]); + const feeds = argv["feed-id"] as string[]; + + const provider = getProvider(argv["full-node"]); + const wormholeStateId = argv["wormhole-state-id"]; + const pythStateId = argv["pyth-state-id"]; + + const client = new SuiPythClient(provider, pythStateId, wormholeStateId); + const newFeeds = []; + const existingFeeds = []; + for (const feed of feeds) { + if ((await client.getPriceFeedObjectId(feed)) == undefined) { + newFeeds.push(feed); + } else { + existingFeeds.push(feed); + } + } + console.log({ + newFeeds, + existingFeeds, + }); + const tx = new Transaction(); + if (existingFeeds.length > 0) { + const updateData = await connection.getPriceFeedsUpdateData(existingFeeds); + await client.updatePriceFeeds(tx, updateData, existingFeeds); + } + if (newFeeds.length > 0) { + const updateData = await connection.getPriceFeedsUpdateData(newFeeds); + await client.createPriceFeed(tx, updateData); + } + + const wallet = Ed25519Keypair.fromSecretKey( + Buffer.from(process.env.SUI_KEY, "hex") + ); + + const result = await provider.signAndExecuteTransaction({ + signer: wallet, + transaction: tx, + options: { + showEffects: true, + showEvents: true, + }, + }); + console.dir(result, { depth: null }); +} + +run(); diff --git a/target_chains/sui/sdk/js-iota/src/index.ts b/target_chains/sui/sdk/js-iota/src/index.ts new file mode 100644 index 0000000000..f4cfecf948 --- /dev/null +++ b/target_chains/sui/sdk/js-iota/src/index.ts @@ -0,0 +1,11 @@ +export { SuiPriceServiceConnection } from "./SuiPriceServiceConnection"; +export { SuiPythClient } from "./client"; + +export { + DurationInMs, + HexString, + Price, + PriceFeed, + PriceServiceConnectionConfig, + UnixTimestamp, +} from "@pythnetwork/price-service-client"; diff --git a/target_chains/sui/sdk/js-iota/tsconfig.json b/target_chains/sui/sdk/js-iota/tsconfig.json new file mode 100644 index 0000000000..927049ab35 --- /dev/null +++ b/target_chains/sui/sdk/js-iota/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "target": "esnext", + "module": "commonjs", + "declaration": true, + "outDir": "./lib", + "rootDir": "src/", + "strict": true, + "esModuleInterop": true + }, + "include": ["src"], + "exclude": ["node_modules", "**/__tests__/*"] +} From b1d7f3c8c6e1b5fb4e6bf4a852c4997f8ffd790b Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Thu, 27 Feb 2025 13:16:02 +0100 Subject: [PATCH 04/10] chore(target_chains/sui): update iota_testnet move.toml and use iota libs --- .../examples/coins/sources/coin.move | 14 +++---- .../examples/coins/sources/coin_10.move | 8 ++-- .../examples/coins/sources/coin_8.move | 8 ++-- .../core_messages/sources/sender.move | 12 +++--- .../templates/wrapped_coin/sources/coin.move | 4 +- .../token_bridge/sources/attest_token.move | 10 ++--- .../sources/complete_transfer.move | 24 +++++------ .../complete_transfer_with_payload.move | 16 ++++---- .../token_bridge/sources/create_wrapped.move | 26 ++++++------ .../sources/datatypes/normalized_amount.move | 2 +- .../sources/governance/register_chain.move | 6 +-- .../sources/governance/upgrade_contract.move | 14 +++---- .../sources/messages/asset_meta.move | 2 +- .../messages/transfer_with_payload.move | 4 +- .../token_bridge/sources/migrate.move | 6 +-- .../sources/resources/native_asset.move | 20 +++++----- .../sources/resources/token_registry.move | 34 ++++++++-------- .../sources/resources/wrapped_asset.move | 28 ++++++------- .../token_bridge/sources/setup.move | 12 +++--- .../token_bridge/sources/state.move | 14 +++---- .../sources/test/coin_native_10.move | 12 +++--- .../sources/test/coin_native_4.move | 12 +++--- .../sources/test/coin_wrapped_12.move | 12 +++--- .../sources/test/coin_wrapped_7.move | 12 +++--- .../sources/test/token_bridge_scenario.move | 6 +-- .../token_bridge/sources/transfer_tokens.move | 18 ++++----- .../sources/transfer_tokens_with_payload.move | 16 ++++---- .../sources/utils/coin_utils.move | 8 ++-- .../sources/utils/string_utils.move | 2 +- .../token_bridge/sources/vaa.move | 4 +- .../token_bridge/sources/version_control.move | 2 +- .../wormhole_iota_testnet/wormhole/Move.lock | 30 +++++++++----- .../wormhole_iota_testnet/wormhole/Move.toml | 12 +++--- .../wormhole/sources/datatypes/bytes32.move | 6 +-- .../sources/datatypes/external_address.move | 4 +- .../wormhole/sources/emitter.move | 16 ++++---- .../wormhole/sources/governance/set_fee.move | 10 ++--- .../sources/governance/transfer_fee.move | 24 +++++------ .../governance/update_guardian_set.move | 8 ++-- .../sources/governance/upgrade_contract.move | 14 +++---- .../wormhole/sources/governance_message.move | 10 ++--- .../wormhole/sources/migrate.move | 8 ++-- .../wormhole/sources/publish_message.move | 26 ++++++------ .../sources/resources/consumed_vaas.move | 2 +- .../sources/resources/fee_collector.move | 40 +++++++++---------- .../wormhole/sources/resources/guardian.move | 4 +- .../sources/resources/guardian_set.move | 2 +- .../wormhole/sources/resources/set.move | 8 ++-- .../wormhole/sources/setup.move | 16 ++++---- .../wormhole/sources/state.move | 30 +++++++------- .../sources/test/wormhole_scenario.move | 6 +-- .../wormhole/sources/utils/package_utils.move | 20 +++++----- .../wormhole/sources/vaa.move | 12 +++--- .../wormhole/sources/version_control.move | 2 +- 54 files changed, 343 insertions(+), 335 deletions(-) diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin.move b/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin.move index 108ebe52d6..9992667e29 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin.move @@ -2,10 +2,10 @@ #[test_only] module coins::coin { - use sui::object::{Self}; - use sui::package::{Self}; - use sui::transfer::{Self}; - use sui::tx_context::{Self, TxContext}; + use iota::object::{Self}; + use iota::package::{Self}; + use iota::transfer::{Self}; + use iota::tx_context::{Self, TxContext}; use token_bridge::create_wrapped::{Self}; @@ -43,9 +43,9 @@ module coins::coin { #[test_only] module coins::coin_tests { - use sui::coin::{Self}; - use sui::package::{UpgradeCap}; - use sui::test_scenario::{Self}; + use iota::coin::{Self}; + use iota::package::{UpgradeCap}; + use iota::test_scenario::{Self}; use token_bridge::create_wrapped::{Self, WrappedAssetSetup}; use token_bridge::state::{Self}; use token_bridge::token_bridge_scenario::{ diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_10.move b/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_10.move index 2c98b87936..02fa816d27 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_10.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_10.move @@ -1,8 +1,8 @@ module coins::coin_10 { use std::option; - use sui::coin::{Self, TreasuryCap, CoinMetadata}; - use sui::transfer; - use sui::tx_context::{Self, TxContext}; + use iota::coin::{Self, TreasuryCap, CoinMetadata}; + use iota::transfer; + use iota::tx_context::{Self, TxContext}; /// The type identifier of coin. The coin will have a type /// tag of kind: `Coin` @@ -47,7 +47,7 @@ module coins::coin_10 { #[test_only] module coins::coin_10_tests { - use sui::test_scenario::{Self}; + use iota::test_scenario::{Self}; use coins::coin_10::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_8.move b/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_8.move index 0edd761603..26bbb35819 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_8.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/coins/sources/coin_8.move @@ -1,8 +1,8 @@ module coins::coin_8 { use std::option::{Self}; - use sui::coin::{Self, TreasuryCap, CoinMetadata}; - use sui::transfer::{Self}; - use sui::tx_context::{Self, TxContext}; + use iota::coin::{Self, TreasuryCap, CoinMetadata}; + use iota::transfer::{Self}; + use iota::tx_context::{Self, TxContext}; /// The type identifier of coin. The coin will have a type /// tag of kind: `Coin` @@ -47,7 +47,7 @@ module coins::coin_8 { #[test_only] module coins::coin_8_tests { - use sui::test_scenario::{Self}; + use iota::test_scenario::{Self}; use coins::coin_8::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/sources/sender.move b/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/sources/sender.move index 960c6355c3..2f26f009f4 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/sources/sender.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/core_messages/sources/sender.move @@ -1,10 +1,10 @@ /// A simple contracts that demonstrates how to send messages with wormhole. module core_messages::sender { - use sui::clock::{Clock}; - use sui::coin::{Self}; - use sui::object::{Self, UID}; - use sui::transfer::{Self}; - use sui::tx_context::{TxContext}; + use iota::clock::{Clock}; + use iota::coin::{Self}; + use iota::object::{Self, UID}; + use iota::transfer::{Self}; + use iota::tx_context::{TxContext}; use wormhole::emitter::{Self, EmitterCap}; use wormhole::state::{State as WormholeState}; @@ -74,7 +74,7 @@ module core_messages::sender { #[test_only] module core_messages::sender_test { - use sui::test_scenario::{Self}; + use iota::test_scenario::{Self}; use wormhole::wormhole_scenario::{ return_clock, return_state, diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/sources/coin.move b/target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/sources/coin.move index 313b9ba919..096432f03e 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/sources/coin.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/sources/coin.move @@ -1,6 +1,6 @@ module wrapped_coin::coin { - use sui::transfer::{Self}; - use sui::tx_context::{Self, TxContext}; + use iota::transfer::{Self}; + use iota::tx_context::{Self, TxContext}; use token_bridge::create_wrapped::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/attest_token.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/attest_token.move index c5e67d1670..16134c5c76 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/attest_token.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/attest_token.move @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache 2 /// This module implements the method `attest_token` which allows someone -/// to send asset metadata of a coin type native to Sui. Part of this process +/// to send asset metadata of a coin type native to Iota. Part of this process /// is registering this asset in the `TokenRegistry`. /// /// NOTE: If an asset has not been attested for, it cannot be bridged using @@ -10,7 +10,7 @@ /// See `asset_meta` module for serialization and deserialization of Wormhole /// message payload. module token_bridge::attest_token { - use sui::coin::{CoinMetadata}; + use iota::coin::{CoinMetadata}; use wormhole::publish_message::{MessageTicket}; use token_bridge::asset_meta::{Self}; @@ -26,7 +26,7 @@ module token_bridge::attest_token { /// `attest_token` takes `CoinMetadata` of a coin type and generates a /// `MessageTicket` with encoded asset metadata for a foreign Token Bridge - /// contract to consume and create a wrapped asset reflecting this Sui + /// contract to consume and create a wrapped asset reflecting this Iota /// asset. Asset metadata is encoded using `AssetMeta`. /// /// See `token_registry` and `asset_meta` module for more info. @@ -110,8 +110,8 @@ module token_bridge::attest_token { module token_bridge::attest_token_tests { use std::ascii::{Self}; use std::string::{Self}; - use sui::coin::{Self}; - use sui::test_scenario::{Self}; + use iota::coin::{Self}; + use iota::test_scenario::{Self}; use wormhole::publish_message::{Self}; use wormhole::state::{chain_id}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer.move index c9086e7dbc..ae58d044f6 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer.move @@ -28,9 +28,9 @@ /// See `transfer` module for serialization and deserialization of Wormhole /// message payload. module token_bridge::complete_transfer { - use sui::balance::{Self, Balance}; - use sui::coin::{Self, Coin}; - use sui::tx_context::{Self, TxContext}; + use iota::balance::{Self, Balance}; + use iota::coin::{Self, Coin}; + use iota::tx_context::{Self, TxContext}; use wormhole::external_address::{Self, ExternalAddress}; use token_bridge::native_asset::{Self}; @@ -44,7 +44,7 @@ module token_bridge::complete_transfer { // Requires `handle_complete_transfer`. friend token_bridge::complete_transfer_with_payload; - /// Transfer not intended to be received on Sui. + /// Transfer not intended to be received on Iota. const E_TARGET_NOT_SUI: u64 = 0; /// Input token info does not match registered info. const E_CANONICAL_TOKEN_INFO_MISMATCH: u64 = 1; @@ -133,7 +133,7 @@ module token_bridge::complete_transfer { /// wrapped asset or withdraws balance from native asset custody. /// /// Depending on whether this coin is a Token Bridge wrapped asset or a - /// natively existing asset on Sui, the coin is either minted or withdrawn + /// natively existing asset on Iota, the coin is either minted or withdrawn /// from Token Bridge's custody. public(friend) fun verify_and_bridge_out( latest_only: &LatestOnly, @@ -146,7 +146,7 @@ module token_bridge::complete_transfer { VerifiedAsset, Balance ) { - // Verify that the intended chain ID for this transfer is for Sui. + // Verify that the intended chain ID for this transfer is for Iota. assert!( target_chain == wormhole::state::chain_id(), E_TARGET_NOT_SUI @@ -197,8 +197,8 @@ module token_bridge::complete_transfer { public(friend) fun emit_transfer_redeemed(msg: &TokenBridgeMessage): u16 { let emitter_chain = vaa::emitter_chain(msg); - // Emit Sui event with `TransferRedeemed`. - sui::event::emit( + // Emit Iota event with `TransferRedeemed`. + iota::event::emit( TransferRedeemed { emitter_chain, emitter_address: vaa::emitter_address(msg), @@ -257,7 +257,7 @@ module token_bridge::complete_transfer { }; // Transfer tokens to the recipient. - sui::transfer::public_transfer( + iota::transfer::public_transfer( coin::from_balance(bridged_out, ctx), recipient ); @@ -277,8 +277,8 @@ module token_bridge::complete_transfer { #[test_only] module token_bridge::complete_transfer_tests { - use sui::coin::{Self, Coin}; - use sui::test_scenario::{Self}; + use iota::coin::{Self, Coin}; + use iota::test_scenario::{Self}; use wormhole::state::{chain_id}; use wormhole::wormhole_scenario::{parse_and_verify_vaa}; @@ -1148,7 +1148,7 @@ module token_bridge::complete_transfer_tests { test_scenario::next_tx(scenario, tx_relayer); // NOTE: this call should revert since the target chain encoded is - // chain 69 instead of chain 21 (Sui). + // chain 69 instead of chain 21 (Iota). let receipt = authorize_transfer( &mut token_bridge_state, diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer_with_payload.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer_with_payload.move index ba35a7a9e8..4489e82945 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer_with_payload.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer_with_payload.move @@ -32,9 +32,9 @@ /// See `transfer_with_payload` module for serialization and deserialization of /// Wormhole message payload. module token_bridge::complete_transfer_with_payload { - use sui::coin::{Self, Coin}; - use sui::object::{Self}; - use sui::tx_context::{TxContext}; + use iota::coin::{Self, Coin}; + use iota::object::{Self}; + use iota::tx_context::{TxContext}; use wormhole::emitter::{EmitterCap}; use token_bridge::complete_transfer::{Self}; @@ -183,9 +183,9 @@ module token_bridge::complete_transfer_with_payload { #[test_only] module token_bridge::complete_transfer_with_payload_tests { - use sui::coin::{Self}; - use sui::object::{Self}; - use sui::test_scenario::{Self}; + use iota::coin::{Self}; + use iota::object::{Self}; + use iota::test_scenario::{Self}; use wormhole::emitter::{Self}; use wormhole::state::{chain_id}; use wormhole::wormhole_scenario::{new_emitter, parse_and_verify_vaa}; @@ -229,7 +229,7 @@ module token_bridge::complete_transfer_with_payload_tests { let wormhole_fee = 350; set_up_wormhole_and_token_bridge(scenario, wormhole_fee); - // Register Sui as a foreign emitter. + // Register Iota as a foreign emitter. let expected_source_chain = 2; register_dummy_emitter(scenario, expected_source_chain); @@ -710,7 +710,7 @@ module token_bridge::complete_transfer_with_payload_tests { let wormhole_fee = 350; set_up_wormhole_and_token_bridge(scenario, wormhole_fee); - // Register Sui as a foreign emitter. + // Register Iota as a foreign emitter. let expected_source_chain = 2; register_dummy_emitter(scenario, expected_source_chain); diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/create_wrapped.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/create_wrapped.move index 639d170373..914fa3dfaa 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/create_wrapped.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/create_wrapped.move @@ -27,16 +27,16 @@ /// See `state` and `wrapped_asset` modules for more details. /// /// References: -/// https://examples.sui.io/basics/one-time-witness.html +/// https://examples.iota.io/basics/one-time-witness.html module token_bridge::create_wrapped { use std::ascii::{Self}; use std::option::{Self}; use std::type_name::{Self}; - use sui::coin::{Self, TreasuryCap, CoinMetadata}; - use sui::object::{Self, UID}; - use sui::package::{UpgradeCap}; - use sui::transfer::{Self}; - use sui::tx_context::{TxContext}; + use iota::coin::{Self, TreasuryCap, CoinMetadata}; + use iota::object::{Self, UID}; + use iota::package::{UpgradeCap}; + use iota::transfer::{Self}; + use iota::tx_context::{TxContext}; use token_bridge::asset_meta::{Self}; use token_bridge::normalized_amount::{max_decimals}; @@ -102,7 +102,7 @@ module token_bridge::create_wrapped { // resembles the same check for `coin::create_currency`. // Technically this check is redundant as it's performed by // `coin::create_currency` below, but it doesn't hurt. - assert!(sui::types::is_one_time_witness(&witness), E_BAD_WITNESS); + assert!(iota::types::is_one_time_witness(&witness), E_BAD_WITNESS); // Ensure that the decimals passed into this method do not exceed max // decimals (see `normalized_amount` module). @@ -174,7 +174,7 @@ module token_bridge::create_wrapped { // `register_wrapped_asset` uses `token_registry::add_new_wrapped`, // which will check whether the asset has already been registered and if - // the token chain ID is not Sui's. + // the token chain ID is not Iota's. // // If both of these conditions are met, `register_wrapped_asset` will // succeed and the new wrapped coin will be registered. @@ -242,7 +242,7 @@ module token_bridge::create_wrapped { ); let upgrade_cap = - sui::package::test_publish( + iota::package::test_publish( object::id_from_address(@token_bridge), ctx ); @@ -280,10 +280,10 @@ module token_bridge::create_wrapped { #[test_only] module token_bridge::create_wrapped_tests { - use sui::coin::{Self}; - use sui::test_scenario::{Self}; - use sui::test_utils::{Self}; - use sui::tx_context::{Self}; + use iota::coin::{Self}; + use iota::test_scenario::{Self}; + use iota::test_utils::{Self}; + use iota::tx_context::{Self}; use wormhole::wormhole_scenario::{parse_and_verify_vaa}; use token_bridge::asset_meta::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/datatypes/normalized_amount.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/datatypes/normalized_amount.move index e63d3d296b..c253b57ab4 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/datatypes/normalized_amount.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/datatypes/normalized_amount.move @@ -8,7 +8,7 @@ /// For inbound transfers, this amount will be denormalized (scaled by the same /// decimal difference). module token_bridge::normalized_amount { - use sui::math::{Self}; + use iota::math::{Self}; use wormhole::bytes32::{Self}; use wormhole::cursor::{Cursor}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/register_chain.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/register_chain.move index 90af5c1fbe..2255842ae6 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/register_chain.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/register_chain.move @@ -3,7 +3,7 @@ /// This module implements handling a governance VAA to enact registering a /// foreign Token Bridge for a particular chain ID. module token_bridge::register_chain { - use sui::table::{Self}; + use iota::table::{Self}; use wormhole::bytes::{Self}; use wormhole::cursor::{Self}; use wormhole::external_address::{Self, ExternalAddress}; @@ -143,8 +143,8 @@ module token_bridge::register_chain { #[test_only] module token_bridge::register_chain_tests { - use sui::table::{Self}; - use sui::test_scenario::{Self}; + use iota::table::{Self}; + use iota::test_scenario::{Self}; use wormhole::bytes::{Self}; use wormhole::cursor::{Self}; use wormhole::external_address::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/upgrade_contract.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/upgrade_contract.move index e03729b021..9ac95e13de 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/upgrade_contract.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/governance/upgrade_contract.move @@ -8,8 +8,8 @@ /// 3. Upgrade. /// 4. Commit upgrade. module token_bridge::upgrade_contract { - use sui::object::{ID}; - use sui::package::{UpgradeReceipt, UpgradeTicket}; + use iota::object::{ID}; + use iota::package::{UpgradeReceipt, UpgradeTicket}; use wormhole::bytes32::{Self, Bytes32}; use wormhole::cursor::{Self}; use wormhole::governance_message::{Self, DecreeTicket, DecreeReceipt}; @@ -50,15 +50,15 @@ module token_bridge::upgrade_contract { } /// Redeem governance VAA to issue an `UpgradeTicket` for the upgrade given - /// a contract upgrade VAA. This governance message is only relevant for Sui + /// a contract upgrade VAA. This governance message is only relevant for Iota /// because a contract upgrade is only relevant to one particular network - /// (in this case Sui), whose build digest is encoded in this message. + /// (in this case Iota), whose build digest is encoded in this message. public fun authorize_upgrade( token_bridge_state: &mut State, receipt: DecreeReceipt ): UpgradeTicket { // current package checking when consuming VAA hashes. This is because - // upgrades are protected by the Sui VM, enforcing the latest package + // upgrades are protected by the Iota VM, enforcing the latest package // is the one performing the upgrade. let consumed = state::borrow_mut_consumed_vaas_unchecked(token_bridge_state); @@ -72,7 +72,7 @@ module token_bridge::upgrade_contract { /// Finalize the upgrade that ran to produce the given `receipt`. This /// method invokes `state::commit_upgrade` which interacts with - /// `sui::package`. + /// `iota::package`. public fun commit_upgrade( self: &mut State, receipt: UpgradeReceipt, @@ -80,7 +80,7 @@ module token_bridge::upgrade_contract { let (old_contract, new_contract) = state::commit_upgrade(self, receipt); // Emit an event reflecting package ID change. - sui::event::emit(ContractUpgraded { old_contract, new_contract }); + iota::event::emit(ContractUpgraded { old_contract, new_contract }); } /// Privileged method only to be used by this module and `migrate` module. diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/asset_meta.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/asset_meta.move index 30f03f2cde..df28384cea 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/asset_meta.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/asset_meta.move @@ -5,7 +5,7 @@ module token_bridge::asset_meta { use std::string::{Self, String}; use std::vector::{Self}; - use sui::coin::{Self, CoinMetadata}; + use iota::coin::{Self, CoinMetadata}; use wormhole::bytes::{Self}; use wormhole::bytes32::{Self}; use wormhole::external_address::{Self, ExternalAddress}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer_with_payload.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer_with_payload.move index 3180616872..9c00a36385 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer_with_payload.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer_with_payload.move @@ -11,7 +11,7 @@ /// modules for more details. module token_bridge::transfer_with_payload { use std::vector::{Self}; - use sui::object::{Self, ID}; + use iota::object::{Self, ID}; use wormhole::bytes::{Self}; use wormhole::cursor::{Self}; use wormhole::external_address::{Self, ExternalAddress}; @@ -208,7 +208,7 @@ module token_bridge::transfer_with_payload { #[test_only] module token_bridge::transfer_with_payload_tests { use std::vector::{Self}; - use sui::object::{Self}; + use iota::object::{Self}; use wormhole::emitter::{Self}; use wormhole::external_address::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/migrate.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/migrate.move index e3559de48a..b8d62bcab8 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/migrate.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/migrate.move @@ -8,7 +8,7 @@ /// any of Token Bridge's methods by enforcing the current build version as /// their required minimum version. module token_bridge::migrate { - use sui::object::{ID}; + use iota::object::{ID}; use wormhole::governance_message::{Self, DecreeReceipt}; use token_bridge::state::{Self, State}; @@ -75,7 +75,7 @@ module token_bridge::migrate { // Finally emit an event reflecting a successful migrate. let package = state::current_package(&latest_only, token_bridge_state); - sui::event::emit(MigrateComplete { package }); + iota::event::emit(MigrateComplete { package }); } #[test_only] @@ -86,7 +86,7 @@ module token_bridge::migrate { #[test_only] module token_bridge::migrate_tests { - use sui::test_scenario::{Self}; + use iota::test_scenario::{Self}; use wormhole::wormhole_scenario::{ parse_and_verify_vaa, verify_governance_vaa diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/native_asset.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/native_asset.move index dba6b91960..89898868da 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/native_asset.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/native_asset.move @@ -1,16 +1,16 @@ // SPDX-License-Identifier: Apache 2 /// This module implements a custom type that keeps track of info relating to -/// assets (coin types) native to Sui. Token Bridge takes custody of these +/// assets (coin types) native to Iota. Token Bridge takes custody of these /// assets when someone invokes a token transfer outbound. Likewise, Token /// Bridge releases some of its balance from its custody of when someone redeems -/// an inbound token transfer intended for Sui. +/// an inbound token transfer intended for Iota. /// /// See `token_registry` module for more details. module token_bridge::native_asset { - use sui::balance::{Self, Balance}; - use sui::coin::{Self, CoinMetadata}; - use sui::object::{Self}; + use iota::balance::{Self, Balance}; + use iota::coin::{Self, CoinMetadata}; + use iota::object::{Self}; use wormhole::external_address::{Self, ExternalAddress}; use wormhole::state::{chain_id}; @@ -65,7 +65,7 @@ module token_bridge::native_asset { balance::value(&self.custody) } - /// Retrieve canonical token chain ID (Sui's) and token address. + /// Retrieve canonical token chain ID (Iota's) and token address. public fun canonical_info( self: &NativeAsset ): (u16, ExternalAddress) { @@ -123,10 +123,10 @@ module token_bridge::native_asset { #[test_only] module token_bridge::native_asset_tests { - use sui::balance::{Self}; - use sui::coin::{Self}; - use sui::object::{Self}; - use sui::test_scenario::{Self}; + use iota::balance::{Self}; + use iota::coin::{Self}; + use iota::object::{Self}; + use iota::test_scenario::{Self}; use wormhole::external_address::{Self}; use wormhole::state::{chain_id}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/token_registry.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/token_registry.move index f03f1d3383..1a299ec40f 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/token_registry.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/token_registry.move @@ -8,12 +8,12 @@ module token_bridge::token_registry { use std::ascii::{String}; use std::type_name::{Self}; - use sui::coin::{TreasuryCap, CoinMetadata}; - use sui::dynamic_field::{Self}; - use sui::object::{Self, UID}; - use sui::package::{UpgradeCap}; - use sui::table::{Self, Table}; - use sui::tx_context::{TxContext}; + use iota::coin::{TreasuryCap, CoinMetadata}; + use iota::dynamic_field::{Self}; + use iota::object::{Self, UID}; + use iota::package::{UpgradeCap}; + use iota::table::{Self, Table}; + use iota::tx_context::{TxContext}; use wormhole::external_address::{Self, ExternalAddress}; use token_bridge::asset_meta::{Self, AssetMeta}; @@ -210,7 +210,7 @@ module token_bridge::token_registry { token_meta, coin_meta, treasury_cap, - sui::package::test_publish( + iota::package::test_publish( object::id_from_address(@token_bridge), ctx ) @@ -223,7 +223,7 @@ module token_bridge::token_registry { /// NOTE: This method does not verify if `CoinType` is already in the /// registry because `attest_token` already takes care of this check. If /// This method were to be called on an already-registered asset, this - /// will throw with an error from `sui::dynamic_field` reflectina duplicate + /// will throw with an error from `iota::dynamic_field` reflectina duplicate /// field. /// /// See `attest_token` module for more info. @@ -338,9 +338,9 @@ module token_bridge::token_registry { #[test_only] module token_bridge::token_registry_tests { use std::type_name::{Self}; - use sui::balance::{Self}; - use sui::coin::{CoinMetadata}; - use sui::test_scenario::{Self}; + use iota::balance::{Self}; + use iota::coin::{CoinMetadata}; + use iota::test_scenario::{Self}; use wormhole::external_address::{Self}; use wormhole::state::{chain_id}; @@ -585,7 +585,7 @@ module token_bridge::token_registry_tests { } #[test] - #[expected_failure(abort_code = sui::dynamic_field::EFieldAlreadyExists)] + #[expected_failure(abort_code = iota::dynamic_field::EFieldAlreadyExists)] /// In this negative test case, we try to register a native token twice. fun test_cannot_add_new_native_again() { let caller = person(); @@ -613,7 +613,7 @@ module token_bridge::token_registry_tests { // You shall not pass! // // NOTE: We don't have a custom error for this. This will trigger a - // `sui::dynamic_field` error. + // `iota::dynamic_field` error. token_registry::add_new_native_test_only( &mut registry, &coin_meta @@ -623,7 +623,7 @@ module token_bridge::token_registry_tests { } #[test] - #[expected_failure(abort_code = sui::dynamic_field::EFieldTypeMismatch)] + #[expected_failure(abort_code = iota::dynamic_field::EFieldTypeMismatch)] // In this negative test case, we attempt to deposit a wrapped token into // a TokenRegistry object, resulting in failure. A wrapped coin can // only be minted and burned, not deposited. @@ -672,7 +672,7 @@ module token_bridge::token_registry_tests { // You shall not pass! // // NOTE: We don't have a custom error for this. This will trigger a - // `sui::dynamic_field` error. + // `iota::dynamic_field` error. native_asset::deposit_test_only( token_registry::borrow_mut_native_test_only( &mut registry @@ -684,7 +684,7 @@ module token_bridge::token_registry_tests { } #[test] - #[expected_failure(abort_code = sui::dynamic_field::EFieldTypeMismatch)] + #[expected_failure(abort_code = iota::dynamic_field::EFieldTypeMismatch)] // In this negative test case, we attempt to deposit a wrapped token into // a TokenRegistry object, resulting in failure. A wrapped coin can // only be minted and burned, not deposited. @@ -715,7 +715,7 @@ module token_bridge::token_registry_tests { // You shall not pass! // // NOTE: We don't have a custom error for this. This will trigger a - // `sui::dynamic_field` error. + // `iota::dynamic_field` error. let minted = wrapped_asset::mint_test_only( token_registry::borrow_mut_wrapped_test_only( diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/wrapped_asset.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/wrapped_asset.move index 7df31c540e..4e60f21995 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/wrapped_asset.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/resources/wrapped_asset.move @@ -8,9 +8,9 @@ /// See `create_wrapped` and 'token_registry' modules for more details. module token_bridge::wrapped_asset { use std::string::{String}; - use sui::balance::{Self, Balance}; - use sui::coin::{Self, TreasuryCap, CoinMetadata}; - use sui::package::{Self, UpgradeCap}; + use iota::balance::{Self, Balance}; + use iota::coin::{Self, TreasuryCap, CoinMetadata}; + use iota::package::{Self, UpgradeCap}; use wormhole::external_address::{ExternalAddress}; use wormhole::state::{chain_id}; @@ -23,7 +23,7 @@ module token_bridge::wrapped_asset { friend token_bridge::token_registry; friend token_bridge::transfer_tokens; - /// Token chain ID matching Sui's are not allowed. + /// Token chain ID matching Iota's are not allowed. const E_SUI_CHAIN: u64 = 0; /// Canonical token info does match `AssetMeta` payload. const E_ASSET_META_MISMATCH: u64 = 1; @@ -73,7 +73,7 @@ module token_bridge::wrapped_asset { name ) = asset_meta::unpack(token_meta); - // Protect against adding `AssetMeta` which has Sui's chain ID. + // Protect against adding `AssetMeta` which has Iota's chain ID. assert!(token_chain != chain_id(), E_SUI_CHAIN); // Set metadata. @@ -136,7 +136,7 @@ module token_bridge::wrapped_asset { // have not changed (because changing this info is not desirable, as // this change means the supply changed on its native network). // - // NOTE: This implicitly verifies that `token_chain` is not Sui's + // NOTE: This implicitly verifies that `token_chain` is not Iota's // because this was checked already when the asset was first added. let (expected_chain, expected_address) = canonical_info(self); assert!( @@ -258,7 +258,7 @@ module token_bridge::wrapped_asset { decimals: _, upgrade_cap } = asset; - sui::test_utils::destroy(treasury_cap); + iota::test_utils::destroy(treasury_cap); let ForeignInfo { token_chain: _, @@ -267,18 +267,18 @@ module token_bridge::wrapped_asset { symbol: _ } = info; - sui::package::make_immutable(upgrade_cap); + iota::package::make_immutable(upgrade_cap); } } #[test_only] module token_bridge::wrapped_asset_tests { use std::string::{Self}; - use sui::balance::{Self}; - use sui::coin::{Self, CoinMetadata}; - use sui::object::{Self}; - use sui::package::{Self}; - use sui::test_scenario::{Self}; + use iota::balance::{Self}; + use iota::coin::{Self, CoinMetadata}; + use iota::object::{Self}; + use iota::package::{Self}; + use iota::test_scenario::{Self}; use wormhole::external_address::{Self}; use wormhole::state::{chain_id}; @@ -580,7 +580,7 @@ module token_bridge::wrapped_asset_tests { // Ignore effects. test_scenario::next_tx(scenario, caller); - // Sui's chain ID is not allowed. + // Iota's chain ID is not allowed. let invalid_meta = asset_meta::new( external_address::default(), diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/setup.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/setup.move index 89beaf418b..246dae0100 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/setup.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/setup.move @@ -3,10 +3,10 @@ /// This module implements the mechanism to publish the Token Bridge contract /// and initialize `State` as a shared object. module token_bridge::setup { - use sui::object::{Self, UID}; - use sui::package::{Self, UpgradeCap}; - use sui::transfer::{Self}; - use sui::tx_context::{Self, TxContext}; + use iota::object::{Self, UID}; + use iota::package::{Self, UpgradeCap}; + use iota::transfer::{Self}; + use iota::tx_context::{Self, TxContext}; use wormhole::emitter::{EmitterCap}; use token_bridge::state::{Self}; @@ -29,8 +29,8 @@ module token_bridge::setup { #[test_only] public fun init_test_only(ctx: &mut TxContext) { - // NOTE: This exists to mock up sui::package for proposed upgrades. - use sui::package::{Self}; + // NOTE: This exists to mock up iota::package for proposed upgrades. + use iota::package::{Self}; init(ctx); diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/state.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/state.move index fbe8dab4a4..f16ad7ffab 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/state.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/state.move @@ -6,10 +6,10 @@ /// accessing registered assets and verifying `VAA` intended for Token Bridge by /// checking the emitter against its own registered emitters. module token_bridge::state { - use sui::object::{Self, ID, UID}; - use sui::package::{UpgradeCap, UpgradeReceipt, UpgradeTicket}; - use sui::table::{Self, Table}; - use sui::tx_context::{TxContext}; + use iota::object::{Self, ID, UID}; + use iota::package::{UpgradeCap, UpgradeReceipt, UpgradeTicket}; + use iota::table::{Self, Table}; + use iota::tx_context::{TxContext}; use wormhole::bytes32::{Self, Bytes32}; use wormhole::consumed_vaas::{Self, ConsumedVAAs}; use wormhole::emitter::{EmitterCap}; @@ -173,7 +173,7 @@ module token_bridge::state { public fun test_upgrade(self: &mut State) { let test_digest = bytes32::from_bytes(b"new build"); let ticket = authorize_upgrade(self, test_digest); - let receipt = sui::package::test_upgrade(ticket); + let receipt = iota::package::test_upgrade(ticket); commit_upgrade(self, receipt); } @@ -306,7 +306,7 @@ module token_bridge::state { /// Issue an `UpgradeTicket` for the upgrade. /// - /// NOTE: The Sui VM performs a check that this method is executed from the + /// NOTE: The Iota VM performs a check that this method is executed from the /// latest published package. If someone were to try to execute this using /// a stale build, the transaction will revert with `PackageUpgradeError`, /// specifically `PackageIDDoesNotMatch`. @@ -320,7 +320,7 @@ module token_bridge::state { /// Finalize the upgrade that ran to produce the given `receipt`. /// - /// NOTE: The Sui VM performs a check that this method is executed from the + /// NOTE: The Iota VM performs a check that this method is executed from the /// latest published package. If someone were to try to execute this using /// a stale build, the transaction will revert with `PackageUpgradeError`, /// specifically `PackageIDDoesNotMatch`. diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_10.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_10.move index c87282a157..f58a56d727 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_10.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_10.move @@ -3,11 +3,11 @@ #[test_only] module token_bridge::coin_native_10 { use std::option::{Self}; - use sui::balance::{Self, Balance}; - use sui::coin::{Self, CoinMetadata, TreasuryCap}; - use sui::test_scenario::{Self, Scenario}; - use sui::transfer::{Self}; - use sui::tx_context::{TxContext}; + use iota::balance::{Self, Balance}; + use iota::coin::{Self, CoinMetadata, TreasuryCap}; + use iota::test_scenario::{Self, Scenario}; + use iota::transfer::{Self}; + use iota::tx_context::{TxContext}; use token_bridge::native_asset::{Self}; use token_bridge::state::{Self}; @@ -15,7 +15,7 @@ module token_bridge::coin_native_10 { struct COIN_NATIVE_10 has drop {} - // This module creates a Sui-native token for testing purposes, + // This module creates a Iota-native token for testing purposes, // for example in complete_transfer, where we create a native coin, // mint some and deposit in the token bridge, then complete transfer // and ultimately transfer a portion of those native coins to a recipient. diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_4.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_4.move index 889d036200..b9c2621005 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_4.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_4.move @@ -3,11 +3,11 @@ #[test_only] module token_bridge::coin_native_4 { use std::option::{Self}; - use sui::balance::{Self, Balance}; - use sui::coin::{Self, CoinMetadata, TreasuryCap}; - use sui::test_scenario::{Self, Scenario}; - use sui::transfer::{Self}; - use sui::tx_context::{TxContext}; + use iota::balance::{Self, Balance}; + use iota::coin::{Self, CoinMetadata, TreasuryCap}; + use iota::test_scenario::{Self, Scenario}; + use iota::transfer::{Self}; + use iota::tx_context::{TxContext}; use token_bridge::native_asset::{Self}; use token_bridge::state::{Self}; @@ -15,7 +15,7 @@ module token_bridge::coin_native_4 { struct COIN_NATIVE_4 has drop {} - // This module creates a Sui-native token for testing purposes, + // This module creates a Iota-native token for testing purposes, // for example in complete_transfer, where we create a native coin, // mint some and deposit in the token bridge, then complete transfer // and ultimately transfer a portion of those native coins to a recipient. diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_12.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_12.move index 74932765ef..242b4b0ccf 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_12.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_12.move @@ -2,12 +2,12 @@ #[test_only] module token_bridge::coin_wrapped_12 { - use sui::balance::{Balance}; - use sui::package::{UpgradeCap}; - use sui::coin::{CoinMetadata, TreasuryCap}; - use sui::test_scenario::{Self, Scenario}; - use sui::transfer::{Self}; - use sui::tx_context::{Self, TxContext}; + use iota::balance::{Balance}; + use iota::package::{UpgradeCap}; + use iota::coin::{CoinMetadata, TreasuryCap}; + use iota::test_scenario::{Self, Scenario}; + use iota::transfer::{Self}; + use iota::tx_context::{Self, TxContext}; use token_bridge::asset_meta::{Self, AssetMeta}; use token_bridge::create_wrapped::{Self, WrappedAssetSetup}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_7.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_7.move index fa09e1b444..9ac16322d7 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_7.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_7.move @@ -2,12 +2,12 @@ #[test_only] module token_bridge::coin_wrapped_7 { - use sui::balance::{Balance}; - use sui::coin::{CoinMetadata, TreasuryCap}; - use sui::package::{UpgradeCap}; - use sui::test_scenario::{Self, Scenario}; - use sui::transfer::{Self}; - use sui::tx_context::{Self, TxContext}; + use iota::balance::{Balance}; + use iota::coin::{CoinMetadata, TreasuryCap}; + use iota::package::{UpgradeCap}; + use iota::test_scenario::{Self, Scenario}; + use iota::transfer::{Self}; + use iota::tx_context::{Self, TxContext}; use token_bridge::asset_meta::{Self, AssetMeta}; use token_bridge::create_wrapped::{Self, WrappedAssetSetup}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/token_bridge_scenario.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/token_bridge_scenario.move index d7921ee809..fab2145773 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/token_bridge_scenario.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/test/token_bridge_scenario.move @@ -3,9 +3,9 @@ #[test_only] module token_bridge::token_bridge_scenario { use std::vector::{Self}; - use sui::balance::{Self}; - use sui::package::{UpgradeCap}; - use sui::test_scenario::{Self, Scenario}; + use iota::balance::{Self}; + use iota::package::{UpgradeCap}; + use iota::test_scenario::{Self, Scenario}; use wormhole::external_address::{Self}; use wormhole::wormhole_scenario::{ deployer, diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens.move index 60f1aa2301..0fbdb5ddc6 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens.move @@ -23,15 +23,15 @@ /// implement `prepare_transfer` in his contract to produce `PrepareTransfer`. /// /// NOTE: Only assets that exist in the `TokenRegistry` can be bridged out, -/// which are native Sui assets that have been attested for via `attest_token` +/// which are native Iota assets that have been attested for via `attest_token` /// and wrapped foreign assets that have been created using foreign asset /// metadata via the `create_wrapped` module. /// /// See `transfer` module for serialization and deserialization of Wormhole /// message payload. module token_bridge::transfer_tokens { - use sui::balance::{Self, Balance}; - use sui::coin::{Self, Coin}; + use iota::balance::{Self, Balance}; + use iota::coin::{Self, Coin}; use wormhole::bytes32::{Self}; use wormhole::external_address::{Self, ExternalAddress}; use wormhole::publish_message::{MessageTicket}; @@ -109,7 +109,7 @@ module token_bridge::transfer_tokens { /// `transfer_tokens` is the only method that can unpack the members of /// `TransferTicket`. This method takes the balance from this type and - /// bridges this asset out of Sui by either joining its balance in the Token + /// bridges this asset out of Iota by either joining its balance in the Token /// Bridge's custody for native assets or burning its balance for wrapped /// assets. /// @@ -158,7 +158,7 @@ module token_bridge::transfer_tokens { /// Modify coin based on the decimals of a given coin type, which may /// leave some amount if the decimals lead to truncating the coin's balance. /// This method returns the extracted balance (which will be bridged out of - /// Sui) and the normalized amount, which will be encoded in the token + /// Iota) and the normalized amount, which will be encoded in the token /// transfer payload. /// /// NOTE: This is a privileged method, which only this and the @@ -248,7 +248,7 @@ module token_bridge::transfer_tokens { // Disallow `relayer_fee` to be greater than the `Coin` object's value. // Keep in mind that the relayer fee is evaluated against the truncated // amount. - let amount = sui::balance::value(&bridged_in); + let amount = iota::balance::value(&bridged_in); assert!(relayer_fee <= amount, E_RELAYER_FEE_EXCEEDS_AMOUNT); // Handle funds and get canonical token info for encoded transfer. @@ -304,8 +304,8 @@ module token_bridge::transfer_tokens { #[test_only] module token_bridge::transfer_token_tests { - use sui::coin::{Self}; - use sui::test_scenario::{Self}; + use iota::coin::{Self}; + use iota::test_scenario::{Self}; use wormhole::bytes32::{Self}; use wormhole::external_address::{Self}; use wormhole::publish_message::{Self}; @@ -870,7 +870,7 @@ module token_bridge::transfer_token_tests { scenario, sender ); - sui::test_utils::destroy(treasury_cap); + iota::test_utils::destroy(treasury_cap); // NOTE: This test purposely doesn't `attest` COIN_WRAPPED_7. let transfer_amount = 42069; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens_with_payload.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens_with_payload.move index 0ed204bfb7..48fca22f47 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens_with_payload.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens_with_payload.move @@ -26,16 +26,16 @@ /// `PrepareTransferWithPayload`. /// /// NOTE: Only assets that exist in the `TokenRegistry` can be bridged out, -/// which are native Sui assets that have been attested for via `attest_token` +/// which are native Iota assets that have been attested for via `attest_token` /// and wrapped foreign assets that have been created using foreign asset /// metadata via the `create_wrapped` module. /// /// See `transfer_with_payload` module for serialization and deserialization of /// Wormhole message payload. module token_bridge::transfer_tokens_with_payload { - use sui::balance::{Balance}; - use sui::coin::{Coin}; - use sui::object::{Self, ID}; + use iota::balance::{Balance}; + use iota::coin::{Coin}; + use iota::object::{Self, ID}; use wormhole::bytes32::{Self}; use wormhole::emitter::{EmitterCap}; use wormhole::external_address::{Self}; @@ -109,7 +109,7 @@ module token_bridge::transfer_tokens_with_payload { /// `transfer_tokens_with_payload` is the only method that can unpack the /// members of `TransferTicket`. This method takes the balance - /// from this type and bridges this asset out of Sui by either joining its + /// from this type and bridges this asset out of Iota by either joining its /// balance in the Token Bridge's custody for native assets or burning its /// balance for wrapped assets. /// @@ -228,9 +228,9 @@ module token_bridge::transfer_tokens_with_payload { #[test_only] module token_bridge::transfer_tokens_with_payload_tests { - use sui::coin::{Self}; - use sui::object::{Self}; - use sui::test_scenario::{Self}; + use iota::coin::{Self}; + use iota::object::{Self}; + use iota::test_scenario::{Self}; use wormhole::bytes32::{Self}; use wormhole::emitter::{Self}; use wormhole::external_address::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/coin_utils.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/coin_utils.move index 80d351f933..4eb94151e5 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/coin_utils.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/coin_utils.move @@ -5,9 +5,9 @@ /// between `Coin` and `Balance` avoiding unnecessary object creation and /// destruction. module token_bridge::coin_utils { - use sui::balance::{Self, Balance}; - use sui::coin::{Self, Coin}; - use sui::tx_context::{TxContext}; + use iota::balance::{Self, Balance}; + use iota::coin::{Self, Coin}; + use iota::tx_context::{TxContext}; /// Method similar to `coin::take` where an amount is split from a `Coin` /// object's inner balance. @@ -42,7 +42,7 @@ module token_bridge::coin_utils { if (coin::value(&the_coin) == 0) { coin::destroy_zero(the_coin); } else { - sui::pay::keep(the_coin, ctx) + iota::pay::keep(the_coin, ctx) } } } diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/string_utils.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/string_utils.move index 868fa74927..17a1fbdcdb 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/string_utils.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/utils/string_utils.move @@ -28,7 +28,7 @@ module token_bridge::string_utils { /// out at `ascii::string(bytes)`. For input strings that contain non-ascii /// characters, we will swap the non-ascii character with `?`. /// - /// Note that while the Sui spec limits symbols to only use ascii + /// Note that while the Iota spec limits symbols to only use ascii /// characters, the token bridge spec does allow utf8 symbols. public fun to_ascii(s: &String): ascii::String { let buf = *string::bytes(s); diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/vaa.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/vaa.move index c2e9a26096..726ffa449f 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/vaa.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/vaa.move @@ -11,7 +11,7 @@ /// in its `State`. If the encoded VAA passes through `parse_and_verify` again, /// it will abort. module token_bridge::vaa { - use sui::table::{Self}; + use iota::table::{Self}; use wormhole::external_address::{ExternalAddress}; use wormhole::vaa::{Self, VAA}; @@ -141,7 +141,7 @@ module token_bridge::vaa { #[test_only] module token_bridge::vaa_tests { - use sui::test_scenario::{Self}; + use iota::test_scenario::{Self}; use wormhole::external_address::{Self}; use wormhole::wormhole_scenario::{parse_and_verify_vaa}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/version_control.move b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/version_control.move index caee75794c..d36f8f2fdc 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/version_control.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/token_bridge/sources/version_control.move @@ -43,7 +43,7 @@ module token_bridge::version_control { // //////////////////////////////////////////////////////////////////////////// - /// First published package on Sui mainnet. + /// First published package on Iota mainnet. struct V__0_2_0 has store, drop, copy {} // Dummy. diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.lock b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.lock index 570d17a100..540307f10f 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.lock +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.lock @@ -2,26 +2,34 @@ [move] version = 0 -manifest_digest = "E8C411A83F4F7EF268B73C732B9B6F49F7B204F0C9C2530765365C38B76EF646" +manifest_digest = "6E83C6D69B7F45D2ACF973DFF878405621DC0E67C0EE2D4A28748A7FDD907AA1" deps_digest = "F8BBB0CCB2491CA29A3DF03D6F92277A4F3574266507ACD77214D37ECA3F3082" dependencies = [ - { name = "Sui" }, + { name = "Iota" }, ] [[move.package]] -name = "MoveStdlib" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "041c5f2bae2fe52079e44b70514333532d69f4e6", subdir = "crates/sui-framework/packages/move-stdlib" } - -[[move.package]] -name = "Sui" -source = { git = "https://github.com/MystenLabs/sui.git", rev = "041c5f2bae2fe52079e44b70514333532d69f4e6", subdir = "crates/sui-framework/packages/sui-framework" } +name = "Iota" +source = { git = "https://github.com/iotaledger/iota.git", rev = "751c23caf24efd071463b9ffd07eabcb15f44f31", subdir = "crates/iota-framework/packages/iota-framework" } dependencies = [ { name = "MoveStdlib" }, ] +[[move.package]] +name = "MoveStdlib" +source = { git = "https://github.com/iotaledger/iota.git", rev = "751c23caf24efd071463b9ffd07eabcb15f44f31", subdir = "crates/iota-framework/packages/move-stdlib" } + [move.toolchain-version] -compiler-version = "1.19.0" -edition = "legacy" -flavor = "sui" +compiler-version = "0.9.2-rc" +edition = "2024.beta" +flavor = "iota" + +[env] + +[env.testnet] +chain-id = "2304aa97" +original-published-id = "0xfca58c557f09cddb7930588c4e2a4edbe3cdded1ac1ed2270aa2dfa8d2b9ae0d" +latest-published-id = "0xfca58c557f09cddb7930588c4e2a4edbe3cdded1ac1ed2270aa2dfa8d2b9ae0d" +published-version = "1" diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.toml b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.toml index 42fe952451..e0197a6e62 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.toml +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/Move.toml @@ -1,12 +1,12 @@ [package] name = "Wormhole" version = "0.2.0" -published-at = "0x23a373b70e6e23a39e4846fa6896fa12beb08da061b3d4ec856bc8ead54f1e22" +published-at = "0xfca58c557f09cddb7930588c4e2a4edbe3cdded1ac1ed2270aa2dfa8d2b9ae0d" -[dependencies.Sui] -git = "https://github.com/MystenLabs/sui.git" -subdir = "crates/sui-framework/packages/sui-framework" -rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" +[dependencies.Iota] +git = "https://github.com/iotaledger/iota.git" +subdir = "crates/iota-framework/packages/iota-framework" +rev = "751c23caf24efd071463b9ffd07eabcb15f44f31" [addresses] -wormhole = "0x23a373b70e6e23a39e4846fa6896fa12beb08da061b3d4ec856bc8ead54f1e22" +wormhole = "0xfca58c557f09cddb7930588c4e2a4edbe3cdded1ac1ed2270aa2dfa8d2b9ae0d" diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes32.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes32.move index ab713f6f25..5c0157b9e7 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes32.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes32.move @@ -6,7 +6,7 @@ module wormhole::bytes32 { use std::option::{Self}; use std::string::{Self, String}; use std::vector::{Self}; - use sui::bcs::{Self}; + use iota::bcs::{Self}; use wormhole::bytes::{Self}; use wormhole::cursor::{Self, Cursor}; @@ -106,12 +106,12 @@ module wormhole::bytes32 { /// Destroy `Bytes32` to represent its underlying data as `address`. public fun to_address(value: Bytes32): address { - sui::address::from_bytes(to_bytes(value)) + iota::address::from_bytes(to_bytes(value)) } /// Create `Bytes32` from `address`. public fun from_address(addr: address): Bytes32 { - new(sui::address::to_bytes(addr)) + new(iota::address::to_bytes(addr)) } public fun from_utf8(str: String): Bytes32 { diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/external_address.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/external_address.move index b2134caf35..7bf326c738 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/external_address.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/external_address.move @@ -3,7 +3,7 @@ /// This module implements a custom type for a 32-byte standardized address, /// which is meant to represent an address from any other network. module wormhole::external_address { - use sui::object::{Self, ID}; + use iota::object::{Self, ID}; use wormhole::bytes32::{Self, Bytes32}; use wormhole::cursor::{Cursor}; @@ -55,7 +55,7 @@ module wormhole::external_address { /// Destroy `ExternalAddress` to represent its underlying data as `address`. public fun to_address(ext: ExternalAddress): address { - sui::address::from_bytes(to_bytes(ext)) + iota::address::from_bytes(to_bytes(ext)) } /// Create `ExternalAddress` from `address`. diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/emitter.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/emitter.move index 5c5f2dbe73..109f35f0e5 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/emitter.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/emitter.move @@ -4,8 +4,8 @@ /// Wormhole messages. Its external address is determined by the capability's /// `id`, which is a 32-byte vector. module wormhole::emitter { - use sui::object::{Self, ID, UID}; - use sui::tx_context::{TxContext}; + use iota::object::{Self, ID, UID}; + use iota::tx_context::{TxContext}; use wormhole::state::{Self, State}; @@ -21,7 +21,7 @@ module wormhole::emitter { emitter_cap: ID } - /// `EmitterCap` is a Sui object that gives a user or smart contract the + /// `EmitterCap` is a Iota object that gives a user or smart contract the /// capability to send Wormhole messages. For every Wormhole message /// emitted, a unique `sequence` is used. struct EmitterCap has key, store { @@ -41,7 +41,7 @@ module wormhole::emitter { sequence: 0 }; - sui::event::emit( + iota::event::emit( EmitterCreated { emitter_cap: object::id(&cap)} ); @@ -69,7 +69,7 @@ module wormhole::emitter { public fun destroy(wormhole_state: &State, cap: EmitterCap) { state::assert_latest_only(wormhole_state); - sui::event::emit( + iota::event::emit( EmitterDestroyed { emitter_cap: object::id(&cap) } ); @@ -86,7 +86,7 @@ module wormhole::emitter { #[test_only] public fun dummy(): EmitterCap { EmitterCap { - id: object::new(&mut sui::tx_context::dummy()), + id: object::new(&mut iota::tx_context::dummy()), sequence: 0 } } @@ -94,8 +94,8 @@ module wormhole::emitter { #[test_only] module wormhole::emitter_tests { - use sui::object::{Self}; - use sui::test_scenario::{Self}; + use iota::object::{Self}; + use iota::test_scenario::{Self}; use wormhole::emitter::{Self}; use wormhole::state::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/set_fee.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/set_fee.move index ab3431b7ca..68a7c8bfe1 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/set_fee.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/set_fee.move @@ -29,10 +29,10 @@ module wormhole::set_fee { ) } - /// Redeem governance VAA to configure Wormhole message fee amount in SUI - /// denomination. This governance message is only relevant for Sui because + /// Redeem governance VAA to configure Wormhole message fee amount in IOTA + /// denomination. This governance message is only relevant for Iota because /// fee administration is only relevant to one particular network (in this - /// case Sui). + /// case Iota). /// /// NOTE: This method is guarded by a minimum build version check. This /// method could break backward compatibility on an upgrade. @@ -76,8 +76,8 @@ module wormhole::set_fee { #[test_only] module wormhole::set_fee_tests { - use sui::balance::{Self}; - use sui::test_scenario::{Self}; + use iota::balance::{Self}; + use iota::test_scenario::{Self}; use wormhole::bytes::{Self}; use wormhole::cursor::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/transfer_fee.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/transfer_fee.move index f31274b8e0..6ac812ee4e 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/transfer_fee.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/transfer_fee.move @@ -3,9 +3,9 @@ /// This module implements handling a governance VAA to enact transferring some /// amount of collected fees to an intended recipient. module wormhole::transfer_fee { - use sui::coin::{Self}; - use sui::transfer::{Self}; - use sui::tx_context::{TxContext}; + use iota::coin::{Self}; + use iota::transfer::{Self}; + use iota::tx_context::{TxContext}; use wormhole::bytes32::{Self}; use wormhole::cursor::{Self}; @@ -37,8 +37,8 @@ module wormhole::transfer_fee { /// Redeem governance VAA to transfer collected Wormhole fees to the /// recipient encoded in its Wormhole governance message. This governance - /// message is only relevant for Sui because fee administration is only - /// relevant to one particular network (in this case Sui). + /// message is only relevant for Iota because fee administration is only + /// relevant to one particular network (in this case Iota). /// /// NOTE: This method is guarded by a minimum build version check. This /// method could break backward compatibility on an upgrade. @@ -66,7 +66,7 @@ module wormhole::transfer_fee { governance_payload: vector, ctx: &mut TxContext ): u64 { - // Deserialize the payload as amount to withdraw and to whom SUI should + // Deserialize the payload as amount to withdraw and to whom IOTA should // be sent. let TransferFee { amount, recipient } = deserialize(governance_payload); @@ -106,10 +106,10 @@ module wormhole::transfer_fee { #[test_only] module wormhole::transfer_fee_tests { - use sui::balance::{Self}; - use sui::coin::{Self, Coin}; - use sui::sui::{SUI}; - use sui::test_scenario::{Self}; + use iota::balance::{Self}; + use iota::coin::{Self, Coin}; + use iota::iota::{IOTA}; + use iota::test_scenario::{Self}; use wormhole::bytes::{Self}; use wormhole::bytes32::{Self}; @@ -192,7 +192,7 @@ module wormhole::transfer_fee_tests { // Verify that the recipient received the withdrawal. let withdrawn_coin = - test_scenario::take_from_address>(scenario, recipient); + test_scenario::take_from_address>(scenario, recipient); assert!(coin::value(&withdrawn_coin) == withdrawn, 0); // And there is still a balance on Wormhole's fee collector. @@ -321,7 +321,7 @@ module wormhole::transfer_fee_tests { } #[test] - #[expected_failure(abort_code = sui::balance::ENotEnough)] + #[expected_failure(abort_code = iota::balance::ENotEnough)] fun test_cannot_transfer_fee_insufficient_balance() { // Testing this method. use wormhole::transfer_fee::{transfer_fee}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/update_guardian_set.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/update_guardian_set.move index 15bfb1953b..8eb5671236 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/update_guardian_set.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/update_guardian_set.move @@ -6,7 +6,7 @@ /// mind that the current guardian set has no expiration. module wormhole::update_guardian_set { use std::vector::{Self}; - use sui::clock::{Clock}; + use iota::clock::{Clock}; use wormhole::bytes::{Self}; use wormhole::cursor::{Self}; @@ -104,7 +104,7 @@ module wormhole::update_guardian_set { guardian_set::new(new_index, guardians) ); - sui::event::emit(GuardianSetAdded { new_index }); + iota::event::emit(GuardianSetAdded { new_index }); new_index } @@ -136,8 +136,8 @@ module wormhole::update_guardian_set { #[test_only] module wormhole::update_guardian_set_tests { use std::vector::{Self}; - use sui::clock::{Self}; - use sui::test_scenario::{Self}; + use iota::clock::{Self}; + use iota::test_scenario::{Self}; use wormhole::bytes::{Self}; use wormhole::cursor::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/upgrade_contract.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/upgrade_contract.move index 018511e784..89ea8fa7c2 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/upgrade_contract.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance/upgrade_contract.move @@ -8,8 +8,8 @@ /// 3. Upgrade. /// 4. Commit upgrade. module wormhole::upgrade_contract { - use sui::object::{ID}; - use sui::package::{UpgradeReceipt, UpgradeTicket}; + use iota::object::{ID}; + use iota::package::{UpgradeReceipt, UpgradeTicket}; use wormhole::bytes32::{Self, Bytes32}; use wormhole::cursor::{Self}; @@ -50,16 +50,16 @@ module wormhole::upgrade_contract { } /// Redeem governance VAA to issue an `UpgradeTicket` for the upgrade given - /// a contract upgrade VAA. This governance message is only relevant for Sui + /// a contract upgrade VAA. This governance message is only relevant for Iota /// because a contract upgrade is only relevant to one particular network - /// (in this case Sui), whose build digest is encoded in this message. + /// (in this case Iota), whose build digest is encoded in this message. public fun authorize_upgrade( wormhole_state: &mut State, receipt: DecreeReceipt ): UpgradeTicket { // NOTE: This is the only governance method that does not enforce // current package checking when consuming VAA hashes. This is because - // upgrades are protected by the Sui VM, enforcing the latest package + // upgrades are protected by the Iota VM, enforcing the latest package // is the one performing the upgrade. let consumed = state::borrow_mut_consumed_vaas_unchecked(wormhole_state); @@ -73,7 +73,7 @@ module wormhole::upgrade_contract { /// Finalize the upgrade that ran to produce the given `receipt`. This /// method invokes `state::commit_upgrade` which interacts with - /// `sui::package`. + /// `iota::package`. public fun commit_upgrade( self: &mut State, receipt: UpgradeReceipt, @@ -81,7 +81,7 @@ module wormhole::upgrade_contract { let (old_contract, new_contract) = state::commit_upgrade(self, receipt); // Emit an event reflecting package ID change. - sui::event::emit(ContractUpgraded { old_contract, new_contract }); + iota::event::emit(ContractUpgraded { old_contract, new_contract }); } /// Privileged method only to be used by this module and `migrate` module. diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance_message.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance_message.move index 20d4cfee5f..a28ac2c80f 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance_message.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/governance_message.move @@ -25,7 +25,7 @@ module wormhole::governance_message { const E_INVALID_GOVERNANCE_ACTION: u64 = 5; /// Governance target chain not indicative of global action. const E_GOVERNANCE_TARGET_CHAIN_NONZERO: u64 = 6; - /// Governance target chain not indicative of actino specifically for Sui + /// Governance target chain not indicative of actino specifically for Iota /// Wormhole contract. const E_GOVERNANCE_TARGET_CHAIN_NOT_SUI: u64 = 7; @@ -66,7 +66,7 @@ module wormhole::governance_message { } /// This method prepares `DecreeTicket` for local governance action. This - /// means the VAA encodes target chain ID == 21 (Sui's). + /// means the VAA encodes target chain ID == 21 (Iota's). public fun authorize_verify_local( _witness: T, governance_chain: u16, @@ -162,7 +162,7 @@ module wormhole::governance_message { assert!(action == parsed_action, E_INVALID_GOVERNANCE_ACTION); // Target chain, which determines whether the governance VAA applies to - // all chains or Sui. + // all chains or Iota. if (global) { assert!(chain == 0, E_GOVERNANCE_TARGET_CHAIN_NONZERO); } else { @@ -204,8 +204,8 @@ module wormhole::governance_message { #[test_only] module wormhole::governance_message_tests { - use sui::test_scenario::{Self}; - use sui::tx_context::{Self}; + use iota::test_scenario::{Self}; + use iota::tx_context::{Self}; use wormhole::bytes32::{Self}; use wormhole::consumed_vaas::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/migrate.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/migrate.move index 958a0b12f8..5003e16c02 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/migrate.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/migrate.move @@ -8,8 +8,8 @@ /// any of Wormhole's methods by enforcing the current build version as their /// required minimum version. module wormhole::migrate { - use sui::clock::{Clock}; - use sui::object::{ID}; + use iota::clock::{Clock}; + use iota::object::{ID}; use wormhole::governance_message::{Self}; use wormhole::state::{Self, State}; @@ -94,7 +94,7 @@ module wormhole::migrate { // Finally emit an event reflecting a successful migrate. let package = state::current_package(&latest_only, wormhole_state); - sui::event::emit(MigrateComplete { package }); + iota::event::emit(MigrateComplete { package }); } #[test_only] @@ -105,7 +105,7 @@ module wormhole::migrate { #[test_only] module wormhole::migrate_tests { - use sui::test_scenario::{Self}; + use iota::test_scenario::{Self}; use wormhole::state::{Self}; use wormhole::wormhole_scenario::{ diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/publish_message.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/publish_message.move index 3255fee5ba..cd6cd2c6df 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/publish_message.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/publish_message.move @@ -24,15 +24,15 @@ /// implement `prepare_message` in his contract to produce `MessageTicket`, /// which `publish_message` consumes. module wormhole::publish_message { - use sui::coin::{Self, Coin}; - use sui::clock::{Self, Clock}; - use sui::object::{Self, ID}; - use sui::sui::{SUI}; + use iota::coin::{Self, Coin}; + use iota::clock::{Self, Clock}; + use iota::object::{Self, ID}; + use iota::iota::{IOTA}; use wormhole::emitter::{Self, EmitterCap}; use wormhole::state::{Self, State}; - /// This type is emitted via `sui::event` module. Guardians pick up this + /// This type is emitted via `iota::event` module. Guardians pick up this /// observation and attest to its existence. struct WormholeMessage has drop, copy { /// `EmitterCap` object ID. @@ -89,7 +89,7 @@ module wormhole::publish_message { } } - /// `publish_message` emits a message as a Sui event. This method uses the + /// `publish_message` emits a message as a Iota event. This method uses the /// input `EmitterCap` as the registered sender of the /// `WormholeMessage`. It also produces a new sequence for this emitter. /// @@ -106,7 +106,7 @@ module wormhole::publish_message { /// See `prepare_message` for more details. public fun publish_message( wormhole_state: &mut State, - message_fee: Coin, + message_fee: Coin, prepared_msg: MessageTicket, the_clock: &Clock ): u64 { @@ -132,11 +132,11 @@ module wormhole::publish_message { // Truncate to seconds. let timestamp = clock::timestamp_ms(the_clock) / 1000; - // Sui is an instant finality chain, so we don't need confirmations. + // Iota is an instant finality chain, so we don't need confirmations. let consistency_level = 0; - // Emit Sui event with `WormholeMessage`. - sui::event::emit( + // Emit Iota event with `WormholeMessage`. + iota::event::emit( WormholeMessage { sender, sequence, @@ -164,8 +164,8 @@ module wormhole::publish_message { #[test_only] module wormhole::publish_message_tests { - use sui::coin::{Self}; - use sui::test_scenario::{Self}; + use iota::coin::{Self}; + use iota::test_scenario::{Self}; use wormhole::emitter::{Self, EmitterCap}; use wormhole::fee_collector::{Self}; @@ -259,7 +259,7 @@ module wormhole::publish_message_tests { // Clean up. return_state(worm_state); return_clock(the_clock); - sui::transfer::public_transfer(emitter_cap, user); + iota::transfer::public_transfer(emitter_cap, user); }; // Grab the `TransactionEffects` of the previous transaction. diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/consumed_vaas.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/consumed_vaas.move index a09327cf7e..eeb16a8f38 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/consumed_vaas.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/consumed_vaas.move @@ -1,5 +1,5 @@ module wormhole::consumed_vaas { - use sui::tx_context::{TxContext}; + use iota::tx_context::{TxContext}; use wormhole::bytes32::{Bytes32}; use wormhole::set::{Self, Set}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/fee_collector.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/fee_collector.move index f75ab49719..c9d91533cf 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/fee_collector.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/fee_collector.move @@ -1,21 +1,21 @@ // SPDX-License-Identifier: Apache 2 -/// This module implements a container that collects fees in SUI denomination. +/// This module implements a container that collects fees in IOTA denomination. /// The `FeeCollector` requires that the fee deposited is exactly equal to the /// `fee_amount` configured. module wormhole::fee_collector { - use sui::balance::{Self, Balance}; - use sui::coin::{Self, Coin}; - use sui::sui::{SUI}; - use sui::tx_context::{TxContext}; + use iota::balance::{Self, Balance}; + use iota::coin::{Self, Coin}; + use iota::iota::{IOTA}; + use iota::tx_context::{TxContext}; /// Amount deposited is not exactly the amount configured. const E_INCORRECT_FEE: u64 = 0; - /// Container for configured `fee_amount` and `balance` of SUI collected. + /// Container for configured `fee_amount` and `balance` of IOTA collected. struct FeeCollector has store { fee_amount: u64, - balance: Balance + balance: Balance } /// Create new `FeeCollector` with specified amount to collect. @@ -28,37 +28,37 @@ module wormhole::fee_collector { self.fee_amount } - /// Retrieve current SUI balance. + /// Retrieve current IOTA balance. public fun balance_value(self: &FeeCollector): u64 { balance::value(&self.balance) } - /// Take `Balance` and add it to current collected balance. - public fun deposit_balance(self: &mut FeeCollector, fee: Balance) { + /// Take `Balance` and add it to current collected balance. + public fun deposit_balance(self: &mut FeeCollector, fee: Balance) { assert!(balance::value(&fee) == self.fee_amount, E_INCORRECT_FEE); balance::join(&mut self.balance, fee); } - /// Take `Coin` and add it to current collected balance. - public fun deposit(self: &mut FeeCollector, fee: Coin) { + /// Take `Coin` and add it to current collected balance. + public fun deposit(self: &mut FeeCollector, fee: Coin) { deposit_balance(self, coin::into_balance(fee)) } - /// Create `Balance` of some `amount` by taking from collected balance. + /// Create `Balance` of some `amount` by taking from collected balance. public fun withdraw_balance( self: &mut FeeCollector, amount: u64 - ): Balance { - // This will trigger `sui::balance::ENotEnough` if amount > balance. + ): Balance { + // This will trigger `iota::balance::ENotEnough` if amount > balance. balance::split(&mut self.balance, amount) } - /// Create `Coin` of some `amount` by taking from collected balance. + /// Create `Coin` of some `amount` by taking from collected balance. public fun withdraw( self: &mut FeeCollector, amount: u64, ctx: &mut TxContext - ): Coin { + ): Coin { coin::from_balance(withdraw_balance(self, amount), ctx) } @@ -76,8 +76,8 @@ module wormhole::fee_collector { #[test_only] module wormhole::fee_collector_tests { - use sui::coin::{Self}; - use sui::tx_context::{Self}; + use iota::coin::{Self}; + use iota::tx_context::{Self}; use wormhole::fee_collector::{Self}; @@ -146,7 +146,7 @@ module wormhole::fee_collector_tests { } #[test] - #[expected_failure(abort_code = sui::balance::ENotEnough)] + #[expected_failure(abort_code = iota::balance::ENotEnough)] public fun test_cannot_withdraw_more_than_balance() { let ctx = &mut tx_context::dummy(); diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian.move index 84c6a48eb1..73f82631e3 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian.move @@ -3,8 +3,8 @@ /// This module implements a `Guardian` that warehouses a 20-byte public key. module wormhole::guardian { use std::vector::{Self}; - use sui::hash::{Self}; - use sui::ecdsa_k1::{Self}; + use iota::hash::{Self}; + use iota::ecdsa_k1::{Self}; use wormhole::bytes20::{Self, Bytes20}; use wormhole::guardian_signature::{Self, GuardianSignature}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian_set.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian_set.move index e55ccd3758..fd2cc1057d 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian_set.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian_set.move @@ -9,7 +9,7 @@ /// configured, which defines how long the past Guardian set can be active. module wormhole::guardian_set { use std::vector::{Self}; - use sui::clock::{Self, Clock}; + use iota::clock::{Self, Clock}; use wormhole::guardian::{Self, Guardian}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/set.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/set.move index bb52326489..21da087083 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/set.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/resources/set.move @@ -1,12 +1,12 @@ // SPDX-License-Identifier: Apache 2 /// This module implements a custom type that resembles the set data structure. -/// `Set` leverages `sui::table` to store unique keys of the same type. +/// `Set` leverages `iota::table` to store unique keys of the same type. /// /// NOTE: Items added to this data structure cannot be removed. module wormhole::set { - use sui::table::{Self, Table}; - use sui::tx_context::{TxContext}; + use iota::table::{Self, Table}; + use iota::tx_context::{TxContext}; /// Explicit error if key already exists in `Set`. const E_KEY_ALREADY_EXISTS: u64 = 0; @@ -54,7 +54,7 @@ module wormhole::set { #[test_only] module wormhole::set_tests { - use sui::tx_context::{Self}; + use iota::tx_context::{Self}; use wormhole::set::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/setup.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/setup.move index c7f233e102..55eccb3ce0 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/setup.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/setup.move @@ -4,10 +4,10 @@ /// initialize `State` as a shared object. module wormhole::setup { use std::vector::{Self}; - use sui::object::{Self, UID}; - use sui::package::{Self, UpgradeCap}; - use sui::transfer::{Self}; - use sui::tx_context::{Self, TxContext}; + use iota::object::{Self, UID}; + use iota::package::{Self, UpgradeCap}; + use iota::transfer::{Self}; + use iota::tx_context::{Self, TxContext}; use wormhole::cursor::{Self}; use wormhole::state::{Self}; @@ -35,7 +35,7 @@ module wormhole::setup { // This will be created and sent to the transaction sender // automatically when the contract is published. transfer::public_transfer( - sui::package::test_publish(object::id_from_address(@wormhole), ctx), + iota::package::test_publish(object::id_from_address(@wormhole), ctx), tx_context::sender(ctx) ); } @@ -99,9 +99,9 @@ module wormhole::setup { module wormhole::setup_tests { use std::option::{Self}; use std::vector::{Self}; - use sui::package::{Self}; - use sui::object::{Self}; - use sui::test_scenario::{Self}; + use iota::package::{Self}; + use iota::object::{Self}; + use iota::test_scenario::{Self}; use wormhole::bytes32::{Self}; use wormhole::cursor::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/state.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/state.move index 1db91f87fe..c06bd47db8 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/state.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/state.move @@ -8,13 +8,13 @@ /// emitters for Wormhole integrators. module wormhole::state { use std::vector::{Self}; - use sui::balance::{Balance}; - use sui::clock::{Clock}; - use sui::object::{Self, ID, UID}; - use sui::package::{UpgradeCap, UpgradeReceipt, UpgradeTicket}; - use sui::sui::{SUI}; - use sui::table::{Self, Table}; - use sui::tx_context::{TxContext}; + use iota::balance::{Balance}; + use iota::clock::{Clock}; + use iota::object::{Self, ID, UID}; + use iota::package::{UpgradeCap, UpgradeReceipt, UpgradeTicket}; + use iota::iota::{IOTA}; + use iota::table::{Self, Table}; + use iota::tx_context::{TxContext}; use wormhole::bytes32::{Self, Bytes32}; use wormhole::consumed_vaas::{Self, ConsumedVAAs}; @@ -41,7 +41,7 @@ module wormhole::state { /// Build digest does not agree with current implementation. const E_INVALID_BUILD_DIGEST: u64 = 1; - /// Sui's chain ID is hard-coded to one value. + /// Iota's chain ID is hard-coded to one value. const CHAIN_ID: u16 = 50118; /// Capability reflecting that the current build version is used to invoke @@ -166,7 +166,7 @@ module wormhole::state { self.guardian_set_index } - /// Retrieve how long after a Guardian set can live for in terms of Sui + /// Retrieve how long after a Guardian set can live for in terms of Iota /// timestamp (in seconds). public fun guardian_set_seconds_to_live(self: &State): u32 { self.guardian_set_seconds_to_live @@ -199,7 +199,7 @@ module wormhole::state { } #[test_only] - public fun deposit_fee_test_only(self: &mut State, fee: Balance) { + public fun deposit_fee_test_only(self: &mut State, fee: Balance) { deposit_fee(&assert_latest_only(self), self, fee) } @@ -220,7 +220,7 @@ module wormhole::state { public fun test_upgrade(self: &mut State) { let test_digest = bytes32::from_bytes(b"new build"); let ticket = authorize_upgrade(self, test_digest); - let receipt = sui::package::test_upgrade(ticket); + let receipt = iota::package::test_upgrade(ticket); commit_upgrade(self, receipt); } @@ -270,7 +270,7 @@ module wormhole::state { public(friend) fun deposit_fee( _: &LatestOnly, self: &mut State, - fee: Balance + fee: Balance ) { fee_collector::deposit_balance(&mut self.fee_collector, fee); } @@ -283,7 +283,7 @@ module wormhole::state { _: &LatestOnly, self: &mut State, amount: u64 - ): Balance { + ): Balance { fee_collector::withdraw_balance(&mut self.fee_collector, amount) } @@ -378,7 +378,7 @@ module wormhole::state { /// Issue an `UpgradeTicket` for the upgrade. /// - /// NOTE: The Sui VM performs a check that this method is executed from the + /// NOTE: The Iota VM performs a check that this method is executed from the /// latest published package. If someone were to try to execute this using /// a stale build, the transaction will revert with `PackageUpgradeError`, /// specifically `PackageIDDoesNotMatch`. @@ -392,7 +392,7 @@ module wormhole::state { /// Finalize the upgrade that ran to produce the given `receipt`. /// - /// NOTE: The Sui VM performs a check that this method is executed from the + /// NOTE: The Iota VM performs a check that this method is executed from the /// latest published package. If someone were to try to execute this using /// a stale build, the transaction will revert with `PackageUpgradeError`, /// specifically `PackageIDDoesNotMatch`. diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/test/wormhole_scenario.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/test/wormhole_scenario.move index f587778e4c..cb4ba70403 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/test/wormhole_scenario.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/test/wormhole_scenario.move @@ -9,9 +9,9 @@ /// validate them with this test-only Wormhole instance. module wormhole::wormhole_scenario { use std::vector::{Self}; - use sui::clock::{Self, Clock}; - use sui::package::{UpgradeCap}; - use sui::test_scenario::{Self, Scenario}; + use iota::clock::{Self, Clock}; + use iota::package::{UpgradeCap}; + use iota::test_scenario::{Self, Scenario}; use wormhole::emitter::{EmitterCap}; use wormhole::governance_message::{Self, DecreeTicket, DecreeReceipt}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/package_utils.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/package_utils.move index 12dcfde91a..5c0e47c72e 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/package_utils.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/utils/package_utils.move @@ -1,12 +1,12 @@ // SPDX-License-Identifier: Apache 2 /// This module implements utilities that supplement those methods implemented -/// in `sui::package`. +/// in `iota::package`. module wormhole::package_utils { use std::type_name::{Self, TypeName}; - use sui::dynamic_field::{Self as field}; - use sui::object::{Self, ID, UID}; - use sui::package::{Self, UpgradeCap, UpgradeTicket, UpgradeReceipt}; + use iota::dynamic_field::{Self as field}; + use iota::object::{Self, ID, UID}; + use iota::package::{Self, UpgradeCap, UpgradeTicket, UpgradeReceipt}; use wormhole::bytes32::{Self, Bytes32}; @@ -70,8 +70,8 @@ module wormhole::package_utils { expected_version: u64 ) { let expected_package = - sui::address::from_bytes( - sui::hex::decode( + iota::address::from_bytes( + iota::hex::decode( std::ascii::into_bytes( std::type_name::get_address( &std::type_name::get() @@ -156,7 +156,7 @@ module wormhole::package_utils { update_package_info_from_pending(id); } - /// Helper for `sui::package::authorize_upgrade` to modify pending package + /// Helper for `iota::package::authorize_upgrade` to modify pending package /// info by updating its digest. /// /// NOTE: This digest will be copied over when `migrate_version` is called. @@ -178,7 +178,7 @@ module wormhole::package_utils { ) } - /// Helper for `sui::package::commit_upgrade` to modify pending package info + /// Helper for `iota::package::commit_upgrade` to modify pending package info /// by updating its package ID with from what exists in the `UpgradeCap`. /// This method returns the last package and the upgraded package IDs. /// @@ -276,8 +276,8 @@ module wormhole::package_utils { #[test_only] module wormhole::package_utils_tests { - use sui::object::{Self, UID}; - use sui::tx_context::{Self}; + use iota::object::{Self, UID}; + use iota::tx_context::{Self}; use wormhole::package_utils::{Self}; use wormhole::version_control::{Self}; diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/vaa.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/vaa.move index 03528351d4..0242bbf4c4 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/vaa.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/vaa.move @@ -22,8 +22,8 @@ module wormhole::vaa { use std::option::{Self}; use std::vector::{Self}; - use sui::clock::{Clock}; - use sui::hash::{keccak256}; + use iota::clock::{Clock}; + use iota::hash::{keccak256}; use wormhole::bytes::{Self}; use wormhole::bytes32::{Self, Bytes32}; @@ -262,7 +262,7 @@ module wormhole::vaa { } fun double_keccak256(buf: vector): Bytes32 { - use sui::hash::{keccak256}; + use iota::hash::{keccak256}; bytes32::new(keccak256(&keccak256(&buf))) } @@ -369,7 +369,7 @@ module wormhole::vaa { #[test_only] module wormhole::vaa_tests { use std::vector::{Self}; - use sui::test_scenario::{Self}; + use iota::test_scenario::{Self}; use wormhole::bytes32::{Self}; use wormhole::cursor::{Self}; @@ -587,12 +587,12 @@ module wormhole::vaa_tests { }; let expected_message_hash = - bytes32::new(sui::hash::keccak256(&body_buf)); + bytes32::new(iota::hash::keccak256(&body_buf)); assert!(vaa::compute_message_hash(&parsed) == expected_message_hash, 0); let expected_digest = bytes32::new( - sui::hash::keccak256(&sui::hash::keccak256(&body_buf)) + iota::hash::keccak256(&iota::hash::keccak256(&body_buf)) ); assert!(vaa::digest(&parsed) == expected_digest, 0); diff --git a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/version_control.move b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/version_control.move index 13ee6106df..6821d04072 100644 --- a/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/version_control.move +++ b/target_chains/sui/vendor/wormhole_iota_testnet/wormhole/sources/version_control.move @@ -38,7 +38,7 @@ module wormhole::version_control { // //////////////////////////////////////////////////////////////////////////// - /// First published package on Sui mainnet. + /// First published package on Iota mainnet. struct V__0_2_0 has store, drop, copy {} // Dummy. From 6b85129a35e66f00f6de015047ab79a321dd1236 Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Thu, 27 Feb 2025 13:17:32 +0100 Subject: [PATCH 05/10] feat(target_chains/sui): add iota js sdk --- target_chains/sui/sdk/js-iota/package.json | 8 ++++---- .../js-iota/src/SuiPriceServiceConnection.ts | 2 +- target_chains/sui/sdk/js-iota/src/client.ts | 20 +++++++++---------- .../sui/sdk/js-iota/src/examples/SuiRelay.ts | 16 +++++++-------- target_chains/sui/sdk/js-iota/src/index.ts | 4 ++-- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/target_chains/sui/sdk/js-iota/package.json b/target_chains/sui/sdk/js-iota/package.json index 21f06182ae..b3622e3949 100644 --- a/target_chains/sui/sdk/js-iota/package.json +++ b/target_chains/sui/sdk/js-iota/package.json @@ -1,7 +1,7 @@ { "name": "@pythnetwork/pyth-iota-js", "version": "2.1.0", - "description": "Pyth Network Sui Utilities", + "description": "Pyth Network IOTA Utilities", "homepage": "https://pyth.network", "author": { "name": "Pyth Data Association" @@ -14,7 +14,7 @@ "repository": { "type": "git", "url": "https://github.com/pyth-network/pyth-crosschain", - "directory": "target_chains/sui/sdk/js" + "directory": "target_chains/sui/sdk/js-iota" }, "publishConfig": { "access": "public" @@ -31,7 +31,7 @@ "keywords": [ "pyth", "oracle", - "sui" + "iota" ], "license": "Apache-2.0", "devDependencies": { @@ -52,7 +52,7 @@ "yargs": "^17.0.20" }, "dependencies": { - "@mysten/sui": "^1.3.0", + "@iota/iota-sdk": "^0.5.0", "@pythnetwork/price-service-client": "workspace:*", "buffer": "^6.0.3" } diff --git a/target_chains/sui/sdk/js-iota/src/SuiPriceServiceConnection.ts b/target_chains/sui/sdk/js-iota/src/SuiPriceServiceConnection.ts index 6d520cf3e3..c7292c2e60 100644 --- a/target_chains/sui/sdk/js-iota/src/SuiPriceServiceConnection.ts +++ b/target_chains/sui/sdk/js-iota/src/SuiPriceServiceConnection.ts @@ -4,7 +4,7 @@ import { } from "@pythnetwork/price-service-client"; import { Buffer } from "buffer"; -export class SuiPriceServiceConnection extends PriceServiceConnection { +export class IotaPriceServiceConnection extends PriceServiceConnection { /** * Gets price update data (either batch price attestation VAAs or accumulator messages, depending on the chosen endpoint), which then * can be submitted to the Pyth contract to update the prices. This will throw an axios error if there is a network problem or diff --git a/target_chains/sui/sdk/js-iota/src/client.ts b/target_chains/sui/sdk/js-iota/src/client.ts index f65e2e21aa..86797d695c 100644 --- a/target_chains/sui/sdk/js-iota/src/client.ts +++ b/target_chains/sui/sdk/js-iota/src/client.ts @@ -1,21 +1,21 @@ -import { SuiClient } from "@mysten/sui/client"; -import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui/utils"; -import { Transaction } from "@mysten/sui/transactions"; -import { bcs } from "@mysten/sui/bcs"; +import { IotaClient } from "@iota/iota-sdk/client"; +import { IOTA_CLOCK_OBJECT_ID } from "@iota/iota-sdk/utils"; +import { Transaction } from "@iota/iota-sdk/transactions"; +import { bcs } from "@iota/iota-sdk/bcs"; import { HexString } from "@pythnetwork/price-service-client"; import { Buffer } from "buffer"; const MAX_ARGUMENT_SIZE = 16 * 1024; export type ObjectId = string; -export class SuiPythClient { +export class IotaPythClient { private pythPackageId: ObjectId | undefined; private wormholePackageId: ObjectId | undefined; private priceTableInfo: { id: ObjectId; fieldType: ObjectId } | undefined; private priceFeedObjectIdCache: Map = new Map(); private baseUpdateFee: number | undefined; constructor( - public provider: SuiClient, + public provider: IotaClient, public pythStateId: ObjectId, public wormholeStateId: ObjectId ) { @@ -96,7 +96,7 @@ export class SuiPythClient { }) .toBytes() ), - tx.object(SUI_CLOCK_OBJECT_ID), + tx.object(IOTA_CLOCK_OBJECT_ID), ], }); verifiedVaas.push(verifiedVaa); @@ -138,7 +138,7 @@ export class SuiPythClient { .toBytes() ), verifiedVaas[0], - tx.object(SUI_CLOCK_OBJECT_ID), + tx.object(IOTA_CLOCK_OBJECT_ID), ], }); @@ -164,7 +164,7 @@ export class SuiPythClient { priceUpdatesHotPotato, tx.object(priceInfoObjectId), coins[coinId], - tx.object(SUI_CLOCK_OBJECT_ID), + tx.object(IOTA_CLOCK_OBJECT_ID), ], }); coinId++; @@ -198,7 +198,7 @@ export class SuiPythClient { .toBytes() ), verifiedVaas[0], - tx.object(SUI_CLOCK_OBJECT_ID), + tx.object(IOTA_CLOCK_OBJECT_ID), ], }); } diff --git a/target_chains/sui/sdk/js-iota/src/examples/SuiRelay.ts b/target_chains/sui/sdk/js-iota/src/examples/SuiRelay.ts index cb40f4b1e5..4862b1ae5d 100644 --- a/target_chains/sui/sdk/js-iota/src/examples/SuiRelay.ts +++ b/target_chains/sui/sdk/js-iota/src/examples/SuiRelay.ts @@ -1,12 +1,12 @@ import yargs from "yargs"; import { hideBin } from "yargs/helpers"; -import { SuiClient } from "@mysten/sui/client"; -import { Transaction } from "@mysten/sui/transactions"; -import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519"; +import { IotaClient } from "@iota/iota-sdk/client"; +import { Transaction } from "@iota/iota-sdk/transactions"; +import { Ed25519Keypair } from "@iota/iota-sdk/keypairs/ed25519"; import { Buffer } from "buffer"; -import { SuiPythClient } from "../client"; -import { SuiPriceServiceConnection } from "../index"; +import { IotaPythClient } from "../client"; +import { IotaPriceServiceConnection } from "../index"; const argvPromise = yargs(hideBin(process.argv)) .option("feed-id", { @@ -38,7 +38,7 @@ const argvPromise = yargs(hideBin(process.argv)) }).argv; export function getProvider(url: string) { - return new SuiClient({ url }); + return new IotaClient({ url }); } async function run() { if (process.env.SUI_KEY === undefined) { @@ -48,14 +48,14 @@ async function run() { const argv = await argvPromise; // Fetch the latest price feed update data from the Price Service - const connection = new SuiPriceServiceConnection(argv["hermes"]); + const connection = new IotaPriceServiceConnection(argv["hermes"]); const feeds = argv["feed-id"] as string[]; const provider = getProvider(argv["full-node"]); const wormholeStateId = argv["wormhole-state-id"]; const pythStateId = argv["pyth-state-id"]; - const client = new SuiPythClient(provider, pythStateId, wormholeStateId); + const client = new IotaPythClient(provider, pythStateId, wormholeStateId); const newFeeds = []; const existingFeeds = []; for (const feed of feeds) { diff --git a/target_chains/sui/sdk/js-iota/src/index.ts b/target_chains/sui/sdk/js-iota/src/index.ts index f4cfecf948..4036eaac0b 100644 --- a/target_chains/sui/sdk/js-iota/src/index.ts +++ b/target_chains/sui/sdk/js-iota/src/index.ts @@ -1,5 +1,5 @@ -export { SuiPriceServiceConnection } from "./SuiPriceServiceConnection"; -export { SuiPythClient } from "./client"; +export { IotaPriceServiceConnection } from "./SuiPriceServiceConnection"; +export { IotaPythClient } from "./client"; export { DurationInMs, From 2749ed7d6c5336a04ed23cdc7af7c5337b99e94f Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Thu, 27 Feb 2025 13:18:53 +0100 Subject: [PATCH 06/10] feat(contract_manager): add support for iota --- contract_manager/package.json | 2 + .../scripts/sync_wormhole_guardian_set.ts | 2 + contract_manager/src/chains.ts | 86 +- contract_manager/src/contracts/index.ts | 1 + contract_manager/src/contracts/iota.ts | 92 +- contract_manager/src/contracts/sui.ts | 23 +- contract_manager/src/store.ts | 7 + contract_manager/store/chains/IotaChains.yaml | 5 + .../contracts/IotaPriceFeedContracts.yaml | 4 + .../contracts/IotaWormholeContracts.yaml | 3 + .../store/contracts/SuiWormholeContracts.yaml | 8 +- pnpm-lock.yaml | 810 +++++++++++++----- 12 files changed, 764 insertions(+), 279 deletions(-) create mode 100644 contract_manager/store/chains/IotaChains.yaml create mode 100644 contract_manager/store/contracts/IotaPriceFeedContracts.yaml create mode 100644 contract_manager/store/contracts/IotaWormholeContracts.yaml diff --git a/contract_manager/package.json b/contract_manager/package.json index 676aed91bb..a0ea1ef7ea 100644 --- a/contract_manager/package.json +++ b/contract_manager/package.json @@ -26,6 +26,7 @@ "@cosmjs/cosmwasm-stargate": "^0.32.3", "@cosmjs/stargate": "^0.32.3", "@injectivelabs/networks": "^1.14.6", + "@iota/iota-sdk": "^0.5.0", "@mysten/sui": "^1.3.0", "@pythnetwork/client": "catalog:", "@pythnetwork/cosmwasm-deploy-tools": "workspace:*", @@ -36,6 +37,7 @@ "@pythnetwork/pyth-sdk-solidity": "workspace:^", "@pythnetwork/pyth-starknet-js": "^0.2.1", "@pythnetwork/pyth-sui-js": "workspace:*", + "@pythnetwork/pyth-iota-js": "workspace:*", "@pythnetwork/pyth-ton": "workspace:*", "@pythnetwork/pyth-ton-js": "workspace:*", "@pythnetwork/solana-utils": "workspace:^", diff --git a/contract_manager/scripts/sync_wormhole_guardian_set.ts b/contract_manager/scripts/sync_wormhole_guardian_set.ts index cf14a4af41..392cc83d71 100644 --- a/contract_manager/scripts/sync_wormhole_guardian_set.ts +++ b/contract_manager/scripts/sync_wormhole_guardian_set.ts @@ -5,6 +5,7 @@ import { CosmWasmPriceFeedContract, DefaultStore, EvmPriceFeedContract, + IotaWormholeContract, SuiWormholeContract, toPrivateKey, } from "../src"; @@ -32,6 +33,7 @@ async function main() { for (const contract of Object.values(DefaultStore.wormhole_contracts)) { if ( contract instanceof SuiWormholeContract || + contract instanceof IotaWormholeContract || contract instanceof AptosWormholeContract ) { if (chains && !chains.includes(contract.getChain().getId())) { diff --git a/contract_manager/src/chains.ts b/contract_manager/src/chains.ts index 7dcef89d70..783a0ea445 100644 --- a/contract_manager/src/chains.ts +++ b/contract_manager/src/chains.ts @@ -20,8 +20,10 @@ import { InjectiveExecutor, } from "@pythnetwork/cosmwasm-deploy-tools"; import { Network } from "@injectivelabs/networks"; +import { IotaClient } from "@iota/iota-sdk/client"; import { SuiClient } from "@mysten/sui/client"; -import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519"; +import { Ed25519Keypair as IotaEd25519Keypair } from "@iota/iota-sdk/keypairs/ed25519"; +import { Ed25519Keypair as SuiEd25519Keypair } from "@mysten/sui/keypairs/ed25519"; import { TokenId } from "./token"; import { BN, Provider, Wallet, WalletUnlocked } from "fuels"; import { FUEL_ETH_ASSET_ID } from "@pythnetwork/pyth-fuel-js"; @@ -38,6 +40,8 @@ import { keyPairFromSeed } from "@ton/crypto"; import { PythContract } from "@pythnetwork/pyth-ton-js"; import * as nearAPI from "near-api-js"; import * as bs58 from "bs58"; +import { MIST_PER_SUI } from "@mysten/sui/utils"; +import { NANOS_PER_IOTA } from "@iota/iota-sdk/utils"; /** * Returns the chain rpc url with any environment variables replaced or throws an error if any are missing @@ -333,8 +337,8 @@ export class SuiChain extends Chain { } async getAccountAddress(privateKey: PrivateKey): Promise { - const keypair = Ed25519Keypair.fromSecretKey( - Buffer.from(privateKey, "hex") + const keypair = SuiEd25519Keypair.fromSecretKey( + new Uint8Array(Buffer.from(privateKey, "hex")) ); return keypair.toSuiAddress(); } @@ -344,7 +348,73 @@ export class SuiChain extends Chain { const balance = await provider.getBalance({ owner: await this.getAccountAddress(privateKey), }); - return Number(balance.totalBalance) / 10 ** 9; + return Number(balance.totalBalance) / Number(MIST_PER_SUI); + } +} + +export class IotaChain extends Chain { + static type = "IotaChain"; + + constructor( + id: string, + mainnet: boolean, + wormholeChainName: string, + nativeToken: TokenId | undefined, + public rpcUrl: string + ) { + super(id, mainnet, wormholeChainName, nativeToken); + } + + static fromJson(parsed: ChainConfig): IotaChain { + if (parsed.type !== IotaChain.type) throw new Error("Invalid type"); + return new IotaChain( + parsed.id, + parsed.mainnet, + parsed.wormholeChainName, + parsed.nativeToken, + parsed.rpcUrl + ); + } + + toJson(): KeyValueConfig { + return { + id: this.id, + wormholeChainName: this.wormholeChainName, + mainnet: this.mainnet, + rpcUrl: this.rpcUrl, + type: IotaChain.type, + }; + } + + getType(): string { + return IotaChain.type; + } + + /** + * Returns the payload for a governance contract upgrade instruction for contracts deployed on this chain + * @param digest hex string of the 32 byte digest for the new package without the 0x prefix + */ + generateGovernanceUpgradePayload(digest: string): Buffer { + return new UpgradeContract256Bit(this.wormholeChainName, digest).encode(); + } + + getProvider(): IotaClient { + return new IotaClient({ url: this.rpcUrl }); + } + + async getAccountAddress(privateKey: PrivateKey): Promise { + const keypair = IotaEd25519Keypair.fromSecretKey( + new Uint8Array(Buffer.from(privateKey, "hex")) + ); + return keypair.toIotaAddress(); + } + + async getAccountBalance(privateKey: PrivateKey): Promise { + const provider = this.getProvider(); + const balance = await provider.getBalance({ + owner: await this.getAccountAddress(privateKey), + }); + return Number(balance.totalBalance) / Number(NANOS_PER_IOTA); } } @@ -912,7 +982,9 @@ export class NearChain extends Chain { async getAccountAddress(privateKey: PrivateKey): Promise { return Buffer.from( - Ed25519Keypair.fromSecretKey(Buffer.from(privateKey, "hex")) + SuiEd25519Keypair.fromSecretKey( + new Uint8Array(Buffer.from(privateKey, "hex")) + ) .getPublicKey() .toRawBytes() ).toString("hex"); @@ -931,7 +1003,9 @@ export class NearChain extends Chain { ): Promise { const keyStore = new nearAPI.keyStores.InMemoryKeyStore(); if (typeof senderPrivateKey !== "undefined") { - const key = bs58.encode(Buffer.from(senderPrivateKey, "hex")); + const key = bs58.encode( + new Uint8Array(Buffer.from(senderPrivateKey, "hex")) + ); const keyPair = nearAPI.KeyPair.fromString(key); const address = await this.getAccountAddress(senderPrivateKey); await keyStore.setKey(this.networkId, address, keyPair); diff --git a/contract_manager/src/contracts/index.ts b/contract_manager/src/contracts/index.ts index 7c19c8cd16..a7af983492 100644 --- a/contract_manager/src/contracts/index.ts +++ b/contract_manager/src/contracts/index.ts @@ -3,6 +3,7 @@ export * from "./cosmwasm"; export * from "./evm"; export * from "./fuel"; export * from "./sui"; +export * from "./iota"; export * from "./wormhole"; export * from "./evm_abis"; export * from "./ton"; diff --git a/contract_manager/src/contracts/iota.ts b/contract_manager/src/contracts/iota.ts index af8f7d17a6..fc98d049d8 100644 --- a/contract_manager/src/contracts/iota.ts +++ b/contract_manager/src/contracts/iota.ts @@ -1,21 +1,21 @@ -import { Chain, SuiChain } from "../chains"; +import { Chain, IotaChain } from "../chains"; import { DataSource } from "@pythnetwork/xc-admin-common"; import { WormholeContract } from "./wormhole"; import { PriceFeedContract, PrivateKey, TxResult } from "../base"; -import { SuiPythClient } from "@pythnetwork/pyth-sui-js"; -import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui/utils"; -import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519"; -import { Transaction } from "@mysten/sui/transactions"; +import { IotaPythClient } from "@pythnetwork/pyth-iota-js"; +import { IOTA_CLOCK_OBJECT_ID } from "@iota/iota-sdk/utils"; +import { Ed25519Keypair } from "@iota/iota-sdk/keypairs/ed25519"; +import { Transaction } from "@iota/iota-sdk/transactions"; import { uint8ArrayToBCS } from "@certusone/wormhole-sdk/lib/cjs/sui"; type ObjectId = string; -export class SuiPriceFeedContract extends PriceFeedContract { - static type = "SuiPriceFeedContract"; - private client: SuiPythClient; +export class IotaPriceFeedContract extends PriceFeedContract { + static type = "IotaPriceFeedContract"; + private client: IotaPythClient; /** - * Given the ids of the pyth state and wormhole state, create a new SuiPriceFeedContract + * Given the ids of the pyth state and wormhole state, create a new IotaPriceFeedContract * The package ids are derived based on the state ids * * @param chain the chain which this contract is deployed on @@ -23,12 +23,12 @@ export class SuiPriceFeedContract extends PriceFeedContract { * @param wormholeStateId id of the wormhole state for the wormhole contract that pyth binds to */ constructor( - public chain: SuiChain, + public chain: IotaChain, public stateId: string, public wormholeStateId: string ) { super(); - this.client = new SuiPythClient( + this.client = new IotaPythClient( this.getProvider(), this.stateId, this.wormholeStateId @@ -38,12 +38,12 @@ export class SuiPriceFeedContract extends PriceFeedContract { static fromJson( chain: Chain, parsed: { type: string; stateId: string; wormholeStateId: string } - ): SuiPriceFeedContract { - if (parsed.type !== SuiPriceFeedContract.type) + ): IotaPriceFeedContract { + if (parsed.type !== IotaPriceFeedContract.type) throw new Error("Invalid type"); - if (!(chain instanceof SuiChain)) + if (!(chain instanceof IotaChain)) throw new Error(`Wrong chain type ${chain}`); - return new SuiPriceFeedContract( + return new IotaPriceFeedContract( chain, parsed.stateId, parsed.wormholeStateId @@ -51,10 +51,10 @@ export class SuiPriceFeedContract extends PriceFeedContract { } getType(): string { - return SuiPriceFeedContract.type; + return IotaPriceFeedContract.type; } - getChain(): SuiChain { + getChain(): IotaChain { return this.chain; } @@ -63,7 +63,7 @@ export class SuiPriceFeedContract extends PriceFeedContract { chain: this.chain.getId(), stateId: this.stateId, wormholeStateId: this.wormholeStateId, - type: SuiPriceFeedContract.type, + type: IotaPriceFeedContract.type, }; } @@ -180,7 +180,7 @@ export class SuiPriceFeedContract extends PriceFeedContract { const tx = new Transaction(); await this.client.updatePriceFeeds(tx, vaas, feedIds); const keypair = Ed25519Keypair.fromSecretKey( - Buffer.from(senderPrivateKey, "hex") + new Uint8Array(Buffer.from(senderPrivateKey, "hex")) ); const result = await this.executeTransaction(tx, keypair); return { id: result.digest, info: result }; @@ -192,7 +192,7 @@ export class SuiPriceFeedContract extends PriceFeedContract { const tx = new Transaction(); await this.client.createPriceFeed(tx, vaas); const keypair = Ed25519Keypair.fromSecretKey( - Buffer.from(senderPrivateKey, "hex") + new Uint8Array(Buffer.from(senderPrivateKey, "hex")) ); const result = await this.executeTransaction(tx, keypair); @@ -204,7 +204,7 @@ export class SuiPriceFeedContract extends PriceFeedContract { vaa: Buffer ): Promise { const keypair = Ed25519Keypair.fromSecretKey( - Buffer.from(senderPrivateKey, "hex") + new Uint8Array(Buffer.from(senderPrivateKey, "hex")) ); const tx = new Transaction(); const packageId = await this.getPythPackageId(); @@ -277,7 +277,7 @@ export class SuiPriceFeedContract extends PriceFeedContract { arguments: [ tx.object(this.wormholeStateId), tx.pure.arguments(Array.from(vaa)), - tx.object(SUI_CLOCK_OBJECT_ID), + tx.object(IOTA_CLOCK_OBJECT_ID), ], }); @@ -297,7 +297,7 @@ export class SuiPriceFeedContract extends PriceFeedContract { */ private async executeTransaction(tx: Transaction, keypair: Ed25519Keypair) { const provider = this.getProvider(); - tx.setSender(keypair.toSuiAddress()); + tx.setSender(keypair.toIotaAddress()); const dryRun = await provider.dryRunTransactionBlock({ transactionBlock: await tx.build({ client: provider }), }); @@ -405,23 +405,23 @@ export class SuiPriceFeedContract extends PriceFeedContract { } } -export class SuiWormholeContract extends WormholeContract { - public static type = "SuiWormholeContract"; - private client: SuiPythClient; +export class IotaWormholeContract extends WormholeContract { + public static type = "IotaWormholeContract"; + private client: IotaPythClient; getId(): string { - return `${this.chain.getId()}_${this.address}`; + return `${this.chain.getId()}_${this.stateId}`; } getType(): string { - return SuiWormholeContract.type; + return IotaWormholeContract.type; } toJson() { return { chain: this.chain.getId(), - address: this.address, - type: SuiWormholeContract.type, + stateId: this.stateId, + type: IotaWormholeContract.type, }; } @@ -432,24 +432,20 @@ export class SuiWormholeContract extends WormholeContract { address: string; stateId: string; } - ): SuiWormholeContract { - if (parsed.type !== SuiWormholeContract.type) + ): IotaWormholeContract { + if (parsed.type !== IotaWormholeContract.type) throw new Error("Invalid type"); - if (!(chain instanceof SuiChain)) + if (!(chain instanceof IotaChain)) throw new Error(`Wrong chain type ${chain}`); - return new SuiWormholeContract(chain, parsed.address, parsed.stateId); + return new IotaWormholeContract(chain, parsed.stateId); } - constructor( - public chain: SuiChain, - public address: string, - public stateId: string - ) { + constructor(public chain: IotaChain, public stateId: string) { super(); - this.client = new SuiPythClient( + this.client = new IotaPythClient( this.chain.getProvider(), // HACK: - // We're using the SuiPythClient to work with the Wormhole contract + // We're using the IotaPythClient to work with the Wormhole contract // so there is no Pyth contract here, passing empty string to type- // check. "", @@ -463,7 +459,7 @@ export class SuiWormholeContract extends WormholeContract { } // There doesn't seem to be a way to get a value out of any function call - // via a Sui transaction due to the linear nature of the language, this is + // via a Iota transaction due to the linear nature of the language, this is // enforced at the TransactionBlock level by only allowing you to receive // receipts. async getChainId(): Promise { @@ -472,7 +468,7 @@ export class SuiWormholeContract extends WormholeContract { // NOTE: There's no way to getChain() on the main interface, should update // that interface. - public getChain(): SuiChain { + public getChain(): IotaChain { return this.chain; } @@ -493,8 +489,8 @@ export class SuiWormholeContract extends WormholeContract { target: `${corePackageId}::vaa::parse_and_verify`, arguments: [ tx.object(coreObjectId), - tx.pure(uint8ArrayToBCS(vaa)), - tx.object(SUI_CLOCK_OBJECT_ID), + tx.pure(uint8ArrayToBCS(new Uint8Array(vaa))), + tx.object(IOTA_CLOCK_OBJECT_ID), ], }); @@ -516,12 +512,12 @@ export class SuiWormholeContract extends WormholeContract { arguments: [ tx.object(coreObjectId), decreeReceipt, - tx.object(SUI_CLOCK_OBJECT_ID), + tx.object(IOTA_CLOCK_OBJECT_ID), ], }); const keypair = Ed25519Keypair.fromSecretKey( - Buffer.from(senderPrivateKey, "hex") + new Uint8Array(Buffer.from(senderPrivateKey, "hex")) ); const result = await this.executeTransaction(tx, keypair); return { id: result.digest, info: result }; @@ -551,7 +547,7 @@ export class SuiWormholeContract extends WormholeContract { */ private async executeTransaction(tx: Transaction, keypair: Ed25519Keypair) { const provider = this.chain.getProvider(); - tx.setSender(keypair.toSuiAddress()); + tx.setSender(keypair.toIotaAddress()); const dryRun = await provider.dryRunTransactionBlock({ transactionBlock: await tx.build({ client: provider }), }); diff --git a/contract_manager/src/contracts/sui.ts b/contract_manager/src/contracts/sui.ts index af8f7d17a6..0a7d9291f9 100644 --- a/contract_manager/src/contracts/sui.ts +++ b/contract_manager/src/contracts/sui.ts @@ -180,7 +180,7 @@ export class SuiPriceFeedContract extends PriceFeedContract { const tx = new Transaction(); await this.client.updatePriceFeeds(tx, vaas, feedIds); const keypair = Ed25519Keypair.fromSecretKey( - Buffer.from(senderPrivateKey, "hex") + new Uint8Array(Buffer.from(senderPrivateKey, "hex")) ); const result = await this.executeTransaction(tx, keypair); return { id: result.digest, info: result }; @@ -192,7 +192,7 @@ export class SuiPriceFeedContract extends PriceFeedContract { const tx = new Transaction(); await this.client.createPriceFeed(tx, vaas); const keypair = Ed25519Keypair.fromSecretKey( - Buffer.from(senderPrivateKey, "hex") + new Uint8Array(Buffer.from(senderPrivateKey, "hex")) ); const result = await this.executeTransaction(tx, keypair); @@ -204,7 +204,7 @@ export class SuiPriceFeedContract extends PriceFeedContract { vaa: Buffer ): Promise { const keypair = Ed25519Keypair.fromSecretKey( - Buffer.from(senderPrivateKey, "hex") + new Uint8Array(Buffer.from(senderPrivateKey, "hex")) ); const tx = new Transaction(); const packageId = await this.getPythPackageId(); @@ -410,7 +410,7 @@ export class SuiWormholeContract extends WormholeContract { private client: SuiPythClient; getId(): string { - return `${this.chain.getId()}_${this.address}`; + return `${this.chain.getId()}_${this.stateId}`; } getType(): string { @@ -420,7 +420,7 @@ export class SuiWormholeContract extends WormholeContract { toJson() { return { chain: this.chain.getId(), - address: this.address, + stateId: this.stateId, type: SuiWormholeContract.type, }; } @@ -429,7 +429,6 @@ export class SuiWormholeContract extends WormholeContract { chain: Chain, parsed: { type: string; - address: string; stateId: string; } ): SuiWormholeContract { @@ -437,14 +436,10 @@ export class SuiWormholeContract extends WormholeContract { throw new Error("Invalid type"); if (!(chain instanceof SuiChain)) throw new Error(`Wrong chain type ${chain}`); - return new SuiWormholeContract(chain, parsed.address, parsed.stateId); + return new SuiWormholeContract(chain, parsed.stateId); } - constructor( - public chain: SuiChain, - public address: string, - public stateId: string - ) { + constructor(public chain: SuiChain, public stateId: string) { super(); this.client = new SuiPythClient( this.chain.getProvider(), @@ -493,7 +488,7 @@ export class SuiWormholeContract extends WormholeContract { target: `${corePackageId}::vaa::parse_and_verify`, arguments: [ tx.object(coreObjectId), - tx.pure(uint8ArrayToBCS(vaa)), + tx.pure(uint8ArrayToBCS(new Uint8Array(vaa))), tx.object(SUI_CLOCK_OBJECT_ID), ], }); @@ -521,7 +516,7 @@ export class SuiWormholeContract extends WormholeContract { }); const keypair = Ed25519Keypair.fromSecretKey( - Buffer.from(senderPrivateKey, "hex") + new Uint8Array(Buffer.from(senderPrivateKey, "hex")) ); const result = await this.executeTransaction(tx, keypair); return { id: result.digest, info: result }; diff --git a/contract_manager/src/store.ts b/contract_manager/src/store.ts index 1c787e3a24..6286e5e1cb 100644 --- a/contract_manager/src/store.ts +++ b/contract_manager/src/store.ts @@ -9,6 +9,7 @@ import { SuiChain, TonChain, NearChain, + IotaChain, } from "./chains"; import { AptosPriceFeedContract, @@ -26,6 +27,8 @@ import { EvmExpressRelayContract, TonPriceFeedContract, TonWormholeContract, + IotaWormholeContract, + IotaPriceFeedContract, } from "./contracts"; import { Token } from "./token"; import { PriceFeedContract, Storable } from "./base"; @@ -88,6 +91,8 @@ export class Store { [StarknetChain.type]: StarknetChain, [TonChain.type]: TonChain, [NearChain.type]: NearChain, + [SuiChain.type]: SuiChain, + [IotaChain.type]: IotaChain, }; this.getYamlFiles(`${this.path}/chains/`).forEach((yamlFile) => { @@ -161,6 +166,8 @@ export class Store { [TonWormholeContract.type]: TonWormholeContract, [NearPriceFeedContract.type]: NearPriceFeedContract, [NearWormholeContract.type]: NearWormholeContract, + [IotaPriceFeedContract.type]: IotaPriceFeedContract, + [IotaWormholeContract.type]: IotaWormholeContract, }; this.getYamlFiles(`${this.path}/contracts/`).forEach((yamlFile) => { const parsedArray = parse(readFileSync(yamlFile, "utf-8")); diff --git a/contract_manager/store/chains/IotaChains.yaml b/contract_manager/store/chains/IotaChains.yaml new file mode 100644 index 0000000000..a9c23fa20e --- /dev/null +++ b/contract_manager/store/chains/IotaChains.yaml @@ -0,0 +1,5 @@ +- id: iota_testnet + wormholeChainName: iota_sui_testnet + mainnet: false + rpcUrl: https://api.testnet.iota.cafe/ + type: IotaChain diff --git a/contract_manager/store/contracts/IotaPriceFeedContracts.yaml b/contract_manager/store/contracts/IotaPriceFeedContracts.yaml new file mode 100644 index 0000000000..5819137d92 --- /dev/null +++ b/contract_manager/store/contracts/IotaPriceFeedContracts.yaml @@ -0,0 +1,4 @@ +- chain: iota_testnet + stateId: "0x68dda579251917b3db28e35c4df495c6e664ccc085ede867a9b773c8ebedc2c1" + wormholeStateId: "0x8bc490f69520a97ca1b3de864c96aa2265a0cf5d90f5f3f016b2eddf0cf2af2b" + type: IotaPriceFeedContract diff --git a/contract_manager/store/contracts/IotaWormholeContracts.yaml b/contract_manager/store/contracts/IotaWormholeContracts.yaml new file mode 100644 index 0000000000..132613eb1e --- /dev/null +++ b/contract_manager/store/contracts/IotaWormholeContracts.yaml @@ -0,0 +1,3 @@ +- chain: iota_testnet + stateId: "0x8bc490f69520a97ca1b3de864c96aa2265a0cf5d90f5f3f016b2eddf0cf2af2b" + type: IotaWormholeContract diff --git a/contract_manager/store/contracts/SuiWormholeContracts.yaml b/contract_manager/store/contracts/SuiWormholeContracts.yaml index d2cea478e8..97cb01daae 100644 --- a/contract_manager/store/contracts/SuiWormholeContracts.yaml +++ b/contract_manager/store/contracts/SuiWormholeContracts.yaml @@ -1,3 +1,9 @@ +- chain: sui_mainnet + stateId: "0xaeab97f96cf9877fee2883315d459552b2b921edc16d7ceac6eab944dd88919c" + type: SuiWormholeContract +- chain: sui_testnet + stateId: "0x31358d198147da50db32eda2562951d53973a0c0ad5ed738e9b17d88b213d790" + type: SuiWormholeContract - chain: movement_m2_devnet - address: "0x23a373b70e6e23a39e4846fa6896fa12beb08da061b3d4ec856bc8ead54f1e22" + stateId: "0xcf185fbc1af3a437a600587e0b39e5fede163336ffbb7ff24dca9b6eb19d2656" type: SuiWormholeContract diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7770c006f0..f072907440 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,195 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +catalogs: + default: + '@amplitude/analytics-browser': + specifier: 2.11.8 + version: 2.11.8 + '@amplitude/plugin-autocapture-browser': + specifier: 1.0.0 + version: 1.0.0 + '@axe-core/react': + specifier: 4.9.1 + version: 4.9.1 + '@clickhouse/client': + specifier: 1.8.0 + version: 1.8.0 + '@cprussin/eslint-config': + specifier: 3.0.0 + version: 3.0.0 + '@cprussin/jest-config': + specifier: 1.4.1 + version: 1.4.1 + '@cprussin/prettier-config': + specifier: 2.1.1 + version: 2.1.1 + '@cprussin/tsconfig': + specifier: 3.0.1 + version: 3.0.1 + '@next/third-parties': + specifier: 15.0.2 + version: 15.0.2 + '@phosphor-icons/react': + specifier: 2.1.7 + version: 2.1.7 + '@pythnetwork/client': + specifier: 2.22.1 + version: 2.22.1 + '@react-hookz/web': + specifier: 24.0.4 + version: 24.0.4 + '@solana/web3.js': + specifier: 1.95.4 + version: 1.95.4 + '@storybook/addon-essentials': + specifier: 8.3.5 + version: 8.3.5 + '@storybook/addon-styling-webpack': + specifier: 1.0.0 + version: 1.0.0 + '@storybook/addon-themes': + specifier: 8.3.5 + version: 8.3.5 + '@storybook/blocks': + specifier: 8.3.5 + version: 8.3.5 + '@storybook/nextjs': + specifier: 8.3.5 + version: 8.3.5 + '@storybook/react': + specifier: 8.3.5 + version: 8.3.5 + '@svgr/webpack': + specifier: 8.1.0 + version: 8.1.0 + '@types/jest': + specifier: 29.5.14 + version: 29.5.14 + '@types/node': + specifier: 22.8.2 + version: 22.8.2 + '@types/react': + specifier: 19.0.1 + version: 19.0.1 + '@types/react-dom': + specifier: 19.0.2 + version: 19.0.2 + autoprefixer: + specifier: 10.4.20 + version: 10.4.20 + bcp-47: + specifier: 2.1.0 + version: 2.1.0 + bs58: + specifier: 6.0.0 + version: 6.0.0 + clsx: + specifier: 2.1.1 + version: 2.1.1 + cryptocurrency-icons: + specifier: 0.18.1 + version: 0.18.1 + css-loader: + specifier: 7.1.2 + version: 7.1.2 + dnum: + specifier: 2.14.0 + version: 2.14.0 + eslint: + specifier: 9.13.0 + version: 9.13.0 + framer-motion: + specifier: 11.11.10 + version: 11.11.10 + jest: + specifier: 29.7.0 + version: 29.7.0 + lightweight-charts: + specifier: ^4.2.3 + version: 4.2.3 + modern-normalize: + specifier: 3.0.1 + version: 3.0.1 + motion: + specifier: 11.14.4 + version: 11.14.4 + next: + specifier: 15.1.2 + version: 15.1.2 + next-themes: + specifier: 0.3.0 + version: 0.3.0 + nuqs: + specifier: 2.1.2 + version: 2.1.2 + pino: + specifier: 9.5.0 + version: 9.5.0 + postcss: + specifier: 8.4.47 + version: 8.4.47 + postcss-loader: + specifier: 8.1.1 + version: 8.1.1 + prettier: + specifier: 3.3.3 + version: 3.3.3 + react: + specifier: 19.0.0 + version: 19.0.0 + react-aria: + specifier: 3.36.0 + version: 3.36.0 + react-aria-components: + specifier: 1.5.0 + version: 1.5.0 + react-dom: + specifier: 19.0.0 + version: 19.0.0 + recharts: + specifier: 2.14.1 + version: 2.14.1 + sass: + specifier: 1.80.7 + version: 1.80.7 + sass-loader: + specifier: 16.0.3 + version: 16.0.3 + storybook: + specifier: 8.3.5 + version: 8.3.5 + style-loader: + specifier: 4.0.0 + version: 4.0.0 + stylelint: + specifier: 16.10.0 + version: 16.10.0 + stylelint-config-standard-scss: + specifier: 13.1.0 + version: 13.1.0 + superjson: + specifier: 2.2.2 + version: 2.2.2 + swr: + specifier: 2.2.5 + version: 2.2.5 + tailwindcss: + specifier: 3.4.14 + version: 3.4.14 + typescript: + specifier: 5.6.3 + version: 5.6.3 + vercel: + specifier: 37.12.1 + version: 37.12.1 + zod: + specifier: 3.23.8 + version: 3.23.8 + zod-validation-error: + specifier: 3.4.0 + version: 3.4.0 + overrides: '@injectivelabs/sdk-ts@1.10.72>@injectivelabs/token-metadata': 1.10.42 eslint-config-next>@typescript-eslint/parser: ^7.0.0 @@ -748,6 +937,9 @@ importers: '@injectivelabs/networks': specifier: ^1.14.6 version: 1.14.6(google-protobuf@3.21.4) + '@iota/iota-sdk': + specifier: ^0.5.0 + version: 0.5.0(svelte@4.2.18)(typescript@5.5.4) '@mysten/sui': specifier: ^1.3.0 version: 1.3.0(svelte@4.2.18)(typescript@5.5.4) @@ -769,6 +961,9 @@ importers: '@pythnetwork/pyth-fuel-js': specifier: workspace:* version: link:../target_chains/fuel/sdk/js + '@pythnetwork/pyth-iota-js': + specifier: workspace:* + version: link:../target_chains/sui/sdk/js-iota '@pythnetwork/pyth-sdk-solidity': specifier: workspace:^ version: link:../target_chains/ethereum/sdk/solidity @@ -2445,43 +2640,6 @@ importers: specifier: ^17.0.32 version: 17.0.33 - target_chains/sui/cli-iota: - dependencies: - '@certusone/wormhole-sdk': - specifier: ^0.9.12 - version: 0.9.24(bufferutil@4.0.8)(encoding@0.1.13)(google-protobuf@3.21.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@5.0.10) - '@iota/iota-sdk': - specifier: ^0.5.0 - version: 0.5.0(svelte@4.2.18)(typescript@5.6.3) - '@pythnetwork/contract-manager': - specifier: workspace:* - version: link:../../../contract_manager - '@pythnetwork/price-service-client': - specifier: ^1.4.0 - version: 1.9.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@pythnetwork/price-service-sdk': - specifier: ^1.2.0 - version: 1.7.1 - '@pythnetwork/xc-admin-common': - specifier: workspace:* - version: link:../../../governance/xc_admin/packages/xc_admin_common - prettier: - specifier: ^2.8.7 - version: 2.8.8 - ts-node: - specifier: ^10.9.1 - version: 10.9.2(@types/node@22.8.2)(typescript@5.6.3) - typescript: - specifier: ^5.0.4 - version: 5.6.3 - yargs: - specifier: ^17.7.2 - version: 17.7.2 - devDependencies: - '@types/yargs': - specifier: ^17.0.32 - version: 17.0.33 - target_chains/sui/sdk/js: dependencies: '@mysten/sui': @@ -2540,6 +2698,64 @@ importers: specifier: ^17.0.20 version: 17.7.2 + target_chains/sui/sdk/js-iota: + dependencies: + '@iota/iota-sdk': + specifier: ^0.5.0 + version: 0.5.0(svelte@4.2.18)(typescript@5.6.3) + '@pythnetwork/price-service-client': + specifier: workspace:* + version: link:../../../../price_service/client/js + buffer: + specifier: ^6.0.3 + version: 6.0.3 + devDependencies: + '@truffle/hdwallet-provider': + specifier: ^2.1.5 + version: 2.1.15(@babel/core@7.25.8)(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + '@types/ethereum-protocol': + specifier: ^1.0.2 + version: 1.0.2 + '@types/jest': + specifier: ^29.4.0 + version: 29.5.14 + '@types/node': + specifier: ^18.11.18 + version: 18.19.57 + '@types/web3-provider-engine': + specifier: ^14.0.1 + version: 14.0.1 + '@types/yargs': + specifier: ^17.0.20 + version: 17.0.33 + '@typescript-eslint/eslint-plugin': + specifier: ^6.0.0 + version: 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.6.3))(eslint@8.56.0)(typescript@5.6.3) + '@typescript-eslint/parser': + specifier: ^6.0.0 + version: 6.21.0(eslint@8.56.0)(typescript@5.6.3) + eslint: + specifier: ^8.14.0 + version: 8.56.0 + jest: + specifier: ^29.4.1 + version: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)) + prettier: + specifier: ^2.6.2 + version: 2.8.8 + ts-jest: + specifier: ^29.0.5 + version: 29.2.4(@babel/core@7.25.8)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.8))(jest@29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)))(typescript@5.6.3) + typescript: + specifier: ^5.3.3 + version: 5.6.3 + web3: + specifier: ^1.8.2 + version: 1.10.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) + yargs: + specifier: ^17.0.20 + version: 17.7.2 + target_chains/ton/contracts: devDependencies: '@pythnetwork/hermes-client': @@ -22328,41 +22544,6 @@ snapshots: - subscriptions-transport-ws - utf-8-validate - '@certusone/wormhole-sdk@0.9.24(bufferutil@4.0.8)(encoding@0.1.13)(google-protobuf@3.21.4)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@5.0.10)': - dependencies: - '@certusone/wormhole-sdk-proto-web': 0.0.6(google-protobuf@3.21.4) - '@certusone/wormhole-sdk-wasm': 0.0.1 - '@coral-xyz/borsh': 0.2.6(@solana/web3.js@1.92.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) - '@mysten/sui.js': 0.32.2(bufferutil@4.0.8)(utf-8-validate@5.0.10) - '@project-serum/anchor': 0.25.0(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) - '@solana/spl-token': 0.3.7(@solana/web3.js@1.92.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.92.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10) - '@terra-money/terra.js': 3.1.9 - '@xpla/xpla.js': 0.2.3 - algosdk: 2.7.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.6 - js-base64: 3.7.5 - near-api-js: 1.1.0(encoding@0.1.13) - optionalDependencies: - '@injectivelabs/networks': 1.10.12(google-protobuf@3.21.4) - '@injectivelabs/sdk-ts': 1.10.72(bufferutil@4.0.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@5.0.10) - '@injectivelabs/utils': 1.10.12(google-protobuf@3.21.4) - transitivePeerDependencies: - - bufferutil - - debug - - encoding - - google-protobuf - - graphql-ws - - react - - react-dom - - subscriptions-transport-ws - - utf-8-validate - '@certusone/wormhole-sdk@0.9.24(bufferutil@4.0.8)(encoding@0.1.13)(google-protobuf@3.21.4)(utf-8-validate@5.0.10)': dependencies: '@certusone/wormhole-sdk-proto-web': 0.0.6(google-protobuf@3.21.4) @@ -22907,7 +23088,7 @@ snapshots: '@cosmjs/socket': 0.30.1(bufferutil@4.0.8)(utf-8-validate@5.0.10) '@cosmjs/stream': 0.30.1 '@cosmjs/utils': 0.30.1 - axios: 0.21.4 + axios: 0.21.4(debug@4.3.7) readonly-date: 1.0.0 xstream: 11.14.0 transitivePeerDependencies: @@ -24123,7 +24304,7 @@ snapshots: '@fuel-ts/utils': 0.94.5 '@fuel-ts/versions': 0.94.5 '@fuels/vm-asm': 0.56.0 - '@noble/curves': 1.7.0 + '@noble/curves': 1.8.1 events: 3.3.0 graphql: 16.9.0 graphql-request: 5.0.0(encoding@0.1.13)(graphql@16.9.0) @@ -24148,7 +24329,7 @@ snapshots: '@fuel-ts/utils': 0.96.1 '@fuel-ts/versions': 0.96.1 '@fuels/vm-asm': 0.58.0 - '@noble/curves': 1.7.0 + '@noble/curves': 1.8.1 events: 3.3.0 graphql: 16.9.0 graphql-request: 5.0.0(encoding@0.1.13)(graphql@16.9.0) @@ -24163,7 +24344,7 @@ snapshots: '@fuel-ts/errors': 0.94.5 '@fuel-ts/interfaces': 0.94.5 '@fuel-ts/utils': 0.94.5 - '@noble/hashes': 1.6.1 + '@noble/hashes': 1.7.1 bech32: 2.0.0 '@fuel-ts/address@0.96.1': @@ -24172,7 +24353,7 @@ snapshots: '@fuel-ts/errors': 0.96.1 '@fuel-ts/interfaces': 0.96.1 '@fuel-ts/utils': 0.96.1 - '@noble/hashes': 1.6.1 + '@noble/hashes': 1.7.1 bech32: 2.0.0 '@fuel-ts/contract@0.94.5(encoding@0.1.13)': @@ -24220,7 +24401,7 @@ snapshots: '@fuel-ts/interfaces': 0.94.5 '@fuel-ts/math': 0.94.5 '@fuel-ts/utils': 0.94.5 - '@noble/hashes': 1.6.1 + '@noble/hashes': 1.7.1 '@fuel-ts/crypto@0.96.1': dependencies: @@ -24228,7 +24409,7 @@ snapshots: '@fuel-ts/interfaces': 0.96.1 '@fuel-ts/math': 0.96.1 '@fuel-ts/utils': 0.96.1 - '@noble/hashes': 1.6.1 + '@noble/hashes': 1.7.1 '@fuel-ts/errors@0.94.5': dependencies: @@ -24243,14 +24424,14 @@ snapshots: '@fuel-ts/crypto': 0.94.5 '@fuel-ts/interfaces': 0.94.5 '@fuel-ts/utils': 0.94.5 - '@noble/hashes': 1.6.1 + '@noble/hashes': 1.7.1 '@fuel-ts/hasher@0.96.1': dependencies: '@fuel-ts/crypto': 0.96.1 '@fuel-ts/interfaces': 0.96.1 '@fuel-ts/utils': 0.96.1 - '@noble/hashes': 1.6.1 + '@noble/hashes': 1.7.1 '@fuel-ts/interfaces@0.94.5': {} @@ -24949,7 +25130,7 @@ snapshots: '@injectivelabs/test-utils@1.14.4': dependencies: - axios: 0.21.4 + axios: 0.21.4(debug@4.3.7) bignumber.js: 9.1.2 link-module-alias: 1.2.0 shx: 0.3.4 @@ -25001,7 +25182,7 @@ snapshots: dependencies: '@injectivelabs/exceptions': 1.14.6(google-protobuf@3.21.4) '@injectivelabs/ts-types': 1.14.6 - axios: 0.21.4 + axios: 0.21.4(debug@4.3.7) bignumber.js: 9.1.2 http-status-codes: 2.2.0 link-module-alias: 1.2.0 @@ -25017,7 +25198,7 @@ snapshots: dependencies: '@injectivelabs/exceptions': 1.14.6(google-protobuf@3.21.4) '@injectivelabs/ts-types': 1.14.6 - axios: 0.21.4 + axios: 0.21.4(debug@4.3.7) bignumber.js: 9.1.2 http-status-codes: 2.2.0 link-module-alias: 1.2.0 @@ -25049,6 +25230,24 @@ snapshots: dependencies: bs58: 6.0.0 + '@iota/iota-sdk@0.5.0(svelte@4.2.18)(typescript@5.5.4)': + dependencies: + '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0) + '@iota/bcs': 0.2.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 + '@suchipi/femver': 1.0.0 + bech32: 2.0.0 + gql.tada: 1.8.2(graphql@16.9.0)(svelte@4.2.18)(typescript@5.5.4) + graphql: 16.9.0 + tweetnacl: 1.0.3 + valibot: 0.36.0 + transitivePeerDependencies: + - svelte + - typescript + '@iota/iota-sdk@0.5.0(svelte@4.2.18)(typescript@5.6.3)': dependencies: '@graphql-typed-document-node/core': 3.2.0(graphql@16.9.0) @@ -25097,7 +25296,7 @@ snapshots: '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 @@ -25110,14 +25309,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@16.18.101)(typescript@4.9.5)) + jest-config: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@16.18.101)(typescript@4.9.5)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -25145,14 +25344,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@18.19.57)(typescript@4.9.5)) + jest-config: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@4.9.5)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -25180,14 +25379,49 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.5.4)) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3))': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 18.19.57 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.5.4)) + jest-config: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -25215,14 +25449,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)) + jest-config: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -25250,14 +25484,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.6.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -25285,14 +25519,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@22.8.2)(typescript@4.9.5)) + jest-config: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@22.8.2)(typescript@4.9.5)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -25320,14 +25554,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@22.8.2)(typescript@5.5.4)) + jest-config: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@22.8.2)(typescript@5.5.4)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -25355,14 +25589,14 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@22.8.2)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@22.8.2)(typescript@5.6.3)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -25391,7 +25625,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -25409,7 +25643,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 20.14.15 + '@types/node': 18.19.57 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -25431,7 +25665,7 @@ snapshots: '@jest/transform': 29.7.0 '@jest/types': 29.6.3 '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 20.14.15 + '@types/node': 18.19.57 chalk: 4.1.2 collect-v8-coverage: 1.0.2 exit: 0.1.2 @@ -25509,7 +25743,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 20.14.15 + '@types/node': 18.19.57 '@types/yargs': 17.0.33 chalk: 4.1.2 @@ -26189,8 +26423,8 @@ snapshots: dependencies: '@ethereumjs/tx': 4.2.0 '@metamask/superstruct': 3.0.0 - '@noble/hashes': 1.6.1 - '@scure/base': 1.2.1 + '@noble/hashes': 1.7.1 + '@scure/base': 1.2.4 '@types/debug': 4.1.12 debug: 4.3.7(supports-color@8.1.1) pony-cause: 2.1.11 @@ -26278,10 +26512,10 @@ snapshots: '@mysten/sui.js@0.32.2(bufferutil@4.0.7)(utf-8-validate@6.0.3)': dependencies: '@mysten/bcs': 0.7.1 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 - '@scure/bip32': 1.6.0 - '@scure/bip39': 1.5.0 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 '@suchipi/femver': 1.0.0 jayson: 4.1.1(bufferutil@4.0.7)(utf-8-validate@6.0.3) rpc-websockets: 7.5.1 @@ -26310,10 +26544,10 @@ snapshots: '@mysten/sui.js@0.32.2(bufferutil@4.0.8)(utf-8-validate@6.0.4)': dependencies: '@mysten/bcs': 0.7.1 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 - '@scure/bip32': 1.6.0 - '@scure/bip39': 1.5.0 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 '@suchipi/femver': 1.0.0 jayson: 4.1.1(bufferutil@4.0.8)(utf-8-validate@6.0.4) rpc-websockets: 7.5.1 @@ -27399,20 +27633,6 @@ snapshots: transitivePeerDependencies: - axios - '@pythnetwork/price-service-client@1.9.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)': - dependencies: - '@pythnetwork/price-service-sdk': 1.7.1 - '@types/ws': 8.5.13 - axios: 1.7.7 - axios-retry: 3.9.1 - isomorphic-ws: 4.0.1(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)) - ts-log: 2.2.7 - ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - transitivePeerDependencies: - - bufferutil - - debug - - utf-8-validate - '@pythnetwork/price-service-client@1.9.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)': dependencies: '@pythnetwork/price-service-sdk': 1.7.1 @@ -30356,7 +30576,7 @@ snapshots: '@solana/wallet-adapter-unsafe-burner@0.1.7(@solana/web3.js@1.92.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10))': dependencies: - '@noble/curves': 1.7.0 + '@noble/curves': 1.8.1 '@solana/wallet-adapter-base': 0.9.23(@solana/web3.js@1.92.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)) '@solana/wallet-standard-features': 1.2.0 '@solana/wallet-standard-util': 1.1.1 @@ -30556,7 +30776,7 @@ snapshots: '@solana/wallet-standard-util@1.1.1': dependencies: - '@noble/curves': 1.7.0 + '@noble/curves': 1.8.1 '@solana/wallet-standard-chains': 1.1.0 '@solana/wallet-standard-features': 1.2.0 @@ -30631,8 +30851,8 @@ snapshots: '@solana/web3.js@1.77.4(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@5.0.10)': dependencies: '@babel/runtime': 7.25.7 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 '@solana/buffer-layout': 4.0.1 agentkeepalive: 4.5.0 bigint-buffer: 1.1.5 @@ -30653,8 +30873,8 @@ snapshots: '@solana/web3.js@1.92.3(bufferutil@4.0.7)(encoding@0.1.13)(utf-8-validate@6.0.3)': dependencies: '@babel/runtime': 7.25.7 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 '@solana/buffer-layout': 4.0.1 agentkeepalive: 4.5.0 bigint-buffer: 1.1.5 @@ -30697,8 +30917,8 @@ snapshots: '@solana/web3.js@1.92.3(bufferutil@4.0.8)(encoding@0.1.13)(utf-8-validate@6.0.4)': dependencies: '@babel/runtime': 7.25.7 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 '@solana/buffer-layout': 4.0.1 agentkeepalive: 4.5.0 bigint-buffer: 1.1.5 @@ -30719,8 +30939,8 @@ snapshots: '@solana/web3.js@1.92.3(encoding@0.1.13)': dependencies: '@babel/runtime': 7.25.7 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 '@solana/buffer-layout': 4.0.1 agentkeepalive: 4.5.0 bigint-buffer: 1.1.5 @@ -32360,7 +32580,7 @@ snapshots: '@types/bn.js@4.11.6': dependencies: - '@types/node': 22.8.2 + '@types/node': 18.19.57 '@types/bn.js@5.1.1': dependencies: @@ -32368,7 +32588,7 @@ snapshots: '@types/bn.js@5.1.6': dependencies: - '@types/node': 22.8.2 + '@types/node': 18.19.57 '@types/body-parser@1.19.2': dependencies: @@ -32385,7 +32605,7 @@ snapshots: dependencies: '@types/http-cache-semantics': 4.0.1 '@types/keyv': 3.1.4 - '@types/node': 20.14.15 + '@types/node': 18.19.57 '@types/responselike': 1.0.0 '@types/chai@4.3.17': {} @@ -32496,7 +32716,7 @@ snapshots: '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 20.14.15 + '@types/node': 18.19.57 '@types/hast@2.3.10': dependencies: @@ -32544,7 +32764,7 @@ snapshots: '@types/keyv@3.1.4': dependencies: - '@types/node': 20.14.15 + '@types/node': 18.19.57 '@types/lodash.values@4.3.7': dependencies: @@ -32611,7 +32831,7 @@ snapshots: '@types/pbkdf2@3.1.0': dependencies: - '@types/node': 22.8.2 + '@types/node': 18.19.57 '@types/pino@7.0.5': dependencies: @@ -32640,11 +32860,11 @@ snapshots: '@types/responselike@1.0.0': dependencies: - '@types/node': 20.14.15 + '@types/node': 18.19.57 '@types/secp256k1@4.0.6': dependencies: - '@types/node': 22.8.2 + '@types/node': 18.19.57 '@types/seedrandom@3.0.1': {} @@ -32777,6 +32997,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.6.3))(eslint@8.56.0)(typescript@5.6.3)': + dependencies: + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 6.21.0(eslint@8.56.0)(typescript@5.6.3) + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/type-utils': 6.21.0(eslint@8.56.0)(typescript@5.6.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.7(supports-color@8.1.1) + eslint: 8.56.0 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.6.3) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/eslint-plugin@7.13.1(@typescript-eslint/parser@7.13.1(eslint@8.56.0)(typescript@5.5.4))(eslint@8.56.0)(typescript@5.5.4)': dependencies: '@eslint-community/regexpp': 4.11.0 @@ -32942,6 +33182,19 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/parser@6.21.0(eslint@8.56.0)(typescript@5.6.3)': + dependencies: + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.7(supports-color@8.1.1) + eslint: 8.56.0 + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@7.13.1(eslint@8.56.0)(typescript@5.5.4)': dependencies: '@typescript-eslint/scope-manager': 7.13.1 @@ -33082,6 +33335,18 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/type-utils@6.21.0(eslint@8.56.0)(typescript@5.6.3)': + dependencies: + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.6.3) + '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.6.3) + debug: 4.3.7(supports-color@8.1.1) + eslint: 8.56.0 + ts-api-utils: 1.3.0(typescript@5.6.3) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/type-utils@7.13.1(eslint@8.56.0)(typescript@5.5.4)': dependencies: '@typescript-eslint/typescript-estree': 7.13.1(typescript@5.5.4) @@ -33211,6 +33476,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@6.21.0(typescript@5.6.3)': + dependencies: + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/visitor-keys': 6.21.0 + debug: 4.3.7(supports-color@8.1.1) + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.3 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.6.3) + optionalDependencies: + typescript: 5.6.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/typescript-estree@7.13.1(typescript@5.5.4)': dependencies: '@typescript-eslint/types': 7.13.1 @@ -33346,6 +33626,20 @@ snapshots: - supports-color - typescript + '@typescript-eslint/utils@6.21.0(eslint@8.56.0)(typescript@5.6.3)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 6.21.0 + '@typescript-eslint/types': 6.21.0 + '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.6.3) + eslint: 8.56.0 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + - typescript + '@typescript-eslint/utils@7.13.1(eslint@8.56.0)(typescript@5.5.4)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) @@ -35093,12 +35387,6 @@ snapshots: axios: 1.7.7(debug@4.3.7) is-retry-allowed: 2.2.0 - axios@0.21.4: - dependencies: - follow-redirects: 1.15.9 - transitivePeerDependencies: - - debug - axios@0.21.4(debug@4.3.7): dependencies: follow-redirects: 1.15.9(debug@4.3.7) @@ -35107,7 +35395,7 @@ snapshots: axios@0.24.0: dependencies: - follow-redirects: 1.15.9 + follow-redirects: 1.15.9(debug@4.3.7) transitivePeerDependencies: - debug @@ -35119,13 +35407,13 @@ snapshots: axios@0.26.1: dependencies: - follow-redirects: 1.15.9 + follow-redirects: 1.15.9(debug@4.3.7) transitivePeerDependencies: - debug axios@0.27.2: dependencies: - follow-redirects: 1.15.9 + follow-redirects: 1.15.9(debug@4.3.7) form-data: 4.0.1 transitivePeerDependencies: - debug @@ -35146,14 +35434,6 @@ snapshots: transitivePeerDependencies: - debug - axios@1.7.7: - dependencies: - follow-redirects: 1.15.9 - form-data: 4.0.1 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - axios@1.7.7(debug@4.3.7): dependencies: follow-redirects: 1.15.9(debug@4.3.7) @@ -36572,6 +36852,21 @@ snapshots: - supports-color - ts-node + create-jest@29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + create-jest@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)): dependencies: '@jest/types': 29.6.3 @@ -38570,7 +38865,7 @@ snapshots: ethereum-bloom-filters@1.2.0: dependencies: - '@noble/hashes': 1.6.1 + '@noble/hashes': 1.7.1 ethereum-common@0.0.18: {} @@ -39234,8 +39529,6 @@ snapshots: flow-parser@0.250.0: {} - follow-redirects@1.15.9: {} - follow-redirects@1.15.9(debug@4.3.7): optionalDependencies: debug: 4.3.7(supports-color@8.1.1) @@ -40795,10 +41088,6 @@ snapshots: dependencies: ws: 7.5.10(bufferutil@4.0.8)(utf-8-validate@6.0.4) - isomorphic-ws@4.0.1(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10)): - dependencies: - ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@5.0.10) - isomorphic-ws@4.0.1(ws@8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4)): dependencies: ws: 8.18.0(bufferutil@4.0.8)(utf-8-validate@6.0.4) @@ -40977,7 +41266,7 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 chalk: 4.1.2 co: 4.6.0 dedent: 1.5.3 @@ -41054,6 +41343,25 @@ snapshots: - supports-color - ts-node + jest-cli@29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)) + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)) + exit: 0.1.2 + import-local: 3.1.0 + jest-config: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest-cli@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)) @@ -41180,6 +41488,37 @@ snapshots: - babel-plugin-macros - supports-color + jest-config@29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@16.18.101)(typescript@4.9.5)): + dependencies: + '@babel/core': 7.25.8 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.25.8) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 18.19.57 + ts-node: 10.9.2(@types/node@16.18.101)(typescript@4.9.5) + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-config@29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@4.9.5)): dependencies: '@babel/core': 7.25.8 @@ -41242,7 +41581,7 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@16.18.101)(typescript@4.9.5)): + jest-config@29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)): dependencies: '@babel/core': 7.25.8 '@jest/test-sequencer': 29.7.0 @@ -41267,13 +41606,13 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.14.15 - ts-node: 10.9.2(@types/node@16.18.101)(typescript@4.9.5) + '@types/node': 18.19.57 + ts-node: 10.9.2(@types/node@18.19.57)(typescript@5.6.3) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@18.19.57)(typescript@4.9.5)): + jest-config@29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)): dependencies: '@babel/core': 7.25.8 '@jest/test-sequencer': 29.7.0 @@ -41298,13 +41637,13 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.14.15 - ts-node: 10.9.2(@types/node@18.19.57)(typescript@4.9.5) + '@types/node': 18.19.57 + ts-node: 10.9.2(@types/node@20.14.15)(typescript@5.5.4) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.5.4)): + jest-config@29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.6.3)): dependencies: '@babel/core': 7.25.8 '@jest/test-sequencer': 29.7.0 @@ -41329,13 +41668,13 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.14.15 - ts-node: 10.9.2(@types/node@18.19.57)(typescript@5.5.4) + '@types/node': 18.19.57 + ts-node: 10.9.2(@types/node@20.14.15)(typescript@5.6.3) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)): + jest-config@29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@22.8.2)(typescript@4.9.5)): dependencies: '@babel/core': 7.25.8 '@jest/test-sequencer': 29.7.0 @@ -41360,13 +41699,13 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.14.15 - ts-node: 10.9.2(@types/node@20.14.15)(typescript@5.5.4) + '@types/node': 18.19.57 + ts-node: 10.9.2(@types/node@22.8.2)(typescript@4.9.5) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.6.3)): + jest-config@29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@22.8.2)(typescript@5.5.4)): dependencies: '@babel/core': 7.25.8 '@jest/test-sequencer': 29.7.0 @@ -41391,13 +41730,13 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.14.15 - ts-node: 10.9.2(@types/node@20.14.15)(typescript@5.6.3) + '@types/node': 18.19.57 + ts-node: 10.9.2(@types/node@22.8.2)(typescript@5.5.4) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@22.8.2)(typescript@4.9.5)): + jest-config@29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@22.8.2)(typescript@5.6.3)): dependencies: '@babel/core': 7.25.8 '@jest/test-sequencer': 29.7.0 @@ -41422,13 +41761,13 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 20.14.15 - ts-node: 10.9.2(@types/node@22.8.2)(typescript@4.9.5) + '@types/node': 18.19.57 + ts-node: 10.9.2(@types/node@22.8.2)(typescript@5.6.3) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@22.8.2)(typescript@5.5.4)): + jest-config@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)): dependencies: '@babel/core': 7.25.8 '@jest/test-sequencer': 29.7.0 @@ -41454,12 +41793,12 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.14.15 - ts-node: 10.9.2(@types/node@22.8.2)(typescript@5.5.4) + ts-node: 10.9.2(@types/node@20.14.15)(typescript@5.5.4) transitivePeerDependencies: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@22.8.2)(typescript@5.6.3)): + jest-config@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.6.3)): dependencies: '@babel/core': 7.25.8 '@jest/test-sequencer': 29.7.0 @@ -41485,7 +41824,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.14.15 - ts-node: 10.9.2(@types/node@22.8.2)(typescript@5.6.3) + ts-node: 10.9.2(@types/node@20.14.15)(typescript@5.6.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -41644,7 +41983,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -41656,7 +41995,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 20.14.15 + '@types/node': 18.19.57 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -41702,7 +42041,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -41773,7 +42112,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -41801,7 +42140,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 chalk: 4.1.2 cjs-module-lexer: 1.3.1 collect-v8-coverage: 1.0.2 @@ -41847,7 +42186,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -41866,7 +42205,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 20.14.15 + '@types/node': 18.19.57 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -41887,7 +42226,7 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 20.14.15 + '@types/node': 18.19.57 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -41928,6 +42267,18 @@ snapshots: - supports-color - ts-node + jest@29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)): + dependencies: + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)) + '@jest/types': 29.6.3 + import-local: 3.1.0 + jest-cli: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)): dependencies: '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)) @@ -44465,10 +44816,10 @@ snapshots: ox@0.6.5(typescript@5.5.4)(zod@3.23.8): dependencies: '@adraffy/ens-normalize': 1.11.0 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 - '@scure/bip32': 1.6.0 - '@scure/bip39': 1.5.0 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 abitype: 1.0.7(typescript@5.5.4)(zod@3.23.8) eventemitter3: 5.0.1 optionalDependencies: @@ -44479,10 +44830,10 @@ snapshots: ox@0.6.5(typescript@5.6.3)(zod@3.23.8): dependencies: '@adraffy/ens-normalize': 1.11.0 - '@noble/curves': 1.7.0 - '@noble/hashes': 1.6.1 - '@scure/bip32': 1.6.0 - '@scure/bip39': 1.5.0 + '@noble/curves': 1.8.1 + '@noble/hashes': 1.7.1 + '@scure/bip32': 1.6.2 + '@scure/bip39': 1.5.4 abitype: 1.0.7(typescript@5.6.3)(zod@3.23.8) eventemitter3: 5.0.1 optionalDependencies: @@ -48389,6 +48740,25 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.25.8) + ts-jest@29.2.4(@babel/core@7.25.8)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.8))(jest@29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)))(typescript@5.6.3): + dependencies: + bs-logger: 0.2.6 + ejs: 3.1.10 + fast-json-stable-stringify: 2.1.0 + jest: 29.7.0(@types/node@18.19.57)(ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3)) + jest-util: 29.7.0 + json5: 2.2.3 + lodash.memoize: 4.1.2 + make-error: 1.3.6 + semver: 7.6.3 + typescript: 5.6.3 + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.25.8 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.25.8) + ts-jest@29.2.4(@babel/core@7.25.8)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.8))(jest@29.7.0(@types/node@20.14.15)(ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4)))(typescript@5.5.4): dependencies: bs-logger: 0.2.6 @@ -48563,6 +48933,25 @@ snapshots: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + ts-node@10.9.2(@types/node@18.19.57)(typescript@5.6.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.9 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.3 + '@types/node': 18.19.57 + acorn: 8.13.0 + acorn-walk: 8.2.0 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.6.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + optional: true + ts-node@10.9.2(@types/node@20.14.15)(typescript@5.5.4): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -48653,6 +49042,7 @@ snapshots: typescript: 5.6.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + optional: true ts-node@7.0.1: dependencies: From 0526bd88585ec8883c147fdf8c8e979d818337aa Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Thu, 27 Feb 2025 13:19:39 +0100 Subject: [PATCH 07/10] feat(target_chains/sui): add iota cli lib --- target_chains/sui/cli-iota/package.json | 2 +- target_chains/sui/cli-iota/src/cli.ts | 45 ++++++-------- target_chains/sui/cli-iota/src/pyth_deploy.ts | 61 ++++++++++++------- .../sui/cli-iota/src/upgrade_pyth.ts | 28 +++++---- target_chains/sui/cli/src/cli.ts | 21 ++----- target_chains/sui/cli/src/pyth_deploy.ts | 35 +++++++---- 6 files changed, 104 insertions(+), 88 deletions(-) diff --git a/target_chains/sui/cli-iota/package.json b/target_chains/sui/cli-iota/package.json index dea2f7daa0..189d8bc0d4 100644 --- a/target_chains/sui/cli-iota/package.json +++ b/target_chains/sui/cli-iota/package.json @@ -1,7 +1,7 @@ { "name": "pyth-iota-cli", "version": "0.1.0", - "description": "Pyth Sui Integration Cli tools", + "description": "Pyth IOTA Integration Cli tools", "main": "index.js", "license": "Apache-2.0", "scripts": { diff --git a/target_chains/sui/cli-iota/src/cli.ts b/target_chains/sui/cli-iota/src/cli.ts index de2e33a943..4da4b53160 100644 --- a/target_chains/sui/cli-iota/src/cli.ts +++ b/target_chains/sui/cli-iota/src/cli.ts @@ -3,13 +3,13 @@ import { hideBin } from "yargs/helpers"; import { DefaultStore, getDefaultDeploymentConfig, - SuiChain, - SuiPriceFeedContract, + IotaChain, + IotaPriceFeedContract, } from "@pythnetwork/contract-manager"; import { PriceServiceConnection } from "@pythnetwork/price-service-client"; import { execSync } from "child_process"; import { initPyth, publishPackage } from "./pyth_deploy"; -import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519"; +import { Ed25519Keypair } from "@iota/iota-sdk/keypairs/ed25519"; import { resolve } from "path"; import { buildForBytecodeAndDigest, @@ -26,15 +26,16 @@ const OPTIONS = { contract: { type: "string", demandOption: true, - desc: "Contract to use for the command (e.g sui_testnet_0xe8c2ddcd5b10e8ed98e53b12fcf8f0f6fd9315f810ae61fa4001858851f21c88)", + desc: "Contract to use for the command (e.g FIXME)", }, path: { type: "string", default: "../../contracts", - desc: "Path to the sui contracts, will use ../../contracts by default", + desc: "Path to the iota contracts, will use ../../contracts by default", }, endpoint: { type: "string", + default: "https://hermes.pyth.network", desc: "Price service endpoint to use, defaults to https://hermes.pyth.network for mainnet and https://hermes-beta.pyth.network for testnet", }, "feed-id": { @@ -44,24 +45,14 @@ const OPTIONS = { }, } as const; -function getContract(contractId: string): SuiPriceFeedContract { - const contract = DefaultStore.contracts[contractId] as SuiPriceFeedContract; +function getContract(contractId: string): IotaPriceFeedContract { + const contract = DefaultStore.contracts[contractId] as IotaPriceFeedContract; if (!contract) { throw new Error(`Contract ${contractId} not found`); } return contract; } -function getPriceService( - contract: SuiPriceFeedContract, - endpointOverride: string | undefined -): PriceServiceConnection { - const defaultEndpoint = contract.getChain().isMainnet() - ? "https://hermes.pyth.network" - : "https://hermes-beta.pyth.network"; - return new PriceServiceConnection(endpointOverride || defaultEndpoint); -} - yargs(hideBin(process.argv)) .command( "create", @@ -80,7 +71,7 @@ yargs(hideBin(process.argv)) }, async (argv) => { const contract = getContract(argv.contract); - const priceService = getPriceService(contract, argv.endpoint); + const priceService = new PriceServiceConnection(argv.endpoint); const feedIds = argv["feed-id"] as string[]; const vaas = await priceService.getLatestVaas(feedIds); const digest = await contract.executeCreatePriceFeed( @@ -106,7 +97,7 @@ yargs(hideBin(process.argv)) }, async (argv) => { const contract = getContract(argv.contract); - const priceService = getPriceService(contract, argv.endpoint); + const priceService = new PriceServiceConnection(argv.endpoint); const feedIds = await priceService.getPriceFeedIds(); const BATCH_SIZE = 10; for (let i = 0; i < feedIds.length; i += BATCH_SIZE) { @@ -138,7 +129,7 @@ yargs(hideBin(process.argv)) digest: number[]; } = JSON.parse( execSync( - `sui move build --dump-bytecode-as-base64 --path ${__dirname}/${argv.path} 2> /dev/null`, + `iota move build --dump-bytecode-as-base64 --path ${__dirname}/${argv.path} 2> /dev/null`, { encoding: "utf-8", } @@ -158,26 +149,26 @@ yargs(hideBin(process.argv)) chain: { type: "string", demandOption: true, - desc: "Chain to deploy the code to. Can be sui_mainnet or sui_testnet", + desc: "Chain to deploy the code to. Can be iota_mainnet or iota_testnet", }, path: OPTIONS.path, }) .usage( - "$0 deploy --private-key --chain [sui_mainnet|sui_testnet] --path " + "$0 deploy --private-key --chain [iota_mainnet|iota_testnet] --path " ); }, async (argv) => { const walletPrivateKey = argv["private-key"]; - const chain = DefaultStore.chains[argv.chain] as SuiChain; + const chain = DefaultStore.chains[argv.chain] as IotaChain; const keypair = Ed25519Keypair.fromSecretKey( - Buffer.from(walletPrivateKey, "hex") + new Uint8Array(Buffer.from(walletPrivateKey, "hex")) ); const result = await publishPackage( keypair, chain.getProvider(), argv.path ); - const deploymentType = chain.isMainnet() ? "stable" : "beta"; + const deploymentType = "stable"; const config = getDefaultDeploymentConfig(deploymentType); await initPyth( keypair, @@ -206,7 +197,7 @@ yargs(hideBin(process.argv)) }, async (argv) => { const contract = getContract(argv.contract); - const priceService = getPriceService(contract, argv.endpoint); + const priceService = new PriceServiceConnection(argv.endpoint); const feedIds = argv["feed-id"] as string[]; const vaas = await priceService.getLatestVaas(feedIds); const digest = await contract.executeUpdatePriceFeedWithFeeds( @@ -239,7 +230,7 @@ yargs(hideBin(process.argv)) async (argv) => { const contract = getContract(argv.contract); const keypair = Ed25519Keypair.fromSecretKey( - Buffer.from(argv["private-key"], "hex") + new Uint8Array(Buffer.from(argv["private-key"], "hex")) ); const pythContractsPath = resolve(`${__dirname}/${argv.path}`); diff --git a/target_chains/sui/cli-iota/src/pyth_deploy.ts b/target_chains/sui/cli-iota/src/pyth_deploy.ts index 82518df0a9..8552c1e37c 100644 --- a/target_chains/sui/cli-iota/src/pyth_deploy.ts +++ b/target_chains/sui/cli-iota/src/pyth_deploy.ts @@ -1,15 +1,20 @@ -import { Transaction } from "@mysten/sui/transactions"; +import { Transaction } from "@iota/iota-sdk/transactions"; -import { MIST_PER_SUI, normalizeSuiObjectId, fromB64 } from "@mysten/sui/utils"; +import { + NANOS_PER_IOTA, + normalizeIotaObjectId, + fromB64, +} from "@iota/iota-sdk/utils"; -import { Ed25519Keypair } from "@mysten/sui/dist/cjs/keypairs/ed25519"; +import { Ed25519Keypair } from "@iota/iota-sdk/keypairs/ed25519"; import { execSync } from "child_process"; import { DataSource } from "@pythnetwork/xc-admin-common"; -import { SuiClient } from "@mysten/sui/client"; +import { IotaClient } from "@iota/iota-sdk/client"; +import { bcs } from "@iota/iota-sdk/bcs"; export async function publishPackage( keypair: Ed25519Keypair, - provider: SuiClient, + provider: IotaClient, packagePath: string ): Promise<{ packageId: string; upgradeCapId: string; deployerCapId: string }> { // Build contracts @@ -18,7 +23,7 @@ export async function publishPackage( dependencies: string[]; } = JSON.parse( execSync( - `sui move build --dump-bytecode-as-base64 --path ${__dirname}/${packagePath} 2> /dev/null`, + `iota move build --dump-bytecode-as-base64 --path ${__dirname}/${packagePath} 2> /dev/null`, { encoding: "utf-8", } @@ -31,17 +36,17 @@ export async function publishPackage( // const transactionBlock = new TransactionBlock(); const txb = new Transaction(); - txb.setGasBudget(MIST_PER_SUI / 2n); // 0.5 SUI + txb.setGasBudget(NANOS_PER_IOTA / 2n); // 0.5 SUI const [upgradeCap] = txb.publish({ modules: buildOutput.modules.map((m: string) => Array.from(fromB64(m))), dependencies: buildOutput.dependencies.map((d: string) => - normalizeSuiObjectId(d) + normalizeIotaObjectId(d) ), }); // Transfer upgrade capability to deployer - txb.transferObjects([upgradeCap], txb.pure.address(keypair.toSuiAddress())); + txb.transferObjects([upgradeCap], txb.pure.address(keypair.toIotaAddress())); // Execute transactions const result = await provider.signAndExecuteTransaction({ @@ -97,7 +102,7 @@ export async function publishPackage( export async function initPyth( keypair: Ed25519Keypair, - provider: SuiClient, + provider: IotaClient, pythPackageId: string, deployerCapId: string, upgradeCapId: string, @@ -109,19 +114,31 @@ export async function initPyth( const tx = new Transaction(); const baseUpdateFee = tx.pure.u64(1); - const dataSourceEmitterAddresses = tx.pure.arguments( - config.dataSources.map((dataSource) => [ - ...Buffer.from(dataSource.emitterAddress, "hex"), - ]) + const dataSourceEmitterAddresses = tx.pure( + bcs + .vector(bcs.vector(bcs.u8())) + .serialize( + config.dataSources.map((dataSource) => [ + ...Buffer.from(dataSource.emitterAddress, "hex"), + ]) + ) ); - const dataSourceEmitterChainIds = tx.pure.arguments( - config.dataSources.map((dataSource) => dataSource.emitterChain) + const dataSourceEmitterChainIds = tx.pure( + bcs + .vector(bcs.u64()) + .serialize( + config.dataSources.map((dataSource) => dataSource.emitterChain) + ) ); - const governanceEmitterAddress = tx.pure.arguments([ - ...Buffer.from(config.governanceDataSource.emitterAddress, "hex"), - ]); - const governanceEmitterChainId = tx.pure.arguments( - config.governanceDataSource.emitterChain + const governanceEmitterAddress = tx.pure( + bcs + .vector(bcs.u8()) + .serialize([ + ...Buffer.from(config.governanceDataSource.emitterAddress, "hex"), + ]) + ); + const governanceEmitterChainId = tx.pure( + bcs.u64().serialize(config.governanceDataSource.emitterChain) ); const stalePriceThreshold = tx.pure.u64(60); tx.moveCall({ @@ -138,7 +155,7 @@ export async function initPyth( ], }); - tx.setGasBudget(MIST_PER_SUI / 10n); // 0.1 sui + tx.setGasBudget(NANOS_PER_IOTA / 10n); // 0.1 sui let result = await provider.signAndExecuteTransaction({ signer: keypair, diff --git a/target_chains/sui/cli-iota/src/upgrade_pyth.ts b/target_chains/sui/cli-iota/src/upgrade_pyth.ts index ee2b83cf92..5a78ea5d51 100644 --- a/target_chains/sui/cli-iota/src/upgrade_pyth.ts +++ b/target_chains/sui/cli-iota/src/upgrade_pyth.ts @@ -1,10 +1,14 @@ -import { Transaction } from "@mysten/sui/transactions"; -import { fromB64, MIST_PER_SUI, normalizeSuiObjectId } from "@mysten/sui/utils"; -import { SuiClient } from "@mysten/sui/client"; -import { Ed25519Keypair } from "@mysten/sui/keypairs/ed25519"; +import { Transaction } from "@iota/iota-sdk/transactions"; +import { + fromB64, + NANOS_PER_IOTA, + normalizeIotaObjectId, +} from "@iota/iota-sdk/utils"; +import { IotaClient } from "@iota/iota-sdk/client"; +import { Ed25519Keypair } from "@iota/iota-sdk/keypairs/ed25519"; import { execSync } from "child_process"; -import { SuiPriceFeedContract } from "@pythnetwork/contract-manager"; +import { IotaPriceFeedContract } from "@pythnetwork/contract-manager"; export function buildForBytecodeAndDigest(packagePath: string) { const buildOutput: { @@ -20,7 +24,7 @@ export function buildForBytecodeAndDigest(packagePath: string) { return { modules: buildOutput.modules.map((m: string) => Array.from(fromB64(m))), dependencies: buildOutput.dependencies.map((d: string) => - normalizeSuiObjectId(d) + normalizeIotaObjectId(d) ), digest: Buffer.from(buildOutput.digest), }; @@ -28,11 +32,11 @@ export function buildForBytecodeAndDigest(packagePath: string) { export async function upgradePyth( keypair: Ed25519Keypair, - provider: SuiClient, + provider: IotaClient, modules: number[][], dependencies: string[], signedVaa: Buffer, - contract: SuiPriceFeedContract + contract: IotaPriceFeedContract ) { const pythPackage = await contract.getPackageId(contract.stateId); @@ -64,7 +68,7 @@ export async function upgradePyth( arguments: [tx.object(contract.stateId), upgradeReceipt], }); - tx.setGasBudget(MIST_PER_SUI / 4n); // 0.25 SUI + tx.setGasBudget(NANOS_PER_IOTA / 4n); // 0.25 SUI return provider.signAndExecuteTransaction({ signer: keypair, @@ -78,9 +82,9 @@ export async function upgradePyth( export async function migratePyth( keypair: Ed25519Keypair, - provider: SuiClient, + provider: IotaClient, signedUpgradeVaa: Buffer, - contract: SuiPriceFeedContract, + contract: IotaPriceFeedContract, pythPackageOld: string ) { const pythPackage = await contract.getPackageId(contract.stateId); @@ -98,7 +102,7 @@ export async function migratePyth( arguments: [tx.object(contract.stateId), verificationReceipt as any], }); - tx.setGasBudget(MIST_PER_SUI / 10n); //0.1 SUI + tx.setGasBudget(NANOS_PER_IOTA / 10n); //0.1 SUI return provider.signAndExecuteTransaction({ signer: keypair, diff --git a/target_chains/sui/cli/src/cli.ts b/target_chains/sui/cli/src/cli.ts index de2e33a943..7898d22e4d 100644 --- a/target_chains/sui/cli/src/cli.ts +++ b/target_chains/sui/cli/src/cli.ts @@ -35,6 +35,7 @@ const OPTIONS = { }, endpoint: { type: "string", + default: "https://hermes.pyth.network", desc: "Price service endpoint to use, defaults to https://hermes.pyth.network for mainnet and https://hermes-beta.pyth.network for testnet", }, "feed-id": { @@ -52,16 +53,6 @@ function getContract(contractId: string): SuiPriceFeedContract { return contract; } -function getPriceService( - contract: SuiPriceFeedContract, - endpointOverride: string | undefined -): PriceServiceConnection { - const defaultEndpoint = contract.getChain().isMainnet() - ? "https://hermes.pyth.network" - : "https://hermes-beta.pyth.network"; - return new PriceServiceConnection(endpointOverride || defaultEndpoint); -} - yargs(hideBin(process.argv)) .command( "create", @@ -80,7 +71,7 @@ yargs(hideBin(process.argv)) }, async (argv) => { const contract = getContract(argv.contract); - const priceService = getPriceService(contract, argv.endpoint); + const priceService = new PriceServiceConnection(argv.endpoint); const feedIds = argv["feed-id"] as string[]; const vaas = await priceService.getLatestVaas(feedIds); const digest = await contract.executeCreatePriceFeed( @@ -106,7 +97,7 @@ yargs(hideBin(process.argv)) }, async (argv) => { const contract = getContract(argv.contract); - const priceService = getPriceService(contract, argv.endpoint); + const priceService = new PriceServiceConnection(argv.endpoint); const feedIds = await priceService.getPriceFeedIds(); const BATCH_SIZE = 10; for (let i = 0; i < feedIds.length; i += BATCH_SIZE) { @@ -170,7 +161,7 @@ yargs(hideBin(process.argv)) const walletPrivateKey = argv["private-key"]; const chain = DefaultStore.chains[argv.chain] as SuiChain; const keypair = Ed25519Keypair.fromSecretKey( - Buffer.from(walletPrivateKey, "hex") + new Uint8Array(Buffer.from(walletPrivateKey, "hex")) ); const result = await publishPackage( keypair, @@ -206,7 +197,7 @@ yargs(hideBin(process.argv)) }, async (argv) => { const contract = getContract(argv.contract); - const priceService = getPriceService(contract, argv.endpoint); + const priceService = new PriceServiceConnection(argv.endpoint); const feedIds = argv["feed-id"] as string[]; const vaas = await priceService.getLatestVaas(feedIds); const digest = await contract.executeUpdatePriceFeedWithFeeds( @@ -239,7 +230,7 @@ yargs(hideBin(process.argv)) async (argv) => { const contract = getContract(argv.contract); const keypair = Ed25519Keypair.fromSecretKey( - Buffer.from(argv["private-key"], "hex") + new Uint8Array(Buffer.from(argv["private-key"], "hex")) ); const pythContractsPath = resolve(`${__dirname}/${argv.path}`); diff --git a/target_chains/sui/cli/src/pyth_deploy.ts b/target_chains/sui/cli/src/pyth_deploy.ts index 82518df0a9..f5be4fb78f 100644 --- a/target_chains/sui/cli/src/pyth_deploy.ts +++ b/target_chains/sui/cli/src/pyth_deploy.ts @@ -6,6 +6,7 @@ import { Ed25519Keypair } from "@mysten/sui/dist/cjs/keypairs/ed25519"; import { execSync } from "child_process"; import { DataSource } from "@pythnetwork/xc-admin-common"; import { SuiClient } from "@mysten/sui/client"; +import { bcs } from "@mysten/sui/dist/cjs/bcs"; export async function publishPackage( keypair: Ed25519Keypair, @@ -109,19 +110,31 @@ export async function initPyth( const tx = new Transaction(); const baseUpdateFee = tx.pure.u64(1); - const dataSourceEmitterAddresses = tx.pure.arguments( - config.dataSources.map((dataSource) => [ - ...Buffer.from(dataSource.emitterAddress, "hex"), - ]) + const dataSourceEmitterAddresses = tx.pure( + bcs + .vector(bcs.vector(bcs.u8())) + .serialize( + config.dataSources.map((dataSource) => [ + ...Buffer.from(dataSource.emitterAddress, "hex"), + ]) + ) ); - const dataSourceEmitterChainIds = tx.pure.arguments( - config.dataSources.map((dataSource) => dataSource.emitterChain) + const dataSourceEmitterChainIds = tx.pure( + bcs + .vector(bcs.u64()) + .serialize( + config.dataSources.map((dataSource) => dataSource.emitterChain) + ) ); - const governanceEmitterAddress = tx.pure.arguments([ - ...Buffer.from(config.governanceDataSource.emitterAddress, "hex"), - ]); - const governanceEmitterChainId = tx.pure.arguments( - config.governanceDataSource.emitterChain + const governanceEmitterAddress = tx.pure( + bcs + .vector(bcs.u8()) + .serialize([ + ...Buffer.from(config.governanceDataSource.emitterAddress, "hex"), + ]) + ); + const governanceEmitterChainId = tx.pure( + bcs.u64().serialize(config.governanceDataSource.emitterChain) ); const stalePriceThreshold = tx.pure.u64(60); tx.moveCall({ From 817c7d49c1853f7261a731da9590cf811855a7f2 Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Thu, 27 Feb 2025 13:25:05 +0100 Subject: [PATCH 08/10] chore(target_chains/sui): add some misc files math lib is deprecated. --- .../sui/contracts/Move.iota_testnet.toml | 15 ++++++++ target_chains/sui/iota-patch-libs.sh | 36 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 target_chains/sui/contracts/Move.iota_testnet.toml create mode 100644 target_chains/sui/iota-patch-libs.sh diff --git a/target_chains/sui/contracts/Move.iota_testnet.toml b/target_chains/sui/contracts/Move.iota_testnet.toml new file mode 100644 index 0000000000..e21819c7b3 --- /dev/null +++ b/target_chains/sui/contracts/Move.iota_testnet.toml @@ -0,0 +1,15 @@ +[package] +name = "Pyth" +version = "0.0.2" +published-at = "0x23994dd119480ea614f7623520337058dca913cb1bb6e5d8d51c7b067d3ca3bb" + +[dependencies.Iota] +git = "https://github.com/iotaledger/iota.git" +subdir = "crates/iota-framework/packages/iota-framework" +rev = "751c23caf24efd071463b9ffd07eabcb15f44f31" + +[dependencies.Wormhole] +local = "../vendor/wormhole_iota_testnet/wormhole" + +[addresses] +pyth = "0x23994dd119480ea614f7623520337058dca913cb1bb6e5d8d51c7b067d3ca3bb" diff --git a/target_chains/sui/iota-patch-libs.sh b/target_chains/sui/iota-patch-libs.sh new file mode 100644 index 0000000000..c4079d6073 --- /dev/null +++ b/target_chains/sui/iota-patch-libs.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +set -euo pipefail + +# This script patches the SUI code to be compatible with IOTA. IOTA is a fork +# of SUI but is not compatible with SUI. You'd need to run this script for +# deploying Pyth contracts and updating the vendored libs. +# +# Note: Do not commit the patched Pyth code to the repo. + +# Check if exactly one argument (base path) is provided +if [ $# -ne 1 ]; then + echo "Usage: $0 " + exit 1 +fi + +# Detect OS to determine correct sed syntax +if sed --version >/dev/null 2>&1; then + SED_CMD=sed +else + if ! command -v gsed >/dev/null 2>&1; then + echo "Error: GNU sed (gsed) is required for macOS/BSD. Install core-utils via Homebrew." + exit 1 + fi + SED_CMD=gsed +fi + +# Use find to get all .move files recursively and process them +find "$1" -type f -name "*.move" | while read -r file; do + echo "Processing: $file" + $SED_CMD -i -e 's/\bSUI\b/IOTA/g' \ + -e 's/\bSui\b/Iota/g' \ + -e 's/\bsui\b/iota/g' "$file" +done + +echo "Replacements complete." From 2726192a63d137b47a6445db6e407b31ce83be8a Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Fri, 7 Mar 2025 14:28:13 +0100 Subject: [PATCH 09/10] fix: address feedbacks --- target_chains/sui/cli-iota/README.md | 10 ++-- target_chains/sui/cli-iota/src/cli.ts | 4 +- target_chains/sui/cli-iota/src/pyth_deploy.ts | 5 +- .../sui/cli-iota/src/upgrade_pyth.ts | 6 +- target_chains/sui/cli/src/cli.ts | 2 +- target_chains/sui/cli/src/pyth_deploy.ts | 1 - target_chains/sui/sdk/js-iota/README.md | 59 ++++++++++--------- ...ction.ts => IotaPriceServiceConnection.ts} | 0 .../examples/{SuiRelay.ts => IotaRelay.ts} | 8 +-- target_chains/sui/sdk/js-iota/src/index.ts | 2 +- 10 files changed, 48 insertions(+), 49 deletions(-) rename target_chains/sui/sdk/js-iota/src/{SuiPriceServiceConnection.ts => IotaPriceServiceConnection.ts} (100%) rename target_chains/sui/sdk/js-iota/src/examples/{SuiRelay.ts => IotaRelay.ts} (91%) diff --git a/target_chains/sui/cli-iota/README.md b/target_chains/sui/cli-iota/README.md index cf4a0275a6..aede9ba8d4 100644 --- a/target_chains/sui/cli-iota/README.md +++ b/target_chains/sui/cli-iota/README.md @@ -8,10 +8,10 @@ Configure the `Move.toml` file accordingly. The wormhole address should be speci We can deploy the pyth oracle and initialize it with the following command: ```bash -npm run cli -- deploy --private-key --chain [sui_mainnet|sui_testnet] +npm run cli -- deploy --private-key --chain [iota_sui|iota_sui] ``` -You can then add your sui contract configs to the contract manager store. +You can then add your iota contract configs to the contract manager store. You can also manually create all the price feeds available at the moment to make it easier for devs to test the oracle. @@ -33,14 +33,14 @@ npm run cli -- update-feeds --feed-id --private-key --co # Upgrade process: -The following steps are needed to upgrade our sui contracts: +The following steps are needed to upgrade our iota contracts: - Contract changes: - Create a new struct for the new version and update `current_version` and `previous_version` functions in `version_control` module - Implement any custom logic needed to migrate the data from the old struct to the new one in the `migrate` module - Update dependency (e.g. wormhole) addresses if needed - Generate the digest for the new contract build -- Create a governance proposal, proposing the sui package to be upgraded to this specific digest +- Create a governance proposal, proposing the iota package to be upgraded to this specific digest - Approve and execute the governance proposal - Run the upgrade transaction and publish the new package @@ -60,7 +60,7 @@ To upgrade the contract after the governance vaa was executed run: npm run cli -- upgrade --private-key --contract --vaa ``` -The upgrade procedure consists of 2 transactions. The first one is to upgrade the contract (sui level) and the second one is to run the `migrate` function and upgrade the version (package level). +The upgrade procedure consists of 2 transactions. The first one is to upgrade the contract (iota level) and the second one is to run the `migrate` function and upgrade the version (package level). Since clients try to fetch the latest version of the package automatically, it's important to run the second transaction as soon as possible after the first one. ### FAQ: diff --git a/target_chains/sui/cli-iota/src/cli.ts b/target_chains/sui/cli-iota/src/cli.ts index 4da4b53160..ea5311556a 100644 --- a/target_chains/sui/cli-iota/src/cli.ts +++ b/target_chains/sui/cli-iota/src/cli.ts @@ -26,7 +26,7 @@ const OPTIONS = { contract: { type: "string", demandOption: true, - desc: "Contract to use for the command (e.g FIXME)", + desc: "Contract to use for the command (e.g iota_0x68dda579251917b3db28e35c4df495c6e664ccc085ede867a9b773c8ebedc2c1)", }, path: { type: "string", @@ -36,7 +36,7 @@ const OPTIONS = { endpoint: { type: "string", default: "https://hermes.pyth.network", - desc: "Price service endpoint to use, defaults to https://hermes.pyth.network for mainnet and https://hermes-beta.pyth.network for testnet", + desc: "Price service endpoint to use, defaults to https://hermes.pyth.network", }, "feed-id": { type: "array", diff --git a/target_chains/sui/cli-iota/src/pyth_deploy.ts b/target_chains/sui/cli-iota/src/pyth_deploy.ts index 8552c1e37c..2b28dc0c63 100644 --- a/target_chains/sui/cli-iota/src/pyth_deploy.ts +++ b/target_chains/sui/cli-iota/src/pyth_deploy.ts @@ -33,10 +33,9 @@ export async function publishPackage( console.log("buildOutput: ", buildOutput); // Publish contracts - // const transactionBlock = new TransactionBlock(); const txb = new Transaction(); - txb.setGasBudget(NANOS_PER_IOTA / 2n); // 0.5 SUI + txb.setGasBudget(NANOS_PER_IOTA / 2n); // 0.5 IOTA const [upgradeCap] = txb.publish({ modules: buildOutput.modules.map((m: string) => Array.from(fromB64(m))), @@ -155,7 +154,7 @@ export async function initPyth( ], }); - tx.setGasBudget(NANOS_PER_IOTA / 10n); // 0.1 sui + tx.setGasBudget(NANOS_PER_IOTA / 10n); // 0.1 IOTA let result = await provider.signAndExecuteTransaction({ signer: keypair, diff --git a/target_chains/sui/cli-iota/src/upgrade_pyth.ts b/target_chains/sui/cli-iota/src/upgrade_pyth.ts index 5a78ea5d51..260733956b 100644 --- a/target_chains/sui/cli-iota/src/upgrade_pyth.ts +++ b/target_chains/sui/cli-iota/src/upgrade_pyth.ts @@ -17,7 +17,7 @@ export function buildForBytecodeAndDigest(packagePath: string) { digest: number[]; } = JSON.parse( execSync( - `sui move build --dump-bytecode-as-base64 -p ${packagePath} 2> /dev/null`, + `iota move build --dump-bytecode-as-base64 -p ${packagePath} 2> /dev/null`, { encoding: "utf-8" } ) ); @@ -68,7 +68,7 @@ export async function upgradePyth( arguments: [tx.object(contract.stateId), upgradeReceipt], }); - tx.setGasBudget(NANOS_PER_IOTA / 4n); // 0.25 SUI + tx.setGasBudget(NANOS_PER_IOTA / 4n); // 0.25 IOTA return provider.signAndExecuteTransaction({ signer: keypair, @@ -102,7 +102,7 @@ export async function migratePyth( arguments: [tx.object(contract.stateId), verificationReceipt as any], }); - tx.setGasBudget(NANOS_PER_IOTA / 10n); //0.1 SUI + tx.setGasBudget(NANOS_PER_IOTA / 10n); //0.1 IOTA return provider.signAndExecuteTransaction({ signer: keypair, diff --git a/target_chains/sui/cli/src/cli.ts b/target_chains/sui/cli/src/cli.ts index 7898d22e4d..0d35ebf7b8 100644 --- a/target_chains/sui/cli/src/cli.ts +++ b/target_chains/sui/cli/src/cli.ts @@ -36,7 +36,7 @@ const OPTIONS = { endpoint: { type: "string", default: "https://hermes.pyth.network", - desc: "Price service endpoint to use, defaults to https://hermes.pyth.network for mainnet and https://hermes-beta.pyth.network for testnet", + desc: "Price service endpoint to use, defaults to https://hermes.pyth.network", }, "feed-id": { type: "array", diff --git a/target_chains/sui/cli/src/pyth_deploy.ts b/target_chains/sui/cli/src/pyth_deploy.ts index f5be4fb78f..f737e35079 100644 --- a/target_chains/sui/cli/src/pyth_deploy.ts +++ b/target_chains/sui/cli/src/pyth_deploy.ts @@ -29,7 +29,6 @@ export async function publishPackage( console.log("buildOutput: ", buildOutput); // Publish contracts - // const transactionBlock = new TransactionBlock(); const txb = new Transaction(); txb.setGasBudget(MIST_PER_SUI / 2n); // 0.5 SUI diff --git a/target_chains/sui/sdk/js-iota/README.md b/target_chains/sui/sdk/js-iota/README.md index d39cd67b8f..45de93b3c7 100644 --- a/target_chains/sui/sdk/js-iota/README.md +++ b/target_chains/sui/sdk/js-iota/README.md @@ -1,19 +1,20 @@ -# Pyth Sui JS SDK +# Pyth IOTA JS SDK -[Pyth](https://pyth.network/) provides real-time pricing data in a variety of asset classes, including cryptocurrency, equities, FX and commodities. This library allows you to use these real-time prices on the [Sui network](https://sui.io/). +[Pyth](https://pyth.network/) provides real-time pricing data in a variety of asset classes, including cryptocurrency, equities, FX and commodities. +This library allows you to use these real-time prices on the [IOTA network](https://www.iota.org/). ## Installation ### npm ``` -$ npm install --save @pythnetwork/pyth-sui-js +$ npm install --save @pythnetwork/pyth-iota-js ``` ### Yarn ``` -$ yarn add @pythnetwork/pyth-sui-js +$ yarn add @pythnetwork/pyth-iota-js ``` ## Quickstart @@ -21,19 +22,19 @@ $ yarn add @pythnetwork/pyth-sui-js Pyth stores prices off-chain to minimize gas fees, which allows us to offer a wider selection of products and faster update times. See [On-Demand Updates](https://docs.pyth.network/documentation/pythnet-price-feeds/on-demand) for more information about this approach. Typically, to use Pyth prices on chain, -they must be fetched from an off-chain Hermes instance. The `SuiPriceServiceConnection` class can be used to interact with these services, +they must be fetched from an off-chain Hermes instance. The `IotaPriceServiceConnection` class can be used to interact with these services, providing a way to fetch these prices directly in your code. The following example wraps an existing RPC provider and shows how to obtain Pyth prices and submit them to the network: ```typescript -const connection = new SuiPriceServiceConnection( - "https://hermes-beta.pyth.network" +const connection = new IotaPriceServiceConnection( + "https://hermes.pyth.network" ); // See Hermes endpoints section below for other endpoints const priceIds = [ - // You can find the ids of prices at https://pyth.network/developers/price-feed-ids#sui-testnet - "0xf9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b", // BTC/USD price id in testnet - "0xca80ba6dc32e08d06f1aa886011eed1d77c77be9eb761cc10d72b7d0a2fd57a6", // ETH/USD price id in testnet + // You can find the ids of prices at https://pyth.network/developers/price-feed-ids + "0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43", // BTC/USD price id + "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace", // ETH/USD price id ]; // In order to use Pyth prices in your protocol you need to submit the price update data to Pyth contract in your target @@ -46,19 +47,19 @@ const priceUpdateData = await connection.getPriceFeedsUpdateData(priceIds); ### **_Important Note for Integrators_** -Your Sui Move module **should NOT** have a hard-coded call to `pyth::update_single_price_feed`. In other words, the Sui Pyth `pyth::update_single_price_feed` entry point should never be called by a contract, instead it should be called directly from client code (e.g. Typescript or Rust). +Your IOTA Move module **should NOT** have a hard-coded call to `pyth::update_single_price_feed`. In other words, the Iota Pyth `pyth::update_single_price_feed` entry point should never be called by a contract, instead it should be called directly from client code (e.g. Typescript or Rust). -This is because when a Sui contract is [upgraded](https://docs.sui.io/build/package-upgrades), the new address is different from the original. If your module has a hard-coded call to `pyth::update_single_price_feed` living at a fixed call-site, it may eventually get bricked due to the way Pyth upgrades are implemented. (We only allows users to interact with the most recent package version for security reasons). +This is because when a IOTA contract is [upgraded](https://docs.iota.org/developer/iota-101/move-overview/package-upgrades/upgrade), the new address is different from the original. If your module has a hard-coded call to `pyth::update_single_price_feed` living at a fixed call-site, it may eventually get bricked due to the way Pyth upgrades are implemented. (We only allows users to interact with the most recent package version for security reasons). -Therefore, you should build a [Sui programmable transaction](https://docs.sui.io/build/prog-trans-ts-sdk) that first updates the price by calling `pyth::update_single_price_feed` at the latest call-site from the client-side and then call a function in your contract that invokes `pyth::get_price` on the `PriceInfoObject` to get the recently updated price. -You can use `SuiPythClient` to build such transactions. +Therefore, you should build a [Iota programmable transaction](https://docs.iota.org/ts-sdk/typescript/transaction-building/basics) that first updates the price by calling `pyth::update_single_price_feed` at the latest call-site from the client-side and then call a function in your contract that invokes `pyth::get_price` on the `PriceInfoObject` to get the recently updated price. +You can use `IotaPythClient` to build such transactions. ### Example ```ts -import { SuiPythClient } from "@pythnetwork/pyth-sui-js"; -import { Transaction } from "@mysten/sui/transactions"; -import { SuiClient } from "@mysten/sui/client"; +import { IotaPythClient } from "@pythnetwork/pyth-iota-js"; +import { Transaction } from "@iota/iota-sdk/transactions"; +import { IotaClient } from "@iota/iota-sdk/client"; const priceUpdateData = await connection.getPriceFeedsUpdateData(priceIds); // see quickstart section @@ -70,8 +71,8 @@ const wallet: SignerWithProvider = getWallet(); const wormholeStateId = " 0xFILL_ME"; const pythStateId = "0xFILL_ME"; -const provider = new SuiClient({ url: "https://fill-sui-endpoint" }); -const client = new SuiPythClient(wallet.provider, pythStateId, wormholeStateId); +const provider = new IotaClient({ url: "https://fill-iota-endpoint" }); +const client = new IotaPythClient(wallet.provider, pythStateId, wormholeStateId); const tx = new Transaction(); const priceInfoObjectIds = await client.updatePriceFeeds(tx, priceFeedUpdateData, priceIds); @@ -98,26 +99,26 @@ Now in your contract you can consume the price by calling `pyth::get_price` or o ### CLI Example -[This example](./src/examples/SuiRelay.ts) shows how to update prices on an Sui network. It does the following: +[This example](./src/examples/IotaRelay.ts) shows how to update prices on an IOTA network. It does the following: 1. Fetches update data from Hermes for the given price feeds. -2. Calls the Pyth Sui contract with the update data. +2. Calls the Pyth IOTA contract with the update data. -You can run this example with `npm run example-relay`. A full command that updates prices on Sui testnet looks like: +You can run this example with `npm run example-relay`. A full command that updates prices on IOTA testnet looks like: ```bash -export SUI_KEY=YOUR_PRIV_KEY; -npm run example-relay -- --feed-id "5a035d5440f5c163069af66062bac6c79377bf88396fa27e6067bfca8096d280" \ ---price-service "https://hermes-beta.pyth.network" \ ---full-node "https://fullnode.testnet.sui.io:443" \ ---pyth-state-id "0xd3e79c2c083b934e78b3bd58a490ec6b092561954da6e7322e1e2b3c8abfddc0" \ ---wormhole-state-id "0x31358d198147da50db32eda2562951d53973a0c0ad5ed738e9b17d88b213d790" +export IOTA_KEY=YOUR_PRIV_KEY; +npm run example-relay -- --feed-id "ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace" \ +--price-service "https://hermes.pyth.network" \ +--full-node "https://api.testnet.iota.cafe" \ +--pyth-state-id "0x68dda579251917b3db28e35c4df495c6e664ccc085ede867a9b773c8ebedc2c1" \ +--wormhole-state-id "0x8bc490f69520a97ca1b3de864c96aa2265a0cf5d90f5f3f016b2eddf0cf2af2b" ``` ## Off-chain prices Many applications additionally need to display Pyth prices off-chain, for example, in their frontend application. -The `SuiPriceServiceConnection` provides two different ways to fetch the current Pyth price. +The `IotaPriceServiceConnection` provides two different ways to fetch the current Pyth price. The code blocks below assume that the `connection` and `priceIds` objects have been initialized as shown above. The first method is a single-shot query: diff --git a/target_chains/sui/sdk/js-iota/src/SuiPriceServiceConnection.ts b/target_chains/sui/sdk/js-iota/src/IotaPriceServiceConnection.ts similarity index 100% rename from target_chains/sui/sdk/js-iota/src/SuiPriceServiceConnection.ts rename to target_chains/sui/sdk/js-iota/src/IotaPriceServiceConnection.ts diff --git a/target_chains/sui/sdk/js-iota/src/examples/SuiRelay.ts b/target_chains/sui/sdk/js-iota/src/examples/IotaRelay.ts similarity index 91% rename from target_chains/sui/sdk/js-iota/src/examples/SuiRelay.ts rename to target_chains/sui/sdk/js-iota/src/examples/IotaRelay.ts index 4862b1ae5d..c0c9ed13ab 100644 --- a/target_chains/sui/sdk/js-iota/src/examples/SuiRelay.ts +++ b/target_chains/sui/sdk/js-iota/src/examples/IotaRelay.ts @@ -22,7 +22,7 @@ const argvPromise = yargs(hideBin(process.argv)) }) .option("full-node", { description: - "URL of the full Sui node RPC endpoint. e.g: https://fullnode.testnet.sui.io:443", + "URL of the full IOTA node RPC endpoint. e.g: https://api.testnet.iota.cafe/", type: "string", demandOption: true, }) @@ -41,8 +41,8 @@ export function getProvider(url: string) { return new IotaClient({ url }); } async function run() { - if (process.env.SUI_KEY === undefined) { - throw new Error(`SUI_KEY environment variable should be set.`); + if (process.env.IOTA_KEY === undefined) { + throw new Error(`IOTA_KEY environment variable should be set.`); } const argv = await argvPromise; @@ -80,7 +80,7 @@ async function run() { } const wallet = Ed25519Keypair.fromSecretKey( - Buffer.from(process.env.SUI_KEY, "hex") + Buffer.from(process.env.IOTA_KEY, "hex") ); const result = await provider.signAndExecuteTransaction({ diff --git a/target_chains/sui/sdk/js-iota/src/index.ts b/target_chains/sui/sdk/js-iota/src/index.ts index 4036eaac0b..6a67047575 100644 --- a/target_chains/sui/sdk/js-iota/src/index.ts +++ b/target_chains/sui/sdk/js-iota/src/index.ts @@ -1,4 +1,4 @@ -export { IotaPriceServiceConnection } from "./SuiPriceServiceConnection"; +export { IotaPriceServiceConnection } from "./IotaPriceServiceConnection"; export { IotaPythClient } from "./client"; export { From 21f2bbf027640d803e05f9270a32d9e23425bf5d Mon Sep 17 00:00:00 2001 From: Ali Behjati Date: Fri, 7 Mar 2025 14:40:38 +0100 Subject: [PATCH 10/10] fix: remove any --- contract_manager/src/contracts/iota.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/contract_manager/src/contracts/iota.ts b/contract_manager/src/contracts/iota.ts index fc98d049d8..a6946bcfa0 100644 --- a/contract_manager/src/contracts/iota.ts +++ b/contract_manager/src/contracts/iota.ts @@ -523,6 +523,7 @@ export class IotaWormholeContract extends WormholeContract { return { id: result.digest, info: result }; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any private async getStateFields(): Promise { const provider = this.chain.getProvider(); const result = await provider.getObject({