From 7335be2c8904890b548e2d224219053316740b37 Mon Sep 17 00:00:00 2001 From: Account Date: Sat, 22 Mar 2025 00:07:53 +0200 Subject: [PATCH] fix: restore Sui folder and separate IOTA code --- target_chains/iota/README.md | 9 + target_chains/iota/cli-iota/.gitignore | 1 + target_chains/iota/cli-iota/README.md | 69 + target_chains/iota/cli-iota/package.json | 27 + target_chains/iota/cli-iota/src/cli.ts | 288 + .../iota/cli-iota/src/pyth_deploy.ts | 185 + .../iota/cli-iota/src/upgrade_pyth.ts | 115 + target_chains/iota/cli-iota/tsconfig.json | 9 + target_chains/iota/cli/.gitignore | 1 + target_chains/iota/cli/README.md | 69 + target_chains/iota/cli/package.json | 27 + target_chains/iota/cli/src/cli.ts | 288 + target_chains/iota/cli/src/pyth_deploy.ts | 181 + target_chains/iota/cli/src/upgrade_pyth.ts | 111 + target_chains/iota/cli/tsconfig.json | 9 + target_chains/iota/contracts/Makefile | 9 + .../iota/contracts/Move.iota_testnet.toml | 15 + target_chains/iota/contracts/Move.lock | 36 + .../iota/contracts/Move.mainnet.toml | 17 + .../contracts/Move.movement_m2_devnet.toml | 15 + .../iota/contracts/Move.testnet.toml | 17 + target_chains/iota/contracts/Move.toml | 15 + target_chains/iota/contracts/README.md | 18 + .../iota/contracts/rust-toolchain.toml | 2 + .../sources/batch_price_attestation.move | 247 + .../iota/contracts/sources/data_source.move | 85 + .../iota/contracts/sources/deserialize.move | 143 + .../iota/contracts/sources/event.move | 33 + .../sources/governance/contract_upgrade.move | 145 + .../sources/governance/governance.move | 116 + .../sources/governance/governance_action.move | 48 + .../governance/governance_instruction.move | 91 + .../sources/governance/set_data_sources.move | 100 + .../sources/governance/set_fee_recipient.move | 32 + .../set_governance_data_source.move | 36 + .../governance/set_stale_price_threshold.move | 72 + .../sources/governance/set_update_fee.move | 85 + .../contracts/sources/hot_potato_vector.move | 66 + target_chains/iota/contracts/sources/i64.move | 140 + .../iota/contracts/sources/merkle_tree.move | 397 ++ .../iota/contracts/sources/migrate.move | 77 + .../iota/contracts/sources/price.move | 46 + .../iota/contracts/sources/price_feed.move | 47 + .../contracts/sources/price_identifier.move | 21 + .../iota/contracts/sources/price_info.move | 223 + .../iota/contracts/sources/price_status.move | 53 + .../iota/contracts/sources/pyth.move | 1549 +++++ .../contracts/sources/pyth_accumulator.move | 123 + target_chains/iota/contracts/sources/set.move | 46 + .../iota/contracts/sources/setup.move | 75 + .../iota/contracts/sources/state.move | 404 ++ .../contracts/sources/version_control.move | 81 + target_chains/iota/iota-patch-libs.sh | 36 + target_chains/iota/sdk/js-iota/.eslintrc.js | 9 + target_chains/iota/sdk/js-iota/.gitignore | 1 + target_chains/iota/sdk/js-iota/README.md | 155 + target_chains/iota/sdk/js-iota/jest.config.js | 5 + target_chains/iota/sdk/js-iota/package.json | 59 + .../js-iota/src/IotaPriceServiceConnection.ts | 21 + target_chains/iota/sdk/js-iota/src/client.ts | 304 + .../sdk/js-iota/src/examples/IotaRelay.ts | 97 + target_chains/iota/sdk/js-iota/src/index.ts | 11 + target_chains/iota/sdk/js-iota/tsconfig.json | 14 + target_chains/iota/sdk/js/.eslintrc.js | 9 + target_chains/iota/sdk/js/.gitignore | 1 + target_chains/iota/sdk/js/README.md | 154 + target_chains/iota/sdk/js/jest.config.js | 5 + target_chains/iota/sdk/js/package.json | 59 + .../sdk/js/src/SuiPriceServiceConnection.ts | 21 + target_chains/iota/sdk/js/src/client.ts | 304 + .../iota/sdk/js/src/examples/SuiRelay.ts | 97 + target_chains/iota/sdk/js/src/index.ts | 11 + target_chains/iota/sdk/js/tsconfig.json | 14 + target_chains/iota/vendor/README.md | 11 + .../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 + .../vendor/wormhole_iota_testnet/Makefile | 15 + .../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 | 35 + .../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 + .../wormhole_movement_m2_devnet/.gitignore | 2 + .../wormhole_movement_m2_devnet/Docker.md | 13 + .../wormhole_movement_m2_devnet/Dockerfile | 33 + .../Dockerfile.base | 24 + .../wormhole_movement_m2_devnet/Makefile | 15 + .../wormhole_movement_m2_devnet/NOTES.md | 114 + .../wormhole_movement_m2_devnet/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 + .../devnet/client.yaml | 12 + .../devnet/fullnode.yaml | 107 + .../devnet/genesis.blob | Bin 0 -> 329923 bytes .../devnet/genesis_config | 55 + .../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 + .../scripts/deploy.sh | 119 + .../scripts/node_builder.sh | 11 + .../scripts/register_devnet.sh | 22 + .../scripts/setup_rust.sh | 3 + .../scripts/start_node.sh | 5 + .../scripts/switch.sh | 31 + .../scripts/wait_for_devnet.sh | 6 + .../testing/.gitignore | 4 + .../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/.gitignore | 1 + .../wormhole/Makefile | 18 + .../wormhole/Move.devnet.toml | 11 + .../wormhole/Move.lock | 27 + .../wormhole/Move.mainnet.toml | 12 + .../wormhole/Move.testnet.toml | 12 + .../wormhole/Move.toml | 12 + .../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 + target_chains/sui/contracts/Move.lock | 26 +- target_chains/sui/contracts/Move.toml | 16 +- .../sources/batch_price_attestation.move | 6 +- .../sui/contracts/sources/data_source.move | 6 +- .../sui/contracts/sources/event.move | 2 +- .../sources/governance/contract_upgrade.move | 12 +- .../sources/governance/set_data_sources.move | 4 +- .../governance/set_stale_price_threshold.move | 4 +- .../sources/governance/set_update_fee.move | 8 +- .../sui/contracts/sources/merkle_tree.move | 6 +- .../sui/contracts/sources/migrate.move | 4 +- .../sui/contracts/sources/price_info.move | 30 +- target_chains/sui/contracts/sources/pyth.move | 78 +- .../contracts/sources/pyth_accumulator.move | 2 +- target_chains/sui/contracts/sources/set.move | 4 +- .../sui/contracts/sources/setup.move | 10 +- .../sui/contracts/sources/state.move | 12 +- 359 files changed, 63138 insertions(+), 114 deletions(-) create mode 100644 target_chains/iota/README.md create mode 100644 target_chains/iota/cli-iota/.gitignore create mode 100644 target_chains/iota/cli-iota/README.md create mode 100644 target_chains/iota/cli-iota/package.json create mode 100644 target_chains/iota/cli-iota/src/cli.ts create mode 100644 target_chains/iota/cli-iota/src/pyth_deploy.ts create mode 100644 target_chains/iota/cli-iota/src/upgrade_pyth.ts create mode 100644 target_chains/iota/cli-iota/tsconfig.json create mode 100644 target_chains/iota/cli/.gitignore create mode 100644 target_chains/iota/cli/README.md create mode 100644 target_chains/iota/cli/package.json create mode 100644 target_chains/iota/cli/src/cli.ts create mode 100644 target_chains/iota/cli/src/pyth_deploy.ts create mode 100644 target_chains/iota/cli/src/upgrade_pyth.ts create mode 100644 target_chains/iota/cli/tsconfig.json create mode 100644 target_chains/iota/contracts/Makefile create mode 100644 target_chains/iota/contracts/Move.iota_testnet.toml create mode 100644 target_chains/iota/contracts/Move.lock create mode 100644 target_chains/iota/contracts/Move.mainnet.toml create mode 100644 target_chains/iota/contracts/Move.movement_m2_devnet.toml create mode 100644 target_chains/iota/contracts/Move.testnet.toml create mode 100644 target_chains/iota/contracts/Move.toml create mode 100644 target_chains/iota/contracts/README.md create mode 100644 target_chains/iota/contracts/rust-toolchain.toml create mode 100644 target_chains/iota/contracts/sources/batch_price_attestation.move create mode 100644 target_chains/iota/contracts/sources/data_source.move create mode 100644 target_chains/iota/contracts/sources/deserialize.move create mode 100644 target_chains/iota/contracts/sources/event.move create mode 100644 target_chains/iota/contracts/sources/governance/contract_upgrade.move create mode 100644 target_chains/iota/contracts/sources/governance/governance.move create mode 100644 target_chains/iota/contracts/sources/governance/governance_action.move create mode 100644 target_chains/iota/contracts/sources/governance/governance_instruction.move create mode 100644 target_chains/iota/contracts/sources/governance/set_data_sources.move create mode 100644 target_chains/iota/contracts/sources/governance/set_fee_recipient.move create mode 100644 target_chains/iota/contracts/sources/governance/set_governance_data_source.move create mode 100644 target_chains/iota/contracts/sources/governance/set_stale_price_threshold.move create mode 100644 target_chains/iota/contracts/sources/governance/set_update_fee.move create mode 100644 target_chains/iota/contracts/sources/hot_potato_vector.move create mode 100644 target_chains/iota/contracts/sources/i64.move create mode 100644 target_chains/iota/contracts/sources/merkle_tree.move create mode 100644 target_chains/iota/contracts/sources/migrate.move create mode 100644 target_chains/iota/contracts/sources/price.move create mode 100644 target_chains/iota/contracts/sources/price_feed.move create mode 100644 target_chains/iota/contracts/sources/price_identifier.move create mode 100644 target_chains/iota/contracts/sources/price_info.move create mode 100644 target_chains/iota/contracts/sources/price_status.move create mode 100644 target_chains/iota/contracts/sources/pyth.move create mode 100644 target_chains/iota/contracts/sources/pyth_accumulator.move create mode 100644 target_chains/iota/contracts/sources/set.move create mode 100644 target_chains/iota/contracts/sources/setup.move create mode 100644 target_chains/iota/contracts/sources/state.move create mode 100644 target_chains/iota/contracts/sources/version_control.move create mode 100644 target_chains/iota/iota-patch-libs.sh create mode 100644 target_chains/iota/sdk/js-iota/.eslintrc.js create mode 100644 target_chains/iota/sdk/js-iota/.gitignore create mode 100644 target_chains/iota/sdk/js-iota/README.md create mode 100644 target_chains/iota/sdk/js-iota/jest.config.js create mode 100644 target_chains/iota/sdk/js-iota/package.json create mode 100644 target_chains/iota/sdk/js-iota/src/IotaPriceServiceConnection.ts create mode 100644 target_chains/iota/sdk/js-iota/src/client.ts create mode 100644 target_chains/iota/sdk/js-iota/src/examples/IotaRelay.ts create mode 100644 target_chains/iota/sdk/js-iota/src/index.ts create mode 100644 target_chains/iota/sdk/js-iota/tsconfig.json create mode 100644 target_chains/iota/sdk/js/.eslintrc.js create mode 100644 target_chains/iota/sdk/js/.gitignore create mode 100644 target_chains/iota/sdk/js/README.md create mode 100644 target_chains/iota/sdk/js/jest.config.js create mode 100644 target_chains/iota/sdk/js/package.json create mode 100644 target_chains/iota/sdk/js/src/SuiPriceServiceConnection.ts create mode 100644 target_chains/iota/sdk/js/src/client.ts create mode 100644 target_chains/iota/sdk/js/src/examples/SuiRelay.ts create mode 100644 target_chains/iota/sdk/js/src/index.ts create mode 100644 target_chains/iota/sdk/js/tsconfig.json create mode 100644 target_chains/iota/vendor/README.md create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/.gitignore create mode 100755 target_chains/iota/vendor/wormhole_iota_testnet/Docker.md create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/Dockerfile create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/Dockerfile.base create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/Makefile create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/NOTES.md create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/README.md create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36219.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36853.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39101.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39187.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/devnet/client.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/devnet/fullnode.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/devnet/genesis.blob create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/devnet/genesis_config create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/devnet/network.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/coins/.gitignore create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/coins/Makefile create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/coins/Move.devnet.toml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/coins/Move.lock create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/coins/Move.toml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/coins/sources/coin.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/coins/sources/coin_10.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/coins/sources/coin_8.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/core_messages/Makefile create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/core_messages/Move.devnet.toml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/core_messages/Move.lock create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/core_messages/Move.toml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/core_messages/sources/sender.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/templates/README.md create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/Move.toml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/sources/coin.move create mode 100755 target_chains/iota/vendor/wormhole_iota_testnet/scripts/deploy.sh create mode 100755 target_chains/iota/vendor/wormhole_iota_testnet/scripts/node_builder.sh create mode 100755 target_chains/iota/vendor/wormhole_iota_testnet/scripts/register_devnet.sh create mode 100755 target_chains/iota/vendor/wormhole_iota_testnet/scripts/setup_rust.sh create mode 100755 target_chains/iota/vendor/wormhole_iota_testnet/scripts/start_node.sh create mode 100755 target_chains/iota/vendor/wormhole_iota_testnet/scripts/switch.sh create mode 100755 target_chains/iota/vendor/wormhole_iota_testnet/scripts/wait_for_devnet.sh create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/.gitignore create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/Makefile create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/js/00_environment.ts create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/js/01_wormhole.ts create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/build.ts create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/consts.ts create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/error/moveAbort.ts create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/error/wormhole.ts create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/setup.ts create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/upgrade.ts create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/utils.ts create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/wormhole/testPublishMessage.ts create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/package-lock.json create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/package.json create mode 100755 target_chains/iota/vendor/wormhole_iota_testnet/testing/run_integration_test.sh create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/scripts/upgrade-token-bridge.ts create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/scripts/upgrade-wormhole.ts create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/sui_config/client.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/sui_config/fullnode.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/sui_config/genesis.blob create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/sui_config/network.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/sui_config/sui.keystore create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/sui_config/validator-config-0.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/sui_config/validator-config-1.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/sui_config/validator-config-2.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/sui_config/validator-config-3.yaml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/testing/tsconfig.json create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/.gitignore create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/Makefile create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/Move.devnet.toml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/Move.lock create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/Move.mainnet.toml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/Move.testnet.toml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/Move.toml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/attest_token.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer_with_payload.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/create_wrapped.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/datatypes/normalized_amount.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/governance/register_chain.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/governance/upgrade_contract.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/messages/asset_meta.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer_with_payload.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/migrate.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/resources/native_asset.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/resources/token_registry.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/resources/wrapped_asset.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/setup.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/state.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_10.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_4.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_12.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_7.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/dummy_message.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/token_bridge_scenario.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens_with_payload.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/utils/coin_utils.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/utils/string_utils.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/vaa.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/version_control.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/.gitignore create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Makefile create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Move.devnet.toml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Move.lock create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Move.mainnet.toml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Move.testnet.toml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Move.toml create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/README.md create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes20.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes32.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/external_address.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/guardian_signature.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/emitter.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance/set_fee.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance/transfer_fee.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance/update_guardian_set.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance/upgrade_contract.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance_message.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/migrate.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/publish_message.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/consumed_vaas.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/fee_collector.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian_set.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/set.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/setup.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/state.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/test/wormhole_scenario.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/utils/bytes.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/utils/cursor.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/utils/package_utils.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/vaa.move create mode 100644 target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/version_control.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/.gitignore create mode 100755 target_chains/iota/vendor/wormhole_movement_m2_devnet/Docker.md create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/Dockerfile create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/Dockerfile.base create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/Makefile create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/NOTES.md create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/README.md create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/127.0.0.1-36219.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/127.0.0.1-36853.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/127.0.0.1-39101.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/127.0.0.1-39187.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/client.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/fullnode.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/genesis.blob create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/genesis_config create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/network.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/coins/.gitignore create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/coins/Makefile create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/coins/Move.devnet.toml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/coins/Move.lock create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/coins/Move.toml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/coins/sources/coin.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/coins/sources/coin_10.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/coins/sources/coin_8.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/Makefile create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/Move.devnet.toml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/Move.lock create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/Move.toml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/sources/sender.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/templates/README.md create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/templates/wrapped_coin/Move.toml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/templates/wrapped_coin/sources/coin.move create mode 100755 target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/deploy.sh create mode 100755 target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/node_builder.sh create mode 100755 target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/register_devnet.sh create mode 100755 target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/setup_rust.sh create mode 100755 target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/start_node.sh create mode 100755 target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/switch.sh create mode 100755 target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/wait_for_devnet.sh create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/.gitignore create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/Makefile create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/00_environment.ts create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/01_wormhole.ts create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/build.ts create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/consts.ts create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/error/moveAbort.ts create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/error/wormhole.ts create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/setup.ts create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/upgrade.ts create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/utils.ts create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/wormhole/testPublishMessage.ts create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/package-lock.json create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/package.json create mode 100755 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/run_integration_test.sh create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/scripts/upgrade-token-bridge.ts create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/scripts/upgrade-wormhole.ts create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/client.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/fullnode.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/genesis.blob create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/network.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/sui.keystore create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/validator-config-0.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/validator-config-1.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/validator-config-2.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/validator-config-3.yaml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/tsconfig.json create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/.gitignore create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/Makefile create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/Move.devnet.toml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/Move.lock create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/Move.mainnet.toml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/Move.testnet.toml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/Move.toml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/attest_token.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/complete_transfer.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/complete_transfer_with_payload.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/create_wrapped.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/datatypes/normalized_amount.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/governance/register_chain.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/governance/upgrade_contract.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/messages/asset_meta.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/messages/transfer.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/messages/transfer_with_payload.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/migrate.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/resources/native_asset.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/resources/token_registry.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/resources/wrapped_asset.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/setup.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/state.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/coin_native_10.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/coin_native_4.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/coin_wrapped_12.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/coin_wrapped_7.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/dummy_message.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/token_bridge_scenario.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/transfer_tokens.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/transfer_tokens_with_payload.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/utils/coin_utils.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/utils/string_utils.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/vaa.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/version_control.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/.gitignore create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/Makefile create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.devnet.toml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.lock create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.mainnet.toml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.testnet.toml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.toml create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/README.md create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/datatypes/bytes20.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/datatypes/bytes32.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/datatypes/external_address.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/datatypes/guardian_signature.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/emitter.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance/set_fee.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance/transfer_fee.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance/update_guardian_set.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance/upgrade_contract.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance_message.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/migrate.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/publish_message.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/consumed_vaas.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/fee_collector.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/guardian.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/guardian_set.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/set.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/setup.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/state.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/test/wormhole_scenario.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/utils/bytes.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/utils/cursor.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/utils/package_utils.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/vaa.move create mode 100644 target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/version_control.move diff --git a/target_chains/iota/README.md b/target_chains/iota/README.md new file mode 100644 index 0000000000..975fffa158 --- /dev/null +++ b/target_chains/iota/README.md @@ -0,0 +1,9 @@ +# Pyth on Sui + +This directory contains the Pyth contract for Sui and utilities to deploy and use it. + +- `cli` folder contains tools for deploying and upgrading the Pyth contract. +- `contracts` folder contains the Pyth contract source code in Move. +- `sdk` folder contains the Pyth javascript SDK for Sui that should be used by dApp developers. + +For more information regarding Pyth integration on Sui please refer to our [docs](https://docs.pyth.network/documentation/pythnet-price-feeds/sui). diff --git a/target_chains/iota/cli-iota/.gitignore b/target_chains/iota/cli-iota/.gitignore new file mode 100644 index 0000000000..a65b41774a --- /dev/null +++ b/target_chains/iota/cli-iota/.gitignore @@ -0,0 +1 @@ +lib diff --git a/target_chains/iota/cli-iota/README.md b/target_chains/iota/cli-iota/README.md new file mode 100644 index 0000000000..aede9ba8d4 --- /dev/null +++ b/target_chains/iota/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 [iota_sui|iota_sui] +``` + +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. + +```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 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 iota 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 (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: + +- 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/iota/cli-iota/package.json b/target_chains/iota/cli-iota/package.json new file mode 100644 index 0000000000..189d8bc0d4 --- /dev/null +++ b/target_chains/iota/cli-iota/package.json @@ -0,0 +1,27 @@ +{ + "name": "pyth-iota-cli", + "version": "0.1.0", + "description": "Pyth IOTA 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/iota/cli-iota/src/cli.ts b/target_chains/iota/cli-iota/src/cli.ts new file mode 100644 index 0000000000..ea5311556a --- /dev/null +++ b/target_chains/iota/cli-iota/src/cli.ts @@ -0,0 +1,288 @@ +import yargs from "yargs"; +import { hideBin } from "yargs/helpers"; +import { + DefaultStore, + getDefaultDeploymentConfig, + 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 "@iota/iota-sdk/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 iota_0x68dda579251917b3db28e35c4df495c6e664ccc085ede867a9b773c8ebedc2c1)", + }, + path: { + type: "string", + default: "../../contracts", + 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", + }, + "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): IotaPriceFeedContract { + const contract = DefaultStore.contracts[contractId] as IotaPriceFeedContract; + if (!contract) { + throw new Error(`Contract ${contractId} not found`); + } + return contract; +} + +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 = new PriceServiceConnection(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 = new PriceServiceConnection(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( + `iota 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 iota_mainnet or iota_testnet", + }, + path: OPTIONS.path, + }) + .usage( + "$0 deploy --private-key --chain [iota_mainnet|iota_testnet] --path " + ); + }, + async (argv) => { + const walletPrivateKey = argv["private-key"]; + const chain = DefaultStore.chains[argv.chain] as IotaChain; + const keypair = Ed25519Keypair.fromSecretKey( + new Uint8Array(Buffer.from(walletPrivateKey, "hex")) + ); + const result = await publishPackage( + keypair, + chain.getProvider(), + argv.path + ); + const deploymentType = "stable"; + 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 = new PriceServiceConnection(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( + new Uint8Array(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/iota/cli-iota/src/pyth_deploy.ts b/target_chains/iota/cli-iota/src/pyth_deploy.ts new file mode 100644 index 0000000000..2b28dc0c63 --- /dev/null +++ b/target_chains/iota/cli-iota/src/pyth_deploy.ts @@ -0,0 +1,185 @@ +import { Transaction } from "@iota/iota-sdk/transactions"; + +import { + NANOS_PER_IOTA, + normalizeIotaObjectId, + fromB64, +} from "@iota/iota-sdk/utils"; + +import { Ed25519Keypair } from "@iota/iota-sdk/keypairs/ed25519"; +import { execSync } from "child_process"; +import { DataSource } from "@pythnetwork/xc-admin-common"; +import { IotaClient } from "@iota/iota-sdk/client"; +import { bcs } from "@iota/iota-sdk/bcs"; + +export async function publishPackage( + keypair: Ed25519Keypair, + provider: IotaClient, + packagePath: string +): Promise<{ packageId: string; upgradeCapId: string; deployerCapId: string }> { + // Build contracts + const buildOutput: { + modules: string[]; + dependencies: string[]; + } = JSON.parse( + execSync( + `iota move build --dump-bytecode-as-base64 --path ${__dirname}/${packagePath} 2> /dev/null`, + { + encoding: "utf-8", + } + ) + ); + + console.log("buildOutput: ", buildOutput); + + // Publish contracts + const txb = new Transaction(); + + txb.setGasBudget(NANOS_PER_IOTA / 2n); // 0.5 IOTA + + const [upgradeCap] = txb.publish({ + modules: buildOutput.modules.map((m: string) => Array.from(fromB64(m))), + dependencies: buildOutput.dependencies.map((d: string) => + normalizeIotaObjectId(d) + ), + }); + + // Transfer upgrade capability to deployer + txb.transferObjects([upgradeCap], txb.pure.address(keypair.toIotaAddress())); + + // 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: IotaClient, + 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( + bcs + .vector(bcs.vector(bcs.u8())) + .serialize( + config.dataSources.map((dataSource) => [ + ...Buffer.from(dataSource.emitterAddress, "hex"), + ]) + ) + ); + const dataSourceEmitterChainIds = tx.pure( + bcs + .vector(bcs.u64()) + .serialize( + config.dataSources.map((dataSource) => dataSource.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({ + target: `${pythPackageId}::pyth::init_pyth`, + arguments: [ + tx.object(deployerCapId), + tx.object(upgradeCapId), + stalePriceThreshold, + governanceEmitterChainId, + governanceEmitterAddress, + dataSourceEmitterChainIds, + dataSourceEmitterAddresses, + baseUpdateFee, + ], + }); + + tx.setGasBudget(NANOS_PER_IOTA / 10n); // 0.1 IOTA + + 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/iota/cli-iota/src/upgrade_pyth.ts b/target_chains/iota/cli-iota/src/upgrade_pyth.ts new file mode 100644 index 0000000000..260733956b --- /dev/null +++ b/target_chains/iota/cli-iota/src/upgrade_pyth.ts @@ -0,0 +1,115 @@ +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 { IotaPriceFeedContract } from "@pythnetwork/contract-manager"; + +export function buildForBytecodeAndDigest(packagePath: string) { + const buildOutput: { + modules: string[]; + dependencies: string[]; + digest: number[]; + } = JSON.parse( + execSync( + `iota 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) => + normalizeIotaObjectId(d) + ), + digest: Buffer.from(buildOutput.digest), + }; +} + +export async function upgradePyth( + keypair: Ed25519Keypair, + provider: IotaClient, + modules: number[][], + dependencies: string[], + signedVaa: Buffer, + contract: IotaPriceFeedContract +) { + 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(NANOS_PER_IOTA / 4n); // 0.25 IOTA + + return provider.signAndExecuteTransaction({ + signer: keypair, + transaction: tx, + options: { + showEffects: true, + showEvents: true, + }, + }); +} + +export async function migratePyth( + keypair: Ed25519Keypair, + provider: IotaClient, + signedUpgradeVaa: Buffer, + contract: IotaPriceFeedContract, + 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(NANOS_PER_IOTA / 10n); //0.1 IOTA + + return provider.signAndExecuteTransaction({ + signer: keypair, + transaction: tx, + options: { + showEffects: true, + showEvents: true, + }, + }); +} diff --git a/target_chains/iota/cli-iota/tsconfig.json b/target_chains/iota/cli-iota/tsconfig.json new file mode 100644 index 0000000000..be27cdded2 --- /dev/null +++ b/target_chains/iota/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/iota/cli/.gitignore b/target_chains/iota/cli/.gitignore new file mode 100644 index 0000000000..a65b41774a --- /dev/null +++ b/target_chains/iota/cli/.gitignore @@ -0,0 +1 @@ +lib diff --git a/target_chains/iota/cli/README.md b/target_chains/iota/cli/README.md new file mode 100644 index 0000000000..cf4a0275a6 --- /dev/null +++ b/target_chains/iota/cli/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/iota/cli/package.json b/target_chains/iota/cli/package.json new file mode 100644 index 0000000000..8aebfa4f33 --- /dev/null +++ b/target_chains/iota/cli/package.json @@ -0,0 +1,27 @@ +{ + "name": "pyth-sui-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", + "@mysten/sui": "^1.3.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/iota/cli/src/cli.ts b/target_chains/iota/cli/src/cli.ts new file mode 100644 index 0000000000..0d35ebf7b8 --- /dev/null +++ b/target_chains/iota/cli/src/cli.ts @@ -0,0 +1,288 @@ +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", + default: "https://hermes.pyth.network", + desc: "Price service endpoint to use, defaults to https://hermes.pyth.network", + }, + "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; +} + +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 = new PriceServiceConnection(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 = new PriceServiceConnection(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( + new Uint8Array(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 = new PriceServiceConnection(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( + new Uint8Array(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/iota/cli/src/pyth_deploy.ts b/target_chains/iota/cli/src/pyth_deploy.ts new file mode 100644 index 0000000000..80d585482c --- /dev/null +++ b/target_chains/iota/cli/src/pyth_deploy.ts @@ -0,0 +1,181 @@ +import { Transaction } from "@mysten/sui/transactions"; + +import { MIST_PER_SUI, normalizeSuiObjectId, fromB64 } from "@mysten/sui/utils"; + +import { Ed25519Keypair } from "@mysten/sui/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/bcs"; + +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 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( + bcs + .vector(bcs.vector(bcs.u8())) + .serialize( + config.dataSources.map((dataSource) => [ + ...Buffer.from(dataSource.emitterAddress, "hex"), + ]) + ) + ); + const dataSourceEmitterChainIds = tx.pure( + bcs + .vector(bcs.u64()) + .serialize( + config.dataSources.map((dataSource) => dataSource.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({ + 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/iota/cli/src/upgrade_pyth.ts b/target_chains/iota/cli/src/upgrade_pyth.ts new file mode 100644 index 0000000000..ee2b83cf92 --- /dev/null +++ b/target_chains/iota/cli/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/iota/cli/tsconfig.json b/target_chains/iota/cli/tsconfig.json new file mode 100644 index 0000000000..be27cdded2 --- /dev/null +++ b/target_chains/iota/cli/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/iota/contracts/Makefile b/target_chains/iota/contracts/Makefile new file mode 100644 index 0000000000..03eb12fe17 --- /dev/null +++ b/target_chains/iota/contracts/Makefile @@ -0,0 +1,9 @@ +TARGETS := test + +.PHONY: test +test: + sui move test + +.PHONY: build +build: + sui move build -d diff --git a/target_chains/iota/contracts/Move.iota_testnet.toml b/target_chains/iota/contracts/Move.iota_testnet.toml new file mode 100644 index 0000000000..e21819c7b3 --- /dev/null +++ b/target_chains/iota/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/iota/contracts/Move.lock b/target_chains/iota/contracts/Move.lock new file mode 100644 index 0000000000..4d695c4552 --- /dev/null +++ b/target_chains/iota/contracts/Move.lock @@ -0,0 +1,36 @@ +# @generated by Move, please check-in and do not edit manually. + +[move] +version = 0 +manifest_digest = "915D6459031D76A0618631BDF08328171A3494876C5E17735BCBFF462BEDF464" +deps_digest = "3C4103934B1E040BB6B23F1D610B4EF9F2F1166A50A104EADCF77467C004C600" + +dependencies = [ + { name = "Iota" }, + { name = "Wormhole" }, +] + +[[move.package]] +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.package]] +name = "Wormhole" +source = { local = "../vendor/wormhole_iota_testnet/wormhole" } + +dependencies = [ + { name = "Iota" }, +] + +[move.toolchain-version] +compiler-version = "0.9.2-rc" +edition = "2024.beta" +flavor = "iota" diff --git a/target_chains/iota/contracts/Move.mainnet.toml b/target_chains/iota/contracts/Move.mainnet.toml new file mode 100644 index 0000000000..d89bb0bf4b --- /dev/null +++ b/target_chains/iota/contracts/Move.mainnet.toml @@ -0,0 +1,17 @@ +[package] +name = "Pyth" +version = "0.0.2" +published-at = "0x04e20ddf36af412a4096f9014f4a565af9e812db9a05cc40254846cf6ed0ad91" + +[dependencies.Sui] +git = "https://github.com/MystenLabs/sui.git" +subdir = "crates/sui-framework/packages/sui-framework" +rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" + +[dependencies.Wormhole] +git = "https://github.com/wormhole-foundation/wormhole.git" +subdir = "sui/wormhole" +rev = "sui-upgrade-mainnet" + +[addresses] +pyth = "0x8d97f1cd6ac663735be08d1d2b6d02a159e711586461306ce60a2b7a6a565a9e" diff --git a/target_chains/iota/contracts/Move.movement_m2_devnet.toml b/target_chains/iota/contracts/Move.movement_m2_devnet.toml new file mode 100644 index 0000000000..42fdd11c08 --- /dev/null +++ b/target_chains/iota/contracts/Move.movement_m2_devnet.toml @@ -0,0 +1,15 @@ +[package] +name = "Pyth" +version = "0.0.2" +published-at = "0x46522b54385efa424e0582ea4886bb5cfbe11d5c2a6a19ac0c82c2c81c73f9c5" + +[dependencies.Sui] +git = "https://github.com/MystenLabs/sui.git" +subdir = "crates/sui-framework/packages/sui-framework" +rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" + +[dependencies.Wormhole] +local = "../vendor/wormhole_movement_m2_devnet/wormhole" + +[addresses] +pyth = "0x46522b54385efa424e0582ea4886bb5cfbe11d5c2a6a19ac0c82c2c81c73f9c5" diff --git a/target_chains/iota/contracts/Move.testnet.toml b/target_chains/iota/contracts/Move.testnet.toml new file mode 100644 index 0000000000..01a289f97e --- /dev/null +++ b/target_chains/iota/contracts/Move.testnet.toml @@ -0,0 +1,17 @@ +[package] +name = "Pyth" +version = "0.0.2" +published-at = "0xabf837e98c26087cba0883c0a7a28326b1fa3c5e1e2c5abdb486f9e8f594c837" + +[dependencies.Sui] +git = "https://github.com/MystenLabs/sui.git" +subdir = "crates/sui-framework/packages/sui-framework" +rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" + +[dependencies.Wormhole] +git = "https://github.com/wormhole-foundation/wormhole.git" +subdir = "sui/wormhole" +rev = "sui-upgrade-testnet" + +[addresses] +pyth = "0xabf837e98c26087cba0883c0a7a28326b1fa3c5e1e2c5abdb486f9e8f594c837" diff --git a/target_chains/iota/contracts/Move.toml b/target_chains/iota/contracts/Move.toml new file mode 100644 index 0000000000..e21819c7b3 --- /dev/null +++ b/target_chains/iota/contracts/Move.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/iota/contracts/README.md b/target_chains/iota/contracts/README.md new file mode 100644 index 0000000000..aaabaf1a9b --- /dev/null +++ b/target_chains/iota/contracts/README.md @@ -0,0 +1,18 @@ +# Build information + +Contracts are compiled with sui cli version `sui 1.0.0-09b208149` that can be installed via the following command: + +```commandline +cargo install --locked --git https://github.com/MystenLabs/sui.git --rev 09b2081498366df936abae26eea4b2d5cafb2788 sui sui-faucet +``` + +## Gas Profiling + +Using the [`sui-tool` binary](https://github.com/MystenLabs/sui/pull/12680), you can profile gas usage of transactions by running: + +```bash +env MOVE_VM_PROFILE=1 ./sui-tool replay --rpc https://fullnode.mainnet.sui.io:443 tx -t +``` + +`sui-tool` gas profiling works only when built with debug profile and should be compiled by your own (you can't use the precompiled binary). +We suggest benchmarking on mainnet or where the number of wormhole signature checks is the same as on mainnet. diff --git a/target_chains/iota/contracts/rust-toolchain.toml b/target_chains/iota/contracts/rust-toolchain.toml new file mode 100644 index 0000000000..83025f9721 --- /dev/null +++ b/target_chains/iota/contracts/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.77.0" diff --git a/target_chains/iota/contracts/sources/batch_price_attestation.move b/target_chains/iota/contracts/sources/batch_price_attestation.move new file mode 100644 index 0000000000..461c8daac3 --- /dev/null +++ b/target_chains/iota/contracts/sources/batch_price_attestation.move @@ -0,0 +1,247 @@ +module pyth::batch_price_attestation { + use std::vector::{Self}; + use iota::clock::{Self, Clock}; + + use pyth::price_feed::{Self}; + use pyth::price_info::{Self, PriceInfo}; + use pyth::price_identifier::{Self}; + use pyth::price_status; + use pyth::deserialize::{Self}; + + use wormhole::cursor::{Self, Cursor}; + use wormhole::bytes::{Self}; + + #[test_only] + use pyth::price; + #[test_only] + use pyth::i64; + + const MAGIC: u64 = 0x50325748; // "P2WH" (Pyth2Wormhole) raw ASCII bytes + const E_INVALID_ATTESTATION_MAGIC_VALUE: u64 = 0; + const E_INVALID_BATCH_ATTESTATION_HEADER_SIZE: u64 = 1; + + /// @notice This struct is based on the legacy wormhole attester implementation in pythnet_sdk + struct BatchPriceAttestation { + header: Header, + attestation_size: u64, + attestation_count: u64, + price_infos: vector, + } + + struct Header { + magic: u64, + version_major: u64, + version_minor: u64, + header_size: u64, + payload_id: u8, + } + + fun deserialize_header(cur: &mut Cursor): Header { + let magic = (deserialize::deserialize_u32(cur) as u64); + assert!(magic == MAGIC, E_INVALID_ATTESTATION_MAGIC_VALUE); + let version_major = deserialize::deserialize_u16(cur); + let version_minor = deserialize::deserialize_u16(cur); + let header_size = deserialize::deserialize_u16(cur); + let payload_id = deserialize::deserialize_u8(cur); + + assert!(header_size >= 1, E_INVALID_BATCH_ATTESTATION_HEADER_SIZE); + let unknown_header_bytes = header_size - 1; + let _unknown = bytes::take_bytes(cur, (unknown_header_bytes as u64)); + + Header { + magic, + header_size: (header_size as u64), + version_minor: (version_minor as u64), + version_major: (version_major as u64), + payload_id, + } + } + + public fun destroy(batch: BatchPriceAttestation): vector { + let BatchPriceAttestation { + header: Header { + magic: _, + version_major: _, + version_minor: _, + header_size: _, + payload_id: _, + }, + attestation_size: _, + attestation_count: _, + price_infos, + } = batch; + price_infos + } + + public fun get_attestation_count(batch: &BatchPriceAttestation): u64 { + batch.attestation_count + } + + public fun get_price_info(batch: &BatchPriceAttestation, index: u64): &PriceInfo { + vector::borrow(&batch.price_infos, index) + } + + public fun deserialize(bytes: vector, clock: &Clock): BatchPriceAttestation { + let cur = cursor::new(bytes); + let header = deserialize_header(&mut cur); + + let attestation_count = deserialize::deserialize_u16(&mut cur); + let attestation_size = deserialize::deserialize_u16(&mut cur); + let price_infos = vector::empty(); + + let i = 0; + while (i < attestation_count) { + let price_info = deserialize_price_info(&mut cur, clock); + vector::push_back(&mut price_infos, price_info); + + // Consume any excess bytes + let parsed_bytes = 32+32+8+8+4+8+8+1+4+4+8+8+8+8+8; + let _excess = bytes::take_bytes(&mut cur, (attestation_size - parsed_bytes as u64)); + + i = i + 1; + }; + cursor::destroy_empty(cur); + + BatchPriceAttestation { + header, + attestation_count: (attestation_count as u64), + attestation_size: (attestation_size as u64), + price_infos, + } + } + + fun deserialize_price_info(cur: &mut Cursor, clock: &Clock): PriceInfo { + + // Skip obsolete field + let _product_identifier = deserialize::deserialize_vector(cur, 32); + let price_identifier = price_identifier::from_byte_vec(deserialize::deserialize_vector(cur, 32)); + let price = deserialize::deserialize_i64(cur); + let conf = deserialize::deserialize_u64(cur); + let expo = deserialize::deserialize_i32(cur); + let ema_price = deserialize::deserialize_i64(cur); + let ema_conf = deserialize::deserialize_u64(cur); + let status = price_status::from_u64((deserialize::deserialize_u8(cur) as u64)); + + // Skip obsolete fields + let _num_publishers = deserialize::deserialize_u32(cur); + let _max_num_publishers = deserialize::deserialize_u32(cur); + + let attestation_time = deserialize::deserialize_u64(cur); + let publish_time = deserialize::deserialize_u64(cur); // + let prev_publish_time = deserialize::deserialize_u64(cur); + let prev_price = deserialize::deserialize_i64(cur); + let prev_conf = deserialize::deserialize_u64(cur); + + // Handle the case where the status is not trading. This logic will soon be moved into + // the attester. + + // If status is trading, use the current price. + // If not, use the last known trading price. + let current_price = pyth::price::new(price, conf, expo, publish_time); + if (status != price_status::new_trading()) { + current_price = pyth::price::new(prev_price, prev_conf, expo, prev_publish_time); + }; + + // If status is trading, use the timestamp of the aggregate as the timestamp for the + // EMA price. If not, the EMA will have last been updated when the aggregate last had + // trading status, so use prev_publish_time (the time when the aggregate last had trading status). + let ema_timestamp = publish_time; + if (status != price_status::new_trading()) { + ema_timestamp = prev_publish_time; + }; + + price_info::new_price_info( + attestation_time, + clock::timestamp_ms(clock) / 1000, // Divide by 1000 to get timestamp in seconds + price_feed::new( + price_identifier, + current_price, + pyth::price::new(ema_price, ema_conf, expo, ema_timestamp), + ) + ) + } + + #[test] + #[expected_failure] + fun test_deserialize_batch_price_attestation_invalid_magic() { + use iota::test_scenario::{Self, ctx}; + let test = test_scenario::begin(@0x1234); + let test_clock = clock::create_for_testing(ctx(&mut test)); + // A batch price attestation with a magic number of 0x50325749 + let bytes = x"5032574900030000000102000400951436e0be37536be96f0896366089506a59763d036728332d3e3038047851aea7c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1000000000000049a0000000000000008fffffffb00000000000005dc0000000000000003000000000100000001000000006329c0eb000000006329c0e9000000006329c0e400000000000006150000000000000007215258d81468614f6b7e194c5d145609394f67b041e93e6695dcc616faadd0603b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe000000000000041a0000000000000003fffffffb00000000000005cb0000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e4000000000000048600000000000000078ac9cf3ab299af710d735163726fdae0db8465280502eb9f801f74b3c1bd190333832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d00000000000003f20000000000000002fffffffb00000000000005e70000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e40000000000000685000000000000000861db714e9ff987b6fedf00d01f9fea6db7c30632d6fc83b7bc9459d7192bc44a21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db800000000000006cb0000000000000001fffffffb00000000000005e40000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e400000000000007970000000000000001"; + let _ = destroy(deserialize(bytes, &test_clock)); + clock::destroy_for_testing(test_clock); + test_scenario::end(test); + } + + #[test] + fun test_deserialize_batch_price_attestation() { + use iota::test_scenario::{Self, ctx}; + // Set the arrival time + let test = test_scenario::begin(@0x1234); + let test_clock = clock::create_for_testing(ctx(&mut test)); + test_scenario::next_tx(&mut test, @0x1234); + let arrival_time_in_seconds = clock::timestamp_ms(&test_clock) / 1000; + + // let arrival_time = tx_context::epoch(ctx(&mut test)); + + // A raw batch price attestation + // The first attestation has a status of UNKNOWN + let bytes = x"5032574800030000000102000400951436e0be37536be96f0896366089506a59763d036728332d3e3038047851aea7c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1000000000000049a0000000000000008fffffffb00000000000005dc0000000000000003000000000100000001000000006329c0eb000000006329c0e9000000006329c0e400000000000006150000000000000007215258d81468614f6b7e194c5d145609394f67b041e93e6695dcc616faadd0603b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe000000000000041a0000000000000003fffffffb00000000000005cb0000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e4000000000000048600000000000000078ac9cf3ab299af710d735163726fdae0db8465280502eb9f801f74b3c1bd190333832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d00000000000003f20000000000000002fffffffb00000000000005e70000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e40000000000000685000000000000000861db714e9ff987b6fedf00d01f9fea6db7c30632d6fc83b7bc9459d7192bc44a21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db800000000000006cb0000000000000001fffffffb00000000000005e40000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e400000000000007970000000000000001"; + + let expected = BatchPriceAttestation { + header: Header { + magic: 0x50325748, + version_major: 3, + version_minor: 0, + payload_id: 2, + header_size: 1, + }, + attestation_count: 4, + attestation_size: 149, + price_infos: vector[ + price_info::new_price_info( + 1663680747, + arrival_time_in_seconds, + price_feed::new( + price_identifier::from_byte_vec(x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1"), + price::new(i64::new(1557, false), 7, i64::new(5, true), 1663680740), + price::new(i64::new(1500, false), 3, i64::new(5, true), 1663680740), + ) ), + price_info::new_price_info( + 1663680747, + arrival_time_in_seconds, + price_feed::new( + price_identifier::from_byte_vec(x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe"), + price::new(i64::new(1050, false), 3, i64::new(5, true), 1663680745), + price::new(i64::new(1483, false), 3, i64::new(5, true), 1663680745), + ) ), + price_info::new_price_info( + 1663680747, + arrival_time_in_seconds, + price_feed::new( + price_identifier::from_byte_vec(x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d"), + price::new(i64::new(1010, false), 2, i64::new(5, true), 1663680745), + price::new(i64::new(1511, false), 3, i64::new(5, true), 1663680745), + ) ), + price_info::new_price_info( + 1663680747, + arrival_time_in_seconds, + price_feed::new( + price_identifier::from_byte_vec(x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8"), + price::new(i64::new(1739, false), 1, i64::new(5, true), 1663680745), + price::new(i64::new(1508, false), 3, i64::new(5, true), 1663680745), + ) + ), + ], + }; + + let deserialized = deserialize(bytes, &test_clock); + + assert!(&expected == &deserialized, 1); + destroy(expected); + destroy(deserialized); + clock::destroy_for_testing(test_clock); + test_scenario::end(test); + } +} diff --git a/target_chains/iota/contracts/sources/data_source.move b/target_chains/iota/contracts/sources/data_source.move new file mode 100644 index 0000000000..2b7e4a56e9 --- /dev/null +++ b/target_chains/iota/contracts/sources/data_source.move @@ -0,0 +1,85 @@ +module pyth::data_source { + use iota::dynamic_field::{Self}; + use iota::object::{UID}; + use iota::tx_context::{TxContext}; + + use pyth::set::{Self}; + + use wormhole::external_address::ExternalAddress; + + friend pyth::state; + friend pyth::set_data_sources; + friend pyth::pyth; + friend pyth::set_governance_data_source; + friend pyth::governance; + #[test_only] + friend pyth::pyth_tests; + #[test_only] + friend pyth::set_data_sources_tests; + + const KEY: vector = b"data_sources"; + const E_DATA_SOURCE_REGISTRY_ALREADY_EXISTS: u64 = 0; + const E_DATA_SOURCE_ALREADY_REGISTERED: u64 = 1; + + struct DataSource has copy, drop, store { + emitter_chain: u64, + emitter_address: ExternalAddress, + } + + public(friend) fun new_data_source_registry(parent_id: &mut UID, ctx: &mut TxContext) { + assert!( + !dynamic_field::exists_(parent_id, KEY), + E_DATA_SOURCE_REGISTRY_ALREADY_EXISTS // TODO - add custom error type + ); + dynamic_field::add( + parent_id, + KEY, + set::new(ctx) + ) + } + + public(friend) fun add(parent_id: &mut UID, data_source: DataSource) { + assert!( + !contains(parent_id, data_source), + E_DATA_SOURCE_ALREADY_REGISTERED + ); + set::add( + dynamic_field::borrow_mut(parent_id, KEY), + data_source + ) + } + + public(friend) fun empty(parent_id: &mut UID){ + set::empty( + dynamic_field::borrow_mut(parent_id, KEY) + ) + } + + public fun contains(parent_id: &UID, data_source: DataSource): bool { + let ref = dynamic_field::borrow(parent_id, KEY); + set::contains(ref, data_source) + } + + public(friend) fun new(emitter_chain: u64, emitter_address: ExternalAddress): DataSource { + DataSource { + emitter_chain, + emitter_address, + } + } + + public fun emitter_chain(data_source: &DataSource): u64{ + data_source.emitter_chain + } + + public fun emitter_address(data_source: &DataSource): ExternalAddress{ + data_source.emitter_address + } + + #[test_only] + public fun new_data_source_for_test(emitter_chain: u64, emitter_address: ExternalAddress): DataSource { + DataSource { + emitter_chain, + emitter_address, + } + } +} diff --git a/target_chains/iota/contracts/sources/deserialize.move b/target_chains/iota/contracts/sources/deserialize.move new file mode 100644 index 0000000000..310107d6ea --- /dev/null +++ b/target_chains/iota/contracts/sources/deserialize.move @@ -0,0 +1,143 @@ + module pyth::deserialize { + use wormhole::bytes::{Self}; + use wormhole::cursor::{Cursor}; + use pyth::i64::{Self, I64}; + #[test_only] + use wormhole::cursor::{take_rest}; + + #[test_only] + use wormhole::cursor::{Self}; + + public fun deserialize_vector(cur: &mut Cursor, n: u64): vector { + bytes::take_bytes(cur, n) + } + + public fun deserialize_u8(cur: &mut Cursor): u8 { + bytes::take_u8(cur) + } + + public fun deserialize_u16(cur: &mut Cursor): u16 { + bytes::take_u16_be(cur) + } + + public fun deserialize_u32(cur: &mut Cursor): u32 { + bytes::take_u32_be(cur) + } + + public fun deserialize_i32(cur: &mut Cursor): I64 { + let deserialized = deserialize_u32(cur); + // If negative, pad the value + let negative = (deserialized >> 31) == 1; + if (negative) { + let padded = (0xFFFFFFFF << 32) + (deserialized as u64); + i64::from_u64((padded as u64)) + } else { + i64::from_u64((deserialized as u64)) + } + } + + public fun deserialize_u64(cur: &mut Cursor): u64 { + bytes::take_u64_be(cur) + } + + public fun deserialize_i64(cur: &mut Cursor): I64 { + i64::from_u64(deserialize_u64(cur)) + } + + #[test] + fun test_deserialize_u8() { + let input = x"48258963"; + let cursor = cursor::new(input); + + let result = deserialize_u8(&mut cursor); + assert!(result == 0x48, 1); + + let rest = take_rest(cursor); + assert!(rest == x"258963", 1); + } + + #[test] + fun test_deserialize_u16() { + let input = x"48258963"; + let cursor = cursor::new(input); + + let result = deserialize_u16(&mut cursor); + assert!(result == 0x4825, 1); + + let rest = take_rest(cursor); + assert!(rest == x"8963", 1); + } + + #[test] + fun test_deserialize_u32() { + let input = x"4825896349741695"; + let cursor = cursor::new(input); + + let result = deserialize_u32(&mut cursor); + assert!(result == 0x48258963, 1); + + let rest = take_rest(cursor); + assert!(rest == x"49741695", 1); + } + + #[test] + fun test_deserialize_i32_positive() { + let input = x"4825896349741695"; + let cursor = cursor::new(input); + + let result = deserialize_i32(&mut cursor); + assert!(result == i64::from_u64(0x48258963), 1); + + let rest = take_rest(cursor); + assert!(rest == x"49741695", 1); + } + + #[test] + fun test_deserialize_i32_negative() { + let input = x"FFFFFDC349741695"; + + let cursor = cursor::new(input); + + let result = deserialize_i32(&mut cursor); + assert!(result == i64::from_u64(0xFFFFFFFFFFFFFDC3), 1); + + let rest = take_rest(cursor); + assert!(rest == x"49741695", 1); + } + + #[test] + fun test_deserialize_u64() { + let input = x"48258963497416957497253486"; + let cursor = cursor::new(input); + + let result = deserialize_u64(&mut cursor); + assert!(result == 0x4825896349741695, 1); + + let rest = take_rest(cursor); + assert!(rest == x"7497253486", 1); + } + + #[test] + fun test_deserialize_i64_positive() { + let input = x"48258963497416957497253486"; + let cursor = cursor::new(input); + + let result = deserialize_i64(&mut cursor); + assert!(result == i64::from_u64(0x4825896349741695), 1); + + let rest = take_rest(cursor); + assert!(rest == x"7497253486", 1); + } + + #[test] + fun test_deserialize_i64_negative() { + let input = x"FFFFFFFFFFFFFDC37497253486"; + let cursor = cursor::new(input); + + let result = deserialize_i64(&mut cursor); + assert!(result == i64::from_u64(0xFFFFFFFFFFFFFDC3), 1); + + let rest = take_rest(cursor); + assert!(rest == x"7497253486", 1); + } +} diff --git a/target_chains/iota/contracts/sources/event.move b/target_chains/iota/contracts/sources/event.move new file mode 100644 index 0000000000..ab23be344d --- /dev/null +++ b/target_chains/iota/contracts/sources/event.move @@ -0,0 +1,33 @@ +module pyth::event { + use iota::event::{Self}; + use pyth::price_feed::{PriceFeed}; + + friend pyth::pyth; + friend pyth::state; + + struct PythInitializationEvent has copy, drop {} + + /// Signifies that a price feed has been updated + struct PriceFeedUpdateEvent has copy, store, drop { + /// Value of the price feed + price_feed: PriceFeed, + /// Timestamp of the update + timestamp: u64, + } + + public(friend) fun emit_price_feed_update(price_feed: PriceFeed, timestamp: u64 /* in seconds */) { + event::emit( + PriceFeedUpdateEvent { + price_feed, + timestamp, + } + ); + } + + public(friend) fun emit_pyth_initialization_event() { + event::emit( + PythInitializationEvent {} + ); + } + +} diff --git a/target_chains/iota/contracts/sources/governance/contract_upgrade.move b/target_chains/iota/contracts/sources/governance/contract_upgrade.move new file mode 100644 index 0000000000..5f1918dff1 --- /dev/null +++ b/target_chains/iota/contracts/sources/governance/contract_upgrade.move @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: Apache 2 + +/// This module implements handling a governance VAA to enact upgrading the +/// Pyth 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 pyth::contract_upgrade { + use iota::event::{Self}; + use iota::object::{ID}; + use iota::package::{UpgradeReceipt, UpgradeTicket}; + use wormhole::bytes32::{Self, Bytes32}; + use wormhole::cursor::{Self}; + + use pyth::state::{Self, State}; + use pyth::governance_instruction::{Self}; + use pyth::governance_action::{Self}; + use pyth::governance::{Self, WormholeVAAVerificationReceipt}; + + friend pyth::migrate; + + /// Digest is all zeros. + const E_DIGEST_ZERO_BYTES: u64 = 0; + const E_GOVERNANCE_ACTION_MUST_BE_CONTRACT_UPGRADE: u64 = 1; + const E_GOVERNANCE_CONTRACT_UPGRADE_CHAIN_ID_ZERO: u64 = 2; + const E_CANNOT_EXECUTE_GOVERNANCE_ACTION_WITH_OBSOLETE_SEQUENCE_NUMBER: u64 = 3; + + // Event reflecting package upgrade. + struct ContractUpgraded has drop, copy { + old_contract: ID, + new_contract: ID + } + + struct UpgradeContract { + digest: Bytes32 + } + + /// Redeem governance VAA to issue an `UpgradeTicket` for the upgrade given + /// 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 Iota), whose build digest is encoded in this message. + public fun authorize_upgrade( + pyth_state: &mut State, + receipt: WormholeVAAVerificationReceipt, + ): UpgradeTicket { + + // Get the sequence number of the governance VAA that was used to + // generate the receipt. + let sequence = governance::take_sequence(&receipt); + + // Require that new sequence number is greater than last executed sequence number. + assert!(sequence > state::get_last_executed_governance_sequence(pyth_state), + E_CANNOT_EXECUTE_GOVERNANCE_ACTION_WITH_OBSOLETE_SEQUENCE_NUMBER); + + // Update latest executed sequence number to current one. + state::set_last_executed_governance_sequence_unchecked(pyth_state, sequence); + + let digest = take_upgrade_digest(receipt); + // Proceed with processing new implementation version. + handle_upgrade_contract(pyth_state, digest) + } + + + public(friend) fun take_upgrade_digest(receipt: WormholeVAAVerificationReceipt): Bytes32 { + let payload = governance::take_payload(&receipt); + + let instruction = governance_instruction::from_byte_vec(payload); + + // Get the governance action. + let action = governance_instruction::get_action(&instruction); + + assert!(action == governance_action::new_contract_upgrade(), + E_GOVERNANCE_ACTION_MUST_BE_CONTRACT_UPGRADE); + + assert!(governance_instruction::get_target_chain_id(&instruction) != 0, + E_GOVERNANCE_CONTRACT_UPGRADE_CHAIN_ID_ZERO); + + governance::destroy(receipt); + // upgrade_payload contains a 32-byte digest + let upgrade_payload = governance_instruction::destroy(instruction); + + take_digest(upgrade_payload) + } + + /// Finalize the upgrade that ran to produce the given `receipt`. This + /// method invokes `state::commit_upgrade` which interacts with + /// `iota::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. + 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( + pyth_state: &mut State, + digest: Bytes32 + ): UpgradeTicket { + state::authorize_upgrade(pyth_state, digest) + } + + fun deserialize(payload: vector): UpgradeContract { + let cur = cursor::new(payload); + let digest = bytes32::take_bytes(&mut cur); + assert!(bytes32::is_nonzero(&digest), E_DIGEST_ZERO_BYTES); + + // there might be additional appended to payload in the future, + // which is why we don't cursor::destroy_empty(&mut cur) + cursor::take_rest(cur); + UpgradeContract { digest } + } + + #[test_only] + /// Specific governance payload ID (action) to complete upgrading the + /// contract. + /// TODO: is it okay for the contract upgrade action for Pyth to be 0? Or should it be 1? + const CONTRACT_UPGRADE: u8 = 0; + + + #[test_only] + public fun action(): u8 { + CONTRACT_UPGRADE + } +} + +#[test_only] +module pyth::upgrade_contract_tests { + // TODO +} diff --git a/target_chains/iota/contracts/sources/governance/governance.move b/target_chains/iota/contracts/sources/governance/governance.move new file mode 100644 index 0000000000..c09b031a6b --- /dev/null +++ b/target_chains/iota/contracts/sources/governance/governance.move @@ -0,0 +1,116 @@ +module pyth::governance { + use pyth::governance_instruction; + use pyth::governance_action; + use pyth::set_governance_data_source; + use pyth::set_data_sources; + use pyth::set_stale_price_threshold; + use pyth::set_fee_recipient; + use pyth::state::{Self, State}; + use pyth::set_update_fee; + + use wormhole::vaa::{Self, VAA}; + use wormhole::bytes32::Bytes32; + + const E_INVALID_GOVERNANCE_ACTION: u64 = 0; + const E_MUST_USE_CONTRACT_UPGRADE_MODULE_TO_DO_UPGRADES: u64 = 1; + const E_CANNOT_EXECUTE_GOVERNANCE_ACTION_WITH_OBSOLETE_SEQUENCE_NUMBER: u64 = 2; + const E_INVALID_GOVERNANCE_DATA_SOURCE: u64 = 4; + + // this struct does not have the store or key ability so it must be + // used in the same txn chain in which it is created + struct WormholeVAAVerificationReceipt{ + payload: vector, + digest: Bytes32, + sequence: u64, // used for replay protection + } + + public fun take_payload(receipt: &WormholeVAAVerificationReceipt): vector { + receipt.payload + } + + public fun take_digest(receipt: &WormholeVAAVerificationReceipt): Bytes32 { + receipt.digest + } + + public fun take_sequence(receipt: &WormholeVAAVerificationReceipt): u64 { + receipt.sequence + } + + public fun destroy(receipt: WormholeVAAVerificationReceipt) { + let WormholeVAAVerificationReceipt{payload: _, digest: _, sequence: _} = receipt; + } + + // We define a custom verify_vaa function instead of using wormhole::governance_message::verify_vaa + // because that function makes extra assumptions about the VAA payload headers. Pyth uses a + // different header format compared to Wormhole, so + public fun verify_vaa( + pyth_state: &State, + verified_vaa: VAA, + ): WormholeVAAVerificationReceipt { + state::assert_latest_only(pyth_state); + + let vaa_data_source = pyth::data_source::new((vaa::emitter_chain(&verified_vaa) as u64), vaa::emitter_address(&verified_vaa)); + + // The emitter chain and address must correspond to the Pyth governance emitter chain and contract. + assert!( + pyth::state::is_valid_governance_data_source(pyth_state, vaa_data_source), + E_INVALID_GOVERNANCE_DATA_SOURCE + ); + + let digest = vaa::digest(&verified_vaa); + + let sequence = vaa::sequence(&verified_vaa); + + let payload = vaa::take_payload(verified_vaa); + + WormholeVAAVerificationReceipt { payload, digest, sequence } + } + + /// Execute a governance instruction other than contract upgrade, which is + /// handled separately in the contract_upgrade.move module. + public fun execute_governance_instruction( + pyth_state : &mut State, + receipt: WormholeVAAVerificationReceipt, + ) { + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(pyth_state); + + // Get the sequence number of the governance VAA that was used to + // generate the receipt. + let sequence = receipt.sequence; + + // Require that new sequence number is greater than last executed sequence number. + assert!(sequence > state::get_last_executed_governance_sequence(pyth_state), + E_CANNOT_EXECUTE_GOVERNANCE_ACTION_WITH_OBSOLETE_SEQUENCE_NUMBER); + + // Update latest executed sequence number to current one. + state::set_last_executed_governance_sequence(&latest_only, pyth_state, sequence); + + let payload = receipt.payload; + + destroy(receipt); + + let instruction = governance_instruction::from_byte_vec(payload); + + // Get the governance action. + let action = governance_instruction::get_action(&instruction); + + // Dispatch the instruction to the appropriate handler. + if (action == governance_action::new_contract_upgrade()) { + abort(E_MUST_USE_CONTRACT_UPGRADE_MODULE_TO_DO_UPGRADES) + } else if (action == governance_action::new_set_governance_data_source()) { + set_governance_data_source::execute(&latest_only, pyth_state, governance_instruction::destroy(instruction)); + } else if (action == governance_action::new_set_data_sources()) { + set_data_sources::execute(&latest_only, pyth_state, governance_instruction::destroy(instruction)); + } else if (action == governance_action::new_set_update_fee()) { + set_update_fee::execute(&latest_only, pyth_state, governance_instruction::destroy(instruction)); + } else if (action == governance_action::new_set_stale_price_threshold()) { + set_stale_price_threshold::execute(&latest_only, pyth_state, governance_instruction::destroy(instruction)); + } else if (action == governance_action::new_set_fee_recipient()) { + set_fee_recipient::execute(&latest_only, pyth_state, governance_instruction::destroy(instruction)); + } else { + governance_instruction::destroy(instruction); + assert!(false, E_INVALID_GOVERNANCE_ACTION); + } + } +} diff --git a/target_chains/iota/contracts/sources/governance/governance_action.move b/target_chains/iota/contracts/sources/governance/governance_action.move new file mode 100644 index 0000000000..6bfce10a50 --- /dev/null +++ b/target_chains/iota/contracts/sources/governance/governance_action.move @@ -0,0 +1,48 @@ +module pyth::governance_action { + + const CONTRACT_UPGRADE: u8 = 0; + const SET_GOVERNANCE_DATA_SOURCE: u8 = 1; + const SET_DATA_SOURCES: u8 = 2; + const SET_UPDATE_FEE: u8 = 3; + const SET_STALE_PRICE_THRESHOLD: u8 = 4; + const SET_FEE_RECIPIENT: u8 = 5; + + const E_INVALID_GOVERNANCE_ACTION: u64 = 6; + + struct GovernanceAction has copy, drop { + value: u8, + } + + public fun from_u8(value: u8): GovernanceAction { + assert!(CONTRACT_UPGRADE <= value && value <= SET_FEE_RECIPIENT, E_INVALID_GOVERNANCE_ACTION); + GovernanceAction { value } + } + + public fun get_value(a: GovernanceAction): u8{ + a.value + } + + public fun new_contract_upgrade(): GovernanceAction { + GovernanceAction { value: CONTRACT_UPGRADE } + } + + public fun new_set_governance_data_source(): GovernanceAction { + GovernanceAction { value: SET_GOVERNANCE_DATA_SOURCE } + } + + public fun new_set_data_sources(): GovernanceAction { + GovernanceAction { value: SET_DATA_SOURCES } + } + + public fun new_set_update_fee(): GovernanceAction { + GovernanceAction { value: SET_UPDATE_FEE } + } + + public fun new_set_stale_price_threshold(): GovernanceAction { + GovernanceAction { value: SET_STALE_PRICE_THRESHOLD } + } + + public fun new_set_fee_recipient(): GovernanceAction { + GovernanceAction { value: SET_FEE_RECIPIENT } + } +} diff --git a/target_chains/iota/contracts/sources/governance/governance_instruction.move b/target_chains/iota/contracts/sources/governance/governance_instruction.move new file mode 100644 index 0000000000..ee008308e7 --- /dev/null +++ b/target_chains/iota/contracts/sources/governance/governance_instruction.move @@ -0,0 +1,91 @@ +module pyth::governance_instruction { + use wormhole::cursor; + use pyth::deserialize; + use pyth::governance_action::{Self, GovernanceAction}; + + const MAGIC: vector = x"5054474d"; // "PTGM": Pyth Governance Message + const MODULE: u8 = 1; + + const E_INVALID_GOVERNANCE_MODULE: u64 = 0; + const E_INVALID_GOVERNANCE_MAGIC_VALUE: u64 = 1; + const E_TARGET_CHAIN_MISMATCH: u64 = 2; + + struct GovernanceInstruction { + module_: u8, + action: GovernanceAction, + target_chain_id: u64, + payload: vector, + } + + fun validate(instruction: &GovernanceInstruction) { + assert!(instruction.module_ == MODULE, E_INVALID_GOVERNANCE_MODULE); + let target_chain_id = instruction.target_chain_id; + assert!(target_chain_id == (wormhole::state::chain_id() as u64) || target_chain_id == 0, E_TARGET_CHAIN_MISMATCH); + } + + public fun from_byte_vec(bytes: vector): GovernanceInstruction { + let cursor = cursor::new(bytes); + let magic = deserialize::deserialize_vector(&mut cursor, 4); + assert!(magic == MAGIC, E_INVALID_GOVERNANCE_MAGIC_VALUE); + // "module" is a reserved keyword, so we use "module_" instead. + let module_ = deserialize::deserialize_u8(&mut cursor); + let action = governance_action::from_u8(deserialize::deserialize_u8(&mut cursor)); + let target_chain_id = deserialize::deserialize_u16(&mut cursor); + let payload = cursor::take_rest(cursor); + + let instruction = GovernanceInstruction { + module_, + action, + target_chain_id : (target_chain_id as u64), + payload + }; + + // validate validates that module and target chain are correct + validate(&instruction); + + instruction + } + + public fun get_module(instruction: &GovernanceInstruction): u8 { + instruction.module_ + } + + public fun get_action(instruction: &GovernanceInstruction): GovernanceAction { + instruction.action + } + + public fun get_target_chain_id(instruction: &GovernanceInstruction): u64 { + instruction.target_chain_id + } + + public fun destroy(instruction: GovernanceInstruction): vector { + let GovernanceInstruction { + module_: _, + action: _, + target_chain_id: _, + payload + } = instruction; + payload + } + + #[test] + #[expected_failure] + fun test_from_byte_vec_invalid_magic() { + let bytes = x"5054474eb01087a85361f738f19454e66664d3c9"; + destroy(from_byte_vec(bytes)); + } + + #[test] + #[expected_failure] + fun test_from_byte_vec_invalid_module() { + let bytes = x"5054474db00187a85361f738f19454e66664d3c9"; + destroy(from_byte_vec(bytes)); + } + + #[test] + #[expected_failure] + fun test_from_byte_vec_invalid_target_chain_id() { + let bytes = x"5054474db00187a85361f738f19454e66664d3c9"; + destroy(from_byte_vec(bytes)); + } +} diff --git a/target_chains/iota/contracts/sources/governance/set_data_sources.move b/target_chains/iota/contracts/sources/governance/set_data_sources.move new file mode 100644 index 0000000000..e3fbe4287a --- /dev/null +++ b/target_chains/iota/contracts/sources/governance/set_data_sources.move @@ -0,0 +1,100 @@ +module pyth::set_data_sources { + use std::vector; + + use wormhole::cursor; + use wormhole::external_address::{Self}; + use wormhole::bytes32::{Self}; + + use pyth::deserialize; + use pyth::data_source::{Self, DataSource}; + use pyth::state::{Self, State, LatestOnly}; + + friend pyth::governance; + + struct DataSources { + sources: vector, + } + + public(friend) fun execute( + latest_only: &LatestOnly, + state: &mut State, + payload: vector + ) { + let DataSources { sources } = from_byte_vec(payload); + state::set_data_sources(latest_only, state, sources); + } + + fun from_byte_vec(bytes: vector): DataSources { + let cursor = cursor::new(bytes); + let data_sources_count = deserialize::deserialize_u8(&mut cursor); + + let sources = vector::empty(); + + let i = 0; + while (i < data_sources_count) { + let emitter_chain_id = deserialize::deserialize_u16(&mut cursor); + let emitter_address = external_address::new(bytes32::from_bytes(deserialize::deserialize_vector(&mut cursor, 32))); + vector::push_back(&mut sources, data_source::new((emitter_chain_id as u64), emitter_address)); + + i = i + 1; + }; + + cursor::destroy_empty(cursor); + + DataSources { + sources + } + } +} + +#[test_only] +module pyth::set_data_sources_tests { + use iota::test_scenario::{Self}; + use iota::coin::Self; + + use wormhole::external_address::{Self}; + use wormhole::bytes32::{Self}; + + use pyth::pyth_tests::{Self, setup_test, take_wormhole_and_pyth_states}; + use pyth::state::Self; + use pyth::data_source::Self; + + const SET_DATA_SOURCES_VAA: vector = x"01000000000100b29ee59868b9066b04d8d59e1c7cc66f0678eaf4c58b8c87e4405d6de615f64b04da4025719aeed349e03900f37829454d62cc7fc7bca80328c31fe40be7b21b010000000000000000000163278d271099bfd491951b3e648f08b1c71631e4a53674ad43e8f9f98068c3850000000000000001015054474d0102001503001ae101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71001aa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b60001f346195ac02f37d60d4db8ffa6ef74cb1be3550047543a4a9ee9acf4d78697b0"; + // VAA Info: + // module name: 0x1 + // action: 2 + // chain: 21 + // data sources (chain, addr) pairs: [(1, 0xf346195ac02f37d60d4db8ffa6ef74cb1be3550047543a4a9ee9acf4d78697b0), (26, 0xa27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b6), (26, 0xe101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71)] + + const DEPLOYER: address = @0x1234; + const DEFAULT_BASE_UPDATE_FEE: u64 = 0; + const DEFAULT_COIN_TO_MINT: u64 = 0; + + #[test] + fun set_data_sources(){ + let (scenario, test_coins, clock) = setup_test(500, 1, x"63278d271099bfd491951b3e648f08b1c71631e4a53674ad43e8f9f98068c385", pyth_tests::data_sources_for_test_vaa(), vector[x"13947bd48b18e53fdaeee77f3473391ac727c638"], DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); + test_scenario::next_tx(&mut scenario, DEPLOYER); + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + let verified_vaa = wormhole::vaa::parse_and_verify(&worm_state, SET_DATA_SOURCES_VAA, &clock); + + let receipt = pyth::governance::verify_vaa(&pyth_state, verified_vaa); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + pyth::governance::execute_governance_instruction(&mut pyth_state, receipt); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + // assert data sources are set correctly + + assert!(state::is_valid_data_source(&pyth_state, data_source::new(1, external_address::new(bytes32::from_bytes(x"f346195ac02f37d60d4db8ffa6ef74cb1be3550047543a4a9ee9acf4d78697b0")))), 0); + assert!(state::is_valid_data_source(&pyth_state, data_source::new(26, external_address::new(bytes32::from_bytes(x"a27839d641b07743c0cb5f68c51f8cd31d2c0762bec00dc6fcd25433ef1ab5b6")))), 0); + assert!(state::is_valid_data_source(&pyth_state, data_source::new(26, external_address::new(bytes32::from_bytes(x"e101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa71")))), 0); + + // clean up + coin::burn_for_testing(test_coins); + pyth_tests::cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } +} diff --git a/target_chains/iota/contracts/sources/governance/set_fee_recipient.move b/target_chains/iota/contracts/sources/governance/set_fee_recipient.move new file mode 100644 index 0000000000..602b5e1324 --- /dev/null +++ b/target_chains/iota/contracts/sources/governance/set_fee_recipient.move @@ -0,0 +1,32 @@ +/// The previous version of the contract sent the fees to a recipient address but this state is not used anymore +/// This module is kept for backward compatibility +module pyth::set_fee_recipient { + use wormhole::cursor; + use wormhole::external_address::{Self}; + + use pyth::state::{Self, State, LatestOnly}; + + friend pyth::governance; + + struct PythFeeRecipient { + recipient: address + } + + public(friend) fun execute(latest_only: &LatestOnly, state: &mut State, payload: vector) { + let PythFeeRecipient { recipient } = from_byte_vec(payload); + state::set_fee_recipient(latest_only, state, recipient); + } + + fun from_byte_vec(payload: vector): PythFeeRecipient { + let cur = cursor::new(payload); + + // Recipient must be non-zero address. + let recipient = external_address::take_nonzero(&mut cur); + + cursor::destroy_empty(cur); + + PythFeeRecipient { + recipient: external_address::to_address(recipient) + } + } +} diff --git a/target_chains/iota/contracts/sources/governance/set_governance_data_source.move b/target_chains/iota/contracts/sources/governance/set_governance_data_source.move new file mode 100644 index 0000000000..682a1a7182 --- /dev/null +++ b/target_chains/iota/contracts/sources/governance/set_governance_data_source.move @@ -0,0 +1,36 @@ +module pyth::set_governance_data_source { + use pyth::deserialize; + use pyth::data_source; + use pyth::state::{Self, State, LatestOnly}; + + use wormhole::cursor; + use wormhole::external_address::{Self, ExternalAddress}; + use wormhole::bytes32::{Self}; + + friend pyth::governance; + + struct GovernanceDataSource { + emitter_chain_id: u64, + emitter_address: ExternalAddress, + initial_sequence: u64, + } + + public(friend) fun execute(latest_only: &LatestOnly, pyth_state: &mut State, payload: vector) { + let GovernanceDataSource { emitter_chain_id, emitter_address, initial_sequence: initial_sequence } = from_byte_vec(payload); + state::set_governance_data_source(latest_only, pyth_state, data_source::new(emitter_chain_id, emitter_address)); + state::set_last_executed_governance_sequence(latest_only, pyth_state, initial_sequence); + } + + fun from_byte_vec(bytes: vector): GovernanceDataSource { + let cursor = cursor::new(bytes); + let emitter_chain_id = deserialize::deserialize_u16(&mut cursor); + let emitter_address = external_address::new(bytes32::from_bytes(deserialize::deserialize_vector(&mut cursor, 32))); + let initial_sequence = deserialize::deserialize_u64(&mut cursor); + cursor::destroy_empty(cursor); + GovernanceDataSource { + emitter_chain_id: (emitter_chain_id as u64), + emitter_address, + initial_sequence + } + } +} diff --git a/target_chains/iota/contracts/sources/governance/set_stale_price_threshold.move b/target_chains/iota/contracts/sources/governance/set_stale_price_threshold.move new file mode 100644 index 0000000000..69efd66312 --- /dev/null +++ b/target_chains/iota/contracts/sources/governance/set_stale_price_threshold.move @@ -0,0 +1,72 @@ +module pyth::set_stale_price_threshold { + use wormhole::cursor; + + use pyth::deserialize; + use pyth::state::{Self, State, LatestOnly}; + + friend pyth::governance; + + struct StalePriceThreshold { + threshold: u64, + } + + public(friend) fun execute(latest_only: &LatestOnly, state: &mut State, payload: vector) { + let StalePriceThreshold { threshold } = from_byte_vec(payload); + state::set_stale_price_threshold_secs(latest_only, state, threshold); + } + + fun from_byte_vec(bytes: vector): StalePriceThreshold { + let cursor = cursor::new(bytes); + let threshold = deserialize::deserialize_u64(&mut cursor); + cursor::destroy_empty(cursor); + StalePriceThreshold { + threshold + } + } +} + +#[test_only] +module pyth::set_stale_price_threshold_test { + use iota::test_scenario::{Self}; + use iota::coin::Self; + + use pyth::pyth_tests::{Self, setup_test, take_wormhole_and_pyth_states}; + use pyth::state::Self; + + const SET_STALE_PRICE_THRESHOLD_VAA: vector = x"010000000001000393eabdb4983e91e0fcfe7e6b2fc5c8fca2847fde52fd2f51a9b26b12298da13af09c271ce7723af8e0b1f52afa02b56f0b64764739b1b05e2f2c5cec80567c000000000000000000000163278d271099bfd491951b3e648f08b1c71631e4a53674ad43e8f9f98068c3850000000000000001015054474d0104001500000000000f4020"; + // VAA Info: + // module name: 0x1 + // action: 4 + // chain: 21 + // stale price threshold: 999456 + + const DEPLOYER: address = @0x1234; + const DEFAULT_BASE_UPDATE_FEE: u64 = 0; + const DEFAULT_COIN_TO_MINT: u64 = 0; + + #[test] + fun set_stale_price_threshold(){ + + let (scenario, test_coins, clock) = setup_test(500, 1, x"63278d271099bfd491951b3e648f08b1c71631e4a53674ad43e8f9f98068c385", pyth_tests::data_sources_for_test_vaa(), vector[x"13947bd48b18e53fdaeee77f3473391ac727c638"], DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); + test_scenario::next_tx(&mut scenario, DEPLOYER); + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + let verified_vaa = wormhole::vaa::parse_and_verify(&worm_state, SET_STALE_PRICE_THRESHOLD_VAA, &clock); + + let receipt = pyth::governance::verify_vaa(&pyth_state, verified_vaa); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + pyth::governance::execute_governance_instruction(&mut pyth_state, receipt); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + // assert stale price threshold is set correctly + assert!(state::get_stale_price_threshold_secs(&pyth_state)==999456, 0); + + // clean up + coin::burn_for_testing(test_coins); + pyth_tests::cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } +} diff --git a/target_chains/iota/contracts/sources/governance/set_update_fee.move b/target_chains/iota/contracts/sources/governance/set_update_fee.move new file mode 100644 index 0000000000..c5c7fa07f7 --- /dev/null +++ b/target_chains/iota/contracts/sources/governance/set_update_fee.move @@ -0,0 +1,85 @@ +module pyth::set_update_fee { + use std::u64; + + use wormhole::cursor; + + use pyth::deserialize; + use pyth::state::{Self, State, LatestOnly}; + + friend pyth::governance; + + const E_EXPONENT_DOES_NOT_FIT_IN_U8: u64 = 0; + + struct UpdateFee { + mantissa: u64, + exponent: u64, + } + + public(friend) fun execute(latest_only: &LatestOnly, pyth_state: &mut State, payload: vector) { + let UpdateFee { mantissa, exponent } = from_byte_vec(payload); + assert!(exponent <= 255, E_EXPONENT_DOES_NOT_FIT_IN_U8); + let fee = apply_exponent(mantissa, (exponent as u8)); + state::set_base_update_fee(latest_only, pyth_state, fee); + } + + fun from_byte_vec(bytes: vector): UpdateFee { + let cursor = cursor::new(bytes); + let mantissa = deserialize::deserialize_u64(&mut cursor); + let exponent = deserialize::deserialize_u64(&mut cursor); + cursor::destroy_empty(cursor); + UpdateFee { + mantissa, + exponent, + } + } + + fun apply_exponent(mantissa: u64, exponent: u8): u64 { + mantissa * u64::pow(10, exponent) + } +} + +#[test_only] +module pyth::set_update_fee_tests { + use iota::test_scenario::{Self}; + use iota::coin::Self; + + use pyth::pyth_tests::{Self, setup_test, take_wormhole_and_pyth_states}; + use pyth::state::Self; + + const SET_FEE_VAA: vector = x"01000000000100189d01616814b185b5a26bde6123d48e0d44dd490bbb3bde5d12076247b2180068a8261165777076ae532b7b0739aaee6411c8ba0695d20d4fa548227ce15d8d010000000000000000000163278d271099bfd491951b3e648f08b1c71631e4a53674ad43e8f9f98068c3850000000000000001015054474d0103001500000000000000050000000000000005"; + // VAA Info: + // module name: 0x1 + // action: 3 + // chain: 21 + // new fee: 5, new exponent: 5 + + const DEPLOYER: address = @0x1234; + const DEFAULT_BASE_UPDATE_FEE: u64 = 0; + const DEFAULT_COIN_TO_MINT: u64 = 0; + + #[test] + fun test_set_update_fee(){ + + let (scenario, test_coins, clock) = setup_test(500, 1, x"63278d271099bfd491951b3e648f08b1c71631e4a53674ad43e8f9f98068c385", pyth_tests::data_sources_for_test_vaa(), vector[x"13947bd48b18e53fdaeee77f3473391ac727c638"], DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); + test_scenario::next_tx(&mut scenario, DEPLOYER); + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + let verified_vaa = wormhole::vaa::parse_and_verify(&worm_state, SET_FEE_VAA, &clock); + + let receipt = pyth::governance::verify_vaa(&pyth_state, verified_vaa); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + pyth::governance::execute_governance_instruction(&mut pyth_state, receipt); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + // assert fee is set correctly + assert!(state::get_base_update_fee(&pyth_state)==500000, 0); + + // clean up + coin::burn_for_testing(test_coins); + pyth_tests::cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } +} diff --git a/target_chains/iota/contracts/sources/hot_potato_vector.move b/target_chains/iota/contracts/sources/hot_potato_vector.move new file mode 100644 index 0000000000..a733a6ce51 --- /dev/null +++ b/target_chains/iota/contracts/sources/hot_potato_vector.move @@ -0,0 +1,66 @@ +/// This class represents a vector of objects wrapped +/// inside of a hot potato struct. +module pyth::hot_potato_vector { + use std::vector; + + friend pyth::pyth; + #[test_only] + friend pyth::pyth_tests; + + // A hot potato containing a vector of elements + struct HotPotatoVector { + contents: vector + } + + // A public destroy function. + public fun destroy(hot_potato_vector: HotPotatoVector) { + let HotPotatoVector { contents: _ } = hot_potato_vector; + } + + // Only certain on-chain functions are allowed to create a new hot potato vector. + public(friend) fun new(vec: vector): HotPotatoVector { + HotPotatoVector { + contents: vec + } + } + + public fun length(potato: &HotPotatoVector): u64 { + vector::length(&potato.contents) + } + + public fun is_empty(potato: &HotPotatoVector): bool { + vector::is_empty(&potato.contents) + } + + public(friend) fun borrow(potato: &HotPotatoVector, i: u64): &T { + vector::borrow(&potato.contents, i) + } + + public(friend) fun pop_back(hot_potato_vector: HotPotatoVector): (T, HotPotatoVector) { + let elem = vector::pop_back(&mut hot_potato_vector.contents); + return (elem, hot_potato_vector) + } + + #[test_only] + struct A has copy, drop { + a: u64 + } + + #[test] + fun test_hot_potato_vector() { + let vec_of_a = vector::empty(); + vector::push_back(&mut vec_of_a, A { a: 5 }); + vector::push_back(&mut vec_of_a, A { a: 11 }); + vector::push_back(&mut vec_of_a, A { a: 23 }); + + let hot_potato = new(vec_of_a); + let (b, hot_potato) = pop_back(hot_potato); + assert!(b.a == 23, 0); + (b, hot_potato) = pop_back(hot_potato); + assert!(b.a == 11, 0); + let (b, hot_potato) = pop_back(hot_potato); + assert!(b.a == 5, 0); + + destroy(hot_potato); + } +} diff --git a/target_chains/iota/contracts/sources/i64.move b/target_chains/iota/contracts/sources/i64.move new file mode 100644 index 0000000000..889e7ba752 --- /dev/null +++ b/target_chains/iota/contracts/sources/i64.move @@ -0,0 +1,140 @@ +module pyth::i64 { + //use pyth::error; + + const MAX_POSITIVE_MAGNITUDE: u64 = (1 << 63) - 1; + const MAX_NEGATIVE_MAGNITUDE: u64 = (1 << 63); + + /// As Move does not support negative numbers natively, we use our own internal + /// representation. + /// + /// To consume these values, first call `get_is_negative()` to determine if the I64 + /// represents a negative or positive value. Then call `get_magnitude_if_positive()` or + /// `get_magnitude_if_negative()` to get the magnitude of the number in unsigned u64 format. + /// This API forces consumers to handle positive and negative numbers safely. + struct I64 has copy, drop, store { + negative: bool, + magnitude: u64, + } + + public fun new(magnitude: u64, negative: bool): I64 { + let max_magnitude = MAX_POSITIVE_MAGNITUDE; + if (negative) { + max_magnitude = MAX_NEGATIVE_MAGNITUDE; + }; + assert!(magnitude <= max_magnitude, 0); //error::magnitude_too_large() + + + // Ensure we have a single zero representation: (0, false). + // (0, true) is invalid. + if (magnitude == 0) { + negative = false; + }; + + I64 { + magnitude, + negative, + } + } + + public fun get_is_negative(i: &I64): bool { + i.negative + } + + public fun get_magnitude_if_positive(in: &I64): u64 { + assert!(!in.negative, 0); // error::negative_value() + in.magnitude + } + + public fun get_magnitude_if_negative(in: &I64): u64 { + assert!(in.negative, 0); //error::positive_value() + in.magnitude + } + + public fun from_u64(from: u64): I64 { + // Use the MSB to determine whether the number is negative or not. + let negative = (from >> 63) == 1; + let magnitude = parse_magnitude(from, negative); + + new(magnitude, negative) + } + + fun parse_magnitude(from: u64, negative: bool): u64 { + // If positive, then return the input verbatamin + if (!negative) { + return from + }; + + // Otherwise convert from two's complement by inverting and adding 1 + let inverted = from ^ 0xFFFFFFFFFFFFFFFF; + inverted + 1 + } + + #[test] + fun test_max_positive_magnitude() { + new(0x7FFFFFFFFFFFFFFF, false); + assert!(&new(1<<63 - 1, false) == &from_u64(1<<63 - 1), 1); + } + + #[test] + #[expected_failure] + fun test_magnitude_too_large_positive() { + new(0x8000000000000000, false); + } + + #[test] + fun test_max_negative_magnitude() { + new(0x8000000000000000, true); + assert!(&new(1<<63, true) == &from_u64(1<<63), 1); + } + + #[test] + #[expected_failure] + fun test_magnitude_too_large_negative() { + new(0x8000000000000001, true); + } + + #[test] + fun test_from_u64_positive() { + assert!(from_u64(0x64673) == new(0x64673, false), 1); + } + + #[test] + fun test_from_u64_negative() { + assert!(from_u64(0xFFFFFFFFFFFEDC73) == new(0x1238D, true), 1); + } + + #[test] + fun test_get_is_negative() { + assert!(get_is_negative(&new(234, true)) == true, 1); + assert!(get_is_negative(&new(767, false)) == false, 1); + } + + #[test] + fun test_get_magnitude_if_positive_positive() { + assert!(get_magnitude_if_positive(&new(7686, false)) == 7686, 1); + } + + #[test] + #[expected_failure] + fun test_get_magnitude_if_positive_negative() { + assert!(get_magnitude_if_positive(&new(7686, true)) == 7686, 1); + } + + #[test] + fun test_get_magnitude_if_negative_negative() { + assert!(get_magnitude_if_negative(&new(7686, true)) == 7686, 1); + } + + #[test] + #[expected_failure] + fun test_get_magnitude_if_negative_positive() { + assert!(get_magnitude_if_negative(&new(7686, false)) == 7686, 1); + } + + #[test] + fun test_single_zero_representation() { + assert!(&new(0, true) == &new(0, false), 1); + assert!(&new(0, true) == &from_u64(0), 1); + assert!(&new(0, false) == &from_u64(0), 1); + } +} diff --git a/target_chains/iota/contracts/sources/merkle_tree.move b/target_chains/iota/contracts/sources/merkle_tree.move new file mode 100644 index 0000000000..eb96cde668 --- /dev/null +++ b/target_chains/iota/contracts/sources/merkle_tree.move @@ -0,0 +1,397 @@ +// Implementation of a Merkle tree in Move. Supports constructing a new tree +// with a given depth, as well as proving that a leaf node belongs to the tree. +module pyth::merkle_tree { + use std::vector::{Self}; + use iota::hash::{keccak256}; + use wormhole::bytes20::{Self, Bytes20, data}; + use wormhole::cursor::Cursor; + use pyth::deserialize::{Self}; + + #[test_only] + use wormhole::cursor::{Self}; + + const MERKLE_LEAF_PREFIX: u8 = 0; + const MERKLE_NODE_PREFIX: u8 = 1; + const MERKLE_EMPTY_LEAF_PREFIX: u8 = 2; + + const E_DEPTH_NOT_LARGE_ENOUGH_FOR_MESSAGES: u64 = 1212121; + + // take keccak256 of input data, then return 20 leftmost bytes of result + fun hash(bytes: &vector): Bytes20 { + let hashed_bytes = keccak256(bytes); + let hash_prefix = vector::empty(); + let i = 0; + while (i < 20) { + vector::push_back(&mut hash_prefix, *vector::borrow(&hashed_bytes, i)); + i = i + 1; + }; + bytes20::new(hash_prefix) + } + + fun empty_leaf_hash(): Bytes20 { + let v = vector[MERKLE_EMPTY_LEAF_PREFIX]; + hash(&v) + } + + fun leaf_hash(data: &vector): Bytes20 { + let v = vector[MERKLE_LEAF_PREFIX]; + let i = 0; + while (i < vector::length(data)) { + vector::push_back(&mut v, *vector::borrow(data, i)); + i = i + 1; + }; + hash(&v) + } + + fun node_hash( + childA: Bytes20, + childB: Bytes20 + ): Bytes20 { + if (greater_than(&childA, &childB)) { + (childA, childB) = (childB, childA); + }; + // append data_B to data_A + let data_A = bytes20::data(&childA); + let data_B = bytes20::data(&childB); + + // create a vector containing MERKLE_NODE_PREFIX + data_A + data_B + let v = vector[MERKLE_NODE_PREFIX]; + let i = 0; + while (i < 20) { + vector::push_back(&mut v, *vector::borrow(&data_A, i)); + i = i + 1; + }; + let i = 0; + while (i < 20) { + vector::push_back(&mut v, *vector::borrow(&data_B, i)); + i = i + 1; + }; + hash(&v) + } + + // greater_than returns whether a is strictly greater than b + // note that data(&a) and data(&b) are both vectors of length 20 + fun greater_than(a: &Bytes20, b: &Bytes20): bool { + // aa and bb both have length 20 + let a_vector = data(a); + let b_vector = data(b); + let i = 0; + while (i < 20) { + let a_value = *vector::borrow(&a_vector, i); + let b_value = *vector::borrow(&b_vector, i); + if (a_value > b_value) { + return true + } else if (b_value > a_value) { + return false + }; + i = i + 1; + }; + false + } + + // The Iota Move stdlb insert function shifts v[i] and subsequent elements to the right. + // We don't want this behavior, so we define our own set_element function that instead replaces the ith element. + // Reference: https://github.com/MystenLabs/iota/blob/main/crates/iota-framework/packages/move-stdlib/sources/vector.move + fun set_element(a: &mut vector, value: T, index: u64){ + vector::push_back(a, value); // push value to end + vector::swap_remove(a, index); // swap value to correct position and pop last value + } + + // is_proof_valid returns whether a merkle proof is valid + public fun is_proof_valid( + encoded_proof: &mut Cursor, + root: Bytes20, + leaf_data: vector, + ): bool { + let current_digest: Bytes20 = leaf_hash(&leaf_data); + let proofSize: u8 = deserialize::deserialize_u8(encoded_proof); + while (proofSize > 0){ + let sibling_digest: Bytes20 = bytes20::new( + deserialize::deserialize_vector(encoded_proof, 20) + ); + + current_digest = node_hash( + current_digest, + sibling_digest + ); + proofSize = proofSize - 1; + }; + bytes20::data(¤t_digest) == bytes20::data(&root) + } + + // construct_proofs constructs a merkle tree and returns the root of the tree as + // a Bytes20 as well as the vector of encoded proofs + public fun construct_proofs( + messages: &vector>, + depth: u8 + ) : (Bytes20, vector) { + + if ( 1 << depth < vector::length(messages)) { + abort E_DEPTH_NOT_LARGE_ENOUGH_FOR_MESSAGES + }; + + // empty tree + // The tree is structured as follows: + // 1 + // 2 3 + // 4 5 6 7 + // ... + // In this structure the parent of node x is x//2 and the children + // of node x are x*2 and x*2 + 1. Also, the sibling of the node x + // is x^1. The root is at index 1 and index 0 is not used. + let tree = vector::empty(); + + // empty leaf hash + let cachedEmptyLeafHash: Bytes20 = empty_leaf_hash(); + + // Instantiate tree to be a full binary tree with the appropriate depth. + // Add an entry at the end for swapping + let i: u64 = 0; + while (i < (1 << (depth+1)) + 1){ + vector::push_back(&mut tree, cachedEmptyLeafHash); + i = i + 1; + }; + + // Fill in bottom row with leaf hashes + let j: u64 = 0; + while (j < vector::length(messages)){ + set_element(&mut tree, leaf_hash(vector::borrow(messages, j)), (1 << depth) + j); + j = j + 1; + }; + + // Filling the node hashes from bottom to top + let k: u8 = depth; + while (k>0){ + let level: u8 = k-1; + let levelNumNodes = 1 << level; + let i: u64 = 0; + while (i < levelNumNodes ){ + let id = (1 << level) + i; + let node_hash = node_hash(*vector::borrow(&tree, id * 2), *vector::borrow(&tree, id * 2 + 1)); + set_element(&mut tree, node_hash, id); + i = i + 1; + }; + k = k - 1; + }; + + let root = *vector::borrow(&tree, 1); + + // construct proofs and create encoded proofs vector + let proofs = vector::empty(); + let i: u64 = 0; + while (i < vector::length(messages)){ + let cur_proof = vector::empty(); + vector::push_back(&mut cur_proof, depth); + let idx = (1 << depth) + i; + while (idx > 1) { + vector::append(&mut cur_proof, bytes20::data(vector::borrow(&tree, idx ^ 1))); + + // Jump to parent + idx = idx / 2; + }; + vector::append(&mut proofs, cur_proof); + i = i + 1; + }; + + (root, proofs) + } + + #[test] + fun testGreaterThan(){ + // test 1 + let x = bytes20::new(x"0000000000000000000000000000000000001000"); + let y = bytes20::new(x"0000000000000000000000000000000000000001"); + let res = greater_than(&x, &y); + assert!(res==true, 0); + res = greater_than(&y, &x); + assert!(res==false, 0); + + // test 2 + x = bytes20::new(x"1100000000000000000000000000000000001000"); + y = bytes20::new(x"1100000000000000000000000000000000000001"); + res = greater_than(&x, &y); + assert!(res==true, 0); + + // equality case + x = bytes20::new(x"1100000000000000000000000000000000001001"); + y = bytes20::new(x"1100000000000000000000000000000000001001"); + res = greater_than(&x, &y); + assert!(res==false, 0); + } + + #[test] + fun test_hash_leaf() { + let data: vector = x"00640000000000000000000000000000000000000000000000000000000000000000000000000000640000000000000064000000640000000000000064000000000000006400000000000000640000000000000064"; + let hash = leaf_hash(&data); + let expected = bytes20::new(x"afc6a8ac466430f35895055f8a4c951785dad5ce"); + assert!(hash == expected, 1); + } + + #[test] + fun test_hash_node() { + let h1 = bytes20::new(x"05c51b04b820c0f704e3fdd2e4fc1e70aff26dff"); + let h2 = bytes20::new(x"1e108841c8d21c7a5c4860c8c3499c918ea9e0ac"); + let hash = node_hash(h1, h2); + let expected = bytes20::new(x"2d0e4fde68184c7ce8af426a0865bd41ef84dfa4"); + assert!(hash == expected, 1); + } + + #[test] + fun testMerkleTreeDepth1(){ + let messages = vector::empty>(); + vector::push_back(&mut messages, x"1234"); + + let (root, proofs) = construct_proofs(&messages, 1); + + let proofs_cursor = cursor::new(proofs); + let valid = is_proof_valid(&mut proofs_cursor, root, x"1234"); + assert!(valid==true, 0); + + // destroy cursor + cursor::take_rest(proofs_cursor); + } + + #[test] + fun testMerkleTreeDepth2(){ + let messages = vector::empty>(); + vector::push_back(&mut messages, x"1234"); + vector::push_back(&mut messages, x"4321"); + vector::push_back(&mut messages, x"11"); + vector::push_back(&mut messages, x"22"); + + let (root, proofs) = construct_proofs(&messages, 2); + + let proofs_cursor = cursor::new(proofs); + assert!(is_proof_valid(&mut proofs_cursor, root, x"1234")==true, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"4321")==true, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"11")==true, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"22")==true, 0); + + // destroy cursor + cursor::take_rest(proofs_cursor); + } + + #[test] + fun test_merkle_tree_depth_3(){ + let messages = vector::empty>(); + vector::push_back(&mut messages, x"00"); + vector::push_back(&mut messages, x"4321"); + vector::push_back(&mut messages, x"444444"); + vector::push_back(&mut messages, x"22222222"); + vector::push_back(&mut messages, x"22"); + vector::push_back(&mut messages, x"11"); + vector::push_back(&mut messages, x"100000"); + vector::push_back(&mut messages, x"eeeeee"); + + let (root, proofs) = construct_proofs(&messages, 3); + + let proofs_cursor = cursor::new(proofs); + assert!(is_proof_valid(&mut proofs_cursor, root, x"00")==true, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"4321")==true, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"444444")==true, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"22222222")==true, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"22")==true, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"11")==true, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"100000")==true, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"eeeeee")==true, 0); + + // destroy cursor + cursor::take_rest(proofs_cursor); + } + + #[test] + fun test_merkle_tree_depth_1_invalid_proofs(){ + let messages = vector::empty>(); + vector::push_back(&mut messages, x"1234"); + + let (root, proofs) = construct_proofs(&messages, 1); + + let proofs_cursor = cursor::new(proofs); + + // use wrong leaf data (it is not included in the tree) + let valid = is_proof_valid(&mut proofs_cursor, root, x"432222"); + assert!(valid==false, 0); + + // destroy cursor + cursor::take_rest(proofs_cursor); + } + + #[test] + fun test_merkle_tree_depth_2_invalid_proofs(){ + let messages = vector::empty>(); + vector::push_back(&mut messages, x"1234"); + vector::push_back(&mut messages, x"4321"); + vector::push_back(&mut messages, x"11"); + vector::push_back(&mut messages, x"22"); + + let (root, proofs) = construct_proofs(&messages, 2); + + let proofs_cursor = cursor::new(proofs); + // proof fails because we used the proof of x"1234" to try to prove that x"4321" is in the tree + assert!(is_proof_valid(&mut proofs_cursor, root, x"4321")==false, 0); + // proof succeeds + assert!(is_proof_valid(&mut proofs_cursor, root, x"4321")==true, 0); + // proof fails because we used the proof of x"11" to try to prove that x"22" is in the tree + assert!(is_proof_valid(&mut proofs_cursor, root, x"22")==false, 0); + // proof succeeds + assert!(is_proof_valid(&mut proofs_cursor, root, x"22")==true, 0); + + // destroy cursor + cursor::take_rest(proofs_cursor); + } + + #[test] + fun test_merkle_tree_depth_3_invalid_proofs(){ + let messages = vector::empty>(); + vector::push_back(&mut messages, x"00"); + vector::push_back(&mut messages, x"4321"); + vector::push_back(&mut messages, x"444444"); + vector::push_back(&mut messages, x"22222222"); + vector::push_back(&mut messages, x"22"); + vector::push_back(&mut messages, x"11"); + vector::push_back(&mut messages, x"100000"); + vector::push_back(&mut messages, x"eeeeee"); + + let (root, proofs) = construct_proofs(&messages, 3); + + let proofs_cursor = cursor::new(proofs); + + // test various proof failure cases (because of mismatch between proof and leaf data) + assert!(is_proof_valid(&mut proofs_cursor, root, x"00")==true, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"22")==false, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"22222222")==false, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"22222222")==true, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"22")==true, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"eeeeee")==false, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"4321")==false, 0); + assert!(is_proof_valid(&mut proofs_cursor, root, x"eeeeee")==true, 0); + + // destroy cursor + cursor::take_rest(proofs_cursor); + } + + #[test] + #[expected_failure(abort_code = pyth::merkle_tree::E_DEPTH_NOT_LARGE_ENOUGH_FOR_MESSAGES)] + fun test_merkle_tree_depth_exceeded_1(){ + let messages = vector::empty>(); + vector::push_back(&mut messages, x"00"); + vector::push_back(&mut messages, x"4321"); + vector::push_back(&mut messages, x"444444"); + + construct_proofs(&messages, 1); //depth 1 + } + + #[test] + #[expected_failure(abort_code = pyth::merkle_tree::E_DEPTH_NOT_LARGE_ENOUGH_FOR_MESSAGES)] + fun test_merkle_tree_depth_exceeded_2(){ + let messages = vector::empty>(); + vector::push_back(&mut messages, x"00"); + vector::push_back(&mut messages, x"4321"); + vector::push_back(&mut messages, x"444444"); + vector::push_back(&mut messages, x"22222222"); + vector::push_back(&mut messages, x"22"); + + construct_proofs(&messages, 2); // depth 2 + } + +} diff --git a/target_chains/iota/contracts/sources/migrate.move b/target_chains/iota/contracts/sources/migrate.move new file mode 100644 index 0000000000..49d53017b3 --- /dev/null +++ b/target_chains/iota/contracts/sources/migrate.move @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: Apache 2 + + +/// Note: this module is adapted from Wormhole's migrade.move module. +/// +/// This module implements a public method intended to be called after an +/// upgrade has been commited. The purpose is to add one-off migration logic +/// that would alter Pyth `State`. +/// +/// Included in migration is the ability to ensure that breaking changes for +/// any of Pyth's methods by enforcing the current build version as +/// their required minimum version. +module pyth::migrate { + use iota::object::{ID}; + + use pyth::state::{Self, State}; + use pyth::contract_upgrade::{Self}; + use pyth::governance::{WormholeVAAVerificationReceipt}; + + struct MigrateComplete has drop, copy { + package: ID + } + + public fun migrate( + pyth_state: &mut State, + receipt: WormholeVAAVerificationReceipt, + ) { + + // Perform standard migrate. + handle_migrate(pyth_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( + pyth_state: &mut State, + receipt: WormholeVAAVerificationReceipt, + ) { + // See `version_control` module for hard-coded configuration. + state::migrate_version(pyth_state); + + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(pyth_state); + + let digest = contract_upgrade::take_upgrade_digest(receipt); + state::assert_authorized_digest( + &latest_only, + pyth_state, + digest + ); + + // Finally emit an event reflecting a successful migrate. + let package = state::current_package(&latest_only, pyth_state); + iota::event::emit(MigrateComplete { package }); + } + + #[test_only] + public fun set_up_migrate(pyth_state: &mut State) { + state::reverse_migrate__v__0_1_0(pyth_state); + } +} diff --git a/target_chains/iota/contracts/sources/price.move b/target_chains/iota/contracts/sources/price.move new file mode 100644 index 0000000000..ea92602b9e --- /dev/null +++ b/target_chains/iota/contracts/sources/price.move @@ -0,0 +1,46 @@ +module pyth::price { + use pyth::i64::I64; + + /// A price with a degree of uncertainty, represented as a price +- a confidence interval. + /// + /// The confidence interval roughly corresponds to the standard error of a normal distribution. + /// Both the price and confidence are stored in a fixed-point numeric representation, + /// `x * (10^expo)`, where `expo` is the exponent. + // + /// Please refer to the documentation at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for how + /// to how this price safely. + struct Price has copy, drop, store { + price: I64, + /// Confidence interval around the price + conf: u64, + /// The exponent + expo: I64, + /// Unix timestamp of when this price was computed + timestamp: u64, + } + + public fun new(price: I64, conf: u64, expo: I64, timestamp: u64): Price { + Price { + price, + conf, + expo, + timestamp, + } + } + + public fun get_price(price: &Price): I64 { + price.price + } + + public fun get_conf(price: &Price): u64 { + price.conf + } + + public fun get_timestamp(price: &Price): u64 { + price.timestamp + } + + public fun get_expo(price: &Price): I64 { + price.expo + } +} diff --git a/target_chains/iota/contracts/sources/price_feed.move b/target_chains/iota/contracts/sources/price_feed.move new file mode 100644 index 0000000000..11a236f833 --- /dev/null +++ b/target_chains/iota/contracts/sources/price_feed.move @@ -0,0 +1,47 @@ +module pyth::price_feed { + use pyth::price_identifier::PriceIdentifier; + use pyth::price::Price; + + /// PriceFeed represents a current aggregate price for a particular product. + struct PriceFeed has copy, drop, store { + /// The price identifier + price_identifier: PriceIdentifier, + /// The current aggregate price + price: Price, + /// The current exponentially moving average aggregate price + ema_price: Price, + } + + public fun new( + price_identifier: PriceIdentifier, + price: Price, + ema_price: Price): PriceFeed { + PriceFeed { + price_identifier, + price, + ema_price, + } + } + + public fun from( + price_feed: &PriceFeed + ): PriceFeed { + PriceFeed { + price_identifier: price_feed.price_identifier, + price: price_feed.price, + ema_price: price_feed.ema_price, + } + } + + public fun get_price_identifier(price_feed: &PriceFeed): PriceIdentifier { + price_feed.price_identifier + } + + public fun get_price(price_feed: &PriceFeed): Price { + price_feed.price + } + + public fun get_ema_price(price_feed: &PriceFeed): Price { + price_feed.ema_price + } +} diff --git a/target_chains/iota/contracts/sources/price_identifier.move b/target_chains/iota/contracts/sources/price_identifier.move new file mode 100644 index 0000000000..dc05d22f29 --- /dev/null +++ b/target_chains/iota/contracts/sources/price_identifier.move @@ -0,0 +1,21 @@ +module pyth::price_identifier { + use std::vector; + + const IDENTIFIER_BYTES_LENGTH: u64 = 32; + const E_INCORRECT_IDENTIFIER_LENGTH: u64 = 0; + + struct PriceIdentifier has copy, drop, store { + bytes: vector, + } + + public fun from_byte_vec(bytes: vector): PriceIdentifier { + assert!(vector::length(&bytes) == IDENTIFIER_BYTES_LENGTH, E_INCORRECT_IDENTIFIER_LENGTH); + PriceIdentifier { + bytes + } + } + + public fun get_bytes(price_identifier: &PriceIdentifier): vector { + price_identifier.bytes + } +} diff --git a/target_chains/iota/contracts/sources/price_info.move b/target_chains/iota/contracts/sources/price_info.move new file mode 100644 index 0000000000..14b8acb5ee --- /dev/null +++ b/target_chains/iota/contracts/sources/price_info.move @@ -0,0 +1,223 @@ +module pyth::price_info { + use iota::object::{Self, UID, ID}; + use iota::tx_context::{TxContext}; + use iota::dynamic_object_field::{Self}; + use iota::table::{Self}; + use iota::coin::{Self, Coin}; + use iota::iota::IOTA; + + use pyth::price_feed::{Self, PriceFeed}; + use pyth::price_identifier::{PriceIdentifier}; + + const KEY: vector = b"price_info"; + const FEE_STORAGE_KEY: vector = b"fee_storage"; + const E_PRICE_INFO_REGISTRY_ALREADY_EXISTS: u64 = 0; + const E_PRICE_IDENTIFIER_ALREADY_REGISTERED: u64 = 1; + const E_PRICE_IDENTIFIER_NOT_REGISTERED: u64 = 2; + + friend pyth::pyth; + friend pyth::state; + + /// Iota object version of PriceInfo. + /// Has a key ability, is unique for each price identifier, and lives in global store. + struct PriceInfoObject has key, store { + id: UID, + price_info: PriceInfo + } + + /// Copyable and droppable. + struct PriceInfo has copy, drop, store { + attestation_time: u64, + arrival_time: u64, + price_feed: PriceFeed, + } + + /// Creates a table which maps a PriceIdentifier to the + /// UID (in bytes) of the corresponding Iota PriceInfoObject. + public(friend) fun new_price_info_registry(parent_id: &mut UID, ctx: &mut TxContext) { + assert!( + !dynamic_object_field::exists_(parent_id, KEY), + E_PRICE_INFO_REGISTRY_ALREADY_EXISTS + ); + dynamic_object_field::add( + parent_id, + KEY, + table::new(ctx) + ) + } + + public(friend) fun add(parent_id: &mut UID, price_identifier: PriceIdentifier, id: ID) { + assert!( + !contains(parent_id, price_identifier), + E_PRICE_IDENTIFIER_ALREADY_REGISTERED + ); + table::add( + dynamic_object_field::borrow_mut(parent_id, KEY), + price_identifier, + id + ) + } + + + /// Returns ID of price info object corresponding to price_identifier as a byte vector. + public fun get_id_bytes(parent_id: &UID, price_identifier: PriceIdentifier): vector { + assert!( + contains(parent_id, price_identifier), + E_PRICE_IDENTIFIER_NOT_REGISTERED + ); + object::id_to_bytes( + table::borrow( + dynamic_object_field::borrow(parent_id, KEY), + price_identifier + ) + ) + } + + /// Returns ID of price info object corresponding to price_identifier as an ID. + public fun get_id(parent_id: &UID, price_identifier: PriceIdentifier): ID { + assert!( + contains(parent_id, price_identifier), + E_PRICE_IDENTIFIER_NOT_REGISTERED + ); + object::id_from_bytes( + object::id_to_bytes( + table::borrow( + dynamic_object_field::borrow(parent_id, KEY), + price_identifier + ) + ) + ) + } + + public fun contains(parent_id: &UID, price_identifier: PriceIdentifier): bool { + let ref = dynamic_object_field::borrow(parent_id, KEY); + table::contains(ref, price_identifier) + } + + public fun get_balance(price_info_object: &PriceInfoObject): u64 { + if (!dynamic_object_field::exists_with_type, Coin>(&price_info_object.id, FEE_STORAGE_KEY)) { + return 0 + }; + let fee = dynamic_object_field::borrow, Coin>(&price_info_object.id, FEE_STORAGE_KEY); + coin::value(fee) + } + + public fun deposit_fee_coins(price_info_object: &mut PriceInfoObject, fee_coins: Coin) { + if (!dynamic_object_field::exists_with_type, Coin>(&price_info_object.id, FEE_STORAGE_KEY)) { + dynamic_object_field::add(&mut price_info_object.id, FEE_STORAGE_KEY, fee_coins); + } + else { + let current_fee = dynamic_object_field::borrow_mut, Coin>( + &mut price_info_object.id, + FEE_STORAGE_KEY + ); + coin::join(current_fee, fee_coins); + }; + } + + public(friend) fun new_price_info_object( + price_info: PriceInfo, + ctx: &mut TxContext + ): PriceInfoObject { + PriceInfoObject { + id: object::new(ctx), + price_info + } + } + + public fun new_price_info( + attestation_time: u64, + arrival_time: u64, + price_feed: PriceFeed, + ): PriceInfo { + PriceInfo { + attestation_time, + arrival_time, + price_feed, + } + } + + #[test] + public fun test_get_price_info_object_id_from_price_identifier(){ + use iota::object::{Self}; + use iota::test_scenario::{Self, ctx}; + use pyth::price_identifier::{Self}; + let scenario = test_scenario::begin(@pyth); + let uid = object::new(ctx(&mut scenario)); + + // Create a new price info object registry. + new_price_info_registry(&mut uid, ctx(&mut scenario)); + + // Register a price info object in the registry. + let price_identifier = price_identifier::from_byte_vec(x"ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"); + + // Create a new ID. + let id = object::id_from_bytes(x"19f253b07e88634bfd5a3a749f60bfdb83c9748910646803f06b60b76319e7ba"); + + add(&mut uid, price_identifier, id); + + let result = get_id_bytes(&uid, price_identifier); + + // Assert that ID matches original. + assert!(result==x"19f253b07e88634bfd5a3a749f60bfdb83c9748910646803f06b60b76319e7ba", 0); + + // Clean up. + object::delete(uid); + test_scenario::end(scenario); + } + + #[test_only] + public fun destroy(price_info: PriceInfoObject) { + let PriceInfoObject { + id, + price_info: _, + } = price_info; + object::delete(id); + } + + #[test_only] + public fun new_price_info_object_for_test( + price_info: PriceInfo, + ctx: &mut TxContext + ): PriceInfoObject { + PriceInfoObject { + id: object::new(ctx), + price_info + } + } + + public fun uid_to_inner(price_info: &PriceInfoObject): ID { + object::uid_to_inner(&price_info.id) + } + + public fun get_price_info_from_price_info_object(price_info: &PriceInfoObject): PriceInfo { + price_info.price_info + } + + public fun get_price_identifier(price_info: &PriceInfo): PriceIdentifier { + price_feed::get_price_identifier(&price_info.price_feed) + } + + public fun get_price_feed(price_info: &PriceInfo): &PriceFeed { + &price_info.price_feed + } + + public fun get_attestation_time(price_info: &PriceInfo): u64 { + price_info.attestation_time + } + + public fun get_arrival_time(price_info: &PriceInfo): u64 { + price_info.arrival_time + } + + public(friend) fun update_price_info_object( + price_info_object: &mut PriceInfoObject, + price_info: &PriceInfo + ) { + price_info_object.price_info = new_price_info( + price_info.attestation_time, + price_info.arrival_time, + price_info.price_feed + ); + } +} diff --git a/target_chains/iota/contracts/sources/price_status.move b/target_chains/iota/contracts/sources/price_status.move new file mode 100644 index 0000000000..031649ff7d --- /dev/null +++ b/target_chains/iota/contracts/sources/price_status.move @@ -0,0 +1,53 @@ +module pyth::price_status { + //use pyth::error; + + /// The price feed is not currently updating for an unknown reason. + const UNKNOWN: u64 = 0; + /// The price feed is updating as expected. + const TRADING: u64 = 1; + + /// PriceStatus represents the availability status of a price feed. + /// Prices should only be used if they have a status of trading. + struct PriceStatus has copy, drop, store { + status: u64, + } + + public fun from_u64(status: u64): PriceStatus { + assert!(status <= TRADING, 0); + PriceStatus { + status + } + } + + public fun get_status(price_status: &PriceStatus): u64 { + price_status.status + } + + public fun new_unknown(): PriceStatus { + PriceStatus { + status: UNKNOWN, + } + } + + public fun new_trading(): PriceStatus { + PriceStatus { + status: TRADING, + } + } + + #[test] + fun test_unknown_status() { + assert!(PriceStatus{ status: UNKNOWN } == from_u64(0), 1); + } + + #[test] + fun test_trading_status() { + assert!(PriceStatus{ status: TRADING } == from_u64(1), 1); + } + + #[test] + #[expected_failure] + fun test_invalid_price_status() { + from_u64(3); + } +} diff --git a/target_chains/iota/contracts/sources/pyth.move b/target_chains/iota/contracts/sources/pyth.move new file mode 100644 index 0000000000..710a94bb99 --- /dev/null +++ b/target_chains/iota/contracts/sources/pyth.move @@ -0,0 +1,1549 @@ +module pyth::pyth { + use std::vector; + use iota::tx_context::{TxContext}; + use iota::coin::{Self, Coin}; + use iota::iota::{IOTA}; + use iota::transfer::{Self}; + use iota::clock::{Self, Clock}; + use iota::package::{UpgradeCap}; + + use pyth::event::{Self as pyth_event}; + use pyth::data_source::{Self, DataSource}; + use pyth::state::{Self as state, State as PythState, LatestOnly}; + use pyth::price_info::{Self, PriceInfo, PriceInfoObject}; + use pyth::batch_price_attestation::{Self}; + use pyth::price_feed::{Self}; + use pyth::price::{Self, Price}; + use pyth::price_identifier::{PriceIdentifier}; + use pyth::setup::{Self, DeployerCap}; + use pyth::hot_potato_vector::{Self, HotPotatoVector}; + use pyth::accumulator::{Self}; + + use wormhole::external_address::{Self}; + use wormhole::vaa::{Self, VAA}; + use wormhole::bytes32::{Self}; + use wormhole::cursor::{Self}; + + const E_DATA_SOURCE_EMITTER_ADDRESS_AND_CHAIN_IDS_DIFFERENT_LENGTHS: u64 = 0; + const E_INVALID_DATA_SOURCE: u64 = 1; + const E_INSUFFICIENT_FEE: u64 = 2; + const E_STALE_PRICE_UPDATE: u64 = 3; + const E_UPDATE_AND_PRICE_INFO_OBJECT_MISMATCH: u64 = 4; + const E_PRICE_UPDATE_NOT_FOUND_FOR_PRICE_INFO_OBJECT: u64 = 5; + + #[test_only] + friend pyth::pyth_tests; + + /// Init state and emit event corresponding to Pyth initialization. + public entry fun init_pyth( + deployer: DeployerCap, + upgrade_cap: UpgradeCap, + stale_price_threshold: u64, + governance_emitter_chain_id: u64, + governance_emitter_address: vector, + data_sources_emitter_chain_ids: vector, + data_sources_emitter_addresses: vector>, + update_fee: u64, + ctx: &mut TxContext + ) { + setup::init_and_share_state( + deployer, + upgrade_cap, + stale_price_threshold, + update_fee, + data_source::new( + governance_emitter_chain_id, + external_address::new((bytes32::from_bytes(governance_emitter_address))) + ), + parse_data_sources( + data_sources_emitter_chain_ids, + data_sources_emitter_addresses, + ), + ctx + ); + + // Emit Pyth initialization event. + pyth_event::emit_pyth_initialization_event(); + } + + fun parse_data_sources( + emitter_chain_ids: vector, + emitter_addresses: vector> + ): vector { + + assert!(vector::length(&emitter_chain_ids) == vector::length(&emitter_addresses), + E_DATA_SOURCE_EMITTER_ADDRESS_AND_CHAIN_IDS_DIFFERENT_LENGTHS); + + let sources = vector::empty(); + let i = 0; + while (i < vector::length(&emitter_chain_ids)) { + vector::push_back(&mut sources, data_source::new( + *vector::borrow(&emitter_chain_ids, i), + external_address::new(bytes32::from_bytes(*vector::borrow(&emitter_addresses, i))) + )); + + i = i + 1; + }; + sources + } + + /// Create and share new price feed objects if they don't already exist using accumulator message. + public fun create_price_feeds_using_accumulator( + pyth_state: &mut PythState, + accumulator_message: vector, + vaa: VAA, // the verified version of the vaa bytes encoded within the accumulator_message + clock: &Clock, + ctx: &mut TxContext + ){ + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(pyth_state); + + // Check that the VAA is from a valid data source (emitter) + assert!( + state::is_valid_data_source( + pyth_state, + data_source::new( + (vaa::emitter_chain(&vaa) as u64), + vaa::emitter_address(&vaa)) + ), + E_INVALID_DATA_SOURCE + ); + + // decode the price info updates from the VAA payload (first check if it is an accumulator or batch price update) + let accumulator_message_cursor = cursor::new(accumulator_message); + let price_infos = accumulator::parse_and_verify_accumulator_message(&mut accumulator_message_cursor, vaa::take_payload(vaa), clock); + + // Create and share new price info objects, if not already exists. + create_and_share_price_feeds_using_verified_price_infos(&latest_only, pyth_state, price_infos, ctx); + + // destroy rest of cursor + cursor::take_rest(accumulator_message_cursor); + } + + + /// Create and share new price feed objects if they don't already exist using batch price attestation. + /// The name of the function is kept as is to remain backward compatible + public fun create_price_feeds( + pyth_state: &mut PythState, + // These vaas have been verified and consumed, so we don't have to worry about + // doing replay protection for them. + verified_vaas: vector, + clock: &Clock, + ctx: &mut TxContext + ){ + // This capability ensures that the current build version is used. + let latest_only = state::assert_latest_only(pyth_state); + + while (!vector::is_empty(&verified_vaas)) { + let vaa = vector::pop_back(&mut verified_vaas); + + // Check that the VAA is from a valid data source (emitter) + assert!( + state::is_valid_data_source( + pyth_state, + data_source::new( + (vaa::emitter_chain(&vaa) as u64), + vaa::emitter_address(&vaa)) + ), + E_INVALID_DATA_SOURCE + ); + + // Deserialize the batch price attestation + let price_infos = batch_price_attestation::destroy(batch_price_attestation::deserialize(vaa::take_payload(vaa), clock)); + + // Create and share new price info objects, if not already exists. + create_and_share_price_feeds_using_verified_price_infos(&latest_only, pyth_state, price_infos, ctx); + }; + vector::destroy_empty(verified_vaas); + } + + #[allow(lint(share_owned))] + // create_and_share_price_feeds_using_verified_price_infos is a private function used by + // 1) create_price_feeds + // 2) create_price_feeds_using_accumulator + // to create new price feeds for symbols. + fun create_and_share_price_feeds_using_verified_price_infos(latest_only: &LatestOnly, pyth_state: &mut PythState, price_infos: vector, ctx: &mut TxContext){ + while (!vector::is_empty(&price_infos)){ + let cur_price_info = vector::pop_back(&mut price_infos); + + // Only create new Iota PriceInfoObject if not already + // registered with the Pyth State object. + if (!state::price_feed_object_exists( + pyth_state, + price_feed::get_price_identifier( + price_info::get_price_feed(&cur_price_info) + ) + ) + ){ + // Create and share newly created Iota PriceInfoObject containing a price feed, + // and then register a copy of its ID with State. + let new_price_info_object = price_info::new_price_info_object(cur_price_info, ctx); + let price_identifier = price_info::get_price_identifier(&cur_price_info); + let id = price_info::uid_to_inner(&new_price_info_object); + + state::register_price_info_object(latest_only, pyth_state, price_identifier, id); + + transfer::public_share_object(new_price_info_object); + } + } + } + + + // verified_vaa is the verified version of the VAA encoded within the accumulator_message + public fun create_authenticated_price_infos_using_accumulator( + pyth_state: &PythState, + accumulator_message: vector, + verified_vaa: VAA, + clock: &Clock, + ): HotPotatoVector { + state::assert_latest_only(pyth_state); + + // verify that the VAA originates from a valid data source + assert!( + state::is_valid_data_source( + pyth_state, + data_source::new( + (vaa::emitter_chain(&verified_vaa) as u64), + vaa::emitter_address(&verified_vaa)) + ), + E_INVALID_DATA_SOURCE + ); + + // decode the price info updates from the VAA payload (first check if it is an accumulator or batch price update) + let accumulator_message_cursor = cursor::new(accumulator_message); + let price_infos = accumulator::parse_and_verify_accumulator_message(&mut accumulator_message_cursor, vaa::take_payload(verified_vaa), clock); + + // check that accumulator message has been fully consumed + cursor::destroy_empty(accumulator_message_cursor); + hot_potato_vector::new(price_infos) + } + + /// Creates authenticated price infos using batch price attestation + /// Name is kept as is to remain backward compatible + public fun create_price_infos_hot_potato( + pyth_state: &PythState, + verified_vaas: vector, + clock: &Clock + ): HotPotatoVector { + state::assert_latest_only(pyth_state); + + let price_updates = vector::empty(); + while (vector::length(&verified_vaas) != 0){ + let cur_vaa = vector::pop_back(&mut verified_vaas); + + assert!( + state::is_valid_data_source( + pyth_state, + data_source::new( + (vaa::emitter_chain(&cur_vaa) as u64), + vaa::emitter_address(&cur_vaa)) + ), + E_INVALID_DATA_SOURCE + ); + let price_infos = batch_price_attestation::destroy(batch_price_attestation::deserialize(vaa::take_payload(cur_vaa), clock)); + while (vector::length(&price_infos) !=0 ){ + let cur_price_info = vector::pop_back(&mut price_infos); + vector::push_back(&mut price_updates, cur_price_info); + } + }; + vector::destroy_empty(verified_vaas); + return hot_potato_vector::new(price_updates) + } + + /// Update a singular Pyth PriceInfoObject (containing a price feed) with the + /// price data in the authenticated price infos vector (a vector of PriceInfo objects). + /// + /// For more information on the end-to-end process for updating a price feed, please see the README. + /// + /// The given fee must contain a sufficient number of coins to pay the update fee for the given vaas. + /// The update fee amount can be queried by calling get_update_fee(&vaas). + /// + /// Please read more information about the update fee here: https://docs.pyth.network/documentation/pythnet-price-feeds/on-demand#fees + public fun update_single_price_feed( + pyth_state: &PythState, + price_updates: HotPotatoVector, + price_info_object: &mut PriceInfoObject, + fee: Coin, + clock: &Clock + ): HotPotatoVector { + let latest_only = state::assert_latest_only(pyth_state); + + // On Iota, users get to choose which price feeds to update. They specify a single price feed to + // update at a time. We therefore charge the base fee for each such individual update. + // This is a departure from Eth, where users don't get to necessarily choose. + assert!(state::get_base_update_fee(pyth_state) <= coin::value(&fee), E_INSUFFICIENT_FEE); + + // store fee coins within price info object + price_info::deposit_fee_coins(price_info_object, fee); + + // Find price update corresponding to PriceInfoObject within the array of price_updates + // and use it to update PriceInfoObject. + let i = 0; + let found = false; + while (i < hot_potato_vector::length(&price_updates)){ + let cur_price_info = hot_potato_vector::borrow(&price_updates, i); + if (has_same_price_identifier(cur_price_info, price_info_object)){ + found = true; + update_cache(latest_only, cur_price_info, price_info_object, clock); + break + }; + i = i + 1; + }; + if (found==false){ + abort E_PRICE_UPDATE_NOT_FOUND_FOR_PRICE_INFO_OBJECT + }; + price_updates + } + + fun has_same_price_identifier(price_info: &PriceInfo, price_info_object: &PriceInfoObject) : bool { + let price_info_from_object = price_info::get_price_info_from_price_info_object(price_info_object); + let price_identifier_from_object = price_info::get_price_identifier(&price_info_from_object); + let price_identifier_from_price_info = price_info::get_price_identifier(price_info); + price_identifier_from_object == price_identifier_from_price_info + } + + /// Update PriceInfoObject with updated data from a PriceInfo + public(friend) fun update_cache( + _: LatestOnly, + update: &PriceInfo, + price_info_object: &mut PriceInfoObject, + clock: &Clock, + ){ + let has_same_price_identifier = has_same_price_identifier(update, price_info_object); + assert!(has_same_price_identifier, E_UPDATE_AND_PRICE_INFO_OBJECT_MISMATCH); + + // Update the price info object with the new updated price info. + if (is_fresh_update(update, price_info_object)){ + pyth_event::emit_price_feed_update(price_feed::from(price_info::get_price_feed(update)), clock::timestamp_ms(clock)/1000); + price_info::update_price_info_object( + price_info_object, + update + ); + } + } + + /// Determine if the given price update is "fresh": we have nothing newer already cached for that + /// price feed within a PriceInfoObject. + fun is_fresh_update(update: &PriceInfo, price_info_object: &PriceInfoObject): bool { + // Get the timestamp of the update's current price + let price_feed = price_info::get_price_feed(update); + let update_timestamp = price::get_timestamp(&price_feed::get_price(price_feed)); + + // Get the timestamp of the cached data for the price identifier + let cached_price_info = price_info::get_price_info_from_price_info_object(price_info_object); + let cached_price_feed = price_info::get_price_feed(&cached_price_info); + let cached_timestamp = price::get_timestamp(&price_feed::get_price(cached_price_feed)); + + update_timestamp > cached_timestamp + } + + // ----------------------------------------------------------------------------- + // Query the cached prices + // + // It is strongly recommended to update the cached prices using the functions above, + // before using the functions below to query the cached data. + + /// Determine if a price feed for the given price_identifier exists + public fun price_feed_exists(state: &PythState, price_identifier: PriceIdentifier): bool { + state::price_feed_object_exists(state, price_identifier) + } + + /// Get the latest available price cached for the given price identifier, if that price is + /// no older than the stale price threshold. + /// + /// Please refer to the documentation at https://docs.pyth.network/documentation/pythnet-price-feeds/best-practices for + /// how to how this price safely. + /// + /// Important: Pyth uses an on-demand update model, where consumers need to update the + /// cached prices before using them. Please read more about this at https://docs.pyth.network/documentation/pythnet-price-feeds/on-demand. + /// get_price() is likely to abort unless you call update_price_feeds() to update the cached price + /// beforehand, as the cached prices may be older than the stale price threshold. + /// + /// The price_info_object is a Iota object with the key ability that uniquely + /// contains a price feed for a given price_identifier. + /// + public fun get_price(state: &PythState, price_info_object: &PriceInfoObject, clock: &Clock): Price { + get_price_no_older_than(price_info_object, clock, state::get_stale_price_threshold_secs(state)) + } + + /// Get the latest available price cached for the given price identifier, if that price is + /// no older than the given age. + public fun get_price_no_older_than(price_info_object: &PriceInfoObject, clock: &Clock, max_age_secs: u64): Price { + let price = get_price_unsafe(price_info_object); + check_price_is_fresh(&price, clock, max_age_secs); + price + } + + /// Get the latest available price cached for the given price identifier. + /// + /// WARNING: the returned price can be from arbitrarily far in the past. + /// This function makes no guarantees that the returned price is recent or + /// useful for any particular application. Users of this function should check + /// the returned timestamp to ensure that the returned price is sufficiently + /// recent for their application. The checked get_price_no_older_than() + /// function should be used in preference to this. + public fun get_price_unsafe(price_info_object: &PriceInfoObject): Price { + // TODO: extract Price from this guy... + let price_info = price_info::get_price_info_from_price_info_object(price_info_object); + price_feed::get_price( + price_info::get_price_feed(&price_info) + ) + } + + fun abs_diff(x: u64, y: u64): u64 { + if (x > y) { + return x - y + } else { + return y - x + } + } + + /// Get the stale price threshold: the amount of time after which a cached price + /// is considered stale and no longer returned by get_price()/get_ema_price(). + public fun get_stale_price_threshold_secs(state: &PythState): u64 { + state::get_stale_price_threshold_secs(state) + } + + fun check_price_is_fresh(price: &Price, clock: &Clock, max_age_secs: u64) { + let age = abs_diff(clock::timestamp_ms(clock)/1000, price::get_timestamp(price)); + assert!(age < max_age_secs, E_STALE_PRICE_UPDATE); + } + + /// Please read more information about the update fee here: https://docs.pyth.network/documentation/pythnet-price-feeds/on-demand#fees + public fun get_total_update_fee(pyth_state: &PythState, n: u64): u64 { + state::get_base_update_fee(pyth_state) * n + } +} + +#[test_only] +module pyth::pyth_tests{ + use std::vector::{Self}; + + use iota::iota::IOTA; + use iota::coin::{Self, Coin}; + use iota::test_scenario::{Self, Scenario, ctx, take_shared, return_shared}; + use iota::package::Self; + use iota::object::{Self, ID}; + use iota::clock::{Self, Clock}; + + use pyth::state::{State as PythState}; + use pyth::setup::{Self}; + use pyth::price_info::{Self, PriceInfo, PriceInfoObject};//, PriceInfo, PriceInfoObject}; + use pyth::data_source::{Self, DataSource}; + use pyth::pyth::{Self, create_price_infos_hot_potato, update_single_price_feed}; + use pyth::hot_potato_vector::{Self}; + use pyth::price_identifier::{Self}; + use pyth::price_feed::{Self}; + use pyth::accumulator::{Self}; + use pyth::deserialize::{Self}; + + use wormhole::setup::{Self as wormhole_setup, DeployerCap}; + use wormhole::external_address::{Self}; + use wormhole::bytes32::{Self}; + use wormhole::state::{State as WormState}; + use wormhole::vaa::{Self, VAA}; + use wormhole::cursor::{Self}; + + const DEPLOYER: address = @0x1234; + const ACCUMULATOR_TESTS_EMITTER_ADDRESS: vector = x"71f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b"; + const ACCUMULATOR_TESTS_INITIAL_GUARDIANS: vector> = vector[x"7E5F4552091A69125d5DfCb7b8C2659029395Bdf"]; + const DEFAULT_BASE_UPDATE_FEE: u64 = 50; + const DEFAULT_COIN_TO_MINT: u64 = 5000; + const BATCH_ATTESTATION_TEST_INITIAL_GUARDIANS: vector> = vector[x"beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe"]; + + fun ACCUMULATOR_TESTS_DATA_SOURCE(): vector { + vector[data_source::new(1, external_address::new(bytes32::from_bytes(ACCUMULATOR_TESTS_EMITTER_ADDRESS)))] + } + + fun get_verified_test_vaas(worm_state: &WormState, clock: &Clock): vector { + let test_vaas_: vector> = vector[x"0100000000010036eb563b80a24f4253bee6150eb8924e4bdf6e4fa1dfc759a6664d2e865b4b134651a7b021b7f1ce3bd078070b688b6f2e37ce2de0d9b48e6a78684561e49d5201527e4f9b00000001001171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b0000000000000001005032574800030000000102000400951436e0be37536be96f0896366089506a59763d036728332d3e3038047851aea7c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1000000000000049a0000000000000008fffffffb00000000000005dc0000000000000003000000000100000001000000006329c0eb000000006329c0e9000000006329c0e400000000000006150000000000000007215258d81468614f6b7e194c5d145609394f67b041e93e6695dcc616faadd0603b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe000000000000041a0000000000000003fffffffb00000000000005cb0000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e4000000000000048600000000000000078ac9cf3ab299af710d735163726fdae0db8465280502eb9f801f74b3c1bd190333832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d00000000000003f20000000000000002fffffffb00000000000005e70000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e40000000000000685000000000000000861db714e9ff987b6fedf00d01f9fea6db7c30632d6fc83b7bc9459d7192bc44a21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db800000000000006cb0000000000000001fffffffb00000000000005e40000000000000003010000000100000001000000006329c0eb000000006329c0e9000000006329c0e400000000000007970000000000000001"]; + let verified_vaas_reversed = vector::empty(); + let test_vaas = test_vaas_; + let i = 0; + while (i < vector::length(&test_vaas_)) { + let cur_test_vaa = vector::pop_back(&mut test_vaas); + let verified_vaa = vaa::parse_and_verify(worm_state, cur_test_vaa, clock); + vector::push_back(&mut verified_vaas_reversed, verified_vaa); + i=i+1; + }; + let verified_vaas = vector::empty(); + while (vector::length(&verified_vaas_reversed)!=0){ + let cur = vector::pop_back(&mut verified_vaas_reversed); + vector::push_back(&mut verified_vaas, cur); + }; + vector::destroy_empty(verified_vaas_reversed); + verified_vaas + } + + // get_verified_vaa_from_accumulator_message parses the accumulator message up until the vaa, then + // parses the vaa, yielding a verified wormhole::vaa::VAA object + fun get_verified_vaa_from_accumulator_message(worm_state: &WormState, accumulator_message: vector, clock: &Clock): VAA { + let _PYTHNET_ACCUMULATOR_UPDATE_MAGIC: u64 = 1347305813; + + let cursor = cursor::new(accumulator_message); + let header: u32 = deserialize::deserialize_u32(&mut cursor); + assert!((header as u64) == _PYTHNET_ACCUMULATOR_UPDATE_MAGIC, 0); + let _major = deserialize::deserialize_u8(&mut cursor); + let _minor = deserialize::deserialize_u8(&mut cursor); + + let trailing_size = deserialize::deserialize_u8(&mut cursor); + deserialize::deserialize_vector(&mut cursor, (trailing_size as u64)); + + let proof_type = deserialize::deserialize_u8(&mut cursor); + assert!(proof_type == 0, 0); + + let vaa_size = deserialize::deserialize_u16(&mut cursor); + let vaa = deserialize::deserialize_vector(&mut cursor, (vaa_size as u64)); + cursor::take_rest(cursor); + vaa::parse_and_verify(worm_state, vaa, clock) + } + + #[test_only] + /// Init Wormhole core bridge state. + /// Init Pyth state. + /// Set initial Iota clock time. + /// Mint some IOTA fee coins. + public fun setup_test( + stale_price_threshold: u64, + governance_emitter_chain_id: u64, + governance_emitter_address: vector, + data_sources: vector, + initial_guardians: vector>, + base_update_fee: u64, + to_mint: u64 + ): (Scenario, Coin, Clock) { + + let scenario = test_scenario::begin(DEPLOYER); + + // Initialize Wormhole core bridge. + wormhole_setup::init_test_only(ctx(&mut scenario)); + test_scenario::next_tx(&mut scenario, DEPLOYER); + // Take the `DeployerCap` from the sender of the transaction. + 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(@wormhole), + test_scenario::ctx(&mut scenario) + ); + + let governance_chain = 1234; + let governance_contract = + x"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + let guardian_set_seconds_to_live = 5678; + let message_fee = 350; + let guardian_set_index = 0; + wormhole_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(&mut scenario) + ); + + // Initialize Pyth state. + let pyth_upgrade_cap= + package::test_publish( + object::id_from_address(@pyth), + test_scenario::ctx(&mut scenario) + ); + + setup::init_test_only(ctx(&mut scenario)); + test_scenario::next_tx(&mut scenario, DEPLOYER); + let pyth_deployer_cap = test_scenario::take_from_address( + &scenario, + DEPLOYER + ); + + setup::init_and_share_state( + pyth_deployer_cap, + pyth_upgrade_cap, + stale_price_threshold, + base_update_fee, + data_source::new(governance_emitter_chain_id, external_address::new(bytes32::from_bytes(governance_emitter_address))), + data_sources, + ctx(&mut scenario) + ); + + let coins = coin::mint_for_testing(to_mint, ctx(&mut scenario)); + let clock = clock::create_for_testing(ctx(&mut scenario)); + (scenario, coins, clock) + } + + fun get_mock_price_infos(): vector { + use pyth::i64::Self; + use pyth::price::{Self}; + vector[ + price_info::new_price_info( + 1663680747, + 1663074349, + price_feed::new( + price_identifier::from_byte_vec(x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1"), + price::new(i64::new(1557, false), 7, i64::new(5, true), 1663680740), + price::new(i64::new(1500, false), 3, i64::new(5, true), 1663680740), + ), + ), + price_info::new_price_info( + 1663680747, + 1663074349, + price_feed::new( + price_identifier::from_byte_vec(x"3b9551a68d01d954d6387aff4df1529027ffb2fee413082e509feb29cc4904fe"), + price::new(i64::new(1050, false), 3, i64::new(5, true), 1663680745), + price::new(i64::new(1483, false), 3, i64::new(5, true), 1663680745), + ), + ), + price_info::new_price_info( + 1663680747, + 1663074349, + price_feed::new( + price_identifier::from_byte_vec(x"33832fad6e36eb05a8972fe5f219b27b5b2bb2230a79ce79beb4c5c5e7ecc76d"), + price::new(i64::new(1010, false), 2, i64::new(5, true), 1663680745), + price::new(i64::new(1511, false), 3, i64::new(5, true), 1663680745), + ), + ), + price_info::new_price_info( + 1663680747, + 1663074349, + price_feed::new( + price_identifier::from_byte_vec(x"21a28b4c6619968bd8c20e95b0aaed7df2187fd310275347e0376a2cd7427db8"), + price::new(i64::new(1739, false), 1, i64::new(5, true), 1663680745), + price::new(i64::new(1508, false), 3, i64::new(5, true), 1663680745), + ), + ), + ] + } + + /// Compare the expected price feed with the actual Pyth price feeds. + fun check_price_feeds_cached(expected: &vector, actual: &vector) { + // Check that we can retrieve the correct current price and ema price for each price feed + let i = 0; + while (i < vector::length(expected)) { + let price_feed = price_info::get_price_feed(vector::borrow(expected, i)); + let price = price_feed::get_price(price_feed); + let ema_price = price_feed::get_ema_price(price_feed); + let price_identifier = price_info::get_price_identifier(vector::borrow(expected, i)); + + let actual_price_info = price_info::get_price_info_from_price_info_object(vector::borrow(actual, i)); + let actual_price_feed = price_info::get_price_feed(&actual_price_info); + let actual_price = price_feed::get_price(actual_price_feed); + let actual_ema_price = price_feed::get_ema_price(actual_price_feed); + let actual_price_identifier = price_info::get_price_identifier(&actual_price_info); + + assert!(price == actual_price, 0); + assert!(ema_price == actual_ema_price, 0); + assert!(price_identifier::get_bytes(&price_identifier) == price_identifier::get_bytes(&actual_price_identifier), 0); + + i = i + 1; + }; + } + + #[test] + fun test_get_update_fee() { + let (scenario, test_coins, _clock) = setup_test(500 /* stale_price_threshold */, 23 /* governance emitter chain */, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", vector[], BATCH_ATTESTATION_TEST_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, 0); + test_scenario::next_tx(&mut scenario, DEPLOYER, ); + let pyth_state = take_shared(&scenario); + // Pass in a single VAA + + let single_vaa = vector[ + x"fb1543888001083cf2e6ef3afdcf827e89b11efd87c563638df6e1995ada9f93", + ]; + + assert!(pyth::get_total_update_fee(&pyth_state, vector::length>(&single_vaa)) == DEFAULT_BASE_UPDATE_FEE, 1); + + let multiple_vaas = vector[ + x"4ee17a1a4524118de513fddcf82b77454e51be5d6fc9e29fc72dd6c204c0e4fa", + x"c72fdf81cfc939d4286c93fbaaae2eec7bae28a5926fa68646b43a279846ccc1", + x"d9a8123a793529c31200339820a3210059ecace6c044f81ecad62936e47ca049", + x"84e4f21b3e65cef47fda25d15b4eddda1edf720a1d062ccbf441d6396465fbe6", + x"9e73f9041476a93701a0b9c7501422cc2aa55d16100bec628cf53e0281b6f72f" + ]; + + // Pass in multiple VAAs + assert!(pyth::get_total_update_fee(&pyth_state, vector::length>(&multiple_vaas)) == 5*DEFAULT_BASE_UPDATE_FEE, 1); + + return_shared(pyth_state); + coin::burn_for_testing(test_coins); + clock::destroy_for_testing(_clock); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = wormhole::vaa::E_WRONG_VERSION)] + fun test_create_price_feeds_corrupt_vaa() { + let (scenario, test_coins, clock) = setup_test(500 /* stale_price_threshold */, 23 /* governance emitter chain */, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", vector[], vector[x"beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe"], 50, 0); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + // Pass in a corrupt VAA, which should fail deserializing + let corrupt_vaa = x"90F8bf6A479f320ead074411a4B0e7944Ea8c9C1"; + let verified_vaas = vector[vaa::parse_and_verify(&worm_state, corrupt_vaa, &clock)]; + // Create Pyth price feed + pyth::create_price_feeds( + &mut pyth_state, + verified_vaas, + &clock, + ctx(&mut scenario) + ); + + cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + coin::burn_for_testing(test_coins); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = pyth::pyth::E_INVALID_DATA_SOURCE)] + fun test_create_price_feeds_invalid_data_source() { + // Initialize the contract with some valid data sources, excluding our test VAA's source + let data_sources = vector[ + data_source::new( + 4, external_address::new(bytes32::new(x"0000000000000000000000000000000000000000000000000000000000007742")) + ), + data_source::new( + 5, external_address::new(bytes32::new(x"0000000000000000000000000000000000000000000000000000000000007637")) + ) + ]; + let (scenario, test_coins, clock) = setup_test(500, 23, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources, BATCH_ATTESTATION_TEST_INITIAL_GUARDIANS, 50, 0); + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + let verified_vaas = get_verified_test_vaas(&worm_state, &clock); + + pyth::create_price_feeds( + &mut pyth_state, + verified_vaas, + &clock, + ctx(&mut scenario) + ); + + cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + coin::burn_for_testing(test_coins); + test_scenario::end(scenario); + } + + public fun data_sources_for_test_vaa(): vector { + // Set some valid data sources, including our test VAA's source + vector[ + data_source::new( + 1, external_address::new(bytes32::from_bytes(x"0000000000000000000000000000000000000000000000000000000000000004"))), + data_source::new( + 5, external_address::new(bytes32::new(x"0000000000000000000000000000000000000000000000000000000000007637"))), + data_source::new( + 17, external_address::new(bytes32::new(ACCUMULATOR_TESTS_EMITTER_ADDRESS))) + ] + } + + #[test] + // test_create_and_update_price_feeds_with_batch_attestation_success tests the creation and updating of price + // feeds, as well as depositing fee coins into price info objects + fun test_create_and_update_price_feeds_with_batch_attestation_success() { + let (scenario, test_coins, clock) = setup_test(500, 23, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources_for_test_vaa(), vector[x"beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe"], DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + let verified_vaas = get_verified_test_vaas(&worm_state, &clock); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + pyth::create_price_feeds( + &mut pyth_state, + verified_vaas, + &clock, + ctx(&mut scenario) + ); + + // Affirm that 4 objects, which correspond to the 4 new price info objects + // containing the price feeds were created and shared. + let effects = test_scenario::next_tx(&mut scenario, DEPLOYER); + let shared_ids = test_scenario::shared(&effects); + let created_ids = test_scenario::created(&effects); + assert!(vector::length(&shared_ids)==4, 0); + assert!(vector::length(&created_ids)==4, 0); + + let price_info_object_1 = take_shared(&scenario); + let price_info_object_2 = take_shared(&scenario); + let price_info_object_3 = take_shared(&scenario); + let price_info_object_4 = take_shared(&scenario); + + // Create vector of price info objects (Iota objects with key ability and living in global store), + // which contain the price feeds we want to update. Note that these can be passed into + // update_price_feeds in any order! + //let price_info_object_vec = vector[price_info_object_1, price_info_object_2, price_info_object_3, price_info_object_4]; + verified_vaas = get_verified_test_vaas(&worm_state, &clock); + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let vaa_1 = vector::pop_back(&mut verified_vaas); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + // Create authenticated price infos + let vec = create_price_infos_hot_potato( + &pyth_state, + vector[vaa_1], + &clock + ); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let fee_coins = coin::split(&mut test_coins, DEFAULT_BASE_UPDATE_FEE, ctx(&mut scenario)); + vec = update_single_price_feed( + &pyth_state, + vec, + &mut price_info_object_1, + fee_coins, + &clock + ); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + // check price feed updated + assert!(price_feeds_equal(hot_potato_vector::borrow(&vec, 3), &price_info::get_price_info_from_price_info_object(&price_info_object_1)), 0); + + // check fee coins are deposited in the price info object + assert!(price_info::get_balance(&price_info_object_1)==DEFAULT_BASE_UPDATE_FEE, 0); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + hot_potato_vector::destroy(vec); + + vector::destroy_empty(verified_vaas); + return_shared(price_info_object_1); + return_shared(price_info_object_2); + return_shared(price_info_object_3); + return_shared(price_info_object_4); + + coin::burn_for_testing(test_coins); + cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } + + + // TEST_ACCUMULATOR_SINGLE_FEED details: + // Price Identifier: 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6 + // Price: 6887568746747646632 + // Conf: 13092246197863718329 + // Exponent: 1559537863 + // EMA Price: 4772242609775910581 + // EMA Conf: 358129956189946877 + // EMA Expo: 1559537863 + // Published Time: 1687276661 + const TEST_ACCUMULATOR_SINGLE_FEED: vector = x"504e41550100000000a0010000000001005d461ac1dfffa8451edda17e4b28a46c8ae912422b2dc0cb7732828c497778ea27147fb95b4d250651931845e7f3e22c46326716bcf82be2874a9c9ab94b6e42000000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b0000000000000000004155575600000000000000000000000000da936d73429246d131873a0bab90ad7b416510be01005500b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65f958f4883f9d2a8b5b1008d1fa01db95cf4a8c7000000006491cc757be59f3f377c0d3f423a695e81ad1eb504f8554c3620c3fd02f2ee15ea639b73fa3db9b34a245bdfa015c260c5a8a1180177cf30b2c0bebbb1adfe8f7985d051d2"; + + #[test] + fun test_create_and_update_single_price_feed_with_accumulator_success() { + let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_SINGLE_FEED, &clock); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + pyth::create_price_feeds_using_accumulator( + &mut pyth_state, + TEST_ACCUMULATOR_SINGLE_FEED, + verified_vaa, + &clock, + ctx(&mut scenario) + ); + + // Affirm that 1 object, which correspond to the 1 new price info object + // containing the price feeds were created and shared. + let effects = test_scenario::next_tx(&mut scenario, DEPLOYER); + let shared_ids = test_scenario::shared(&effects); + let created_ids = test_scenario::created(&effects); + assert!(vector::length(&shared_ids)==1, 0); + assert!(vector::length(&created_ids)==1, 0); + + let price_info_object_1 = take_shared(&scenario); + + // Create authenticated price infos + verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_SINGLE_FEED, &clock); + let auth_price_infos = pyth::create_authenticated_price_infos_using_accumulator( + &pyth_state, + TEST_ACCUMULATOR_SINGLE_FEED, + verified_vaa, + &clock + ); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + auth_price_infos = update_single_price_feed( + &pyth_state, + auth_price_infos, + &mut price_info_object_1, + coins, + &clock + ); + + // assert that price info obejct is as expected + let expected = accumulator_test_1_to_price_info(); + assert!(price_feeds_equal(&expected, &price_info::get_price_info_from_price_info_object(&price_info_object_1)), 0); + + // clean up test scenario + + test_scenario::next_tx(&mut scenario, DEPLOYER); + hot_potato_vector::destroy(auth_price_infos); + + return_shared(price_info_object_1); + + cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = pyth::accumulator::E_INVALID_PROOF)] + fun test_create_and_update_single_price_feed_with_accumulator_failure() { + + let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + // the verified vaa here contains the wrong merkle root + let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_3_MSGS, &clock); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + pyth::create_price_feeds_using_accumulator( + &mut pyth_state, + TEST_ACCUMULATOR_SINGLE_FEED, + verified_vaa, + &clock, + ctx(&mut scenario) + ); + + // clean up test scenario + test_scenario::next_tx(&mut scenario, DEPLOYER); + coin::burn_for_testing(coins); + + cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } + + #[test_only] + const TEST_ACCUMULATOR_INVALID_PROOF_1: vector = x"504e41550100000000a001000000000100110db9cd8325ccfab0dae92eeb9ea70a1faba5c5e96dc21ff46a8ddc560afc9a60df096b8ff21172804692bbdc958153e838437d8b474cbf45f0dc2a8acae831000000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b0000000000000000004155575600000000000000000000000000a8bea2b5f12f3177ff9b3929d77c3476ab2d32c602005500b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6fa75cd3aa3bb5ace5e2516446f71f85be36bd19bb0703f3154bb3db07be59f3f377c0d3f44661d9a8736c68884c8169e8b636ee3043202397384073120dce9e5d0efe24b44b4a0d62da8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d950055006e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af5f958f4883f9d2a8b5b1008d1fa01db95cf4a8c7423a695e81ad1eb504f8554c3620c3fd40b40f7d581ac802e2de5cb82a9ae672043202397384073120dce9e5d0efe24b44b4a0d62da8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95"; + + #[test] + #[expected_failure(abort_code = pyth::accumulator::E_INVALID_PROOF)] + fun test_accumulator_invalid_proof() { + + let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_INVALID_PROOF_1, &clock); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + pyth::create_price_feeds_using_accumulator( + &mut pyth_state, + TEST_ACCUMULATOR_INVALID_PROOF_1, + verified_vaa, + &clock, + ctx(&mut scenario) + ); + + // clean up test scenario + test_scenario::next_tx(&mut scenario, DEPLOYER); + coin::burn_for_testing(coins); + + cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } + + #[test_only] + const TEST_ACCUMULATOR_INVALID_MAJOR_VERSION: vector = x"504e41553c00000000a001000000000100496b7fbd18dca2f0e690712fd8ca522ff79ca7d9d6d22e9f5d753fba4bd16fff440a811bad710071c79859290bcb1700de49dd8400db90b048437b521200123e010000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b000000000000000000415557560000000000000000000000000005f5db4488a7cae9f9a6c1938340c0fbf4beb9090200550031ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6879bc5a3617ec3444d93c06501cf6a0909c38d4ec81d96026b71ec475e87d69c7b5124289adbf24212bed8c15db354391d2378d2e0454d2655c6c34e7e50580fd8c94511322968bbc6da8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95005500944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c9695a573a6ff665ff63edb5f9a85ad579dc14500a2112c09680fc146134f9a539ca82cb6e3501c801278fd08d80732a24118292866bb049e6e88181a1e1e8b6d3c6bbb95135a73041f3b56a8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95"; + + #[test] + #[expected_failure(abort_code = pyth::accumulator::E_INVALID_ACCUMULATOR_PAYLOAD)] + fun test_accumulator_invalid_major_version() { + + let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_INVALID_MAJOR_VERSION, &clock); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + pyth::create_price_feeds_using_accumulator( + &mut pyth_state, + TEST_ACCUMULATOR_INVALID_MAJOR_VERSION, + verified_vaa, + &clock, + ctx(&mut scenario) + ); + + // clean up test scenario + test_scenario::next_tx(&mut scenario, DEPLOYER); + coin::burn_for_testing(coins); + + cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } + + #[test_only] + const TEST_ACCUMULATOR_INVALID_WH_MSG: vector = x"504e41550100000000a001000000000100e87f98238c5357730936cfdfde3a37249e5219409a4f41b301924b8eb10815a43ea2f96e4fe1bc8cd398250f39448d3b8ca57c96f9cf7a2be292517280683caa010000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b00000000000000000041555755000000000000000000000000000fb6f9f2b3b6cc1c9ef6708985fef226d92a3c0801005500b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6fa75cd3aa3bb5ace5e2516446f71f85be36bd19b000000006491cc747be59f3f377c0d3f44661d9a8736c68884c8169e8b636ee301f2ee15ea639b73fa3db9b34a245bdfa015c260c5"; + + #[test] + #[expected_failure(abort_code = pyth::accumulator::E_INVALID_WORMHOLE_MESSAGE)] + fun test_accumulator_invalid_wormhole_message() { + + let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_INVALID_WH_MSG, &clock); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + pyth::create_price_feeds_using_accumulator( + &mut pyth_state, + TEST_ACCUMULATOR_INVALID_WH_MSG, + verified_vaa, + &clock, + ctx(&mut scenario) + ); + + // clean up test scenario + test_scenario::next_tx(&mut scenario, DEPLOYER); + coin::burn_for_testing(coins); + + cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } + + + #[test_only] + const TEST_ACCUMULATOR_INCREASED_MINOR_VERSION: vector = x"504e4155010a000000a001000000000100496b7fbd18dca2f0e690712fd8ca522ff79ca7d9d6d22e9f5d753fba4bd16fff440a811bad710071c79859290bcb1700de49dd8400db90b048437b521200123e010000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b000000000000000000415557560000000000000000000000000005f5db4488a7cae9f9a6c1938340c0fbf4beb9090200550031ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6879bc5a3617ec3444d93c06501cf6a0909c38d4ec81d96026b71ec475e87d69c7b5124289adbf24212bed8c15db354391d2378d2e0454d2655c6c34e7e50580fd8c94511322968bbc6da8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95005500944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c9695a573a6ff665ff63edb5f9a85ad579dc14500a2112c09680fc146134f9a539ca82cb6e3501c801278fd08d80732a24118292866bb049e6e88181a1e1e8b6d3c6bbb95135a73041f3b56a8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95"; + #[test_only] + const TEST_ACCUMULATOR_EXTRA_PAYLOAD: vector = x"504e41550100000000a001000000000100b2d11f181d81b4ff10beca30091754b464dc48bc1f7432d114f64a7a8f660e7964f2a0c6121bae6c1977514d46ee7a29d9395b20a45f2086071715c1dc19ab74000000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b000000000000000000415557560000000000000000000000000013f83cfdf63a5a1b3189182fa0a52e6de53ba7d002005d0031ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c6879bc5a3617ec3444d93c06501cf6a0909c38d4ec81d96026b71ec475e87d69c7b5124289adbf24212bed8c15db354391d2378d2e000000000000000004a576f4a87f443f7d961a682f508c4f7b06ee1595a8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95005d00944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c9695a573a6ff665ff63edb5f9a85ad579dc14500a2112c09680fc146134f9a539ca82cb6e3501c801278fd08d80732a24118292866bb0000000000000000045be67ba87a8dfbea404827ccbf07790299b6c023a8a1180177cf30b2c0bebbb1adfe8f7985d051d205a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95"; + + #[test] + fun test_accumulator_forward_compatibility() { + + let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + pyth::create_price_feeds_using_accumulator( + &mut pyth_state, + TEST_ACCUMULATOR_EXTRA_PAYLOAD, + get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_EXTRA_PAYLOAD, &clock), + &clock, + ctx(&mut scenario) + ); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + pyth::create_price_feeds_using_accumulator( + &mut pyth_state, + TEST_ACCUMULATOR_INCREASED_MINOR_VERSION, + get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_INCREASED_MINOR_VERSION, &clock), + &clock, + ctx(&mut scenario) + ); + + // clean up test scenario + test_scenario::next_tx(&mut scenario, DEPLOYER); + coin::burn_for_testing(coins); + + cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } + + + // TEST_ACCUMULATOR_3_MSGS details: + // Price Identifier: 0xb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6 + // Price: 100 + // Conf: 50 + // Exponent: 9 + // EMA Price: 99 + // EMA Conf: 52 + // EMA Expo: 9 + // Published Time: 1687276660 + + // Price Identifier: 0x6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af + // Price: 101 + // Conf: 51 + // Exponent: 10 + // EMA Price: 100 + // EMA Conf: 53 + // EMA Expo: 10 + // Published Time: 1687276661 + + // Price Identifier: 0x31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c68 + // Price: 102 + // Conf: 52 + // Exponent: 11 + // EMA Price: 101 + // EMA Conf: 54 + // EMA Expo: 11 + // Published Time: 1687276662 + const TEST_ACCUMULATOR_3_MSGS: vector = x"504e41550100000000a001000000000100d39b55fa311213959f91866d52624f3a9c07350d8956f6d42cfbb037883f31575c494a2f09fea84e4884dc9c244123fd124bc7825cd64d7c11e33ba5cfbdea7e010000000000000000000171f8dcb863d176e2c420ad6610cf687359612b6fb392e0642b0ca6b1f186aa3b000000000000000000415557560000000000000000000000000029da4c066b6e03b16a71e77811570dd9e19f258103005500b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf60000000000000064000000000000003200000009000000006491cc747be59f3f377c0d3f000000000000006300000000000000340436992facb15658a7e9f08c4df4848ca80750f61fadcd96993de66b1fe7aef94e29e3bbef8b12db2305a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d950055006e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af000000000000006500000000000000330000000a000000006491cc7504f8554c3620c3fd0000000000000064000000000000003504171ed10ac4f1eacf3a4951e1da6b119f07c45da5adcd96993de66b1fe7aef94e29e3bbef8b12db2305a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d9500550031ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c68000000000000006600000000000000340000000b000000006491cc76e87d69c7b51242890000000000000065000000000000003604f2ee15ea639b73fa3db9b34a245bdfa015c260c5fe83e4772e0e346613de00e5348158a01bcb27b305a01e2504d9f0c06e7e7cb0cf24116098ca202ac5f6ade2e8f5a12ec006b16d46be1f0228b94d95"; + + #[test] + fun test_create_and_update_multiple_price_feeds_with_accumulator_success() { + use iota::coin::Self; + + let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_3_MSGS, &clock); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + pyth::create_price_feeds_using_accumulator( + &mut pyth_state, + TEST_ACCUMULATOR_3_MSGS, + verified_vaa, + &clock, + ctx(&mut scenario) + ); + + // Affirm that 3 objects, which correspond to the 3 new price info objects + // containing the price feeds were created and shared. + let effects = test_scenario::next_tx(&mut scenario, DEPLOYER); + let shared_ids = test_scenario::shared(&effects); + let created_ids = test_scenario::created(&effects); + assert!(vector::length(&shared_ids)==3, 0); + assert!(vector::length(&created_ids)==3, 0); + + // Create authenticated price infos + verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_3_MSGS, &clock); + let auth_price_infos = pyth::create_authenticated_price_infos_using_accumulator( + &pyth_state, + TEST_ACCUMULATOR_3_MSGS, + verified_vaa, + &clock + ); + + let idx = 0; + let expected_price_infos = accumulator_test_3_to_price_info(0 /*offset argument*/); + + while (idx < 3){ + let coin_split = coin::split(&mut coins, 1000, ctx(&mut scenario)); + let price_info_object = take_shared(&scenario); + auth_price_infos = update_single_price_feed( + &pyth_state, + auth_price_infos, + &mut price_info_object, + coin_split, + &clock + ); + let price_info = price_info::get_price_info_from_price_info_object(&price_info_object); + assert!(price_feeds_equal(&price_info, vector::borrow(&expected_price_infos, idx)), 0); + return_shared(price_info_object); + idx = idx + 1; + }; + coin::burn_for_testing(coins); + + // clean up test scenario + test_scenario::next_tx(&mut scenario, DEPLOYER); + hot_potato_vector::destroy(auth_price_infos); + + cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } + + #[test] + #[expected_failure(abort_code = pyth::pyth::E_INSUFFICIENT_FEE)] + fun test_create_and_update_price_feeds_insufficient_fee() { + + // this is not enough fee and will cause a failure + let coins_to_mint = 1; + + let (scenario, test_coins, clock) = setup_test(500, 23, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources_for_test_vaa(), vector[x"beFA429d57cD18b7F8A4d91A2da9AB4AF05d0FBe"], DEFAULT_BASE_UPDATE_FEE, coins_to_mint); + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + let verified_vaas = get_verified_test_vaas(&worm_state, &clock); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + pyth::create_price_feeds( + &mut pyth_state, + verified_vaas, + &clock, + ctx(&mut scenario) + ); + + // Affirm that 4 objects, which correspond to the 4 new price info objects + // containing the price feeds were created and shared. + let effects = test_scenario::next_tx(&mut scenario, DEPLOYER); + let shared_ids = test_scenario::shared(&effects); + let created_ids = test_scenario::created(&effects); + assert!(vector::length(&shared_ids)==4, 0); + assert!(vector::length(&created_ids)==4, 0); + + let price_info_object_1 = take_shared(&scenario); + let price_info_object_2 = take_shared(&scenario); + let price_info_object_3 = take_shared(&scenario); + let price_info_object_4 = take_shared(&scenario); + + // Create vector of price info objects (Iota objects with key ability and living in global store), + // which contain the price feeds we want to update. Note that these can be passed into + // update_price_feeds in any order! + //let price_info_object_vec = vector[price_info_object_1, price_info_object_2, price_info_object_3, price_info_object_4]; + verified_vaas = get_verified_test_vaas(&worm_state, &clock); + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let vaa_1 = vector::pop_back(&mut verified_vaas); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + // Create authenticated price infos + let vec = create_price_infos_hot_potato( + &pyth_state, + vector[vaa_1], + &clock + ); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + vec = update_single_price_feed( + &pyth_state, + vec, + &mut price_info_object_1, + test_coins, + &clock + ); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + hot_potato_vector::destroy(vec); + + vector::destroy_empty(verified_vaas); + + return_shared(price_info_object_1); + return_shared(price_info_object_2); + return_shared(price_info_object_3); + return_shared(price_info_object_4); + + cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } + + + #[test] + fun test_update_cache(){ + let (scenario, test_coins, clock) = setup_test(500, 23, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources_for_test_vaa(), BATCH_ATTESTATION_TEST_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + + let verified_vaas = get_verified_test_vaas(&worm_state, &clock); + + // Update cache is called by create_price_feeds. + pyth::create_price_feeds( + &mut pyth_state, + verified_vaas, + &clock, + ctx(&mut scenario) + ); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let price_info_object_1 = take_shared(&scenario); + let price_info_object_2 = take_shared(&scenario); + let price_info_object_3 = take_shared(&scenario); + let price_info_object_4 = take_shared(&scenario); + + // These updates are price infos that correspond to the ones in TEST_VAAS. + let updates = get_mock_price_infos(); + let price_info_object_vec = vector[ + price_info_object_1, + price_info_object_2, + price_info_object_3, + price_info_object_4 + ]; + + // Check that TEST_VAAS was indeed used to instantiate the price feeds correctly, + // by confirming that the info in updates is contained in price_info_object_vec. + check_price_feeds_cached(&updates, &price_info_object_vec); + + price_info_object_4 = vector::pop_back(&mut price_info_object_vec); + price_info_object_3 = vector::pop_back(&mut price_info_object_vec); + price_info_object_2 = vector::pop_back(&mut price_info_object_vec); + price_info_object_1 = vector::pop_back(&mut price_info_object_vec); + vector::destroy_empty(price_info_object_vec); + + return_shared(price_info_object_1); + return_shared(price_info_object_2); + return_shared(price_info_object_3); + return_shared(price_info_object_4); + coin::burn_for_testing(test_coins); + + cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } + + #[test] + fun test_update_cache_old_update() { + use pyth::i64::Self; + use pyth::price::Self; + + let (scenario, test_coins, clock) = setup_test(500, 23, x"5d1f252d5de865279b00c84bce362774c2804294ed53299bc4a0389a5defef92", data_sources_for_test_vaa(), BATCH_ATTESTATION_TEST_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let (pyth_state, worm_state) = take_wormhole_and_pyth_states(&scenario); + let verified_vaas = get_verified_test_vaas(&worm_state, &clock); + + pyth::create_price_feeds( + &mut pyth_state, + verified_vaas, + &clock, + ctx(&mut scenario) + ); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + let price_info_object_1 = take_shared(&scenario); + let price_info_object_2 = take_shared(&scenario); + let price_info_object_3 = take_shared(&scenario); + let price_info_object_4 = take_shared(&scenario); + + // Hardcode the price identifier, price, and ema_price for price_info_object_1, because + // it's easier than unwrapping price_info_object_1 and getting the quantities via getters. + let timestamp = 1663680740; + let price_identifier = price_identifier::from_byte_vec(x"c6c75c89f14810ec1c54c03ab8f1864a4c4032791f05747f560faec380a695d1"); + let price = price::new(i64::new(1557, false), 7, i64::new(5, true), timestamp); + let ema_price = price::new(i64::new(1500, false), 3, i64::new(5, true), timestamp); + + // Attempt to update the price with an update older than the current cached one. + let old_price = price::new(i64::new(1243, true), 9802, i64::new(6, false), timestamp - 200); + let old_ema_price = price::new(i64::new(8976, true), 234, i64::new(897, false), timestamp - 200); + let old_update = price_info::new_price_info( + 1257278600, + 1690226180, + price_feed::new( + price_identifier, + old_price, + old_ema_price, + ) + ); + let latest_only = pyth::state::create_latest_only_for_test(); + pyth::update_cache(latest_only, &old_update, &mut price_info_object_1, &clock); + + let current_price_info = price_info::get_price_info_from_price_info_object(&price_info_object_1); + let current_price_feed = price_info::get_price_feed(¤t_price_info); + let current_price = price_feed::get_price(current_price_feed); + let current_ema_price = price_feed::get_ema_price(current_price_feed); + + // Confirm that no price update occurred when we tried to update cache with an + // outdated update: old_update. + assert!(current_price == price, 1); + assert!(current_ema_price == ema_price, 1); + + test_scenario::next_tx(&mut scenario, DEPLOYER); + + // Update the cache with a fresh update. + let fresh_price = price::new(i64::new(5243, true), 2, i64::new(3, false), timestamp + 200); + let fresh_ema_price = price::new(i64::new(8976, true), 21, i64::new(32, false), timestamp + 200); + let fresh_update = price_info::new_price_info( + 1257278600, + 1690226180, + price_feed::new( + price_identifier, + fresh_price, + fresh_ema_price, + ) + ); + + let latest_only = pyth::state::create_latest_only_for_test(); + pyth::update_cache(latest_only, &fresh_update, &mut price_info_object_1, &clock); + + // Confirm that the Pyth cached price got updated to fresh_price. + let current_price_info = price_info::get_price_info_from_price_info_object(&price_info_object_1); + let current_price_feed = price_info::get_price_feed(¤t_price_info); + let current_price = price_feed::get_price(current_price_feed); + let current_ema_price = price_feed::get_ema_price(current_price_feed); + + assert!(current_price==fresh_price, 0); + assert!(current_ema_price==fresh_ema_price, 0); + + return_shared(price_info_object_1); + return_shared(price_info_object_2); + return_shared(price_info_object_3); + return_shared(price_info_object_4); + + coin::burn_for_testing(test_coins); + cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); + test_scenario::end(scenario); + } + + // pyth accumulator tests (included in this file instead of pyth_accumulator.move to avoid dependency cycle - as we need pyth_tests::setup_test) + #[test] + fun test_parse_and_verify_accumulator_updates(){ + use iota::test_scenario::{Self, take_shared, return_shared}; + use iota::transfer::{Self}; + + let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, vector[], ACCUMULATOR_TESTS_INITIAL_GUARDIANS, 50, 0); + let worm_state = take_shared(&scenario); + test_scenario::next_tx(&mut scenario, @0x123); + + let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_3_MSGS, &clock); + + let cur = cursor::new(TEST_ACCUMULATOR_3_MSGS); + + let price_info_updates = accumulator::parse_and_verify_accumulator_message(&mut cur, vaa::take_payload(verified_vaa), &clock); + + let expected_price_infos = accumulator_test_3_to_price_info(0); + let num_updates = vector::length(&price_info_updates); + let i = 0; + while (i < num_updates){ + assert!(price_feeds_equal(vector::borrow(&price_info_updates, i), vector::borrow(&expected_price_infos, i)), 0); + i = i + 1; + }; + + // clean-up + cursor::take_rest(cur); + transfer::public_transfer(coins, @0x1234); + clock::destroy_for_testing(clock); + return_shared(worm_state); + test_scenario::end(scenario); + } + + #[test] + fun test_parse_and_verify_accumulator_updates_with_extra_bytes_at_end_of_message(){ + use iota::test_scenario::{Self, take_shared, return_shared}; + use iota::transfer::{Self}; + + let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, vector[], ACCUMULATOR_TESTS_INITIAL_GUARDIANS, 50, 0); + let worm_state = take_shared(&scenario); + test_scenario::next_tx(&mut scenario, @0x123); + + let verified_vaa = get_verified_vaa_from_accumulator_message(&worm_state, TEST_ACCUMULATOR_3_MSGS, &clock); + + // append some extra garbage bytes at the end of the accumulator message, and make sure + // that parse_and_verify_accumulator_message does not error out + let test_accumulator_3_msgs_modified = TEST_ACCUMULATOR_3_MSGS; + vector::append(&mut test_accumulator_3_msgs_modified, x"1234123412341234"); + + let cur = cursor::new(TEST_ACCUMULATOR_3_MSGS); + + let price_info_updates = accumulator::parse_and_verify_accumulator_message(&mut cur, vaa::take_payload(verified_vaa), &clock); + + let expected_price_infos = accumulator_test_3_to_price_info(0); + let num_updates = vector::length(&price_info_updates); + let i = 0; + while (i < num_updates){ + assert!(price_feeds_equal(vector::borrow(&price_info_updates, i), vector::borrow(&expected_price_infos, i)), 0); + i = i + 1; + }; + + // clean-up + cursor::take_rest(cur); + transfer::public_transfer(coins, @0x1234); + clock::destroy_for_testing(clock); + return_shared(worm_state); + test_scenario::end(scenario); + } + + fun price_feeds_equal(p1: &PriceInfo, p2: &PriceInfo): bool{ + price_info::get_price_feed(p1)== price_info::get_price_feed(p2) + } + + // helper functions for setting up tests + + // accumulator_test_3_to_price_info gets the data encoded within TEST_ACCUMULATOR_3_MSGS + fun accumulator_test_3_to_price_info(offset: u64): vector { + use pyth::i64::{Self}; + use pyth::price::{Self}; + let i = 0; + let feed_ids = vector[x"b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6", + x"6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af", + x"31ecc21a745e3968a04e9570e4425bc18fa8019c68028196b546d1669c200c68"]; + let expected: vector = vector[]; + while (i < 3) { + vector::push_back(&mut expected, price_info::new_price_info( + 1663680747, + 1663074349, + price_feed::new( + price_identifier::from_byte_vec( + *vector::borrow(&feed_ids, i) + ), + price::new( + i64::new(100 + i + offset, false), + 50 + i + offset, + i64::new(9 + i + offset, false), + 1687276660 + i + offset + ), + price::new( + i64::new(99 + i + offset, false), + 52 + i + offset, + i64::new(9 + i + offset, false), + 1687276660 + i + offset + ), + ), + )); + i = i + 1; + }; + return expected + } + + // accumulator_test_1_to_price_info gets the data encoded within TEST_ACCUMULATOR_SINGLE_FEED + fun accumulator_test_1_to_price_info(): PriceInfo { + use pyth::i64::{Self}; + use pyth::price::{Self}; + price_info::new_price_info( + 1663680747, + 1663074349, + price_feed::new( + price_identifier::from_byte_vec( + x"b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6" + ), + price::new( + i64::new(6887568746747646632, false), + 13092246197863718329, + i64::new(1559537863, false), + 1687276661 + ), + price::new( + i64::new(4772242609775910581, false), + 358129956189946877, + i64::new(1559537863, false), + 1687276661 + ), + ), + ) + } + + public fun cleanup_worm_state_pyth_state_and_clock(worm_state: WormState, pyth_state: PythState, clock: Clock){ + return_shared(worm_state); + return_shared(pyth_state); + clock::destroy_for_testing(clock); + } + + public fun take_wormhole_and_pyth_states(scenario: &Scenario): (PythState, WormState){ + (take_shared(scenario), take_shared(scenario)) + } +} diff --git a/target_chains/iota/contracts/sources/pyth_accumulator.move b/target_chains/iota/contracts/sources/pyth_accumulator.move new file mode 100644 index 0000000000..e00da563f0 --- /dev/null +++ b/target_chains/iota/contracts/sources/pyth_accumulator.move @@ -0,0 +1,123 @@ +module pyth::accumulator { + use std::vector::{Self}; + use iota::clock::{Clock, Self}; + use wormhole::bytes20::{Self, Bytes20}; + use wormhole::cursor::{Self, Cursor}; + use pyth::deserialize::{Self}; + use pyth::price_identifier::{Self}; + use pyth::price_info::{Self, PriceInfo}; + use pyth::price_feed::{Self}; + use pyth::merkle_tree::{Self}; + + const PRICE_FEED_MESSAGE_TYPE: u8 = 0; + const E_INVALID_UPDATE_DATA: u64 = 245; + const E_INVALID_PROOF: u64 = 345; + const E_INVALID_WORMHOLE_MESSAGE: u64 = 454; + const E_INVALID_ACCUMULATOR_PAYLOAD: u64 = 554; + const E_INVALID_ACCUMULATOR_HEADER: u64 = 657; + + const ACCUMULATOR_UPDATE_WORMHOLE_VERIFICATION_MAGIC: u32 = 1096111958; + const PYTHNET_ACCUMULATOR_UPDATE_MAGIC: u64 = 1347305813; + + const MINIMUM_SUPPORTED_MINOR_VERSION: u8 = 0; + const MAJOR_VERSION: u8 = 1; + + friend pyth::pyth; + #[test_only] + friend pyth::pyth_tests; + + // parse_and_verify_accumulator_message verifies that the price updates encoded in the + // accumulator message (accessed via cursor) belong to the merkle tree defined by the merkle root encoded in + // vaa_payload. + public(friend) fun parse_and_verify_accumulator_message(cursor: &mut Cursor, vaa_payload: vector, clock: &Clock): vector { + let header = deserialize::deserialize_u32(cursor); + + if ((header as u64) != PYTHNET_ACCUMULATOR_UPDATE_MAGIC) { + abort E_INVALID_ACCUMULATOR_HEADER + }; + + let major = deserialize::deserialize_u8(cursor); + assert!(major == MAJOR_VERSION, E_INVALID_ACCUMULATOR_PAYLOAD); + + let minor = deserialize::deserialize_u8(cursor); + assert!(minor >= MINIMUM_SUPPORTED_MINOR_VERSION, E_INVALID_ACCUMULATOR_PAYLOAD); + + let trailing_size = deserialize::deserialize_u8(cursor); + deserialize::deserialize_vector(cursor, (trailing_size as u64)); + + let proof_type = deserialize::deserialize_u8(cursor); + assert!(proof_type == 0, E_INVALID_ACCUMULATOR_PAYLOAD); + + // Ignore the vaa in the accumulator message because presumably it has already been verified + // and passed to this function as input. + let vaa_size = deserialize::deserialize_u16(cursor); + deserialize::deserialize_vector(cursor, (vaa_size as u64)); + + let merkle_root_hash = parse_accumulator_merkle_root_from_vaa_payload(vaa_payload); + parse_and_verify_accumulator_updates(cursor, merkle_root_hash, clock) + } + + // parse_accumulator_merkle_root_from_vaa_payload takes in some VAA bytes, verifies that the vaa + // corresponds to a merkle update, and finally returns the merkle root. + // Note: this function is adapted from the Aptos Pyth accumulator + fun parse_accumulator_merkle_root_from_vaa_payload(message: vector): Bytes20 { + let msg_payload_cursor = cursor::new(message); + let payload_type = deserialize::deserialize_u32(&mut msg_payload_cursor); + assert!(payload_type == ACCUMULATOR_UPDATE_WORMHOLE_VERIFICATION_MAGIC, E_INVALID_WORMHOLE_MESSAGE); + let wh_message_payload_type = deserialize::deserialize_u8(&mut msg_payload_cursor); + assert!(wh_message_payload_type == 0, E_INVALID_WORMHOLE_MESSAGE); // Merkle variant + let _merkle_root_slot = deserialize::deserialize_u64(&mut msg_payload_cursor); + let _merkle_root_ring_size = deserialize::deserialize_u32(&mut msg_payload_cursor); + let merkle_root_hash = deserialize::deserialize_vector(&mut msg_payload_cursor, 20); + cursor::take_rest(msg_payload_cursor); + bytes20::new(merkle_root_hash) + } + + // Note: this parsing function is adapted from the Aptos Pyth parse_price_feed_message function + fun parse_price_feed_message(message_cur: &mut Cursor, clock: &Clock): PriceInfo { + let message_type = deserialize::deserialize_u8(message_cur); + + assert!(message_type == PRICE_FEED_MESSAGE_TYPE, E_INVALID_UPDATE_DATA); + let price_identifier = price_identifier::from_byte_vec(deserialize::deserialize_vector(message_cur, 32)); + let price = deserialize::deserialize_i64(message_cur); + let conf = deserialize::deserialize_u64(message_cur); + let expo = deserialize::deserialize_i32(message_cur); + let publish_time = deserialize::deserialize_u64(message_cur); + let _prev_publish_time = deserialize::deserialize_i64(message_cur); + let ema_price = deserialize::deserialize_i64(message_cur); + let ema_conf = deserialize::deserialize_u64(message_cur); + let price_info = price_info::new_price_info( + clock::timestamp_ms(clock) / 1000, // not used anywhere kept for backward compatibility + clock::timestamp_ms(clock) / 1000, + price_feed::new( + price_identifier, + pyth::price::new(price, conf, expo, publish_time), + pyth::price::new(ema_price, ema_conf, expo, publish_time), + ) + ); + price_info + } + + // parse_and_verify_accumulator_updates takes as input a merkle root and cursor over the encoded update message (containing encoded + // leafs and merkle proofs), iterates over each leaf/proof pair and verifies it is part of the tree, and finally outputs the set of + // decoded and authenticated PriceInfos. + fun parse_and_verify_accumulator_updates(cursor: &mut Cursor, merkle_root: Bytes20, clock: &Clock): vector { + let update_size = deserialize::deserialize_u8(cursor); + let price_info_updates: vector = vector[]; + while (update_size > 0) { + let message_size = deserialize::deserialize_u16(cursor); + let message = deserialize::deserialize_vector(cursor, (message_size as u64)); //should be safe to go from u16 to u16 + let message_cur = cursor::new(message); + let price_info = parse_price_feed_message(&mut message_cur, clock); + cursor::take_rest(message_cur); + + vector::push_back(&mut price_info_updates, price_info); + + // isProofValid pops the next merkle proof from the front of cursor and checks if it proves that message is part of the + // merkle tree defined by merkle_root + assert!(merkle_tree::is_proof_valid(cursor, merkle_root, message), E_INVALID_PROOF); + update_size = update_size - 1; + }; + price_info_updates + } +} diff --git a/target_chains/iota/contracts/sources/set.move b/target_chains/iota/contracts/sources/set.move new file mode 100644 index 0000000000..f6d76186be --- /dev/null +++ b/target_chains/iota/contracts/sources/set.move @@ -0,0 +1,46 @@ +/// A set data structure. +module pyth::set { + use iota::table::{Self, Table}; + use iota::tx_context::{TxContext}; + use std::vector; + + /// Empty struct. Used as the value type in mappings to encode a set + struct Unit has store, copy, drop {} + + /// A set containing elements of type `A` with support for membership + /// checking. + struct Set has store { + keys: vector, + elems: Table + } + + /// Create a new Set. + public fun new(ctx: &mut TxContext): Set { + Set { + keys: vector::empty(), + elems: table::new(ctx), + } + } + + /// Add a new element to the set. + /// Aborts if the element already exists + public fun add(set: &mut Set, key: A) { + table::add(&mut set.elems, key, Unit {}); + vector::push_back(&mut set.keys, key); + } + + /// Returns true iff `set` contains an entry for `key`. + public fun contains(set: &Set, key: A): bool { + table::contains(&set.elems, key) + } + + /// Removes all elements from the set + public fun empty(set: &mut Set) { + while (!vector::is_empty(&set.keys)) { + table::remove(&mut set.elems, vector::pop_back(&mut set.keys)); + } + } + + // TODO: destroy_empty, but this is tricky because std::table doesn't + // have this functionality. +} diff --git a/target_chains/iota/contracts/sources/setup.move b/target_chains/iota/contracts/sources/setup.move new file mode 100644 index 0000000000..04eaba1bfe --- /dev/null +++ b/target_chains/iota/contracts/sources/setup.move @@ -0,0 +1,75 @@ +module pyth::setup { + use iota::object::{Self, UID}; + use iota::package::{Self, UpgradeCap}; + use iota::transfer::{Self}; + use iota::tx_context::{Self, TxContext}; + + use pyth::state::{Self}; + use pyth::data_source::{DataSource}; + + friend pyth::pyth; + #[test_only] + friend pyth::pyth_tests; + + /// 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 + } + + fun init(ctx: &mut TxContext) { + transfer::public_transfer( + DeployerCap { + id: object::new(ctx) + }, + 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( + iota::package::test_publish(object::id_from_address(@pyth), 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(friend) fun init_and_share_state( + deployer: DeployerCap, + upgrade_cap: UpgradeCap, + stale_price_threshold: u64, + base_update_fee: u64, + governance_data_source: DataSource, + sources: 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( + upgrade_cap, + sources, + governance_data_source, + stale_price_threshold, + base_update_fee, + ctx + )); + } +} diff --git a/target_chains/iota/contracts/sources/state.move b/target_chains/iota/contracts/sources/state.move new file mode 100644 index 0000000000..3dcf6e62b5 --- /dev/null +++ b/target_chains/iota/contracts/sources/state.move @@ -0,0 +1,404 @@ +module pyth::state { + use std::vector; + use iota::object::{Self, UID, ID}; + use iota::tx_context::{Self, TxContext}; + use iota::package::{UpgradeCap, UpgradeTicket, UpgradeReceipt}; + + use pyth::data_source::{Self, DataSource}; + use pyth::price_info::{Self}; + use pyth::price_identifier::{Self, PriceIdentifier}; + use pyth::version_control::{Self}; + + use wormhole::consumed_vaas::{Self, ConsumedVAAs}; + use wormhole::bytes32::{Self, Bytes32}; + use wormhole::package_utils::{Self}; + use wormhole::external_address::{ExternalAddress}; + + friend pyth::pyth; + #[test_only] + friend pyth::pyth_tests; + friend pyth::governance_action; + friend pyth::set_update_fee; + friend pyth::set_stale_price_threshold; + friend pyth::set_data_sources; + friend pyth::governance; + friend pyth::set_governance_data_source; + friend pyth::migrate; + friend pyth::contract_upgrade; + friend pyth::set_fee_recipient; + friend pyth::setup; + + /// Build digest does not agree with current implementation. + const E_INVALID_BUILD_DIGEST: u64 = 0; + + /// Capability reflecting that the current build version is used to invoke + /// state methods. + struct LatestOnly has drop {} + + #[test_only] + public fun create_latest_only_for_test():LatestOnly { + LatestOnly{} + } + + struct State has key, store { + id: UID, + governance_data_source: DataSource, + stale_price_threshold: u64, + base_update_fee: u64, + fee_recipient_address: address, + last_executed_governance_sequence: u64, + consumed_vaas: ConsumedVAAs, + + // Upgrade capability. + upgrade_cap: UpgradeCap + } + + public(friend) fun new( + upgrade_cap: UpgradeCap, + sources: vector, + governance_data_source: DataSource, + stale_price_threshold: u64, + base_update_fee: u64, + ctx: &mut TxContext + ): State { + let uid = object::new(ctx); + + // Create a set that contains all registered data sources and + // attach it to uid as a dynamic field to minimize the + // size of State. + data_source::new_data_source_registry(&mut uid, ctx); + + // Create a table that tracks the object IDs of price feeds and + // attach it to the uid as a dynamic object field to minimize the + // size of State. + price_info::new_price_info_registry(&mut uid, ctx); + + while (!vector::is_empty(&sources)) { + data_source::add(&mut uid, vector::pop_back(&mut sources)); + }; + + let consumed_vaas = consumed_vaas::new(ctx); + + // Initialize package info. This will be used for emitting information + // of successful migrations. + package_utils::init_package_info( + &mut uid, + version_control::current_version(), + &upgrade_cap + ); + + State { + id: uid, + upgrade_cap, + governance_data_source, + stale_price_threshold, + fee_recipient_address: tx_context::sender(ctx), + base_update_fee, + consumed_vaas, + last_executed_governance_sequence: 0 + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Simple Getters + // + // These methods do not require `LatestOnly` for access. Anyone is free to + // access these values. + // + //////////////////////////////////////////////////////////////////////////// + + public fun get_stale_price_threshold_secs(s: &State): u64 { + s.stale_price_threshold + } + + public fun get_base_update_fee(s: &State): u64 { + s.base_update_fee + } + + public fun get_fee_recipient(s: &State): address { + s.fee_recipient_address + } + + public fun is_valid_data_source(s: &State, data_source: DataSource): bool { + data_source::contains(&s.id, data_source) + } + + public fun is_valid_governance_data_source(s: &State, source: DataSource): bool { + s.governance_data_source == source + } + + public fun price_feed_object_exists(s: &State, p: PriceIdentifier): bool { + price_info::contains(&s.id, p) + } + + /// Retrieve governance chain ID, which is governance's emitter chain ID. + public fun governance_data_source(self: &State): DataSource { + self.governance_data_source + } + + public fun get_last_executed_governance_sequence(self: &State): u64{ + return self.last_executed_governance_sequence + } + + /// Retrieve governance module name. + public fun governance_module(): Bytes32 { + bytes32::new( + x"0000000000000000000000000000000000000000000000000000000000000001" + ) + } + + /// Retrieve governance chain ID, which is governance's emitter chain ID. + public fun governance_chain(self: &State): u16 { + let governance_data_source = governance_data_source(self); + (data_source::emitter_chain(&governance_data_source) as u16) + } + + /// Retrieve governance emitter address. + public fun governance_contract(self: &State): ExternalAddress { + let governance_data_source = governance_data_source(self); + data_source::emitter_address(&governance_data_source) + } + + public fun get_price_info_object_id(self: &State, price_identifier_bytes: vector): ID { + let price_identifier = price_identifier::from_byte_vec(price_identifier_bytes); + price_info::get_id(&self.id, price_identifier) + } + + //////////////////////////////////////////////////////////////////////////// + // + // 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 {} + } + + public(friend) fun set_fee_recipient( + _: &LatestOnly, + self: &mut State, + addr: address + ) { + self.fee_recipient_address = addr; + } + + /// 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 + } + + public(friend) fun current_package(_: &LatestOnly, self: &State): ID { + package_utils::current_package(&self.id) + } + + public(friend) fun set_data_sources(_: &LatestOnly, s: &mut State, new_sources: vector) { + // Empty the existing table of data sources registered in state. + data_source::empty(&mut s.id); + // Add the new data sources to the dynamic field registry. + while (!vector::is_empty(&new_sources)) { + data_source::add(&mut s.id, vector::pop_back(&mut new_sources)); + }; + } + + public(friend) fun register_price_info_object(_: &LatestOnly, s: &mut State, price_identifier: PriceIdentifier, id: ID) { + price_info::add(&mut s.id, price_identifier, id); + } + + public(friend) fun set_governance_data_source(_: &LatestOnly, s: &mut State, source: DataSource) { + s.governance_data_source = source; + } + + public(friend) fun set_last_executed_governance_sequence(_: &LatestOnly, s: &mut State, sequence: u64) { + s.last_executed_governance_sequence = sequence; + } + + // We have an unchecked version of set_last_executed_governance_sequence, because in the governance contract + // upgrade code path, no LatestOnly is created (for example, see authorize_upgrade and commit_upgrade in + // governance/contract_upgrade.move) + public(friend) fun set_last_executed_governance_sequence_unchecked(s: &mut State, sequence: u64) { + s.last_executed_governance_sequence = sequence; + } + + public(friend) fun set_base_update_fee(_: &LatestOnly, s: &mut State, fee: u64) { + s.base_update_fee = fee; + } + + public(friend) fun set_stale_price_threshold_secs(_: &LatestOnly, s: &mut State, threshold_secs: u64) { + s.stale_price_threshold = threshold_secs; + } + + //////////////////////////////////////////////////////////////////////////// + // + // 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 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`. + 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 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`. + 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. + // + //////////////////////////////////////////////////////////////////////////// + + public(friend) fun migrate__v__0_1_1(self: &mut State) { + // We need to add dynamic fields via the new package utils method. These + // fields do not exist in the previous build (0.1.0). + // See `state::new` above. + + // Initialize package info. This will be used for emitting information + // of successful migrations. + let upgrade_cap = &self.upgrade_cap; + package_utils::init_package_info( + &mut self.id, + version_control::current_version(), + upgrade_cap, + ); + } + + #[test_only] + /// Bloody hack. + public fun reverse_migrate__v__0_1_0(self: &mut State) { + package_utils::remove_package_info(&mut self.id); + + // Add back in old dynamic field(s)... + + // Add dummy hash since this is the first time the package is published. + iota::dynamic_field::add(&mut self.id, CurrentDigest {}, bytes32::from_bytes(b"new build")); + } + + #[test_only] + public fun register_price_info_object_for_test(self: &mut State, price_identifier: PriceIdentifier, id: ID) { + price_info::add(&mut self.id, price_identifier, id); + } + + #[test_only] + public fun new_state_for_test( + upgrade_cap: UpgradeCap, + governance_data_source: DataSource, + stale_price_threshold: u64, + base_update_fee: u64, + ctx: &mut TxContext + ): State { + State { + id: object::new(ctx), + upgrade_cap, + governance_data_source, + stale_price_threshold, + base_update_fee, + fee_recipient_address: tx_context::sender(ctx), + last_executed_governance_sequence: 0, + consumed_vaas: consumed_vaas::new(ctx), + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Deprecated + // + // Dumping grounds for old structs and methods. These things should not + // be used in future builds. + // + //////////////////////////////////////////////////////////////////////////// + + struct CurrentDigest has store, drop, copy {} + +} diff --git a/target_chains/iota/contracts/sources/version_control.move b/target_chains/iota/contracts/sources/version_control.move new file mode 100644 index 0000000000..ab5b3446d8 --- /dev/null +++ b/target_chains/iota/contracts/sources/version_control.move @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: Apache 2 + +/// Note: this module is adapted from Wormhole's version_control.move module. +/// +/// 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 `pyth::state` and `wormhole::package_utils` for more info. +module pyth::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_1_2 { + V__0_1_2 {} + } + + public(friend) fun previous_version(): V__0_1_1 { + V__0_1_1 {} + } + + //////////////////////////////////////////////////////////////////////////// + // + // 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. + // + //////////////////////////////////////////////////////////////////////////// + + /// RELEASE NOTES + /// + /// - Gas optimizations on merkle tree verifications + struct V__0_1_2 has store, drop, copy {} + + /// RELEASE NOTES + /// + /// - Refactor state to use package management via + /// `wormhole::package_utils`. + /// - Add `MigrateComplete` event in `migrate`. + /// + /// Also added `migrate__v__0_1_1` in `wormhole::state`, which is + /// meant to perform a one-time `State` modification via `migrate`. + struct V__0_1_1 has store, drop, copy {} + + // Dummy. + struct V__DUMMY has store, drop, copy {} + + //////////////////////////////////////////////////////////////////////////// + // + // Implementation and Test-Only Methods + // + //////////////////////////////////////////////////////////////////////////// + + friend pyth::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 {} + } + + #[test_only] + public fun previous_version_test_only(): V__0_1_1 { + previous_version() + } +} diff --git a/target_chains/iota/iota-patch-libs.sh b/target_chains/iota/iota-patch-libs.sh new file mode 100644 index 0000000000..c4079d6073 --- /dev/null +++ b/target_chains/iota/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." diff --git a/target_chains/iota/sdk/js-iota/.eslintrc.js b/target_chains/iota/sdk/js-iota/.eslintrc.js new file mode 100644 index 0000000000..8fa976bb6e --- /dev/null +++ b/target_chains/iota/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/iota/sdk/js-iota/.gitignore b/target_chains/iota/sdk/js-iota/.gitignore new file mode 100644 index 0000000000..a65b41774a --- /dev/null +++ b/target_chains/iota/sdk/js-iota/.gitignore @@ -0,0 +1 @@ +lib diff --git a/target_chains/iota/sdk/js-iota/README.md b/target_chains/iota/sdk/js-iota/README.md new file mode 100644 index 0000000000..45de93b3c7 --- /dev/null +++ b/target_chains/iota/sdk/js-iota/README.md @@ -0,0 +1,155 @@ +# 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 [IOTA network](https://www.iota.org/). + +## Installation + +### npm + +``` +$ npm install --save @pythnetwork/pyth-iota-js +``` + +### Yarn + +``` +$ yarn add @pythnetwork/pyth-iota-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 `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 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 + "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 +// 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 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 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 [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 { 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 + + +// 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 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); + +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/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 IOTA contract with the update data. + +You can run this example with `npm run example-relay`. A full command that updates prices on IOTA testnet looks like: + +```bash +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 `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: + +```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/iota/sdk/js-iota/jest.config.js b/target_chains/iota/sdk/js-iota/jest.config.js new file mode 100644 index 0000000000..21a1e973ab --- /dev/null +++ b/target_chains/iota/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/iota/sdk/js-iota/package.json b/target_chains/iota/sdk/js-iota/package.json new file mode 100644 index 0000000000..b3622e3949 --- /dev/null +++ b/target_chains/iota/sdk/js-iota/package.json @@ -0,0 +1,59 @@ +{ + "name": "@pythnetwork/pyth-iota-js", + "version": "2.1.0", + "description": "Pyth Network IOTA 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-iota" + }, + "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", + "iota" + ], + "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": { + "@iota/iota-sdk": "^0.5.0", + "@pythnetwork/price-service-client": "workspace:*", + "buffer": "^6.0.3" + } +} diff --git a/target_chains/iota/sdk/js-iota/src/IotaPriceServiceConnection.ts b/target_chains/iota/sdk/js-iota/src/IotaPriceServiceConnection.ts new file mode 100644 index 0000000000..c7292c2e60 --- /dev/null +++ b/target_chains/iota/sdk/js-iota/src/IotaPriceServiceConnection.ts @@ -0,0 +1,21 @@ +import { + PriceServiceConnection, + HexString, +} from "@pythnetwork/price-service-client"; +import { Buffer } from "buffer"; + +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 + * 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/iota/sdk/js-iota/src/client.ts b/target_chains/iota/sdk/js-iota/src/client.ts new file mode 100644 index 0000000000..86797d695c --- /dev/null +++ b/target_chains/iota/sdk/js-iota/src/client.ts @@ -0,0 +1,304 @@ +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 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: IotaClient, + 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(IOTA_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(IOTA_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(IOTA_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(IOTA_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/iota/sdk/js-iota/src/examples/IotaRelay.ts b/target_chains/iota/sdk/js-iota/src/examples/IotaRelay.ts new file mode 100644 index 0000000000..c0c9ed13ab --- /dev/null +++ b/target_chains/iota/sdk/js-iota/src/examples/IotaRelay.ts @@ -0,0 +1,97 @@ +import yargs from "yargs"; +import { hideBin } from "yargs/helpers"; +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 { IotaPythClient } from "../client"; +import { IotaPriceServiceConnection } 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 IOTA node RPC endpoint. e.g: https://api.testnet.iota.cafe/", + 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 IotaClient({ url }); +} +async function run() { + if (process.env.IOTA_KEY === undefined) { + throw new Error(`IOTA_KEY environment variable should be set.`); + } + + const argv = await argvPromise; + + // Fetch the latest price feed update data from the Price Service + 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 IotaPythClient(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.IOTA_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/iota/sdk/js-iota/src/index.ts b/target_chains/iota/sdk/js-iota/src/index.ts new file mode 100644 index 0000000000..6a67047575 --- /dev/null +++ b/target_chains/iota/sdk/js-iota/src/index.ts @@ -0,0 +1,11 @@ +export { IotaPriceServiceConnection } from "./IotaPriceServiceConnection"; +export { IotaPythClient } from "./client"; + +export { + DurationInMs, + HexString, + Price, + PriceFeed, + PriceServiceConnectionConfig, + UnixTimestamp, +} from "@pythnetwork/price-service-client"; diff --git a/target_chains/iota/sdk/js-iota/tsconfig.json b/target_chains/iota/sdk/js-iota/tsconfig.json new file mode 100644 index 0000000000..927049ab35 --- /dev/null +++ b/target_chains/iota/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__/*"] +} diff --git a/target_chains/iota/sdk/js/.eslintrc.js b/target_chains/iota/sdk/js/.eslintrc.js new file mode 100644 index 0000000000..8fa976bb6e --- /dev/null +++ b/target_chains/iota/sdk/js/.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/iota/sdk/js/.gitignore b/target_chains/iota/sdk/js/.gitignore new file mode 100644 index 0000000000..a65b41774a --- /dev/null +++ b/target_chains/iota/sdk/js/.gitignore @@ -0,0 +1 @@ +lib diff --git a/target_chains/iota/sdk/js/README.md b/target_chains/iota/sdk/js/README.md new file mode 100644 index 0000000000..d39cd67b8f --- /dev/null +++ b/target_chains/iota/sdk/js/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/iota/sdk/js/jest.config.js b/target_chains/iota/sdk/js/jest.config.js new file mode 100644 index 0000000000..21a1e973ab --- /dev/null +++ b/target_chains/iota/sdk/js/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/iota/sdk/js/package.json b/target_chains/iota/sdk/js/package.json new file mode 100644 index 0000000000..d7674a5121 --- /dev/null +++ b/target_chains/iota/sdk/js/package.json @@ -0,0 +1,59 @@ +{ + "name": "@pythnetwork/pyth-sui-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/ --max-warnings 0", + "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/iota/sdk/js/src/SuiPriceServiceConnection.ts b/target_chains/iota/sdk/js/src/SuiPriceServiceConnection.ts new file mode 100644 index 0000000000..6d520cf3e3 --- /dev/null +++ b/target_chains/iota/sdk/js/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/iota/sdk/js/src/client.ts b/target_chains/iota/sdk/js/src/client.ts new file mode 100644 index 0000000000..f65e2e21aa --- /dev/null +++ b/target_chains/iota/sdk/js/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/iota/sdk/js/src/examples/SuiRelay.ts b/target_chains/iota/sdk/js/src/examples/SuiRelay.ts new file mode 100644 index 0000000000..cb40f4b1e5 --- /dev/null +++ b/target_chains/iota/sdk/js/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/iota/sdk/js/src/index.ts b/target_chains/iota/sdk/js/src/index.ts new file mode 100644 index 0000000000..f4cfecf948 --- /dev/null +++ b/target_chains/iota/sdk/js/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/iota/sdk/js/tsconfig.json b/target_chains/iota/sdk/js/tsconfig.json new file mode 100644 index 0000000000..927049ab35 --- /dev/null +++ b/target_chains/iota/sdk/js/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__/*"] +} diff --git a/target_chains/iota/vendor/README.md b/target_chains/iota/vendor/README.md new file mode 100644 index 0000000000..589fb36c17 --- /dev/null +++ b/target_chains/iota/vendor/README.md @@ -0,0 +1,11 @@ +# 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` 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 +[`e94c8ef4a810cae63d4e54811aa6a843b5fd9e65`](https://github.com/wormhole-foundation/wormhole/tree/e94c8ef4a810cae63d4e54811aa6a843b5fd9e65) +from the Wormhole repository. To update it, pull the latest version and copy it here and update the +README. diff --git a/target_chains/iota/vendor/wormhole_iota_testnet/.gitignore b/target_chains/iota/vendor/wormhole_iota_testnet/.gitignore new file mode 100644 index 0000000000..6ddb4b0ac3 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_iota_testnet/.gitignore @@ -0,0 +1,2 @@ +deploy.out +sui.log.* diff --git a/target_chains/iota/vendor/wormhole_iota_testnet/Docker.md b/target_chains/iota/vendor/wormhole_iota_testnet/Docker.md new file mode 100755 index 0000000000..97ff842f6d --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/Dockerfile b/target_chains/iota/vendor/wormhole_iota_testnet/Dockerfile new file mode 100644 index 0000000000..4bef3a3624 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/Dockerfile.base b/target_chains/iota/vendor/wormhole_iota_testnet/Dockerfile.base new file mode 100644 index 0000000000..4c76dc016d --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/Makefile b/target_chains/iota/vendor/wormhole_iota_testnet/Makefile new file mode 100644 index 0000000000..0ee0e5eb94 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/NOTES.md b/target_chains/iota/vendor/wormhole_iota_testnet/NOTES.md new file mode 100644 index 0000000000..5677cfb924 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/README.md b/target_chains/iota/vendor/wormhole_iota_testnet/README.md new file mode 100644 index 0000000000..729a8c406c --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36219.yaml b/target_chains/iota/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36219.yaml new file mode 100644 index 0000000000..e489d5c3dc --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36853.yaml b/target_chains/iota/vendor/wormhole_iota_testnet/devnet/127.0.0.1-36853.yaml new file mode 100644 index 0000000000..ec936e990a --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39101.yaml b/target_chains/iota/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39101.yaml new file mode 100644 index 0000000000..9f5954a4d3 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39187.yaml b/target_chains/iota/vendor/wormhole_iota_testnet/devnet/127.0.0.1-39187.yaml new file mode 100644 index 0000000000..59b82dc21c --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/devnet/client.yaml b/target_chains/iota/vendor/wormhole_iota_testnet/devnet/client.yaml new file mode 100644 index 0000000000..c9302468b5 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/devnet/fullnode.yaml b/target_chains/iota/vendor/wormhole_iota_testnet/devnet/fullnode.yaml new file mode 100644 index 0000000000..c89b027b28 --- /dev/null +++ b/target_chains/iota/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: AAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAIIsLrJvNn1hEoWtQKz7MnHRemLsqBrEN7ZoBB5dVg2VLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARHRTBY4BAAAAAAAAAAAAAAAAAI7vXhQyHvv4dDflVw3vCcK+sr2/Z98CM1ga+BV/qkaqV2TVoMDJX7LGwN9n4BMYlhg6MAAAAQAAAAAAAwAQAAAAAAABAAIAAwAAASDqtpvbwnXvEkB5o8u1KyS94KNmPgS6OO6XLXm173aVECASBtVyC0JhayaJD7IYjlwt8X4ZA09VwgNMnohN3I3fGQEAAQAAAAACQAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAAAAAAAAAsDYmNzXKEc6wsGAAAABgEAAgMCBgUIBwcPDQgcIAw8BAAAAAEAAQEAAQYJAAEKAgNiY3MIdG9fYnl0ZXMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAgAABGhhc2hqoRzrCwYAAAAGAQACAwIKBQwDBw8XCCYgDEYIAAAAAQAAAAACAAAAAQoCBGhhc2gIc2hhMl8yNTYIc2hhM18yNTYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAgABAQIAAAVhc2NpaZYGoRzrCwYAAAALAQAEAgQOAxJUBGYIBW47B6kByAEI8QIgBpEDCgqbAwsMpgO5Ag3fBQQABQAQAAIHAAAABwABAQcBAAAACAABAAAUAgMAABUCBAAAAwUGAAASBwgAABEJAQAADgUKAAAEBQsAAAoDAgAABgEAAAANAAYAAAsABgABCQ0OAQABDAwGAQABDwgNAQABEw4NAQANAwwDDgMPAwECAQgBAQoCAQgAAQsCAQgAAQYIAAEBAgcIAAgBAAEHCAABAwEGCgIBBgsCAQkAAQsCAQkAAQkAAgMDBENoYXIGT3B0aW9uBlN0cmluZxhhbGxfY2hhcmFjdGVyc19wcmludGFibGUIYXNfYnl0ZXMFYXNjaWkEYnl0ZQVieXRlcwRjaGFyDGRlc3Ryb3lfc29tZQppbnRvX2J5dGVzEWlzX3ByaW50YWJsZV9jaGFyB2lzX3NvbWUNaXNfdmFsaWRfY2hhcgZsZW5ndGgEbm9uZQZvcHRpb24IcG9wX2NoYXIJcHVzaF9jaGFyBHNvbWUGc3RyaW5nCnRyeV9zdHJpbmcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQMIAAABAAAAAAAAAgEHCgIBAgEGAgABAAAICQoAEQoEBAUGBwAnCwASAQIBAQAABAwLABECDAEOATgABAcFCQcAJwsBOAECAgEAAA8cDgBBAAwCBgAAAAAAAAAADAEKAQoCIwQYBQoOAAoBQgAUEQogBBM4AgILAQYBAAAAAAAAABYMAQUFCwASADgDAgMBAAAPIAoAEABBAAwCBgAAAAAAAAAADAEKAQoCIwQcBQsKABAACgFCABQRCyAEFwsAAQkCCwEGAQAAAAAAAAAWDAEFBgsAAQgCBAEAAAgHCwAPAA4BEAEURAACBQEAAAgFCwAPAEUAEgECBgEAAAgECwARB0EAAgcBAAAIAwsAEAACCAEAAAgDCwATAAIJAQAACAMLABMBAgoBAAAIBAsAMX8lAgsBAAAGDQoAMSAmBAkLADF+JQwBBQsJDAELAQIAAAEAAAVkZWJ1Z3ShHOsLBgAAAAYBAAIDAgsFDQUHEh4IMCAMUAgAAAABAAEBAAACAQEAAQYJAAAFZGVidWcFcHJpbnQRcHJpbnRfc3RhY2tfdHJhY2UAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQABAgABAQIAAAZvcHRpb27wCKEc6wsGAAAADQEABAIEBgMKeASCAQ4FkAGHAQeXAtsBCPIDIAaSBBQKpgQHC60EAgyvBP8DDa4IAg6wCAIADwAWAAAHAQAAAA4AAQEAABECAQEAAAwDBAEAAA0DBAEAAAQFBAEAAAEDBgEAAAMFBgEAAAoHAgEDAAkIAAEAAAgJAgEAAAIJCgEAABIIAgEAABMIAQEAAAcLAgECAAYBAgEAAAUBAAEAABQBDAEAAQQOBAEAAQsNBAEAARACDAEAEwISAhECAwIAAgECAgIAAQsAAQkAAQkAAQYLAAEJAAEBAgYLAAEJAAYJAAEGCQACBgsAAQkACQACBwsAAQkACQABBwsAAQkAAQcJAAILAAEJAAkAAQoJAAEGCgkAAgYKCQAGCQACBgkABgoJAAIJAAYKCQABBwoJAAIJAAcKCQADCwABCQALAAEJAAcKCQACCQAKCQAGT3B0aW9uBmJvcnJvdwpib3Jyb3dfbXV0E2JvcnJvd193aXRoX2RlZmF1bHQIY29udGFpbnMMZGVzdHJveV9ub25lDGRlc3Ryb3lfc29tZRRkZXN0cm95X3dpdGhfZGVmYXVsdAdleHRyYWN0BGZpbGwQZ2V0X3dpdGhfZGVmYXVsdAhpc19lbXB0eQdpc19ub25lB2lzX3NvbWUEbm9uZQZvcHRpb24Jc2luZ2xldG9uBHNvbWUEc3dhcAxzd2FwX29yX2ZpbGwGdG9fdmVjA3ZlYwZ2ZWN0b3IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQMIAAAEAAAAAAADCAEABAAAAAAAAAIBFQoJAAACAAEAAAADQAIAAAAAAAAAADkAAgEBAAAABAsAOAA5AAICAQAAAAQLADcAOAECAwEAAAAFCwA3ADgBIAIEAQAAAAULADcACwE4AgIFAQAAAA0KADgDBAQFCAsAAQcBJwsANwAGAAAAAAAAAABCAgIGAQAADxMLADcADAMKAzgBBAsLAwELAQwCBRELAQELAwYAAAAAAAAAAEICDAILAgIHAQAAEBILADcADAMKAzgBBAsLAwELAQwCBRALAwYAAAAAAAAAAEICFAwCCwICCAEAABEQCwA2AAwCCgIuOAEECAUMCwIBBwAnCwILAUQCAgkBAAAADQoALjgDBAUFCQsAAQcBJwsANgBFAgIKAQAAAA4KAC44AwQFBQkLAAEHAScLADYABgAAAAAAAAAAQwICCwEAABIUCgAuOAMEBQUJCwABBwEnCwA2AAwDCgNFAgwCCwMLAUQCCwICDAEAABMVCwA2AAwECgQuOAEECjgEDAIFDgoERQI4BQwCCwIMAwsECwFEAgsDAg0BAAAUDgsAOgAMAw4DOAEECQsBDAIFDA0DRQIMAgsCAg4BAAAUEA4AOAMEBAUGBwEnCwA6AAwCDQJFAgwBCwJGAgAAAAAAAAAACwECDwEAAAAKDgA4BgQEBQYHACcLADoARgIAAAAAAAAAAAIQAQAAAAMLADoAAgAAAAIABnN0cmluZ/kHoRzrCwYAAAALAQAIAggOAxZyBIgBCAWQAXsHiwL4AQiDBCAGowQUCrcEBgy9BIUDDcIHAgATAAQAEQAYAAEHAAEBBwACAAcBAAAAFwABAAAGAgEAABUBAgAAFgADAAAFBAUAAA4EBgAADwQHAAACCAkAAAMKCQAACAsJAAAUDAEAAAcNBwAACQUGAAALDgYAAAwPAAAAChAHAAENAgAAARMAAgACEAkSAQACEhESAQADAhUJAQADDhQGAQATARIBFRMUEwEKAgEIAAEIAQELAgEIAAEGCAABBgoCAQEBAwIHCAAIAAACBwgACgIDBwgAAwgAAwYIAAMDAgYIAAYIAAIGCgIDAwYKAgMDAgYKAgYKAgEJAAELAgEJAAECAQYKCQACBwoJAAoJAAgBAwMDBgoCCAAIAAMFAQEBBgoCAwZPcHRpb24GU3RyaW5nBmFwcGVuZAthcHBlbmRfdXRmOAVhc2NpaQVieXRlcwpmcm9tX2FzY2lpCGluZGV4X29mBmluc2VydBNpbnRlcm5hbF9jaGVja191dGY4EWludGVybmFsX2luZGV4X29mGWludGVybmFsX2lzX2NoYXJfYm91bmRhcnkTaW50ZXJuYWxfc3ViX3N0cmluZwppbnRvX2J5dGVzCGlzX2VtcHR5Bmxlbmd0aARub25lBm9wdGlvbgRzb21lBnN0cmluZwpzdWJfc3RyaW5nCHRvX2FzY2lpCHRyeV91dGY4BHV0ZjgGdmVjdG9yAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCAEAAAAAAAAAAwgCAAAAAAAAAAACAQUKAgABAAAJCQ4AEQwEBAUGBwAnCwASAAIBAQAACQQLABEQEgACAgEAAAkECwATABERAgMBAAADDA4AEQwECAsAEgA4AAwBBQo4AQwBCwECBAEAAAkDCwAQAAIFAQAACQQLABAAOAICBgEAAAkECwAQAEETAgcBAAAJBwsADwAOARAAFDgDAggBAAAJBQsACwERABEHAgkBAAAWOAoAEAAMBwoBCgdBEyUEDQsHCgERDQwDBRELBwEJDAMLAwQUBRgLAAEHAScKAC4RBgwKCgAKAQwELgYAAAAAAAAAAAsEEQoMCQoACwELCgwGDAUuCwULBhEKDAgNCQsCEQcNCQsIEQcLCQsAFQIKAQAAFzALABAADAYKBkETDAcKAgsHJQQPCgEKAiUMAwURCQwDCwMEGAoGCgERDQwEBRoJDAQLBAQhCgYKAhENDAUFIwkMBQsFBCYFKgsGAQcBJwsGCwELAhEOEgACCwEAAAkGCwAQAAsBEAARDwIMAAIADQACAA4AAgAPAAIAAAAABnZlY3RvcpEIoRzrCwYAAAAIAQACAwJmBGgEBWxhB80BmgEI5wIgBocDCgyRA9gEABEABQABAQAACQIDAQAAAQQFAQAACwYAAQAAAgcIAQAACgkKAQAABAEAAQAADwsAAQAADgoBAQAADQkAAQAAAAwAAQAACAINAQAAAw4NAQAABg4PAQAADAcKAQAABxAAAQAAEAcKAQAJCgsKAAEKCQABBgoJAAEDAgYKCQADAQYJAAIHCgkACQACBwoJAAMBBwkAAQcKCQABCQADBwoJAAMDAgcKCQAKCQABAQIGCgkABgkAAgEDAwcKCQAJAAMDAwMDAgMDAwMHCgkAAwZhcHBlbmQGYm9ycm93CmJvcnJvd19tdXQIY29udGFpbnMNZGVzdHJveV9lbXB0eQVlbXB0eQhpbmRleF9vZgZpbnNlcnQIaXNfZW1wdHkGbGVuZ3RoCHBvcF9iYWNrCXB1c2hfYmFjawZyZW1vdmUHcmV2ZXJzZQlzaW5nbGV0b24Ec3dhcAtzd2FwX3JlbW92ZQZ2ZWN0b3IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQMIAAACAAAAAAAAAQIAAQECAAIBAgADAQIABAECAAUBAgAGAQIABwECAAgBAAABB0AKAAAAAAAAAAAMAQ0BCwBECgsBAgkBAAARJgoALkEKDAMKAwYAAAAAAAAAACEECwsAAQIGAAAAAAAAAAAMAgsDBgEAAAAAAAAAFwwBCgIKASMEIwUWCgAKAgoBRwoLAgYBAAAAAAAAABYMAgsBBgEAAAAAAAAAFwwBBRELAAECCgEAAAARDQE4AA4BOAEgBAwFBwoADQFFCkQKBQILAAELAUYKAAAAAAAAAAACCwEAAAAFCwBBCgYAAAAAAAAAACECDAEAABIhBgAAAAAAAAAADAIKAEEKDAMKAgoDIwQbBQoKAAoCQgoKASEEFgsAAQsBAQgCCwIGAQAAAAAAAAAWDAIFBQsAAQsBAQkCDQEAABIjBgAAAAAAAAAADAIKAEEKDAMKAgoDIwQcBQoKAAoCQgoKASEEFwsAAQsBAQgLAgILAgYBAAAAAAAAABYMAgUFCwABCwEBCQYAAAAAAAAAAAIOAQAAEyUKAC5BCgwECgEKBCYEDAsAAQcAJwsEBgEAAAAAAAAAFwwECgEKBCMEIgUVCgAMAwoBDAILAQYBAAAAAAAAABYMAQsDCwIKAUcKBRALAEUKAg8BAAADIAoALkEKDAMKAgoDJAQMCwABBwAnCgALAUQKCgIKAyMEHQUUCgAKAgoDRwoLAgYBAAAAAAAAABYMAgUPCwABAhABAAADFwoALjgBIAQGBQoLAAEHACcKAC5BCgYBAAAAAAAAABcMAgoACwELAkcKCwBFCgIAB2FkZHJlc3NloRzrCwYAAAAGAQACAwIFBQcDBwoPCBkgDDkQAAAAAQABAAABAwdhZGRyZXNzBmxlbmd0aAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAAACBiAAAAAAAAAAAgAJdHlwZV9uYW1l0QihHOsLBgAAAAoBAAYCBggDDjQFQlAHkgGcAQiuAiAGzgJhCq8DBgy1A+oEDZ8IAgAPAAIABAABBwACAAcAAAYAAQEAAAkAAQEAAAsCAwAABQIEAAAHAgUAAAgCBQAACgEFAAEMAAoAAgMEBwACDgsFAAABCAABBggAAQEBBggBAQgBFwoCAQEBAQEBAQEBAQoCAQEBCgIKAgoCCgIKAgoCAQYKAgEGCgIBAgQKAgMDBgoCAQMBCgIFAgYCAwoCBgoCBlN0cmluZwhUeXBlTmFtZQdhZGRyZXNzCGFzX2J5dGVzBWFzY2lpDWJvcnJvd19zdHJpbmcDZ2V0C2dldF9hZGRyZXNzCmdldF9tb2R1bGUVZ2V0X3dpdGhfb3JpZ2luYWxfaWRzC2ludG9fc3RyaW5nDGlzX3ByaW1pdGl2ZQZsZW5ndGgEbmFtZQZzdHJpbmcJdHlwZV9uYW1lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAECAToCAXYCAWUCAWMCAXQCAW8CAXIDCAAAAAAAAAAACgIFBGJvb2wKAgMCdTgKAgQDdTE2CgIEA3UzMgoCBAN1NjQKAgUEdTEyOAoCBQR1MjU2CgIIB2FkZHJlc3MKAgEAAAIBDQgBAAECAAEBAgACAQAABq0BCwAQABEIDBcHCAwBChcOASEEDQgMBwUTBwkMDAoXDgwhDAcLBwQYCAwIBR4HCgwQChcOECEMCAsIBCMIDAkFKQcLDBEKFw4RIQwJCwkELggMCgU0BwwMEgoXDhIhDAoLCgQ5CAwLBT8HDQwTChcOEyEMCwsLBEQIDA0FSgcODBQKFw4UIQwNCw0ETwgMDgVVBw8MFQoXDhUhDA4LDgRcCxcBCAwPBasBChdBCAYGAAAAAAAAACYEaQoXBgAAAAAAAAAAQggUBwEhDBYFawkMFgsWBHUKFwYBAAAAAAAAAEIIFAcCIQwCBXcJDAILAgSBAQoXBgIAAAAAAAAAQggUBwMhDAMFgwEJDAMLAwSNAQoXBgMAAAAAAAAAQggUBwQhDAQFjwEJDAQLBASZAQoXBgQAAAAAAAAAQggUBwUhDAUFmwEJDAULBQSlAQsXBgUAAAAAAAAAQggUBwYhDAYFqQELFwEJDAYLBgwPCw8CAwEAAAADCwAQAAIEAQAACSoKABECIAQFBQkLAAEHBycRBwYCAAAAAAAAABgMAwsAEAARCAwEBxAMAQYAAAAAAAAAAAwCCgIKAyMEJQUaDQEKBAoCQggURAgLAgYBAAAAAAAAABYMAgUVCwQBCwERCQIFAQAADDAKABECIAQFBQkLAAEHBycRBwYCAAAAAAAAABgGAgAAAAAAAAAWDAMLABAAEQgMBQcQDAQKBQoDQggMAgcADAEKAg4BIgQpBSANBAsCFEQICwMGAQAAAAAAAAAWDAMFFQsFAQsCAQsEEQkCBgEAAAAEDgAQABQCAAAACmJpdF92ZWN0b3KpBqEc6wsGAAAACgEAAgICBAMGIwUpJAdNbQi6ASAG2gEoCoICCAyKAu0DDfcFBAACAAAHAAAGAAEAAAcCAwAACQIDAAAIAgMAAAMEBQAABAYAAAAFBAAAAQMBCAACBwgAAwACBggAAwEBAQYIAAIKAQMBBwEFAwcBAwMDCUJpdFZlY3RvcgliaXRfZmllbGQKYml0X3ZlY3Rvcgxpc19pbmRleF9zZXQGbGVuZ3RoIGxvbmdlc3Rfc2V0X3NlcXVlbmNlX3N0YXJ0aW5nX2F0A25ldwNzZXQKc2hpZnRfbGVmdAV1bnNldAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwgAAAIAAAAAAAMIAQACAAAAAAADCAEAAAAAAAAAAwgABAAAAAAAAAACAgQDAQoBAAEAAAcjCgAGAAAAAAAAAAAkBAUFBwcBJwoABwMjBAwFDgcBJwYAAAAAAAAAAAwCQAUAAAAAAAAAAAwBCgIKACMEHwUXDQEJRAULAgYBAAAAAAAAABYMAgUSCwALARIAAgEBAAAIFAoBCgAQAEEFIwQHBQsLAAEHACcLAA8ACwFDBQwCCAsCFQICAQAACBQKAQoAEABBBSMEBwULCwABBwAnCwAPAAsBQwUMAgkLAhUCAwEAAAlZCgEKABABFCYEIQoAEABBBQwGBgAAAAAAAAAADAQKBAoGIwQeBREKAA8ACgRDBQwDCQsDFQsEBgEAAAAAAAAAFgwEBQwLAAEFWAoBDAUKBQoAEAEUIwRBBSoKAAoFDAIuCwIRBAQ3CgAKBQoBFxEBBTwKAAoFCgEXEQILBQYBAAAAAAAAABYMBQUjCgAQARQLARcMBQoFCgAQARQjBFYFTgoACgURAgsFBgEAAAAAAAAAFgwFBUcLAAECBAEAAAMRCgEKABAAQQUjBAcFCwsAAQcAJwsAEAALAUIFFAIFAQAAAwQLABAAQQUCBgEAAAAlCgEKABABFCMEBwULCwABBwAnCgEMAgoCCgAQARQjBCEFFAoACgIRBCAEHAsAAQUhCwIGAQAAAAAAAAAWDAIFDQsCCwEXAgABAAAADWZpeGVkX3BvaW50MzLWBKEc6wsGAAAACgEAAgICBAMGHgUkFgc6egi0ASAG1AFECpgCBQydAokCDaYEAgAEAAAHAAAHAAEAAAMAAQAAAQIDAAACAQMAAAUDAQAABgMEAAIDCAABAwIDAwEIAAEBAQQEAQQEBAAMRml4ZWRQb2ludDMyFGNyZWF0ZV9mcm9tX3JhdGlvbmFsFWNyZWF0ZV9mcm9tX3Jhd192YWx1ZQpkaXZpZGVfdTY0DWZpeGVkX3BvaW50MzINZ2V0X3Jhd192YWx1ZQdpc196ZXJvDG11bHRpcGx5X3U2NAV2YWx1ZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBD//////////wAAAAAAAAAAAwgBAAEAAAAAAAMIAgACAAAAAAADCAMAAgAAAAAAAwgEAAEAAAAAAAMIBQACAAAAAAAAAgEIAwABAAAFFAsANQ4BEAAUNRgxIDAMAgoCBwAlBA8FEQcDJwsCNAIBAQAABR0OARAAFAYAAAAAAAAAACIEBwUJBwQnCwA1MSAvDgEQABQ1GgwCCgIHACUEGAUaBwInCwI0AgIBAAAGMAoANTFALwwFCwE1MSAvDAQKBDIAAAAAAAAAAAAAAAAAAAAAIgQPBREHAScLBQsEGgwDCgMyAAAAAAAAAAAAAAAAAAAAACIEHAgMAgUgCwAGAAAAAAAAAAAhDAILAgQjBSUHBScKAwcAJQQqBSwHBScLAzQSAAIDAQAABwMLABIAAgQBAAAHBA4AEAAUAgUBAAAHBg4AEAAUBgAAAAAAAAAAIQIAAAAHDWZpeGVkX3BvaW50MzIMRml4ZWRQb2ludDMyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEGb3B0aW9uBk9wdGlvbgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBWFzY2lpBlN0cmluZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBWFzY2lpBENoYXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQpiaXRfdmVjdG9yCUJpdFZlY3RvcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBnN0cmluZwZTdHJpbmcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQl0eXBlX25hbWUIVHlwZU5hbWUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQADAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgEAAAAAAAAAMgNiYWfsBaEc6wsGAAAACwEACAIIDAMUcASEAQwFkAFSB+IBuQEImwMgBrsDCgrFAwgMzQPmAQ2zBQQABAALABIAFQAADAACAgQAAwECAAARAAEAAAMCAwIHBAAFBAUCBwQABgYHAgcEABMGCAIHBAAHBAkBBwAIBAkCBwQAEAoLAAAPCgkAAAoBAwABAw4DAgcEAQUPBQIHBAEGEAcCBwQBDA8JAQcBDQ8JAgcEARMQCAIHBAIJDAMAAhEADAAKDQsNDA0PDQ0RDg0BBwgCAQgAAwcIAAkACQEAAgYIAAkAAQYJAQIHCAAJAAEHCQEBCQEBAQEGCAABAwEIAQIJAAkBAwcIAQkACQECBggBCQACBwgBCQABCQACCAEDA0JhZwlUeENvbnRleHQDVUlEA2FkZANiYWcGYm9ycm93CmJvcnJvd19tdXQIY29udGFpbnMSY29udGFpbnNfd2l0aF90eXBlBmRlbGV0ZQ1kZXN0cm95X2VtcHR5DWR5bmFtaWNfZmllbGQHZXhpc3RzXxBleGlzdHNfd2l0aF90eXBlAmlkCGlzX2VtcHR5Bmxlbmd0aANuZXcGb2JqZWN0BnJlbW92ZQRzaXplCnR4X2NvbnRleHQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgMIAAAAAAAAAAAAAgIOCAEUAwABAAADBQsAEREGAAAAAAAAAAASAAIBAQAAAw4KAA8ACwELAjgACgAQARQGAQAAAAAAAAAWCwAPARUCAgEAAAMFCwAQAAsBOAECAwEAAAMFCwAPAAsBOAICBAEAAAgPCgAPAAsBOAMMAgoAEAEUBgEAAAAAAAAAFwsADwEVCwICBQEAAAMFCwAQAAsBOAQCBgEAAAMFCwAQAAsBOAUCBwEAAAMECwAQARQCCAEAAAMGCwAQARQGAAAAAAAAAAAhAgkBAAASDgsAEwAMAgwBCwIGAAAAAAAAAAAhBAkFCwcAJwsBERACAAAAAQADYmNzoBChHOsLBgAAAAsBAAoCCgoDFIwBBKABGAW4AYkBB8EC6AIIqQVABukFNwqgBgYMpgbBCQ3nDwIAAwEDAQoBIAACAAAHAAIBBwEAAAAfAAEBAAAIAQIAAAYCAQAACwMEAAAMAwUAABUDBgAAFAMHAAASAwgAABMDCQAAGAMHAAAWAwoAABcDCwAAGwMBAAAcAwwAABoDDQAAGQMOAAANAw8AAA4DEAAAEQMRAAAQAxIAAA8DEwABHwABAQACCRQjAQACHhUjAQADHRYUAQAEBQEEAAQHFAcAFRUYBhcEFgQXBRYFFwYWBhcHFgcXCBYIAQYJAAEKAgEIAAEHCAABBQEBAQIBAwEEAQ8BCgUBCgEBCgoCAQoDAQoEAQsBAQUBCwEBAQELAQECAQsBAQMBCwEBBAABCQABBwoJAAIKAgMCAQIDAwIDAwQCBAMPDQ8EAwMCAwMDAwoFAwMDCgEDAwMKAgMDAwoKAgMDAwoDAwMDCgQBCwEBCQADQkNTBk9wdGlvbgdhZGRyZXNzA2JjcwVieXRlcwpmcm9tX2J5dGVzFGludG9fcmVtYWluZGVyX2J5dGVzBmxlbmd0aANuZXcEbm9uZQZvcHRpb24McGVlbF9hZGRyZXNzCXBlZWxfYm9vbBNwZWVsX29wdGlvbl9hZGRyZXNzEHBlZWxfb3B0aW9uX2Jvb2wQcGVlbF9vcHRpb25fdTEyOA9wZWVsX29wdGlvbl91NjQOcGVlbF9vcHRpb25fdTgJcGVlbF91MTI4CXBlZWxfdTI1NghwZWVsX3U2NAdwZWVsX3U4EHBlZWxfdmVjX2FkZHJlc3MNcGVlbF92ZWNfYm9vbA9wZWVsX3ZlY19sZW5ndGgNcGVlbF92ZWNfdTEyOAxwZWVsX3ZlY191NjQLcGVlbF92ZWNfdTgPcGVlbF92ZWNfdmVjX3U4B3JldmVyc2UEc29tZQh0b19ieXRlcwZ2ZWN0b3IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwgAAAAAAAAAAAMIAQAAAAAAAAADCAIAAAAAAAAACgUBAAoBAQAKAgEACgoCAQAKAwEACgQBAAACAQQKAgABAAAUAwsAOAACAQEAABQFDQA4AQsAEgACAgEAAAEHCwATAAwBDQE4AQsBAgMBAAAXIwoAEABBBhEaJgQHBQsLAAEHACdABgAAAAAAAAAABgAAAAAAAAAADAIMAQoCERojBB4FFA0BCgAPAEUGRAYLAgYBAAAAAAAAABYMAgUPCwABCwERGQIEAQAAGBULABEFDAIKAjEAIQQKCQwBBRMLAjEBIQQPBREHAScIDAELAQIFAQAAFA8KABAAQQYGAQAAAAAAAAAmBAcFCwsAAQcAJwsADwBFBgIGAQAAGSgKABAAQQYGCAAAAAAAAAAmBAcFCwsAAQcAJwYAAAAAAAAAADEADAIMAwoCMUAjBCQFFAoADwBFBjQMAQsDCwEKAi8WDAMLAjEIFgwCBQ8LAAELAwIHAQAAGigKABAAQQYGEAAAAAAAAAAmBAcFCwsAAQcAJzIAAAAAAAAAAAAAAAAAAAAAMQAMAgwDCgIxgCMEJAUUCgAPAEUGNQwBCwMLAQoCLxYMAwsCMQgWDAIFDwsAAQsDAggBAAAbKQoAEABBBgYgAAAAAAAAACYEBwULCwABBwAnSgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAASAAADAIMAwoCSAABIwQlBRQKAA8ARQZNDAELAwsBCgIzLxYMAwsCSAgAFgwCBQ8LAAELAwIJAQAAHDAGAAAAAAAAAAAxAAYAAAAAAAAAAAwCDAMMBAoCBgQAAAAAAAAAJQQLBQ8LAAEHAicKAA8ARQY0DAELAgYBAAAAAAAAABYMAgsECgEGfwAAAAAAAAAcCgMvGwwECwEGgAAAAAAAAAAcBgAAAAAAAAAAIQQnBSwLAzEHFgwDBQYLAAELBAIKAQAAHRkKABEJBgAAAAAAAAAABwMMAwwBDAIKAQoCIwQVBQwNAwoAEQNEBAsBBgEAAAAAAAAAFgwBBQcLAAELAwILAQAAHhkKABEJBgAAAAAAAAAABwQMAwwBDAIKAQoCIwQVBQwNAwoAEQREBQsBBgEAAAAAAAAAFgwBBQcLAAELAwIMAQAAHxkKABEJBgAAAAAAAAAABwUMAwwBDAIKAQoCIwQVBQwNAwoAEQVEBgsBBgEAAAAAAAAAFgwBBQcLAAELAwINAQAAIBkKABEJBgAAAAAAAAAABwYMAwwBDAIKAQoCIwQVBQwNAwoAEQxEAQsBBgEAAAAAAAAAFgwBBQcLAAELAwIOAQAAIRkKABEJBgAAAAAAAAAABwcMAwwBDAIKAQoCIwQVBQwNAwoAEQZEBwsBBgEAAAAAAAAAFgwBBQcLAAELAwIPAQAAIhkKABEJBgAAAAAAAAAABwgMAwwBDAIKAQoCIwQVBQwNAwoAEQdECAsBBgEAAAAAAAAAFgwBBQcLAAELAwIQAQAADw4KABEEBAgLABEDOAIMAQUMCwABOAMMAQsBAhEBAAAQDgoAEQQECAsAEQQ4BAwBBQwLAAE4BQwBCwECEgEAABEOCgARBAQICwARBTgGDAEFDAsAATgHDAELAQITAQAAEg4KABEEBAgLABEGOAgMAQUMCwABOAkMAQsBAhQBAAATDgoAEQQECAsAEQc4CgwBBQwLAAE4CwwBCwECAAAAA2hleKwKoRzrCwYAAAAIAQAEAwQVBBkCBRsiBz0sCGlABqkBnwYMyAe+AgAEAQUAAwAAAAABAAAAAAIBAQABAAMEAQADAQEKAgECBAoKAgMDCgICBwoJAAoJAAAEAgMDCgIFAQEBAgIGYXBwZW5kBmRlY29kZQtkZWNvZGVfYnl0ZQZlbmNvZGUDaGV4BnZlY3RvcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCAAAAAAAAAAAAwgBAAAAAAAAAAoKAoIGgAICMDACMDECMDICMDMCMDQCMDUCMDYCMDcCMDgCMDkCMGECMGICMGMCMGQCMGUCMGYCMTACMTECMTICMTMCMTQCMTUCMTYCMTcCMTgCMTkCMWECMWICMWMCMWQCMWUCMWYCMjACMjECMjICMjMCMjQCMjUCMjYCMjcCMjgCMjkCMmECMmICMmMCMmQCMmUCMmYCMzACMzECMzICMzMCMzQCMzUCMzYCMzcCMzgCMzkCM2ECM2ICM2MCM2QCM2UCM2YCNDACNDECNDICNDMCNDQCNDUCNDYCNDcCNDgCNDkCNGECNGICNGMCNGQCNGUCNGYCNTACNTECNTICNTMCNTQCNTUCNTYCNTcCNTgCNTkCNWECNWICNWMCNWQCNWUCNWYCNjACNjECNjICNjMCNjQCNjUCNjYCNjcCNjgCNjkCNmECNmICNmMCNmQCNmUCNmYCNzACNzECNzICNzMCNzQCNzUCNzYCNzcCNzgCNzkCN2ECN2ICN2MCN2QCN2UCN2YCODACODECODICODMCODQCODUCODYCODcCODgCODkCOGECOGICOGMCOGQCOGUCOGYCOTACOTECOTICOTMCOTQCOTUCOTYCOTcCOTgCOTkCOWECOWICOWMCOWQCOWUCOWYCYTACYTECYTICYTMCYTQCYTUCYTYCYTcCYTgCYTkCYWECYWICYWMCYWQCYWUCYWYCYjACYjECYjICYjMCYjQCYjUCYjYCYjcCYjgCYjkCYmECYmICYmMCYmQCYmUCYmYCYzACYzECYzICYzMCYzQCYzUCYzYCYzcCYzgCYzkCY2ECY2ICY2MCY2QCY2UCY2YCZDACZDECZDICZDMCZDQCZDUCZDYCZDcCZDgCZDkCZGECZGICZGMCZGQCZGUCZGYCZTACZTECZTICZTMCZTQCZTUCZTYCZTcCZTgCZTkCZWECZWICZWMCZWQCZWUCZWYCZjACZjECZjICZjMCZjQCZjUCZjYCZjcCZjgCZjkCZmECZmICZmMCZmQCZmUCZmYKAgEAAAEAAAIfBgAAAAAAAAAABwMOAEEBDAMMBAwCBwIMAQoCCgMjBB0FDg0EDgEOAAoCQgEUNEIAFDgACwIGAQAAAAAAAAAWDAIFCQsEAgEBAAAFLwYAAAAAAAAAAAcDDgBBAQwDDAQMAgoDBgIAAAAAAAAAGQYAAAAAAAAAACEEDgUQBwAnCgIKAyMELQUVDgAK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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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AQkAAQgNAQsIAQkAAQsCAQkAAQsBAQkAAQYIDgEFAgkABQMDAwMBBgkAAQYLBgEJAAELBgEJAAEGCwgBCQADBwsIAQkAAwcIDgELCQEJAAMLCAEIDQgLCAoBCwQBCQACCwgBCQAHCA4HCggHCAoICgMLDwEIBwgHAwEKCQABBgsPAQkAAgYLDwEJAAYJAAIJAAkBAQkBAgsFAQkBCQIDBwgLCQAJAQIHCw8BCQAJAAIGCAsJAAEGCQECBwsIAQkACwkBCQABCwUBCQECCAcHCw8BCAcCBwgLCQACBwsPAQkABgkAB0JhbGFuY2UEQ29pbgJJRAZPcHRpb24JUHVibGlzaGVyB1J1bGVLZXkDU1VJDlRyYW5zZmVyUG9saWN5EVRyYW5zZmVyUG9saWN5Q2FwFVRyYW5zZmVyUG9saWN5Q3JlYXRlZBdUcmFuc2ZlclBvbGljeURlc3Ryb3llZA9UcmFuc2ZlclJlcXVlc3QJVHhDb250ZXh0CFR5cGVOYW1lA1VJRAZWZWNTZXQDYWRkC2FkZF9yZWNlaXB0CGFkZF9ydWxlDmFkZF90b19iYWxhbmNlB2JhbGFuY2UGYm9ycm93BGNvaW4PY29uZmlybV9yZXF1ZXN0CGNvbnRhaW5zB2RlZmF1bHQGZGVsZXRlFGRlc3Ryb3lfYW5kX3dpdGhkcmF3DGRlc3Ryb3lfc29tZQtkdW1teV9maWVsZA1keW5hbWljX2ZpZWxkBGVtaXQFZW1wdHkFZXZlbnQHZXhpc3RzXwRmcm9tDGZyb21fYmFsYW5jZQxmcm9tX3BhY2thZ2UDZ2V0CGdldF9ydWxlCGhhc19ydWxlAmlkBmluc2VydAlpbnRvX2tleXMHaXNfc29tZQRpdGVtA25ldwtuZXdfcmVxdWVzdAZvYmplY3QGb3B0aW9uB3BhY2thZ2UEcGFpZAlwb2xpY3lfaWQDcHV0CHJlY2VpcHRzBnJlbW92ZQtyZW1vdmVfcnVsZQVydWxlcwZzZW5kZXIMc2hhcmVfb2JqZWN0BHNpemUDc3VpBHRha2UIdHJhbnNmZXIPdHJhbnNmZXJfcG9saWN5CnR4X2NvbnRleHQJdHlwZV9uYW1lA3VpZBB1aWRfbXV0X2FzX293bmVyDHVpZF90b19pbm5lcgV2YWx1ZQd2ZWNfc2V0CHdpdGhkcmF3BHplcm8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwgAAAAAAAAAAAMIAQAAAAAAAAADCAIAAAAAAAAAAwgDAAAAAAAAAAMIBAAAAAAAAAADCAUAAAAAAAAAAAIELQgKMwMjCAo2Cw8BCAcBAgMpCAsUCwgBCA05Cw8BCAcCAgIpCAs0CAoDAgEpCAoEAgEpCAoFAgEdAQAZAxkBGQIZBBkFNQABAAAEBgsACwELAjgAOQACAQEAABogCwA4AQQEBQgLAQEGAAAAAAAAAAAnCgERIQwFDgURIgwGCgY5ATgCCwUMAjgADAM4AwwECwILBAsDOQILAREhCwY5AwICAAQAIQsLAAoBOAQMAjgFCwILAS4RJjgGAgMBAAAmMQoALjgHCwE3ABQhBAkFDwsAAQsDAQcEJw4COAgEJQsCOAkMBgoGCgA3ATgKJQQcBSILAAELAwEHBScLBgwEBSkKADcBOAoMBAsEDAULADYBCwULAzgLAgQBAAAtHg4AOAcOATcAFCEECAUMCwIBBwQnCwE6AwwFDAQLADoCAQwDER8LBBEfCwU5BDgMCwMLAjgNAgUBAAAwNAsBOgAMBgwDDAUMBAsGOA4MAg4CQRcMCAoICgA3AjgPIQQTBRcLAAEHACcKCAYAAAAAAAAAACQELgUcDQJFFwwHCgA3Ag4HOBAEJQUpCwABBwEnCwgGAQAAAAAAAAAXDAgFFwsAAQsECwULAwIGAQAABCIKAS44BwsCNwAUIQQJBQ0LAQEHBCcKAS44ESAEEwUXCwEBBwMnCgE2Awk5BQsDOBILATYCOBM4FAIHAQAABAYLATcDCTkFOBUCCAEAAAQOCgEuOBEEBQUJCwEBBwInCwE2AQsCOBYCCQEAAAQFCwE2BDgTOBQCCgEAAAQGCwA3Awk5BTgXAgsBAAA9HAoALjgHCwE3ABQhBAkFDQsAAQcEJwoANgMJOQU4GAELADYCDAM4EwwCCwMOAjgZAgwBAAAEAwsANwMCDQEAAAQQCgAuOAcLATcAFCEECQUNCwABBwQnCwA2AwIOAQAABAMLADcCAg8BAAAEBAsANwUUAhABAAAEBAsANwYUAhEBAAAEBAsANwcUAgIBAQEBAgEAAAMAAAABAAIAGQEZAhkDGQQZBRkGGQcZABNhdXRoZW50aWNhdG9yX3N0YXRlwxehHOsLBgAAAAsBABACECYDNo4BBMQBHAXgAbACB5AE3QMI7QdABq0IRArxCDQMpQnNDQ3yFhoADQErAS4AFAAmACoAMAAxAAEIAAACBAAAAwcAAAQHAAAABwABBQcBAAACBgcABQgEAAcHAgAACQABAAAeAgEAACADAQAALwQBAAAhAAEAABIFBgAAJQcIAAAkCQoAABELBgAAMgwGAAATDQ0AABcOBgAAGQ8NAAEOKiwBAAEPLS4BAAEYKwYBAAEbKgEBAAEpBikBAAIQEhMAAwsaBgIHBAMOISICBwQDDx4fAgcEBCclJgAFDQYYAAYtHAYBCAcsBRYAExkYGxUZFBkRKBAoDygNKA4oETAQMA8wDTAOMAIGCAQGCAQBAQIGCAIGCAICBggDBggDAgYIBgYIBgEGCAgAAQcIAAEHCAEBBggAAQYIAQEGCggEAwcIAAoIBAYICAEKCAQDBwgAAwYICAIGCAAGCAgDAQEBBwEBAgYKAgIGCgIDAQYIBgEGCgIBAgMIAQgAAwEFAQgEAQgHAgMIAQMHCAcJAAkBAQgAAQkAAgcIAQMCBwgHCQABBwkBAgYIAQMCBggHCQABBgkBAwYIBAYIBAMLAQMDBwgBAwgECggEAwYIBAYIBAoIBAIDAwEDBAMGCAQLBQEIAwoIBAEIAwELBQEJAAEGCwUBCQACBwsFAQkACQABBgkAAQcLBQEJAAEHCQAQAQMGCAQGCAYGCAYDAwcIAQoDAwYIBAMKCAQLBQEIBgsFAQgGBwMBCAYJQWN0aXZlSndrEkF1dGhlbnRpY2F0b3JTdGF0ZRdBdXRoZW50aWNhdG9yU3RhdGVJbm5lcgNKV0sFSndrSWQGT3B0aW9uBlN0cmluZwlUeENvbnRleHQDVUlEEGFjdGl2ZV9qd2tfZXF1YWwLYWN0aXZlX2p3a3MDYWRkA2FsZxNhdXRoZW50aWNhdG9yX3N0YXRlBmJvcnJvdwpib3Jyb3dfbXV0BWJ5dGVzDGNoZWNrX3NvcnRlZAZjcmVhdGULZGVkdXBsaWNhdGUNZHluYW1pY19maWVsZAFlBWVwb2NoC2V4cGlyZV9qd2tzBGZpbGwPZ2V0X2FjdGl2ZV9qd2tzAmlkB2lzX25vbmUDaXNzA2p3awlqd2tfZXF1YWwGandrX2lkDGp3a19pZF9lcXVhbAZqd2tfbHQDa2lkA2t0eQpsb2FkX2lubmVyDmxvYWRfaW5uZXJfbXV0BG1hdGgDbWF4AW4Ebm9uZQZvYmplY3QGb3B0aW9uBnNlbmRlcgxzaGFyZV9vYmplY3QGc3RyaW5nD3N0cmluZ19ieXRlc19sdAh0cmFuc2Zlcgp0eF9jb250ZXh0GnVwZGF0ZV9hdXRoZW50aWNhdG9yX3N0YXRlB3ZlcnNpb24AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAwgAAAAAAAAAAAMIAQAAAAAAAAADCAIAAAAAAAAABSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoDAQAAAgIaCAczAwECAjMDCgoIBAICBCMIBhUIBigIBgwIBgMCAhwIBiIIBgQCAx8IAx0IAhYDAAAAAAEVCgAQAAoBEAARAQQNCwAQAQsBEAERAgwCBRMLAQELAAEJDAILAgIBAAAAECsKABACCgEQAiEEDQoAEAMKARADIQwCBQ8JDAILAgQYCgAQBAoBEAQhDAMFGgkMAwsDBCMLABAFCwEQBSEMBAUpCwEBCwABCQwECwQCAgAAAAEVCgAQBgoBEAYhBA0LABAHCwEQByEMAgUTCwEBCwABCQwCCwICAwAAABFVCwAREgwFCwEREgwHCgVBFAoHQRQjBBMLBwELBQEIDAMFUwoFQRQKB0EUJAQgCwcBCwUBCQwCBVEGAAAAAAAAAAAMCAoICgVBFCMESwUoCgUKCEIUFAwECgcKCEIUFAwGCgQKBiMEPAsHAQsFAQgCCwQLBiQERgsHAQsFAQkCCwgGAQAAAAAAAAAWDAgFIgsHAQsFAQkMAgsCDAMLAwIEAAAABlgKABABEAYKARABEAYiBBALABABEAYLARABEAYRAwIKABABEAcKARABEAciBCALABABEAcLARABEAcRAwIKABAAEAIKARAAEAIiBDALABAAEAILARAAEAIRAwIKABAAEAMKARAAEAMiBEALABAAEAMLARAAEAMRAwIKABAAEAQKARAAEAQiBFALABAAEAQLARAAEAQRAwILABAAEAULARAAEAURAwIFAAAAFRoLABEZBwMhBAYFCAcAJwcBDAMKA0AXAAAAAAAAAAASAQwBERcKAxIADAINAg8ICwMLATgACwI4AQIGAAAAHSEKABAJFAwCCgIHASEECQUNCwABBwEnCgAPCAsAEAkUOAIMAQoBEAoUCwIhBBsFHwsBAQcBJwsBAgcAAAAgIQoAEAkUDAIKAgcBIQQJBQ0LAAEHAScKABAICwAQCRQ4AwwBCgEQChQLAiEEGwUfCwEBBwEnCwECCAAAACMlBgAAAAAAAAAADAMKAwoAQRcGAQAAAAAAAAAXIwQiBQoKAAoDQhcMAQoACgMGAQAAAAAAAAAWQhcMAgsBCwIRBAQZBR0LAAEHAicLAwYBAAAAAAAAABYMAwUCCwABAgkAAAAkqAELAhEZBwMhBAYFCgsAAQcAJw4BEQgLAREKDAkLABEGDAZAFwAAAAAAAAAADA0GAAAAAAAAAAAMBQYAAAAAAAAAAAwHCgYQC0EXDAQOCUEXDAoKBQoEIwQpBSQKBwoKIwwDBSsJDAMLAwSCAQoGEAsKBUIXDAwOCQoHQhcMCwoMCgsRAARTCgwUDAgLDBAMFAsLEAwUERYNCA8MFQ0NCwhEFwsFBgEAAAAAAAAAFgwFCwcGAQAAAAAAAAAWDAcFgQEKDBABCgsQARECBGgLCwENDQsMFEQXCwUGAQAAAAAAAAAWDAULBwYBAAAAAAAAABYMBwWBAQoMCgsRBAR3CwsBDQ0LDBREFwsFBgEAAAAAAAAAFgwFBYEBCwwBDQ0LCxREFwsHBgEAAAAAAAAAFgwHBR8KBQoEIwSTAQWHAQ0NCgYQCwoFQhcURBcLBQYBAAAAAAAAABYMBQWCAQoHCgojBKMBBZgBDQ0OCQoHQhcURBcLBwYBAAAAAAAAABYMBwWTAQsNCwYPCxUCCgAAACc3QBcAAAAAAAAAAAwEBgAAAAAAAAAADAE4BAwDCgEOAEEXIwQ1BQwOAAoBQhcMAg4DOAUEGQ0DCgIQARQ4BgUsDgM4BwoCEAERAgQmCwIBCwEGAQAAAAAAAAAWDAEFBgoCEAEUDQM4CBUNBAsCFEQXCwEGAQAAAAAAAAAWDAEFBgsEAgsAAAAvrAELAhEZBwMhBAYFCgsAAQcAJwsAEQYMCgoKEAtBFwwOBwQMCwYAAAAAAAAAAAwIOAkMEAoICg4jBFoFHAoKEAsKCEIXDAUKBRABEAYMBg4QOAoEMg0QCwYUOAsNCwsFEAwURCYFVQoGDhA4DCEESwsGAQ4LQSYGAQAAAAAAAAAXDAQNCwsEQyYMEgoSFAsFEAwUERYLEhUFVQsGFA0QOA0VDQsLBRAMFEQmCwgGAQAAAAAAAAAWDAgFF0AXAAAAAAAAAAAMDzgJDBEGAAAAAAAAAAAMCQYAAAAAAAAAAAwMCgkKDiMEpwEFZwoKEAsKCUIXDA0KDRABEAYMBw4ROAoEeA0RCwcUOAsFiQEKBw4ROAwiBIcBCwcUDRE4DRULDAYBAAAAAAAAABYMDAWJAQsHAQ4LCgxCJhQKASMEkwEIDAMFmQEKDRAMFAoBJgwDCwMEoAENDwsNFEQXBaIBCw0BCwkGAQAAAAAAAAAWDAkFYgsPCwoPCxUCDAAAAAYPCwERGQcDIQQGBQoLAAEHACcLABEHEAsUAgQBBAACAAIBAgICAwMAAwEAAAABAQABAQQCABN6a2xvZ2luX3ZlcmlmaWVkX2lkyAShHOsLBgAAAAoBAAgCCBADGDIFSj4HiAHJAQjRAkAGkQMKCpsDFAyvA2ANjwQKABEBDgAMAA8AAwgAAQAHAAICBAADAQIAAA0AAQAACgACAAALAAIAAAkAAgAABAACAAAHAwQAABAFBAAABQYHAAAGCAcAAgcJBAABBggAAQUBBggBAQgAAAYIAQgBCAEIAQ8HCAMGBQYIAQYIAQYIAQYIAQ8BAQYFBgoCBgoCBgoCBgoCDwEIAgZTdHJpbmcJVHhDb250ZXh0A1VJRApWZXJpZmllZElECGF1ZGllbmNlEGNoZWNrX3prbG9naW5faWQZY2hlY2tfemtsb2dpbl9pZF9pbnRlcm5hbAZkZWxldGUCaWQGaXNzdWVyDmtleV9jbGFpbV9uYW1lD2tleV9jbGFpbV92YWx1ZQZvYmplY3QFb3duZXIGc3RyaW5nCnR4X2NvbnRleHQRdmVyaWZ5X3prbG9naW5faWQTemtsb2dpbl92ZXJpZmllZF9pZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCAAAAAAAAAAAAAIGCAgCDQUKCAELCAEJCAEECAEAAQAABAQLABAAFAIBAQAABAMLABABAgIBAAAEAwsAEAICAwEAAAQDCwAQAwIEAQAABAMLABAEAgUBAAAECQsAEwABAQEBAREJAgYBAAAEAgcAJwcBAAAEAgcAJwgAAgAAAQACAAMABAAFABRkeW5hbWljX29iamVjdF9maWVsZNkHoRzrCwYAAAAKAQAIAggUAxyKAQSmARoFwAGDAQfDArICCPUEQAq1BQYLuwUCDL0F6QEACwEWAAoAFQADBwEAAAEBBwEAAAMABwADAgQAAAQAAQIHDAAGAgMCBwwACQQFAgcMABcEBgIHDAAMAgcBBwANAgcCBwwAEQIIAQcBFAEYAQABGQoYAQACBAABAgcEAgUSAQEIAgcPCwEIAggTFAEIAg0CBwIHBAIOAg8BBwIPBBMBBwIQFgcBCAIXBAYCBwQCGBYKAQgDEQsMAQgDEhEMAAMaEBEAEwYJDQ4OCgYLBg8ODAYSBhENDQ0QBgcMCAwDBwgDCQAJAQACBggDCQABBgkBAgcIAwkAAQcJAQEJAQEBAQsBAQgCAwsAAQkACAILAAEJAAEJAAEGCQABCAICCwABCQAIAgELAAEJAAIGCAMFAQYIAwEFAgUJAAIHCAMFAQcJAAQLAAEJAAsAAQkACQEFAgUFAgsAAQkABQELAQEJAAJJRAZPcHRpb24DVUlEB1dyYXBwZXIDYWRkEGFkZF9jaGlsZF9vYmplY3QGYm9ycm93E2JvcnJvd19jaGlsZF9vYmplY3QXYm9ycm93X2NoaWxkX29iamVjdF9tdXQKYm9ycm93X211dA1keW5hbWljX2ZpZWxkFGR5bmFtaWNfb2JqZWN0X2ZpZWxkB2V4aXN0c18QZXhpc3RzX3dpdGhfdHlwZQpmaWVsZF9pbmZvDmZpZWxkX2luZm9fbXV0GGhhc19jaGlsZF9vYmplY3Rfd2l0aF90eQJpZA9pZF9mcm9tX2FkZHJlc3MEbmFtZQRub25lBm9iamVjdAZvcHRpb24GcmVtb3ZlE3JlbW92ZV9jaGlsZF9vYmplY3QEc29tZQ51aWRfdG9fYWRkcmVzcwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAgETCQAACgABAAAJFQsBOQAMBQ4COAAMBAoACgULBDgBCwALBQwDLgsDOAIBERULAjgDAgEBAAAOCAsBOQAMAgsACwI4AjgEAgIBAAAOCAsBOQAMAgsACwI4BTgGAgMBAAAVFAsBOQAMAwoACgMMAi4LAjgCDAURFQsFOAcMBAsACwM4CAELBAIEAQAADgcLATkADAILAAsCOAkCBQEAABcUCwE5AAwCCgAKAjgJIAQMCwABCQILAAsCOAIMAxEVCwM4CgIGAQAAFxULATkADAIKAAoCOAkgBAwLAAE4CwILAAsCOAIMAwELAxEUOAwCABd6a2xvZ2luX3ZlcmlmaWVkX2lzc3VlctcEoRzrCwYAAAALAQAKAgoQAxo4BFICBVQ2B4oBzwEI2QJABpkDFAqtAwsMuANpDaEEBAASAQ4ACwAPABAAAwgAAQAHAAICBAAEAQIAAAwAAQAACQACAAAHAwQAABEFBAAABQYHAAAGCAcAAQQCDQACBwkEAAIKCwkAAw8MBAEIBA0KAQAJAwEGCAABBQEGCAEBCAAAAw8IAQcIAwMFDwYIAQEBAwUPBgoCAQgCAQYIAwEHCAMCCQAFAQYKAgZTdHJpbmcJVHhDb250ZXh0A1VJRA5WZXJpZmllZElzc3VlcgVieXRlcxRjaGVja196a2xvZ2luX2lzc3Vlch1jaGVja196a2xvZ2luX2lzc3Vlcl9pbnRlcm5hbAZkZWxldGUCaWQGaXNzdWVyA25ldwZvYmplY3QFb3duZXIGc2VuZGVyBnN0cmluZwh0cmFuc2Zlcgp0eF9jb250ZXh0FXZlcmlmeV96a2xvZ2luX2lzc3Vlchd6a2xvZ2luX3ZlcmlmaWVkX2lzc3VlcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCAAAAAAAAAAAAwgBAAAAAAAAAAACAwgIAgwFCQgBAAEAAAQECwAQABQCAQEAAAQDCwAQAQICAQAABAYLABMAAQERBwIDAQAAARYKAi4RCgwDCgMLAA4BEQQECgUOCwIBBwEnCwIRCAoDCwESAAsDOAACBAEAAAQGCwALAQsCEQYRBQIFAAIAAAEAAgBVCnR4X2NvbnRleHQJVHhDb250ZXh0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGb2JqZWN0AklEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGb2JqZWN0A1VJRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCHRyYW5zZmVyCVJlY2VpdmluZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDWR5bmFtaWNfZmllbGQFRmllbGQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhNhdXRoZW50aWNhdG9yX3N0YXRlEkF1dGhlbnRpY2F0b3JTdGF0ZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE2F1dGhlbnRpY2F0b3Jfc3RhdGUXQXV0aGVudGljYXRvclN0YXRlSW5uZXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhNhdXRoZW50aWNhdG9yX3N0YXRlA0pXSwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACE2F1dGhlbnRpY2F0b3Jfc3RhdGUFSndrSWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhNhdXRoZW50aWNhdG9yX3N0YXRlCUFjdGl2ZUp3awAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA2JhZwNCYWcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgdiYWxhbmNlBlN1cHBseQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACB2JhbGFuY2UHQmFsYW5jZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA2JjcwNCQ1MAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAglncm91cF9vcHMHRWxlbWVudAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCGJsczEyMzgxBlNjYWxhcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACCGJsczEyMzgxAkcxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIIYmxzMTIzODECRzIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAghibHMxMjM4MQJHVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBmJvcnJvdwhSZWZlcmVudAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBmJvcnJvdwZCb3Jyb3cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgVjbG9jawVDbG9jawAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACA3VybANVcmwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgd2ZWNfc2V0BlZlY1NldAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBXRhYmxlBVRhYmxlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIJZGVueV9saXN0CERlbnlMaXN0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIJZGVueV9saXN0C1BlclR5cGVMaXN0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEY29pbgRDb2luAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIEY29pbgxDb2luTWV0YWRhdGEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgRjb2luFVJlZ3VsYXRlZENvaW5NZXRhZGF0YQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBGNvaW4LVHJlYXN1cnlDYXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgRjb2luB0RlbnlDYXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgRjb2luD0N1cnJlbmN5Q3JlYXRlZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACB3ZlY19tYXAGVmVjTWFwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIHdmVjX21hcAVFbnRyeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACB3BhY2thZ2UJUHVibGlzaGVyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIHcGFja2FnZQpVcGdyYWRlQ2FwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIHcGFja2FnZQ1VcGdyYWRlVGlja2V0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIHcGFja2FnZQ5VcGdyYWRlUmVjZWlwdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACB2Rpc3BsYXkHRGlzcGxheQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACB2Rpc3BsYXkORGlzcGxheUNyZWF0ZWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgdkaXNwbGF5DlZlcnNpb25VcGRhdGVkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIUZHluYW1pY19vYmplY3RfZmllbGQHV3JhcHBlcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACB2dyb3RoMTYFQ3VydmUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgdncm90aDE2FFByZXBhcmVkVmVyaWZ5aW5nS2V5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIHZ3JvdGgxNhFQdWJsaWNQcm9vZklucHV0cwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACB2dyb3RoMTYLUHJvb2ZQb2ludHMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgNzdWkDU1VJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIPdHJhbnNmZXJfcG9saWN5D1RyYW5zZmVyUmVxdWVzdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD3RyYW5zZmVyX3BvbGljeQ5UcmFuc2ZlclBvbGljeQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD3RyYW5zZmVyX3BvbGljeRFUcmFuc2ZlclBvbGljeUNhcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACD3RyYW5zZmVyX3BvbGljeRVUcmFuc2ZlclBvbGljeUNyZWF0ZWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg90cmFuc2Zlcl9wb2xpY3kXVHJhbnNmZXJQb2xpY3lEZXN0cm95ZWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg90cmFuc2Zlcl9wb2xpY3kHUnVsZUtleQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBWtpb3NrBUtpb3NrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFa2lvc2sNS2lvc2tPd25lckNhcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBWtpb3NrC1B1cmNoYXNlQ2FwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFa2lvc2sGQm9ycm93AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFa2lvc2sESXRlbQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBWtpb3NrB0xpc3RpbmcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgVraW9zawRMb2NrAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFa2lvc2sKSXRlbUxpc3RlZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBWtpb3NrDUl0ZW1QdXJjaGFzZWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgVraW9zawxJdGVtRGVsaXN0ZWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg9raW9za19leHRlbnNpb24JRXh0ZW5zaW9uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIPa2lvc2tfZXh0ZW5zaW9uDEV4dGVuc2lvbktleQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDGxpbmtlZF90YWJsZQtMaW5rZWRUYWJsZQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDGxpbmtlZF90YWJsZQROb2RlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIKb2JqZWN0X2JhZwlPYmplY3RCYWcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgxvYmplY3RfdGFibGULT2JqZWN0VGFibGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAg5wcmlvcml0eV9xdWV1ZQ1Qcmlvcml0eVF1ZXVlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIOcHJpb3JpdHlfcXVldWUFRW50cnkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgl2ZXJzaW9uZWQJVmVyc2lvbmVkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIJdmVyc2lvbmVkEFZlcnNpb25DaGFuZ2VDYXAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgZyYW5kb20GUmFuZG9tAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIGcmFuZG9tC1JhbmRvbUlubmVyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIJdGFibGVfdmVjCFRhYmxlVmVjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFdG9rZW4FVG9rZW4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgV0b2tlbg5Ub2tlblBvbGljeUNhcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBXRva2VuC1Rva2VuUG9saWN5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIFdG9rZW4NQWN0aW9uUmVxdWVzdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBXRva2VuB1J1bGVLZXkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgV0b2tlbhJUb2tlblBvbGljeUNyZWF0ZWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhN6a2xvZ2luX3ZlcmlmaWVkX2lkClZlcmlmaWVkSUQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhd6a2xvZ2luX3ZlcmlmaWVkX2lzc3Vlcg5WZXJpZmllZElzc3VlcgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAAAAAAAAAMAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAQAAAAAAAAALB2dlbmVzaXOAE6Ec6wsGAAAACwEAGgIaOgNUawS/AQ4FzQGwAgf9A4kJCIYNYAbmDRQK+g1dDNcO2wMNshIYAB8BLAFIAhICFAIrAj0CQgA2AD8AQABDAEYAAwMAAAIDAAAJAAAACAAAAQQHAQAAAwAEAQABBAEMAQABBQsEAAYFAgAHCgIACAYEAAoHBAALDAQAABYAAQAADwIBAAAOAwEAARkZGgEAASQYDgEAAiMWDgEAAxoJAQEAAzUICQEAA0sBCQEABB0eHwEABkEgAQAHGwUGAAgWERIACRYTAQAKFw8QAAsNIgEACyoMCgALNB0BAAwgGxwADCINDgAHBwgHBRUEFwMXCQcGBwYIBwsFAQgICAEKCAAIAgcICQAECwUBCAgKCAMHCggMBwgJAQcKCAwZCggDAwMKAgMDCgIKAgoCCgIKAgoCCgIKAgoCCAoDCwUBCAgLBQEICAUICwgMCggMCgIKAgEGCAkBAwEICAIHCwUBCQADAQsFAQkAAQgMAQgAEAUKAgoCCgIKAgoCCgIKAgoCCgIKAgoCCgIDAwcICQIGCggMBggMAQEIAwMDAwMDAwcICQEICwULBQEICAMDDQcICQEICggIBwoIDAsFAQgIAwMICwgKBwgJBQsFAQgIAwULBAEFBQEIAwEGCgkAAQUBBgsEAQkAAQsEAQkAAQkAAgcKCAwFAQcIDAQHCAwLBQEICAUHCAkCCwUBCQAHCAkBCwYBCQACCwYBCAgFAgMDAgcIDAMHQmFsYW5jZQRDb2luFkdlbmVzaXNDaGFpblBhcmFtZXRlcnMYR2VuZXNpc1ZhbGlkYXRvck1ldGFkYXRhBk9wdGlvbgNTVUkMU3Rha2VTdWJzaWR5EFN5c3RlbVBhcmFtZXRlcnMPVG9rZW5BbGxvY2F0aW9uGVRva2VuRGlzdHJpYnV0aW9uU2NoZWR1bGUJVHhDb250ZXh0A1VJRAlWYWxpZGF0b3IIYWN0aXZhdGUTYWN0aXZhdGVfdmFsaWRhdG9ycw9hbGxvY2F0ZV90b2tlbnMLYWxsb2NhdGlvbnMLYW1vdW50X21pc3QHYmFsYW5jZRhjaGFpbl9zdGFydF90aW1lc3RhbXBfbXMEY29pbg9jb21taXNzaW9uX3JhdGUGY3JlYXRlGGNyZWF0ZV9zeXN0ZW1fcGFyYW1ldGVycwtkZXNjcmlwdGlvbgxkZXN0cm95X3NvbWUMZGVzdHJveV96ZXJvBWVwb2NoEWVwb2NoX2R1cmF0aW9uX21zDGZyb21fYmFsYW5jZQlnYXNfcHJpY2UHZ2VuZXNpcxFnZXRfdmFsaWRhdG9yX211dAlpbWFnZV91cmwWaXNfZHVwbGljYXRlX3ZhbGlkYXRvcghpc19lbXB0eQdpc19zb21lE21heF92YWxpZGF0b3JfY291bnQbbWluX3ZhbGlkYXRvcl9qb2luaW5nX3N0YWtlBG5hbWUPbmV0d29ya19hZGRyZXNzEm5ldHdvcmtfcHVibGljX2tleQNuZXcGb2JqZWN0Bm9wdGlvbgtwMnBfYWRkcmVzcw9wcmltYXJ5X2FkZHJlc3MLcHJvamVjdF91cmwTcHJvb2Zfb2ZfcG9zc2Vzc2lvbhNwcm90b2NvbF9wdWJsaWNfa2V5EHByb3RvY29sX3ZlcnNpb24RcmVjaXBpZW50X2FkZHJlc3MccmVxdWVzdF9hZGRfc3Rha2VfYXRfZ2VuZXNpcwVzcGxpdA1zdGFrZV9zdWJzaWR5G3N0YWtlX3N1YnNpZHlfZGVjcmVhc2VfcmF0ZRdzdGFrZV9zdWJzaWR5X2Z1bmRfbWlzdClzdGFrZV9zdWJzaWR5X2luaXRpYWxfZGlzdHJpYnV0aW9uX2Ftb3VudBtzdGFrZV9zdWJzaWR5X3BlcmlvZF9sZW5ndGgZc3Rha2Vfc3Vic2lkeV9zdGFydF9lcG9jaBVzdGFrZWRfd2l0aF92YWxpZGF0b3IDc3VpC3N1aV9hZGRyZXNzCnN1aV9zeXN0ZW0Wc3VpX3N5c3RlbV9zdGF0ZV9pbm5lcgh0cmFuc2Zlcgp0eF9jb250ZXh0CXZhbGlkYXRvciB2YWxpZGF0b3JfbG93X3N0YWtlX2dyYWNlX3BlcmlvZB12YWxpZGF0b3JfbG93X3N0YWtlX3RocmVzaG9sZA12YWxpZGF0b3Jfc2V0InZhbGlkYXRvcl92ZXJ5X2xvd19zdGFrZV90aHJlc2hvbGQGdmVjdG9yDndvcmtlcl9hZGRyZXNzEXdvcmtlcl9wdWJsaWNfa2V5BHplcm8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDCAAAAAAAAAAAAwgBAAAAAAAAAAACDycKAhgKAiEKAi8KAj4FHgMVAzEKAjAKAikK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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/AAIDGAMw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AQAAGQ8NAQAAIQwDAQAAKw8DAQAACAYQAQAACwYQAQAAEhESAQABIB8QAQABJyANAQABLBwQAQABLgMNAQACHB4KAQADGhQDAAMkABQAAyoVBQAEDSEDAgcEBBAZGwIHBAQRIiMCBwQEFRkaAgcEBCQAHQIHBBsYGRgSFxwYCBcUFw8XEBcRFwkXChcHFxMXGBgaGAEHCAcBCAECBggBBwgHAAEGCAEBBQIGCwIBCQAFAgMDAQsCAQkABAcLAgEJAAMGCAEHCAcBCwQBCQADBwsCAQkABQsDAQkAAwcLAgEJAAYIAQMBCwMBCQADBwsCAQkABggBCwMBCQADBwsCAQkABQMBAwIHCwIBCQAFAQcLAAEJAAIIBQUBCAUBBggFAwYLAAEJAAMDAQkAAgULAAEJAAIGCwYCCQAJAQkAAQEBBgkBAQYLAwEJAAELBgIJAAkBAgsDAQkABwgHAgcLAwEJAAsDAQkAAgcLAwEJAAMDBwsGAgkACQEJAAkBAgcLBgIJAAkBCQABBwkBB0FjY291bnQKQWNjb3VudENhcAdCYWxhbmNlBENvaW4JQ3VzdG9kaWFuBVRhYmxlCVR4Q29udGV4dANVSUQZYWNjb3VudF9hdmFpbGFibGVfYmFsYW5jZQ9hY2NvdW50X2JhbGFuY2UQYWNjb3VudF9iYWxhbmNlcxZhY2NvdW50X2xvY2tlZF9iYWxhbmNlDWFjY291bnRfb3duZXIDYWRkEWF2YWlsYWJsZV9iYWxhbmNlB2JhbGFuY2UGYm9ycm93CmJvcnJvd19tdXQaYm9ycm93X211dF9hY2NvdW50X2JhbGFuY2UHY2xvYl92MgRjb2luCGNvbnRhaW5zGGNyZWF0ZV9jaGlsZF9hY2NvdW50X2NhcAxjdXN0b2RpYW5fdjIfZGVjcmVhc2VfdXNlcl9hdmFpbGFibGVfYmFsYW5jZRxkZWNyZWFzZV91c2VyX2xvY2tlZF9iYWxhbmNlBmRlbGV0ZRJkZWxldGVfYWNjb3VudF9jYXAMZnJvbV9iYWxhbmNlAmlkH2luY3JlYXNlX3VzZXJfYXZhaWxhYmxlX2JhbGFuY2UcaW5jcmVhc2VfdXNlcl9sb2NrZWRfYmFsYW5jZQRqb2luDGxvY2tfYmFsYW5jZQ5sb2NrZWRfYmFsYW5jZRBtaW50X2FjY291bnRf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YXRhD0N1cnJlbmN5Q3JlYXRlZAdEZW55Q2FwCERlbnlMaXN0AklEBk9wdGlvbhVSZWd1bGF0ZWRDb2luTWV0YWRhdGEGU3RyaW5nBlN1cHBseQtUcmVhc3VyeUNhcAlUeENvbnRleHQIVHlwZU5hbWUDVUlEA1VybANhZGQFYXNjaWkHYmFsYW5jZQtiYWxhbmNlX211dARidXJuBGNvaW4UY29pbl9tZXRhZGF0YV9vYmplY3QIY29udGFpbnMPY3JlYXRlX2N1cnJlbmN5GWNyZWF0ZV9yZWd1bGF0ZWRfY3VycmVuY3kNY3JlYXRlX3N1cHBseQhkZWNpbWFscw9kZWNyZWFzZV9zdXBwbHkGZGVsZXRlD2RlbnlfY2FwX29iamVjdAlkZW55X2xpc3QNZGVueV9saXN0X2FkZBJkZW55X2xpc3RfY29udGFpbnMQZGVueV9saXN0X3JlbW92ZQtkZXNjcmlwdGlvbgxkZXN0cm95X3plcm8NZGl2aWRlX2ludG9fbg1mcmVlemVfb2JqZWN0DGZyb21fYmFsYW5jZQxnZXRfZGVjaW1hbHMPZ2V0X2Rlc2NyaXB0aW9uDGdldF9pY29uX3VybAhnZXRfbmFtZQpnZXRfc3ltYm9sFWdldF93aXRoX29yaWdpbmFsX2lkcwhpY29uX3VybAJpZA9pbmNyZWFzZV9zdXBwbHkMaW50b19iYWxhbmNlCmludG9fYnl0ZXMLaW50b19zdHJpbmcTaXNfb25lX3RpbWVfd2l0bmVzcwxpc19wcmltaXRpdmUEam9pbgRtaW50EW1pbnRfYW5kX3RyYW5zZmVyDG1pbnRfYmFsYW5jZQRuYW1lA25ldwpuZXdfdW5zYWZlBm9iamVjdAZvcHRpb24PcHVibGljX3RyYW5zZmVyA3B1dAZyZW1vdmUEc29tZQVzcGxpdAZzdHJpbmcGc3VwcGx5DHN1cHBseV9pbW11dApzdXBwbHlfbXV0DHN1cHBseV92YWx1ZQZzeW1ib2wEdGFrZQx0b3RhbF9zdXBwbHkIdHJhbnNmZXIUdHJlYXN1cnlfaW50b19zdXBwbHkKdHhfY29udGV4dAl0eXBlX25hbWUFdHlwZXMSdXBkYXRlX2Rlc2NyaXB0aW9uD3VwZGF0ZV9pY29uX3VybAt1cGRhdGVfbmFtZQ11cGRh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CQABCQECCA0IDQMHCA4JAAkBAQsHAQkAAQsJAQkAAgkAAwEGCwwBCQACCAYBAgcLCwEJAAsMAQkAAQsIAQkAAwgNAwgNAQsRAQkABggNCA0DCA0IDggNBQgNCA0IDQMDAwgOCA0IDQMDAwMBBgsKAQkAAQsKAQkAAQYLCwEJAAMHCwsBCQADBwgSAQgEAgYIDgkAAQgGAQgFAQYJAQEHCQEHQmFsYW5jZQZCb3Jyb3cEQ29pbgJJRARJdGVtDEl0ZW1EZWxpc3RlZApJdGVtTGlzdGVkDUl0ZW1QdXJjaGFzZWQFS2lvc2sNS2lvc2tPd25lckNhcAdMaXN0aW5nBExvY2sGT3B0aW9uC1B1cmNoYXNlQ2FwA1NVSQ5UcmFuc2ZlclBvbGljeQ9UcmFuc2ZlclJlcXVlc3QJVHhDb250ZXh0A1VJRANhZGQQYWxsb3dfZXh0ZW5zaW9ucwdiYWxhbmNlBmJvcnJvdwpib3Jyb3dfbXV0CmJvcnJvd192YWwSY2xvc2VfYW5kX3dpdGhkcmF3BGNvaW4HZGVmYXVsdAZkZWxldGUGZGVsaXN0DGRlc3Ryb3lfc29tZQ1keW5hbWljX2ZpZWxkFGR5bmFtaWNfb2JqZWN0X2ZpZWxkBGVtaXQFZXZlbnQHZXhpc3RzXxBleGlzdHNfd2l0aF90eXBlA2Zvcgxmcm9tX2JhbGFuY2UKaGFzX2FjY2VzcwhoYXNfaXRlbRJoYXNfaXRlbV93aXRoX3R5cGUCaWQMaXNfZXhjbHVzaXZlCWlzX2xpc3RlZBVpc19saXN0ZWRfZXhjbHVzaXZlbHkJaXNfbG9ja2VkB2lzX3NvbWUKaXRlbV9jb3VudAdpdGVtX2lkBWtpb3NrD2tpb3NrX2V4dGVuc2lvbghraW9za19pZBNraW9za19vd25lcl9jYXBfZm9yBGxpc3QWbGlzdF93aXRoX3B1cmNoYXNlX2NhcARsb2NrDWxvY2tfaW50ZXJuYWwJbWluX3ByaWNlA25ldwtuZXdfcmVxdWVzdAZvYmplY3QGb3B0aW9uBW93bmVyBXBsYWNlDnBsYWNlX2FuZF9saXN0DnBsYWNlX2ludGVybmFsBXByaWNlB3Byb2ZpdHMOcHJvZml0c19hbW91bnQLcHJvZml0c19tdXQIcHVyY2hhc2URcHVyY2hhc2VfY2Fw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CwIBCQABCA4BCQABCwkBCQACCAcLEQEICAELEAIJAAkBAQYJAAEIDQELBQEJAAEGCwkBCQACCQAFAQsGAQkACQgHAwsGAQULBgELCQEJAAcIDwsLAQkAAwsJAQkACA4CCwkBCQAHCA8BCwsBCQAHCAcDCwYBBQsGAQsJAQkABwgPCwABCQADAQYLCwEJAAILCQEJAAgOAgcLCQEJAAsJAQkAAgcLCQEJAAMBBggPBggHAwsGAQULBgELCQEJAAULEQEICAEICAELEQEJAAsKCAgDCxEBCAgDCAcLBgEFBggIBgoICAMFCwYBCwkBCQABBgsGAQkAAgYLEAIJAAkBBgkAAQYJAQEKCQACBgsRAQkABgkAAgsDAQkABwgPAQcLBgEJAAUDCAcLBgEFBQsGAQsJAQkAAQcLDAEJAAEHCwoBCQACBwsKAQkACwkBCQABCQECBwsRAQkACQACCwQBCQEJAgMHCA4JAAkBAwkACQEJAgIGCA4JAAIHCA4JAAEHCQEBCwQBCQEDBwsQAgkACQEJAAkBAgcLEAIJAAkBBgkAAgkACQECCAgHCxEBCAgCBwsRAQkABgkAAgcLCgEJAAMCAwsJAQkAAQoCDUFjdGlvblJlcXVlc3QHQmFsYW5jZQRDb2luAklEBk9wdGlvbgdSdWxlS2V5BlN0cmluZwZTdXBwbHkFVG9rZW4LVG9rZW5Qb2xpY3kOVG9rZW5Qb2xpY3lDYXASVG9rZW5Qb2xpY3lDcmVhdGVkC1RyZWFzdXJ5Q2FwCVR4Q29udGV4dAhUeXBlTmFtZQNVSUQGVmVjTWFwBlZlY1NldAZhY3Rpb24DYWRkDGFkZF9hcHByb3ZhbA9hZGRfcnVsZV9jb25maWcTYWRkX3J1bGVfZm9yX2FjdGlvbgVhbGxvdwZhbW91bnQJYXBwcm92YWxzB2JhbGFuY2UGYm9ycm93CmJvcnJvd19tdXQEYnVybgRjb2luD2NvbmZpcm1fcmVxdWVzdBNjb25maXJtX3JlcXVlc3RfbXV0F2NvbmZpcm1fd2l0aF9wb2xpY3lfY2FwGWNvbmZpcm1fd2l0aF90cmVhc3VyeV9jYXAIY29udGFpbnMPZGVjcmVhc2Vfc3VwcGx5BmRlbGV0ZQxkZXN0cm95X25vbmUMZGVzdHJveV9zb21lDGRlc3Ryb3lfemVybwhkaXNhbGxvdw1keW5hbWljX2ZpZWxkBGVtaXQFZW1wdHkFZXZlbnQHZXhpc3RzXxBleGlzdHNfd2l0aF90eXBlB2V4dHJhY3QFZmx1c2gDZm9yDGZyb21fYmFsYW5jZQlmcm9tX2NvaW4QZnJvbV9jb2luX2FjdGlvbgNnZXQHZ2V0X211dA9oYXNfcnVsZV9jb25maWcZaGFzX3J1bGVfY29uZmlnX3dpdGhfdHlwZQJpZA9pbmNyZWFzZV9zdXBwbHkGaW5zZXJ0DGludG9fYmFsYW5jZQlpbnRvX2tleXMKaXNfYWxsb3dlZAppc19tdXRhYmxlB2lzX25vbmUMaXNfcHJvdGVjdGVkB2lzX3NvbWUEam9pbgRrZWVwA2tleQRtaW50BG5hbWUDbmV3Cm5ld19wb2xpY3kLbmV3X3JlcXVlc3QEbm9uZQZvYmplY3QGb3B0aW9uCXJlY2lwaWVudAZyZW1vdmUScmVtb3ZlX3J1bGVfY29uZmlnFnJlbW92ZV9ydWxlX2Zvcl9hY3Rpb24LcnVsZV9jb25maWcPcnVsZV9jb25maWdfbXV0BXJ1bGVzBnNlbmRlcgxzaGFyZV9vYmplY3QMc2hhcmVfcG9saWN5BHNvbWUFc3BlbmQMc3BlbmRfYWN0aW9uBXNwZW50DXNwZW50X2JhbGFuY2UFc3BsaXQGc3RyaW5nCnN1cHBseV9tdXQHdG9fY29pbg50b19jb2luX2FjdGlvbgV0b2tlbgh0cmFuc2Zlcg90cmFuc2Zlcl9hY3Rpb24KdHhfY29udGV4dAl0eXBlX25hbWUEdXRmOAV2YWx1ZQd2ZWNfbWFwB3ZlY19zZXQEemVybwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCAAAAAAAAAAAAwgBAAAAAAAAAAMIAgAAAAAAAAADCAMAAAAAAAAAAwgEAAAAAAAAAAMIBQAAAAAAAAADCAYAAAAAAAAAAwgHAAAAAAAAAAoCBgVzcGVuZAoCCQh0cmFuc2ZlcgoCCAd0b19jb2luCgIKCWZyb21fY29pbgACAjoIDhoLCQEJAAECAjoIDjIIDQICAzoIDl0LCQEJAFULEAIIBwsRAQgIAwIGSAgHGANWBU8LBgEFXQsGAQsJAQkA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DAMHQmFsYW5jZQRDb2luFkdlbmVzaXNDaGFpblBhcmFtZXRlcnMYR2VuZXNpc1ZhbGlkYXRvck1ldGFkYXRhBk9wdGlvbgNTVUkMU3Rha2VTdWJzaWR5EFN5c3RlbVBhcmFtZXRlcnMPVG9rZW5BbGxvY2F0aW9uGVRva2VuRGlzdHJpYnV0aW9uU2NoZWR1bGUJVHhDb250ZXh0A1VJRAlWYWxpZGF0b3IIYWN0aXZhdGUTYWN0aXZhdGVfdmFsaWRhdG9ycw9hbGxvY2F0ZV90b2tlbnMLYWxsb2NhdGlvbnMLYW1vdW50X21pc3QHYmFsYW5jZRhjaGFpbl9zdGFydF90aW1lc3RhbXBfbXMEY29pbg9jb21taXNzaW9uX3JhdGUGY3JlYXRlGGNyZWF0ZV9zeXN0ZW1fcGFyYW1ldGVycwtkZXNjcmlwdGlvbgxkZXN0cm95X3NvbWUMZGVzdHJveV96ZXJvBWVwb2NoEWVwb2NoX2R1cmF0aW9uX21zDGZyb21fYmFsYW5jZQlnYXNfcHJpY2UHZ2VuZXNpcxFnZXRfdmFsaWRhdG9yX211dAlpbWFnZV91cmwWaXNfZHVwbGljYXRlX3ZhbGlkYXRvcghpc19lbXB0eQdpc19zb21lE21heF92YWxpZGF0b3JfY291bnQbbWluX3ZhbGlkYXRvcl9qb2luaW5nX3N0YWtlBG5hbWUPbmV0d29ya19hZGRyZXNzEm5ldHdvcmtfcHVibGljX2tleQNuZXcGb2JqZWN0Bm9wdGlvbgtwMnBfYWRkcmVzcw9wcmltYXJ5X2FkZHJlc3MLcHJvamVjdF91cmwTcHJvb2Zfb2ZfcG9zc2Vzc2lvbhNwcm90b2NvbF9wdWJsaWNfa2V5EHByb3RvY29sX3ZlcnNpb24RcmVjaXBpZW50X2FkZHJlc3MccmVxdWVzdF9hZGRfc3Rha2VfYXRfZ2VuZXNpcwVzcGxpdA1zdGFrZV9zdWJzaWR5G3N0YWtlX3N1YnNpZHlfZGVjcmVhc2VfcmF0ZRdzdGFrZV9zdWJzaWR5X2Z1bmRfbWlzdClzdGFrZV9zdWJzaWR5X2luaXRpYWxfZGlzdHJpYnV0aW9uX2Ftb3VudBtzdGFrZV9zdWJzaWR5X3BlcmlvZF9sZW5ndGgZc3Rha2Vfc3Vic2lkeV9zdGFydF9l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ABAFCgIKAgoCCgIKAgoCCgIKAgoCCgIKAgoCAwMHCAsBCAECBwgBAwABBwgBBAcIAQsIAQgKBQcICwEIDgMHCAEIDgYICwELCAEICgMHCAEIEAMCBwgBCwgBCAoCBwgBBggLAQYIAQEBAQYIAAEFAQYIBgEGCAwBBgoCAQYLBQEIBgEGCwUBCgIBBggJAQMCBggBAwEIDQEICQIGCAEGCAECBgsFAQkABgkAAgYLBQEJAAYLBQEJAAIHCAEHCAsCBwgBCgIDBwgBCgIKAgEKAgEGCA8ECAADAwcICxYFCAYIBggGCAYLBQEKAgsFAQoCCwUBCgILBQEKAgsFAQgGCwUBCAYKAgsFAQgGCwUBCAYIBwoCCgIKAggGCAYIDAgMAQsFAQkAAQgGCAEBAQEBAQEIAAECAQgEAQgMAQcICwEIBwIHCA8DAwMDCA4BCAoBBgsIAQkAAQYICwQHCA8LCAEICgMHCAsBBwgPAQgCAQkAAgkABQUDAwMDCwgBCAoBBggOAwcIDwgOBggLAQgDAQYIEAEGBQIHCA8LCAEICgIHCA8GCAsCBggPAwEIDwEGCQAdAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBBgsFAQkAAgEBAgUHCAsBBwsFAQkAAwgJCA8FA0JhZwdCYWxhbmNlAklEBk9wdGlvbhVQb29sVG9rZW5FeGNoYW5nZVJhdGUDU1VJCVN0YWtlZFN1aQtTdGFraW5nUG9vbBNTdGFraW5nUmVxdWVzdEV2ZW50BlN0cmluZwlUeENvbnRleHQVVW5zdGFraW5nUmVxdWVzdEV2ZW50A1VybAlWYWxpZGF0b3IRVmFsaWRhdG9yTWV0YWRhdGEVVmFsaWRhdG9yT3BlcmF0aW9uQ2FwCGFjdGl2YXRlFWFjdGl2YXRlX3N0YWtpbmdfcG9vbBphZGp1c3Rfc3Rha2VfYW5kX2dhc19wcmljZQZhbW91bnQFYXNjaWkDYmFnB2JhbGFuY2UDYmNzBmJvcnJvdw9jb21taXNzaW9uX3JhdGUKZGVhY3RpdmF0ZRdkZWFjdGl2YXRlX3N0YWtpbmdfcG9vbA9kZXBvc2l0X3Jld2FyZHMVZGVwb3NpdF9zdGFrZV9yZXdhcmRzC2Rlc2NyaXB0aW9uGmVmZmVjdHVhdGVfc3RhZ2VkX21ldGFkYXRhBGVtaXQFZXBvY2gFZXZlbnQMZXh0cmFfZmllbGRzB2V4dHJhY3QKZnJvbV9hc2NpaQlnYXNfcHJpY2UHZ2VuZXNpcxRnZXRfc3Rha2luZ19wb29sX3JlZgJpZAlpbWFnZV91cmwMaXNfZHVwbGljYXRlDWlzX2VxdWFsX3NvbWUXaXNfZXF1YWxfc29tZV9hbmRfdmFsdWUHaXNfbm9uZQxpc19wcmVhY3RpdmUHaXNfc29tZQhtZXRhZGF0YQRuYW1lC25ldF9hZGRyZXNzD25ldHdvcmtfYWRkcmVzcxRuZXR3b3JrX3B1YmtleV9ieXRlcwNuZXcRbmV3X2Zyb21fbWV0YWRhdGEMbmV3X21ldGFkYXRhFW5ld191bnNhZmVfZnJvbV9ieXRlczNuZXdfdW52ZXJpZmllZF92YWxpZGF0b3Jfb3BlcmF0aW9uX2NhcF9hbmRfdHJhbnNmZXIabmV4dF9lcG9jaF9jb21taXNzaW9uX3JhdGUUbmV4dF9lcG9jaF9nYXNfcHJpY2UWbmV4dF9lcG9jaF9uZXRfYWRkcmVzcxpuZXh0X2Vwb2NoX25ldHdvcmtfYWRkcmVzcx9uZXh0X2Vwb2NoX25ldHdvcmtfcHVia2V5X2J5dGVzFm5leHRfZXBvY2hfcDJwX2FkZHJlc3MabmV4dF9lcG9jaF9wcmltYXJ5X2FkZHJlc3MebmV4dF9lcG9jaF9wcm9vZl9vZl9wb3NzZXNzaW9uIG5leHRfZXBvY2hfcHJvdG9jb2xfcHVia2V5X2J5dGVzEG5leHRfZXBvY2hfc3Rha2UZbmV4dF9lcG9jaF93b3JrZXJfYWRkcmVzcx5uZXh0X2Vwb2NoX3dvcmtlcl9wdWJrZXlfYnl0ZXMEbm9uZQZvYmplY3QQb3BlcmF0aW9uX2NhcF9pZAZvcHRpb24LcDJwX2FkZHJlc3MUcGVuZGluZ19zdGFrZV9hbW91bnQdcGVuZGluZ19zdGFrZV93aXRoZHJhd19hbW91bnQHcG9vbF9pZCFwb29sX3Rva2VuX2V4Y2hhbmdlX3JhdGVfYXRfZXBvY2gPcHJpbWFyeV9hZGRyZXNzEHByaW5jaXBhbF9hbW91bnQVcHJvY2Vzc19wZW5kaW5nX3N0YWtlJHByb2Nlc3NfcGVuZGluZ19zdGFrZXNfYW5kX3dpdGhkcmF3cwtwcm9qZWN0X3VybBNwcm9vZl9vZl9wb3NzZXNzaW9uFXByb3RvY29sX3B1YmtleV9ieXRlcw9wdWJsaWNfdHJhbnNmZXIRcmVxdWVzdF9hZGRfc3Rha2UccmVxdWVzdF9hZGRfc3Rha2VfYXRfZ2VuZXNpcxtyZXF1ZXN0X3NldF9jb21taXNzaW9uX3JhdGUVcmVxdWVzdF9zZXRfZ2FzX3ByaWNlFnJlcXVlc3Rfd2l0aGRyYXdfc3Rha2UNcmV3YXJkX2Ftb3VudAZzZW5kZXIdc2V0X2NhbmRpZGF0ZV9jb21taXNzaW9uX3JhdGUXc2V0X2NhbmRpZGF0ZV9nYXNfcHJpY2UQc2V0X3ZvdGluZ19wb3dlcgRzb21lFnN0YWtlX2FjdGl2YXRpb25fZXBvY2gMc3Rha2VfYW1vdW50EXN0YWtlZF9zdWlfYW1vdW50DnN0YWtlcl9hZGRyZXNzDHN0YWtpbmdfcG9vbA9zdGFraW5nX3Bvb2xfaWQGc3RyaW5nA3N1aQtzdWlfYWRkcmVzcwtzdWlfYmFsYW5jZRZzdWlfc3lzdGVtX3N0YXRlX2lubmVyCHRvX2J5dGVzC3RvdGFsX3N0YWtlEnRvdGFsX3N0YWtlX2Ftb3VudAh0cmFuc2Zlcgp0eF9jb250ZXh0D3Vuc3Rha2luZ19lcG9jaCB1cGRhdGVfY2FuZGlkYXRlX25ldHdvcmtfYWRkcmVzcx91cGRhdGVfY2FuZGlkYXRlX25ldHdvcmtfcHVia2V5HHVwZGF0ZV9jYW5kaWRhdGVfcDJwX2FkZHJlc3MgdXBkYXRlX2NhbmRpZGF0ZV9wcmltYXJ5X2FkZHJlc3MgdXBkYXRlX2NhbmRpZGF0ZV9wcm90b2NvbF9wdWJrZXkfdXBkYXRlX2NhbmRpZGF0ZV93b3JrZXJfYWRkcmVzcx51cGRhdGVfY2FuZGlkYXRlX3dvcmtlcl9wdWJrZXkSdXBkYXRlX2Rlc2NyaXB0aW9uEHVwZGF0ZV9pbWFnZV91cmwLdXBkYXRlX25hbWUhdXBkYXRlX25leHRfZXBvY2hfbmV0d29ya19hZGRyZXNzIHVwZGF0ZV9uZXh0X2Vwb2NoX25ldHdvcmtfcHVia2V5HXVwZGF0ZV9uZXh0X2Vwb2NoX3AycF9hZGRyZXNzIXVwZGF0ZV9uZXh0X2Vwb2NoX3ByaW1hcnlfYWRkcmVzcyF1cGRhdGVfbmV4dF9lcG9jaF9wcm90b2NvbF9wdWJrZXkgdXBkYXRlX25leHRfZXBvY2hfd29ya2VyX2FkZHJlc3MfdXBkYXRlX25leHRfZXBvY2hfd29ya2VyX3B1YmtleRJ1cGRhdGVfcHJvamVjdF91cmwDdXJsEXZhbGlkYXRlX21ldGFkYXRhFXZhbGlkYXRlX21ldGFkYXRhX2Jjcwl2YWxpZGF0b3IRdmFsaWRhdG9yX2FkZHJlc3MNdmFsaWRhdG9yX2NhcA12YWxpZGF0b3Jfc2V0EXZhbGlkYXRvcl93cmFwcGVyBXZhbHVlHnZlcmlmaWVkX29wZXJhdGlvbl9jYXBfYWRkcmVzcwx2b3RpbmdfcG93ZXIOd29ya2VyX2FkZHJlc3MTd29ya2VyX3B1YmtleV9ieXRlcwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgMIAAAAAAAAAAADCAEAAAAAAAAAAwgCAAAAAAAAAAMIAwAAAAAAAAADCAQAAAAAAAAAAwgFAAAAAAAAAAMIBgAAAAAAAAADCAcAAAAAAAAAAwgIAAAAAAAAAAMICQAAAAAAAAADCAoAAAAAAAAAAwgLAAAAAAAAAAMIDAAAAAAAAAADCGQAAAAAAAAAAwhlAAAAAAAAAAMIZgAAAAAAAAADCNAHAAAAAAAAAwgAAQAAAAAAAAMIoIYBAAAAAAAAAhZrBVYKAjUKApIBCgJVCgIyCAYeCAYqCAxUCAwzCAZLCAZQCAaRAQgGQwsFAQoCQgsFAQoCPwsFAQoCRgsFAQoCPQsFAQgGQAsFAQgGQQsFAQgGRQsFAQgGIwgHAQIKMQgAkAEDSQgJJgNnCA8ZA0QDPAM7AyMIBwICBU4ICYoBBWYFIQMTAwMCB04ICYoBBWYFYwNzA1EDXQMAAwAAJUQLAAwOCwEMGQsCDB0LAwweCwQMHwsFDCALBgwhCwcMIgsIDCMLCQwPCwoMEAsLDBELDAwSOAAMEzgADBQ4AAwVOAAMFjgBDBc4AQwYOAEMGjgBDBsLDQwcCw4LGQsdCx4LHwsgCyELIgsjCw8LEAsRCxILEwsWCxQLFQsXCxgLGgsbCxwSAAIBAwAAKIkBDglBKQcRJQQLDgpBKQcRJQwQBQ0JDBALEAQVDgtBKQcRJQwRBRcJDBELEQQfDgxBKQcRJQwSBSEJDBILEgQpDgVBKQcRJQwTBSsJDBMLEwQzDgZBKQcRJQwUBTUJDBQLFAQ9DgdBKQcRJQwVBT8JDBULFQRHDghBKQcRJQwWBUkJDBYLFgRMBVALDwEHCScKDgcQJQRVBVkLDwEHCCcKDQcSIwReBWILDwEHDycLAAsBCwILAwsECwURTRFVCwYRTRFVCwcRXQsIEV0LCRFNEVULChFNEVULCxFNEVULDBFNEVUKDxFWEQAMFw4XEUkLFwsNCw4LDxFMAgIDAAAFBQsADwALARFfAgMDAAAFBQsADwALARFeAgQDAAAFDQoAEAEUCgAPAhUKABADFAsADwQVAgUDAAAvOg4BOAIMBAoEBgAAAAAAAAAAJAQIBQ4LAAELAwEHCycKAy4RWwYBAAAAAAAAABYMBQoADwALAQsFCgMRaAwGCgAQABFhBCIKAA8AEWYKABAFFAoEFgoADwUVCgAuETELABAGEAcUCwILAy4RWwsEEgI4AwsGAgYDAAAYLwoDLhFbBgAAAAAAAAAAIQQHBQ0LAAELAwEHDCcOATgCDAQKBAYAAAAAAAAAACQEFQUbCwABCwMBBwsnCgAPAAsBBgAAAAAAAAAACwMRaAsCOAQKAA8AEWYKABAFFAsEFgsADwUVAgcDAAA4LQ4BEWsMAw4BEWoMBQoADwALAQoCEWkMBw4HOAIMBgoGCgMXDAQKABAFFAsGFwoADwUVCgAuETELABAGEAcUCgIRXAsFCwIRWwsDCwQSAzgFCwcCCAMAAAUcCgIHEiMEBQUJCwABBw8nDgERbhQKABAGEAcUIQQTBRcLAAEHDicLAgsADwEVAgkDAAAFKQoALhEOBAUFCQsAAQcKJwoCBxIjBA4FEgsAAQcPJw4BEW4UCgAQBhAHFCEEHAUgCwABBw4nCgIKAA8BFQsCCwAPAhUCCgMAAAUOCgEHECUEBQUJCwABBwgnCwELAA8DFQILAwAABRcKAC4RDgQFBQkLAAEHCicKAQcQJQQOBRILAAEHCCcLAQsADwQVAgwDAAAFDgoAEAUUDgE4AhYKAA8FFQsADwALARFgAg0DAAAFEAoADwALARFnCgAuESgLABAFFCEEDQUPBwsnAg4BAAAFBAsAEAARYQIPAQAABQMLABAGAhABAAAFBQsAEAYQBxQCEQEAAAUECwAQBhAIAhIBAAAFBAsAEAYQCQITAQAABQQLABAGEAoCFAEAAAUECwAQBhALAhUBAAAFBAsAEAYQDAIWAQAABQQLABAGEA0CFwEAAAUECwAQBhAOAhgBAAAFBAsAEAYQDwIZAQAABQQLABAGEBACGgEAAAUECwAQBhARAhsBAAAFBAsAEAYQEgIcAQAABQQLABAGEBMCHQEAAAUECwAQBhAUAh4BAAAFBAsAEAYQFQIfAQAABQQLABAGEBYCIAEAAAUECwAQBhAXAiEBAAAFBAsAEAYQGAIiAQAABQQLABAGEBkCIwEAAAUECwAQBhAaAiQBAAAFBAsAEAYQGwIlAQAABQMLABAcAiYBAAAFBAsAEAEUAicBAAAFBAsAEAARbAIoAQAABQQLABAAEWwCKQEAAAUDCwARKAIqAQAABQQLABAdFAIrAwAABQULAQsADx0VAiwBAAAFBAsAEAARYwItAQAABQQLABAAEWQCLgEAAAUECwAQAhQCLwEAAAUECwAQBBQCMAEAAAUFCwAQAAsBEWUCMQEAAAUECwAQADgGAjIBAABDlwMKABAGEAcUCgEQBhAHFCEEDQgMAgUXCgAQBhAIFAoBEAYQCBQhDAILAgQcCAwNBSYKABAGEAwUCgEQBhAMFCEMDQsNBCsIDBgFNQoAEAYQDRQKARAGEA0UIQwYCxgEOggMGQVECgAQBhAQFAoBEAYQEBQhDBkLGQRJCAwaBVMKABAGEBIUCgEQBhASFCEMGgsaBFgIDBsFYgoAEAYQEhQKARAGEBMUIQwbCxsEZwgMHAVxCgAQBhATFAoBEAYQExQhDBwLHAR2CAwdBYABCgAQBhATFAoBEAYQEhQhDB0LHQSFAQgMHgWNAQoAEAYQFAoBEAYQFDgHDB4LHgSSAQgMAwWaAQoAEAYQFQoBEAYQFTgHDAMLAwSfAQgMBAWnAQoAEAYQGAoBEAYQGDgIDAQLBASsAQgMBQW0AQoAEAYQGgoBEAYQGjgIDAULBQS5AQgMBgXBAQoAEAYQGgoBEAYQGzgIDAYLBgTGAQgMBwXOAQoAEAYQGwoBEAYQGzgIDAcLBwTTAQgMCAXbAQoAEAYQGwoBEAYQGjgIDAgLCATgAQgMCQXoAQoAEAYQFAoBEAYQDDgJDAkLCQTtAQgMCgX1AQoAEAYQFQoBEAYQDTgJDAoLCgT6AQgMCwWCAgoAEAYQGAoBEAYQEDgKDAsLCwSHAggMDAWPAgoAEAYQGgoBEAYQEjgKDAwLDASUAggMDgWcAgoAEAYQGgoBEAYQEzgKDA4LDgShAggMDwWpAgoAEAYQGwoBEAYQEzgKDA8LDwSuAggMEAW2AgoAEAYQGwoBEAYQEjgKDBALEAS7AggMEQXDAgoBEAYQFAoAEAYQDDgJDBELEQTIAggMEgXQAgoBEAYQFQoAEAYQDTgJDBILEgTVAggMEwXdAgoBEAYQGAoAEAYQEDgKDBMLEwTiAggMFAXqAgoBEAYQGgoAEAYQEjgKDBQLFATvAggMFQX3AgoBEAYQGgoAEAYQEzgKDBULFQT8AggMFgWEAwoBEAYQGwoAEAYQEzgKDBYLFgSNAwsAAQsBAQgMFwWVAwsBEAYQGwsAEAYQEjgKDBcLFwIzAAAADxEKADgLBAoLAQELAAEJDAIFDwsAOAwLASEMAgsCAjQAAABFGgoAOAsEBggMAgUJCgE4CwwCCwIEEgsBAQsAAQkMAwUYCwA4DAsBOAwhDAMLAwI1AwAAERkKAS4RXAwCCgIKABAGEAcUIQQMBRILAAELAQEHDScLAgsBEW0LAA8cFQI2AwAABRIOAUEpBxElBAYFCgsAAQcJJwsBEU0RVQsADwYPCBUCNwMAAAUSDgFBKQcRJQQGBQoLAAEHCScLARFNEVULAA8GDwkVAjgDAAAFEQ4BQSkHESUEBgUKCwABBwknCwERXQsADwYPChUCOQMAAAURDgFBKQcRJQQGBQoLAAEHCScLARFdCwAPBg8LFQI6AwAABRYOAUEpBxElBAYFCgsAAQcJJwsBEU0RVTgNCgAPBg8UFQsAEAYRSQI7AwAABR4KAC4RDgQFBQkLAAEHCicOAUEpBxElBA8FEwsAAQcJJwsBEU0RVQoADwYPDBULABAGEUkCPAMAAAUWDgFBKQcRJQQGBQoLAAEHCScLARFNEVU4DQoADwYPFRULABAGEUkCPQMAAAUeCgAuEQ4EBQUJCwABBwonDgFBKQcRJQQPBRMLAAEHCScLARFNEVUKAA8GDw0VCwAQBhFJAj4DAAAFFg4BQSkHESUEBgUKCwABBwknCwERTRFVOA0KAA8GDxYVCwAQBhFJAj8DAAAFHgoALhEOBAUFCQsAAQcKJw4BQSkHESUEDwUTCwABBwknCwERTRFVCgAPBg8OFQsAEAYRSQJAAwAABRYOAUEpBxElBAYFCgsAAQcJJwsBEU0RVTgNCgAPBg8XFQsAEAYRSQJBAwAABR4KAC4RDgQFBQkLAAEHCicOAUEpBxElBA8FEwsAAQcJJwsBEU0RVQoADwYPDxULABAGEUkCQgMAAAUQCwE4DgoADwYPGBULAjgOCgAPBg8ZFQsAEAYRSQJDAwAABRcKAC4RDgQFBQkLAAEHCicLAQoADwYPEBULAgoADwYPERULABAGEUkCRAMAAAUKCwE4DgoADwYPGhULABAGEUkCRQMAAAUSCgAuEQ4EBQUJCwABBwonCwEKAA8GDxIVCwAQBhFJAkYDAAAFCgsBOA4KAA8GDxsVCwAQBhFJAkcDAAAFEgoALhEOBAUFCQsAAQcKJwsBCgAPBg8TFQsAEAYRSQJIAwAABY8BCgAuER04DwQSCgAPBg8UOBAKAA8GDwwVOAEKAA8GDxQVCgAuER44DwQkCgAPBg8VOBAKAA8GDw0VOAEKAA8GDxUVCgAuER84DwQ2CgAPBg8WOBAKAA8GDw4VOAEKAA8GDxYVCgAuESA4DwRICgAPBg8XOBAKAA8GDw8VOAEKAA8GDxcVCgAuESE4EQRnCgAPBg8YOBIKAA8GDxAVOAAKAA8GDxgVCgAPBg8ZOBIKAA8GDxEVOAAKAA8GDxkVCgAuESM4EQR5CgAPBg8aOBIKAA8GDxIVOAAKAA8GDxoVCgAuESQ4EQSMAQoADwYPGzgSCgAPBg8TFTgACwAPBg8bFQWOAQsAAQJJAQAABQQLADgTEUoCSgECAEsDAAAFAwsAEAACTAAAAEgYDgAQBxQMBgoDEWIMBQsGCgMRbQwECwAGAAAAAAAAAAALBAoBCwUKAgYAAAAAAAAAAAsBCwILAxFWEgECAQQBBwEDAQgBBQEGAQAAAAAFAAYABwAIAAkACgALAAwAAQAEAAIAAwARABIAEwAUAA0ADgAPABABAgEBACcAbQCMAQCNAQCQAQAKc3VpX3N5c3RlbeEeoRzrCwYAAAAMAQAeAh5OA2y8AwSoBBAFuATdAweVCO0NCIIWYAbiFjYKmBcIDKAXgAcNoB4ED6QeAgA5ASECFAIWAhgCIAI4AjwCPQI+ADYANwA6AFMAVAAICAABAwcBAAACAAQBAAEDAQwBAAEFAgcABQ4EAAYFAgAHDAwCBwEEAQkNAgAKBgQACwQHAAsHDAAMCQQADAoEAAwLBAANEAQADg8MAAAXAAEAACoCAQAALAMBAAApAwEAACsDAQAALgQBAAA0BAEAAC0FAQAAMwUBAAAmBgEAACgGBwAAJwgBAAAvCQEAADAJCgAAJQsBAAA/CwEAADEDAQAASQwBAABHDAEAAEgMAQAAUQwBAABKDAEAAEAMAQAATAwBAABCDAEAAE0MAQAAQwwBAABPDAEAAEUMAQAATg0BAABEDQEAAFAMAQAARgwBAABLDAEAAEEMAQAAIg4PAAAREBEAABMSCgAAHhATAAAfEBQAAB0QFAADGSgpAQAEEhoBAgcEBBUyNgIHBAQkMjMCBwQIIyQBAQwINRwBAQgJMiIjAAwRExEADBMwCgAMFxYXAAwbARgADCIvDwAMJSwBAAwmJQcADCcmBwAMKR8BAAwqHQEADCsfAQAMLB4BAAwtIQEADC4gAQAMLysKAAwxHgEADDMhAQAMNCABAAw7ExgADD8sAQAMQC0BAAxBLQEADEItAQAMQy0BAAxELgEADEUtAQAMRi0BAAxHLQEADEgtAQAMSS0BAAxKLQEADEstAQAMTC0BAAxNLQEADE4uAQAMTy0BAAxQLQEADFEtAQAMUhc0ACoZLhstByknLSosGSo1KzUICAUKCA8LAgEIBgMDCA4ICQcICAAQBwgACgIKAgoCCgIKAgoCCgIKAgoCCgIKAgoCAwMHCAgCBwgABwgIAwcIAAYIEAMDBwgAAwcICAQHCAALAwEIBgUHCAgBCAsFBwgACgsDAQgGCwEBAwUHCAgDBwgACAsHCAgBCwIBCAYDBwgABggQBQMHCAAKAgYICAQHCAAKAgoCBggIAgcIAAYIBAEGCwcCAwgKAQcIAAEKBQsLAgEIBgsCAQgGBwgAAwMDAwMDAwcICAEGCA0BBwgNAwgACAwDBwoIDwsCAQgGAwMIDggJBwgIAQgMAQMCAwgMAwcIBQkACQEBCAABCQAQBwgNCgIKAgoCCgIKAgoCCgIKAgoCCgIKAgoCAwMHCAgCBwgNBwgIAgcIDQYICAMHCA0GCBADAwcIDQMGCAgBBggIAQUCCQAFBAcIDQsDAQgGBQcICAUHCA0KCwMBCAYLAQEDBQcICAEIBgILAgEJAAcICAELAwEJAAELAwEIBgMHCA0ICwYICAMHCA0GCBAFAwcIDQoCBggIBAcIDQoCCgIGCAgCBwgNBggECwcIDQMDCwIBCAYLAgEIBgMDAwMDBwgIAgcIDQgNAgcIBQkAAQkBAQgNAgMIDQEHCQEHQmFsYW5jZQRDb2luAklEBk9wdGlvbhVQb29sVG9rZW5FeGNoYW5nZVJhdGUDU1VJDFN0YWtlU3Vic2lkeQlTdGFrZWRTdWkOU3VpU3lzdGVtU3RhdGUTU3VpU3lzdGVtU3RhdGVJbm5lchVTdWlTeXN0ZW1TdGF0ZUlubmVyVjIQU3lzdGVtUGFyYW1ldGVycwVUYWJsZQlUeENvbnRleHQDVUlEH1VudmVyaWZpZWRWYWxpZGF0b3JPcGVyYXRpb25DYXAJVmFsaWRhdG9yGmFjdGl2ZV92YWxpZGF0b3JfYWRkcmVzc2VzA2FkZA1hZHZhbmNlX2Vwb2NoB2JhbGFuY2UKYm9ycm93X211dARjb2luBmNyZWF0ZQ1keW5hbWljX2ZpZWxkDGZyb21fYmFsYW5jZQdnZW5lc2lzHGdlbmVzaXNfc3lzdGVtX3N0YXRlX3ZlcnNpb24CaWQYbG9hZF9pbm5lcl9tYXliZV91cGdyYWRlEWxvYWRfc3lzdGVtX3N0YXRlFWxvYWRfc3lzdGVtX3N0YXRlX211dAZvYmplY3QGb3B0aW9uE3Bvb2xfZXhjaGFuZ2VfcmF0ZXMPcHVibGljX3RyYW5zZmVyBnJlbW92ZRByZXBvcnRfdmFsaWRhdG9yEXJlcXVlc3RfYWRkX3N0YWtlGnJlcXVlc3RfYWRkX3N0YWtlX211bF9jb2luG3JlcXVlc3RfYWRkX3N0YWtlX25vbl9lbnRyeRVyZXF1ZXN0X2FkZF92YWxpZGF0b3IfcmVxdWVzdF9hZGRfdmFsaWRhdG9yX2NhbmRpZGF0ZRhyZXF1ZXN0X3JlbW92ZV92YWxpZGF0b3IicmVxdWVzdF9yZW1vdmVfdmFsaWRhdG9yX2NhbmRpZGF0ZRtyZXF1ZXN0X3NldF9jb21taXNzaW9uX3JhdGUVcmVxdWVzdF9zZXRfZ2FzX3ByaWNlFnJlcXVlc3Rfd2l0aGRyYXdfc3Rha2UgcmVxdWVzdF93aXRoZHJhd19zdGFrZV9ub25fZW50cnkUcm90YXRlX29wZXJhdGlvbl9jYXAGc2VuZGVyJ3NldF9jYW5kaWRhdGVfdmFsaWRhdG9yX2NvbW1pc3Npb25fcmF0ZSFzZXRfY2FuZGlkYXRlX3ZhbGlkYXRvcl9nYXNfcHJpY2UMc2hhcmVfb2JqZWN0DXN0YWtlX3N1YnNpZHkMc3Rha2luZ19wb29sA3N1aQpzdWlfc3lzdGVtFnN1aV9zeXN0ZW1fc3RhdGVfaW5uZXIUc3lzdGVtX3N0YXRlX3ZlcnNpb24FdGFibGUIdHJhbnNmZXIKdHhfY29udGV4dBV1bmRvX3JlcG9ydF92YWxpZGF0b3IqdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfbmV0d29ya19hZGRyZXNzKXVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX25ldHdvcmtfcHVia2V5JnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3AycF9hZGRyZXNzKnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3ByaW1hcnlfYWRkcmVzcyp1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl9wcm90b2NvbF9wdWJrZXkpdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3Jfd29ya2VyX2FkZHJlc3ModXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3Jfd29ya2VyX3B1YmtleRx1cGRhdGVfdmFsaWRhdG9yX2Rlc2NyaXB0aW9uGnVwZGF0ZV92YWxpZGF0b3JfaW1hZ2VfdXJsFXVwZGF0ZV92YWxpZGF0b3JfbmFtZSt1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfbmV0d29ya19hZGRyZXNzKnVwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9uZXR3b3JrX3B1YmtleSd1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfcDJwX2FkZHJlc3MrdXBkYXRlX3ZhbGlkYXRvcl9uZXh0X2Vwb2NoX3ByaW1hcnlfYWRkcmVzcyt1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfcHJvdG9jb2xfcHVia2V5KnVwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF93b3JrZXJfYWRkcmVzcyl1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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/iota/vendor/wormhole_iota_testnet/devnet/genesis.blob b/target_chains/iota/vendor/wormhole_iota_testnet/devnet/genesis.blob new file mode 100644 index 0000000000000000000000000000000000000000..5d1cb997ea2df5e1248fd6e3dd7eae1d21bf21f1 GIT binary patch literal 329923 zcmeFad3VI#qS5>eQ)v>Z#B3)Ki~)ZN=L3v(x**?~C92me0KHX{Xl^>FKJ%sf@?Xn6^79|~ z#4nutQ#J9lfq&`wuJ_#Vp8Fb~edv?FHur&F`Q)FT`(k(h8~TrpS(eGdu}V4*+ftT8 z;NV|>N&T-z$=jxZilmgSzWubqG1 z#u6egae`uBI5L>r5b6?eNGm1i^x@k1`uN=V)UDu7k_NVRe@6@3GS|^I$;>5{2ou5(pKlNP8;v97kR}SH zB`b>0)1zn#$D@_H0;*ij5mE3W-p`9ID{?5A*0Zd&?}*xfKG}>+=_yVSUYfEYmKod^k^kJy((^jpo|=>hhJjwdIS= zh-cDITVCqOLuVWiB8l=`AE;kgYUoh^Li5tO_4DqfP>8q z&)a3y9OXzZgArY1%Y1ftvBa62;z74BrUz`!%O@~YuDn4ITScp2m+Z=EOL-DPK}G9~ zyo{6|y1Yzqg8nEsPApxMexbeHmXhu#q20EM($v-O+DCbG!+c~_2S?moY*k;lk2i>O zi5<((Aku3z=P#dI&jtr2G2mk_?*(1E!m+{RtQn5doYJN;>eFXD-KEOf<@?na?w?zy zw>3?3UezTJKADuBc8{-zS>X%@MyBsX54tb03*Kw&mqCct7wquwok9Nx-AeRMH;CWo zs^tCdx19V>g{tsBLPZ<(GC--HRS>$w7Z*2iq*e3Ubv^Ohjx-}97B>o%?N~WIn!}v* z2bGg%>k7r+%{U}W2AE48yT#Xj4pIO7c?J~PrmLh zE?u}#I@esE>x>N{49$xx>sN$QNQN23=p+ugHAujP=KAs_O%vR;=hs&fapsm+=OkMn z3iLp8!Qs#6Z+&_7s$I)OXeuGo6d#)bVtjm?9$JMth3{xN$LW6NH> z05)sE4V!gNi6UMlFU`&4KQOG=E@2dS-;&o8L*L2X3)jLK7!)YPK84 z4nr?dUOBey*y%`{s95Q-UFzB@eQ87DicT%Vzg&u~Y3Nv_M#(<(5_+xBIY$0zrKy6{ zag<7W9t6Nta@snhG?}mpTg0_0bxLR-gQBuUB6*)PifTAg5LIU&SlvCkGH&EbWultP zixwjHjH-E3;lznuo!U_hb*oZ{tz$GyK^i8fTP4YxOqF7)v?`^7XoiD#4?(<}vJ+RQfp8f1irw+vNSaC%qWm)jwP=i7tt zQ~zRzFLej~ABXOI)b*23xZmUC9|@GzO_wTL{WK**J0MF$bb>p6*I_$}vE)`0@}mVH$)>ux&@P4wMV)0HcP>{OJ(yz|w^l{~<1L584s0 zJOB-x#udFmLHDYs_yS%o@ zf-^TKx-~_~WxRz|fo~D2k%Oqkb{U33lZf7$uqsIa2Nay>-sk3Hyybl2K&Apa$GCOe ziq~(*2gIQ4Cs71zlenWTViCLLD1i-&?Kn#O#2byh*e58==VDb3aKmyCLxOrV<>?!f z+9rPFpXjDw6$ks=qHtjQq*PO6f8^_GxZXf6abwpVB0y0u&l}DqUhKIe-l5pnG|i`6 zRW8tgLb7tqxFxDmvdTr3FpBd075FRi$1nYmuWhj&Gzv5LqI=%;F4Tk1*l)JOzhXu3 zGABx2>wHcIyr-Fqoc<)XD-F&H3764@8)^1ISONw+?Zmi`go(&Ao+vL7H`G+m5uV@j z3qrJ{#i0Qui7kmg7r`{zj5j^_7{M|I;ri2om|h&=6f_6GaCI~bz*si}Rk#g&@&rRx z0!bo22D-U-(t+wfiZy7l0r_mQ*VDJB-OYn`^Qh8X{ybcu;qt}juQo3(KhO+Tn-8!A zX?9NaOz^RujYsS^>=S#8Yh* zIm7ODPe!3S8AbMx+pF_qHLc_(Y)K zKtgdM8v>SC5ibKTq6#r#8N0x24*L+=eVT1xa+ja>)3~jFnyU~NLAfc{03H!3Px*@e zNJNS}gm?2!xARI_<7`44^63J^Fkud>hFDe(X}d?_zqs7Ee4*LXT2L&nE}dHv{$Q!G z7HdqxJe;twVEuV9ryY4=+?9TP~v;?g&zXjXHEU(&48AuuB~n5~O~ z{pFxlxqet4^KIOql-RAem35X--hJLc9$E+O4J)UildV@~BJg@*7xG!o=@nu!_86tK z%Ij^F=TV*;d69E6On@F`+9}w3+=Jd=-i?(M)}EAjio!f$*x|l%rWS62idUqSqxq$E zSvs?1es>G~gt`DyxQ;*U9o!q-b}{@}d(i*9eIR;?3X@l=zjgBe4DiY0hNcJ!;hVtr z2pz#;9m)Dw8iZtmNV1BMl?7xC7rG(Kiiq&Q_MAXg4Y`v`>-Xwfd(q96WqmdQR4-&6{zoUB` z&pM(0a*;Lc?)LifcJ8#dsgtnCkL{HA4=YTZe6&Iu+TAvXKc_w8stVKEr1rs6|}Q7F5E?hbjap-mqnFK|hp z!G`jSOAj_1b1TctGZW)~=w^ICYvAni_VxzHUJ!nWf%6COB0sFWBsS8E$2IoEqWQ8y(2@1@#4Dtcdg_GDfX->wX(3h|y|u{8zCZB?fFtAk_mjRK36 zpH~hXK&wfO-FiE9iq^2&qrrO@bZx3Sc+@;SkUiS(4FpO*swY**?Ns&nQDy~O5=(C% z*OlpT_UQ6J>%7< z@JbI3QYtLV#K6}Sxt)csYWdq!R zfQeICalad?_Zs`o;Lq)!vBS3@eE2w$!B071@)_rU+xZVN^SR*dT~?2*?3pR_5)_Pp zoEg|EKyv7Ma`F8@F5P}8m%zit?Q8m*hD+De{1w zFUkXUcS$l)KqHnaQ*H#`mgI~1d>KR`KybnQ%S_CIpNWlGSU`lF79R>&kYfobY&{V& zX|oI_9qB~?!J=2Lf@F{skDz@g>*sQJKX?kk03g3}&&{WudZQsN_87Q9S0+a=asXM} z_>Cs$esd$DkZ)XJ&9k&%m;s~+OKa%I*Wok<01#&CT0K^F;2#Y3*nghYwkOix3vS5uZM52|6t7o*Rn9KiFQE(RFhz5nEvx1~!lI3ouFkX@+jRC&_3KW!roBO zGg-~j@fjYrfY(gOwrdyd$m4}<&;&c;)r4y&8V4+065)unh5}?<2$sfl<*)$OXqpR$ zSj-pQ6k3ZscwL4vjzDc>DlUJH$ru>!pJl0RI5ljMMbTVJ|7<~qc%^&lK^c++@_>-Zo5P4}HvO?#s846PI^2iP~Um)|2 zNO~EjfRO~!j|l1$7TG%Cmj_1L%>VJ$7;brFYy*A|2Gc%rKA$AygKS4JfFE~@x0yOryv2G1G*>-zv__8qws{dVS1$RWw(e1z@w*R6mq?PxO zyWcw;JJmqY0Z}}NU5!KU+y)SlYb6WNI!a`X#5Wmsdpsns#5<=YQ|hgMqU}P51)SC3 z^sY@gxLf7mveMl$2a$H}-jo9?&qvq6qf>90g8qDR29p;7NzWrQ`m)?YH^Kr-Ppt!HXxNZecF?3D?BT3^fD_f;a*d%RCsN*}&{6 z%&x0%^(3q^K~$r;u-s_I=Ax%NAHatosmOmLQKxP%@n59W*q9ots}PNmZRv)oE3oQPrBN&Z=r% zRp(W8K~)>7+Emp=H9n@st7?2)jZdiYNi{yD#;4Wzj2f@0@mV!qSL5?)d_j#j)Ob^k zFRF<#HBnU)<7#3;O-!nZDK#;zCT7$`O-;jH=gE zeOA@$sy?sk3##5w^`@#Xs`)WBUsdztYJNh^PpbJTH9xK9XViR6&Cja&x|*L?^9yRe zq2`-veo-xqsfDUq7*`7uYGG0>OsR!wwJ@U=YHDFtE!5S*yjoaL3k|iy_*3{yxTCA(Z zd9}Ep78`1@sTLQ7iIL%}c6ZDUXh+)6H8gkv&3Hthojbik-YwXuN2-%{WfB$1nzf1l z$T8c-u2ZJ62G1J8;5ytrx8z#R9TYu zR!{UHcDSxumn26?a&DN3v}!aGCcSC+CX=pCx)=mF_E;LN)UW(f3u9;EN1Z5gx^8pl z3f{#R24A(`XNNxwj_@(`2_A9&5Wyd-s3Pk)ctKf@Q*D+ev|FNEuK@LADddXFdPCY1 zipiV#;sQVOkSp>{v{A(`(>QArmMSn;Xc@&(l#p|RkO)-_qf^KdAAEq>TvP^F;2SSU zR%oMDse?WIsZyd2Nc|!>m8Flqcv@D-DoKas5V{iumZ=vyI?y%=;en+nbd$j}+em`D zz-=)FE$f#WbN4r!EBW+ZFMKY!&m&1Fj&S=unP*N?qi%R%#g)tR7eErS z2!9P7q~@x(wsK)wgHm(K$RftH-$5ODUc zRotBxMgMCT;Gr7ODqz4X=DjmaaidbfP1&QnV8>pE@`0?P(N3hCnqcZEybQGSwLA0e z@gmV+5Hy48c$+fT_3qSa2*@kyf||6o8ulWL4;k-y1n#2I$6|?P3?(xroVCkKpKA>y zdUZh+#+>V~cfX_Hz0&?3J9xEvu?k<~9`}F8U5q~BdCAAUi&ig^6xa)^&)Q}Uz+y0@ zWKkj92?)Gs|1!q(NQ0K)hh^g9q08q}4;DV8?E|Q{%Z^}YNr{J5Q7hcVFh$DgFEZBL zZiZ#zRw}q&!3!*}hqhOt7X^7AnTivI-5?Bsd^coZ2mwQshG6WuqVuA%3z~BV z(Ywl6mOjxMtd;e&!Uzpf(lfbtj9};T>IFR`T;0j$jH{)%3IJgJ`kSwnM?kawGQL|f z5nSubuW*Nd^_AYB#5~qdv;=5&eYw1(r(84w=yEQuFI z!LULn+2-x51hcn?FSCzFziRu*XYBF(x0sUz)}u^YL|TvsW+CAe9AWnZ#K5xmf}Trn z)%cnSegW8+W>R=xdXbSnfaeNo{?x+0*tKL6-)nj%@Ih7cPTniS)ii>=xr)WOnHvKX;NMh?oEt0)YxCYs)gxjsP757VVRyC1ccuPV2$A!*W z(`9i_bxFv+E?G}LP3tP}O(eX(r!;*24JEh$qEE={84O;@?;LIf~G82ebb6helNJiReGkk#E zI)oXj*fG@vc32_=RY!S5rXn~I@w%=1be54+sW-jq{9JUG0-YM}v4|ikfg^>XluR56 zLS7+$@D$uI+<5EL6ng&CV>I+_)Q!Uu3GJ|uz7+VNc{%;EUa(NM0pBGFPepH3&-o;#c8E?rulqZdcT_0b8aDhxqKR9CfX2;X6a)6!deke z=lq_E`3SyrWo|)s?`RoaPTyakF%SR}BR&xfbhYoYDwWznMkg3NXl-G2NpB%+ZAB6t zhK;L*{00^x(JB*ZizK1~(W7Dd{D2O+^K&rN*dNKkh}cDvPsW@sj9}j4_k? z3SZSRr)Hv2Bl+3PPdO`>+fq~ZbeTm`HshKvoS4hp(nY#cl-}UY!nMkXZ(7J*uixKH z)|c1O%uM@7wx)VKHmxn%_e9dW)@9~Q~b`281oz}90*9SsXzhLk8D|8{jO3~vto;V>N4L`*4%;NCF5E6h7O`BOCc0Use8 zPHMYyMywH=DMXDh-&-ZP5H^}=43%vnhEbPORy*6N}uC%SB`PQXk=Lu<6=?}L+-eIyd7_8&*| zYknQeSZJ`MjO+nKBuG?S>*Bs)chWnUM;lEvFKfZ+WYgkvkI5K7Mu2`^+$z^O)q(%Afw0cjl8 ztFoERi~L$5FpE4o>o5~?ErhjvA*dB*-CB_iFF_nC0qslZ@k##E(zYdPC7hL1(gdiA zv^3k`S}iT>>$2)0)#0etRX$K2hSP@^r(9K@P`z76OsPI1rn_rhi7Ke_LD}{z8;_OF zfUZLZ?Aa?%s(uRO>&x3XH3WmTirOYYs4B+xG`fTX?K`JOZExSXJ!*$}Se37<^^n@@ zQNrx${^(cKuFS70YIo*$DrzwEyA?H*`BCW;Ca~SZMfNTNdcn3BAZo;6ka#2hh_M~; zZ5cmK&p;fHLShhf4}?bsliOH{(4Y7dIjmM#XF)xG0Wvi{kF;8w63E@(Tv({z|M(^Z z$b+X@GX1uCU1qRBS)J*s2qq`W3dn9;6cvg^x3RRi z*xiA=t;+VfxyviMOy^?#L5JzZSy_I*yY_9X>p5}eL@J*S(W_mgsTt6WN_(#$i2$m4 zGbfrYe7`gX&CCrkLegx6&hiW$R1`#&m!~IMcz9BlV)jsL=nKGEg;egQ(e0|*Ch~0d z;lx&d!n0L10Lr_I9VFb-cdO)Pv%$t_;JDYc1juBne~H}+%HB#)(kV#Kqht2C2fPGA zL=}3eL2P_|T8wRv5lGF4MXZiCqts=LvZ1MynkIYKJW95lRiah0{iq~aRCblh-6oZL z+?4;t%1-H7!|s+3(h1B%zhmgIY*!Xf>wECdw65hXTv%SX|HqtcWVBT1cDobGo7xrJ z^@8v-_Mrc__TlJF=$F4;-I4z~&pASbP+=3$JJX(H-;5$fMGu+HR(jKZa!^lw+$8#OnB`wn?{!}vQ-3DYx%-UrG>8 zy{oAh^mc~_2mPCOCFe3S=E`QC>^~||`q@OgAo8nAi&wJJuX5$3vtHib0qdXvoc@HS zdTa{3w0>V!Aw%gvG~6JG!@j{}#*jBr!4k1?lP_l;$uhah)zDGz{-x!$`(M5%%N~XR z=;k@j121#jZ#t*FUvyvL2EQJ@EeyX>LY!TGi5>l9S2KQ1cbNQe_YW5G?-}YU{M?XV zd}K&jj!t~>nC(nyobd(BF_G8IM3cjnmY$5`kC8zSk|-Tktc88X<1|2cY)Dt-)&hpY#T%d z2bPmqeYs)W{@h;Nv-_l^sgeDbb@PG!cyH3~EeCef9=mRV*R+q`+)r10?o;3r)Vw_g zyfZRBfqUx-CJ*PnTgCs}Cll*-UkgL)y2H2k+ktx6(dzN3TJ5&k+V~ygXQyj3H6Ry&MnOgaj6=DzLEb<{Kua0!e%O`z*f66*a&#mU z6$=(3P>;UFW>71NMMav3Xo(N=I#UDyahYU5FOoUQba^5nkW^Y!U=n6`X*R57B&k5S z0zW~S1NtSPE~V1&IycFIOpO+?9~L0M1;(e~DgyLml@x47FedOGT{XG#nzU>_HD1J0 zgaUYTqlv7A8)PQ{mq0XLSP1iBp`i0sIw);5-hvqZA$I}L2@3-9fDEy?aq-HPNa{%h zBSAwrcAO(mYC|P>b-Vyg85kt@b}Wj|WXyjCAAVowfn+mj0BD{KpZz3 zUrJ#nscDEBudW*K0PZ6?~*X|w92U7=c3M3)hqTE}V0aL&!&YmD%#14hK(~0ZsGQ!d;#q9P@ z*;}=?zH#0Zh^d76sO9I+p-;`!$@Y`A3~hR}_IGAm24tWMegKh=2x>)%yTSGo4Mqj2 zotYFRk_&ZifjZiX)CQICN_J;pBqLesm;|O`ZZb8=8d5L$0#mS2!vM+5gBL~MW(9wbhf?|=V zteQ`nu`~t=@^m5I$_4B*6|}2Al9ZF)W;u#gr-fz!kF>b7zE()DbM=ezVsUm0AQWa* zD`y`44!V6egy^^I-4N8Ob^BSZ^v_FnU{oc81I{fi(sM8ZtlK03Dk{HYWcu8*VdOWZeaIB)~I2v7OPjxcs{m-$=K;&{hZ57jetKvUQ6i} zvmt3YSwPmZi3A|B)Uq&xRE4ck^4||xwb?Hx6h=>ePMB?1bi$0IbVMYse@>XG`8r~` zCJl>PJ~a5#uojG?yhCB(v?kk6_QndZ7)lYc4|=U!Jd^L0{^LS3dJhr?!rA#QAgiO7x^^i#(Oi-kRmynfLjL7tqC?o-g$-?s>tC{lk zH4+kjv(yyXsjQ^R*B4N@=;AQ}IyAag9TB~38`FFV%tCB;BUHsOkyn0l><+nQcG()M zO1{o#$Q_W?rY&36obq+C#~&-c8oG^?YqPHse5Jnw{(==( z6DwSvjlrf(vGP`Cuj7`HPR-X?-{k4dPqLsx*Laj__6%l1S0+f zxF%HWwv@ZwYrLZp7!$~5lbflPg(U^{tKgubkn8elC_3PeRhL^s+lds%s#}&W<mYrXiRiq77mWW2hL>zhyY^CRnTi;~a5IvXRrgkF*V{3+WrOIa1!xp`;oz z$JQ%uY8IWQD{TdmOY8CKfJElS1;D*=yVM}nLez~UgE6m7tH{Pn@`%%N7+%+Dl=r41 z8ygGe)Y?zQd)*~#{S^f=(W4ObSNqwrCg`vB)3O@= z-LeXfMuTlMGPCMxQ+B))s61UAiX=o>-jVN4^9j`%)Ufd}c8+X(rPk2zd`buEq0hib zhknlVA<1hc?)R|`?uYF2!8h%P?eHBass1vX*M7?h<39wY_?+`4JO2sK zE&L*zUb|%zq*W0uRn+<@Al>*{1jS=VYr6Fnd5YX&id8OUv-QX943_|~1- z2;07G?}kmoSj0ZYmRM#t$GcmM=RSI&^yu|am`SwL)y~GPuWn;?8GYBz{jdKo_Pv_V z+QE`)hV1H22O0@#n1nXODK9{iY&b2KG9FM2VG0W+`(H&m1?l9|&urZPI^D7Vb%sP8 z`(L$0tG)kK-|GFZomsH|l`Mdfh3Ap-btdWlSCNI%y9F^5QK&qZz15IrHp<3$Bs1jJ z8(xc=ZIEn_!I80hm2rXTtvn-P`TpjmH}1`lK}N!W8fZ8#e4p$7q}T9X?SI)1{yF)% zF#KT8asMBBzBBrI?|t#F_9e;h_x;Z}|K8!=!Xv}I)`ESzJ!hW-7TjeWvW{4{Sq*E^ zdY*NEs>;Q#8Vbz87x4=G0m1Szph~caq98-4LUqe2pw`7$Vw}_4QS8#dpqI$gA@E>W zM12-pkTJtSGD_ID$9!@Nd7zf1`OStlM)wR~nz4?hOv{=KMX-BXHYQrn0r9Y`3t*Js ze2Ijd0hvTitRU%kYlW{tt?)I}CCXWQy2VAwRj5f%58@<7`CYwz`g(W&Hhq2Sz;-I( zTD2WJrD11x?9{h?yCmm>+U?z?bGXx7Zrm;9?;Mo)s#f`W^03Sp_Y4b#eD)5J|1Iqk zw~0{PdZsw@;!KItwO^=T|9*+TcfU})JN6&M-L_wze%Ag&xHmZ0&}7@sjN^{XP>#KS z2AIztdy2Rx&QO|tO1rl@Gx(oAimT2)cf@k0os$sEy?2nkd*CRKy4ODEDDLU&CFD6z z7XQ+e_%As4y@}alxKBDJ{^#CyJMPkT#QNQL2|4q(-*cl}cHDcPTt@HNX$St%dv?mr z*?Z4&39cDMHW%(Y;#lD^z)tiA@<*)PeT0U&#x!np@9_gxJbc4WMTa@;ROK>#!;Ny8 zdB%Nmx$fzR_N@5qGoHyM8NPX^?IeetQ@GbXmCTdtPu+xj_SyKKGl?ju?CB?GM~@tx+%vHUS}Q=B)+7_C z(I|CkurW1HEUI8e2ac6amQ<@_3BI3Ud~kFUsdc$P=(Jy)PCYgq3OHFriQGYOkC{n> z@cCNVOc$>;&KNtSkZ6Ea1AM08kQC86)5VC28g{fgZ$av^C{O&7rFnvB3njN49GZkN zttd3cI}-r&VQIDF33+V{Os5ZsW?VZrtm?N42eK6XI$e+SjU}N8kxZ}!X&w~~^RlS4 zLKmeH+^0&0X#Dc7;-GFQ`fsqAxTdbas{&YRC=_GkI)}KV=S{ssUZ78{YVK!nsm_V1 zbW<}^8H0D~bYR5#Dpl0MD6Vh#Dw15k5Ve%=ZIWyCKB<%o_v&1K$B&}ac*W&6Ib^B}}1JW8b{6Aj1uk^> z!HK&%Z;eXh*1H2X=p_30HWzNIugK*t>~~=docalZobWe?E#TE;H5C(l{2gF2|@INbxgLj(Q>2Zs5fzugI0HRH|R5(57dchr)DpV?H2=U=T=ci zU0FhooEn4aN+IKp_w4NqYWKFdBT2EtFSYmNel3NQH^9T`jj5bAK7kG;8_p8#W*u2> z*7Mq(OO-V@s9JWYRR%Pne7Zt<&9YdR!MX(oZH7t*!{1hA@YCm8w3tR%9tpo3YoTdbnnewJE z=y&e*?vM%M**H4MHcehx8J~pBCcFG`TaDVI-l#R|jJl)3s2|1zb@?eZw$;pH#Rgd6 zDHx|mEfUxmsLrg3LX-wo2v!Fq0^|$-W$nRGJ*u0SGl;<_Ykql;z<9L)OpBd>Ob?_v zDhn1CLFJpv!vInqD4eP>BT77+zf~L5@wp@_u$;(buh02Rcr7LgkWb>ay$ zO@-hjC-qVYb^Vy%&=K<_|}Z#GLX zU|)H^2osjRCxVdt^eqK@ z#;$c|z?2}7GZZ`H)G8TXX;I=a0L_F(N`RBeHjnxIm*0^mtr5DSihY95axiPvdS~K<*tdF!R z({4hrNvZR;4z1XLYEQd)d)lrI$T!f-Z~5ea_yFnKwQlRJy{&f*1PbM)wB&;B0nQW$ zyc6I!P2_1*^j1MqJ5%tslt~xaLE4?QUAEp{w&6Kh2J$gc?X2wvY7?^*PIp9i1$>+F zD?`|pLSIjVr}lJE8v-ibqxD>!Je_tJ6o~H1(uc~;5MA4=(NAZmWHGE|Bl^Sw5_D!N zR^~f0t?kngDNg~NnWyM`I$H^$o9L>fw&@k{nN+1VqH$E|in{3i8bxj09?%-Hyi7+9 z@-hH$ItN~`)>S*GAyh}7eUX*3Ar*u85b`a7RUI%$Y7?2-TMWIdi`**yoYGvgY1H7!yUY|fI!4*J(Fjo#|E;;m5$764a6Fml^#RCq-< zO`g}Nt-gUcXfzgmk6d|5gQ=qWfmfF3cq6>~j5o$B3wr$^O2PXxe`w%L9iFQwS>kJd zR8g|T*M5|RS?F7b5VGlkgGhwyQRu1YUCL6UR@HV)4l{63*_dT_zU@G!0WLuCM4gv4 zF+0{RNWapA-y8KugVAs_HyVus56O0f^BFn>WkgR7wKP8{41@;;lbbTNG}d%1EbOiJ zbC^6d>A1RM^wd)L@IP8+Mh!6A?+jk+J~Q%~cLg7_f7A~DP#yQ*Kwo^1(;a^TSoRm3 z@67+4>lQwV8l9T`ZGW(y$>gbRtxJ0-x2D@B6OL3MB|ZWA%d5tjh@k7BznHnkW*nY?PI&3Rw)L zYh$y(WP)(Oc6BTZYNG_x-Xs;?*<@osoKqWQOPh>iU zy)g1PNr;Rg6)7;|nF*RcPp^=pKsZMS!ehg`2+JeAfup!ITm3&;Mt_)4kq=-D^ZRZ0 z?QArC*d7jk-TsUdz7flSf8_n49lehQ^2dUAtNfqk+~SvVC2PC31hiL-0}5uSjaqO@ z4uWG*V(b;z67$3sn%gKHVq6x27z!%r%!X+rD)SVB*03h40a?3mb068?Niu57l=XcR@N$K zqC+e_S9H_Ff~Mq7&%OC{y9>&`yUbc&cm4AEc{=FQLLHmhVvm{~%2@Err55d%uxqU~ zyXH{!FlPOX4zgB)O2q+eYZHT*Y_M9n(78RXQzEdX`qP$u+?=YTcTUsYBQ#9X&L85{ z!9_fJU6@AXvwIyYcktV3Tx{-ij0~N!C=`j>iq3!qzjgo|~T|vw0qItY!$*;X0XdCv_`ZJ=zC40#WsWMGk z@l3Zy*F3w?2_I{e68E?R*&BSE9K6R(;XiJQ;BixgkCy^7l$o}U^>}IJy+tii*v&ZE|2?ivf@DC8-;Xle?SDZ$z$yL$Tswi*z)H_ z=^Gi<++fXTbWC7sAz?HBSO~r)4&u%N4qn5k!Hl2ki z3&?FZ2WFLfG{-BqH5;V^z0pfDl{?MFW>HvZk!@K%s}6}1!L^*Xg*{lpP#wbHg3i5% zz+T-^NR6}-Nm!l)@=8NUFH#b8np=IAzQ+~f2gjF6a7^p~v@KzMg7 z1Xi5E8?bTWty~QL*nYhV|IRt?|FnBL`bW1Xd8PLwPX1veyG6U7y7W-N0ohG~7zus_ zgQ&7O!-XBdN(1y$$ZjNLSI%azbK77vdH2|IDkTUh`<^4ztI_fDJoLsf{od7!o9|{v znzs$z1B?h2;{@9>XkLpMZ=)QXX#s<2V|kd>d?+hGA+QcS0K{5oE9U><9JmqVBtl!o z^bkoHdAY%Vv;u-{0RbN6zr5XfP(k#6NMwnrd@ zo*w%xaJthW(@^Z7FW3*RdHd4C8d?Q0rJIS8jMAaFfXN9e>qpfIK zU%HRg622mEH5vf!Hph3$P6@lG z`~q2~mBH_aGD9XZle2eA>)-`U;^xQ5hyN_=eTKsIu!n_@PF#7HC>WFzc|>-*r0Xq~ z*9(~!F@vHf;C{4sUh|8sRLd8_jgd0aMR=0&T5 zMTIbU+Sa+?J)94MqmNZ9h#|-lBobmwSR`jo#LxzgIivWztiaIbU_XZ^6iP@GEGw%Q zF%$`LVC2i*NlMf7fGxhVLy1ZwPBE4evbr1l2f}%!b|O2@7Bm>RKD*Bh)(#HIz=?7= zj~JFL@Mq2u)MGv~u*^QlbVqzq7$Z458s|ulra(JvtsE0mE`I zjV=ymr!8FV+Meh2V3Y>qPZ~}b`~V}`%$I^Fa+HV|(ax7Kav`@em%C+{WNxDI$im$3 z@w8N#$MvQ$wI9Q0hr21h|S-)jb-!+cD$X zw)Q8+Xk~@MLI+!*JQ1U@Ssmk0VlK1oLy6l)`i>ECVniG1yI~*dZmE&Jy|GoD5et3j zU{>oRE*e|aML9F@sIkyD&Iw3@;#BWzo9B~$%A|~vknzw|*>;9cx6S~CiNG2X1N$!b zobr|yg5R{iXov4bAL=)qTcS6(QT&kWChu^+pz`1H-NH))xA-#pz8u=F^*7)R=;fLk zF*qowsNrJzI>klW1P{s?gm<$ig!h*f3W`wT?~*k*OCqVKE60O#JcRh6uw~xbE=V~h@SoilN66y!6%t+y@;lskN~7TupWE+;^WVfjK(tSUmO9Ogawn+yG3d~FDu_7QN-3BxjvjHYn zjckGgBDF!+sdH7w=fK#w2~@~I@f=!){wQA=}Th-@UUPa;==HXF(FV< zrZG-0@PQby$7U)6;=yFLjSn4yypfzEpv29w2UM*8iCfhEoeb*G>O)s6@)1{`Duh&)Nwq1Jw2 z0idt4EY(zrJ$X8Sni!8LO$4XSH!h^tQ$|fe0GqR726XLwb2a4^+SE%B9N%au6w(F5 zJ=(Zj!vQ_eY6`6_It0M`>l2cinT;gb%_f5C>^@7JbvU=#SXzISG~d_foUNI?Xe(fi zkUkbBsUSTs5_7;L4mNy2Q=^ncflRatf_X4qegx^Gn@#zo^6r!-rVPtgD~t_bNFiNr zq{|5{9yVfPz08oo^ccS=Pvs#TC?vlB^BP@d2_3Xwgu_g=lrLQjjC`IOOag=k$S* zM&8L%g`FaDL2SG6h%!WFkYWH!*nK2dOp2rBF1?q>wJJzspj|iFmZ6C$b>m{o5$uTc z%8<@ju}_8bHYF3O00Eq{>~Y&aqASfIR9U69n5nbIsXf?XIa5H}gVi;4OierZD_9(P zVYoL+VK>4wTW@O&$5we_X;sE;7GcD(49c20C8bokNY}e0BOWEj4!D1vPgLi5d4!-x|Ee{#zA(6OQdi z++ESf-NEDw?uYFBt2uv*Sq7$)g&i#;jLYOOCZ{PBGn2vj(SvkAf zT*1;r1FD2g1vn~{w&Js^%gc-U9JsZzCTa^hH#U#u-hz%C$o!zD>2-kO({mGnR5O(#+S#QXpFo zAs3uj&WwwzM}$*_W6)H}a;kEwqviz|IzkF!wi;^A&h76!9WoEa19Lp>X^)+GIO5ZU zIC$8dDD0igtX)P=UVG_o5=#DT?14klYa0>4(T{Z)UGpR_EOF`tkRfOWv7V_vn>rK1UX<23A^?gV^lz&#g2=8Wv~xc zNpfVAxclYwr~~3mq9AWgGn}j`_ycQHOlusKLnIE0fsI4r9Kq1JRpzZYq74Jpy^>4FOzc|}OBf;t7+(&1i2O*NPeLHpBv3tAa4EFX} z?#K>>rRT!~+bw@*539I6m4S*?8MJp4C?!B4m5MRUr7;E26d@8zJK_7yk_s*e5+R5t z5GNOC2P~Z+pu+fYFU$_hIFUN(?8L^6=8@28O}QtMV6A{``Fm_OXMa%hZP_r<S#Y9?L;H63JG5 z>0&{n+IAQghKNW?_sC61vqs&sy1NzQwXwVcVE{=9*6 z`cIt@4H4T)=p|^55e#o7voBo4NSLvDD9Vemsf4x(DUz_19qZL~ZZ%r^nrmepEbo+s zWMj%V@3)zKD`iayyQ$B*W+S|5TTUJ|%wmivO+J|Ds1`GS8W$qo29n|oJY%sak4TcQ zIcHk2klv+RDA_8`FEe13p8g8Q_ogW57M;?{To`trQ9& ztIB?nvOoDmmf+4n80-z*-5RIXet+882l3{RZGUg*qgo_HT$wwez@Szvz$KWu zN-ljU?QrS!^d%33${oi6$Ylq+OIQn`O(k~&F{gzvwZ;05P9t|kfOan3aX~Ig017E6 zyiS>87UVAK>Xu7?my}@p+u15?mv_*0>6mzmIqD<}5GYm0cxNuHuU<)aU-$-UXl)+Q9+XDbHtqv$ z;hmz(~u5+lvXL|lA!cb~y=bLk) z*IL5R%wUZpiue4jb)F}Q`Nx$MTfo}NMP%C>YSTV<5!^y+P6bj`>S)xavhniYcxYFf zHQ#E6U<`oNr+okce@+ad$qq}yrypnYviX6w3aZefSKV5|X;@&zZTIfI!TMGmW;w$4k}HW&Jp6LAYTstYJW0=I(7rnbc>7#Rn@p{c5;g(Aa%ct$z;VjwL%$}{A3 z!b}gAXhXEm{u|Qs#7-8qq6!9#fGIH`Sp_Y;q18Zo{@8WEq~W~E_W3Q8VxOoa-f?Wh znP1`_NBto6L!2TGvLE)kIVXk&5S?VsYjRHaV}jQ|J+SG-s6uC<5Pvyt%CZIr+nA2P z+Pn#cvnexkd9s&b3HOE-jBH`8pDx znMWicC;Mj9pHTz(-Z(QDxth>*BMrMl8=My^&VIH>Q}He6_Eyw5rNZq!Qja3Pk0QM( z+0Cs1xu7Aqd`X{dZHR2TkLSOUtU?YQ+9`$joZ~I-JtdSvPqcojunq}IK^T=(qx5t8 zQB?CZ>=TLd9TYR$RpQiOtArqmr5B-SnqqmH3Z@hb42^hAu|$Eu{Dw4{6Yv9}k0x(; zF^5ittIs}qDiWyvMN>u75#5pYbbd%y##W^C*xTw{QiBrJ7 zB$m5z*cQzP%HdSRDMvVYalqOES0r6mg43tu$Zd`%w)(~CDd6lBXP}7FE6$D*&H+ic zy^OQR<1FqWadvm(>=S3Wf-@-2-X5H5db?2R*ukC%JGW~$uV=^oax*e4?m-iDXs?dB zX4fGqZrRh@p(Qi&%%Q#mePexxcOKk%Xz0Mu*wEoU2lrevbY#z=eFyf9?K?bjaO9eO zM@A0qKd^sn|KS4%4~!n{M3yz$!bV$~5i;a!pK;;Xbg;O>cFM)LoymmpGzlV|oGm|c zD|>fAZFF4fc0L8zlP2hV3eb?JgkP15aXX(9CbFF%&4QZq1kAkIybZVvQPM!8B!=JB zt{4)PbnPIM9yFgEbs}1T_Y_;B8jzMuO=^7sj#3h-aciz9juHY?EooG6=0fu#45+%X zs%{)tH{R{vwNSrMUv&UIT0Orv8v=9MlHdI)R@}ECcx~xi8t!0pJnlFmtV1Qo2g)XA zxsWK&MMFSFkC(f1O|Ax{B!!O6S?qi373wy z;p~yt+sOXh3zAG%cU^J`MA;fFT+CO>6*Rl;9d}5-UCSxAes$2NCDB-l(uU#1GcPqM zr=jhZE_Q}+^m>+f>Cyw4m(nsXwamQeUS?h@9jDod3P&EELu?@;H&|SH5Os}t#`u_yO!fA7;S-1Frp|LY3|zV?k{Uyr`9=T|-+srqyOv*m0m_Hl2ejMshfn&;m6 zZEt(-cfR-cAA0%zJDN|w|95`pO@kj9`0(fJAOBz9b@t|oTmP=B`}NPh?@RCfPVd?7 zH7``R{MZM+{_5zpBcZqd$frtA{guCb!5{qF@oUwGo*Fk^@E5=P{P?}j$h*9cf9(%m zbnmBsap)5ti_|0TCuK(Z&-`SH{=dKf+XAf?Fukbj>{mbcZT}wq;KLvIT=o3#{L%jX zM^115+S{LX_DP@nO7fNO|KMMJzLeEWO;e$Q8b=;7ac(T~)B;ftf&zq$SV zx761^Ryy=&k9_vCKRM8S{^O^g^&LkB-{}1P?D@C;#jVbR@BD$)cYg2#y`L}a`3YzH z-GBV8`^H}Ph7Z2#R`j>5+QXjkYg_qy=$lAXJ&p7+kgEL>ea{I34!}afY z&F4O`=s$D!x4yLV1^2ya?We0}hyLceZLhnd{IL)1{mf0jQ2Y0eP_PsTi^b}x4+`SuifG?Z;<)N=U_`DClabob({`;oGgQahOSLss^z3rR-^XK=x_Mu0f@unZMKk$!xZhTT- z_iMYJ`L5f3+4_!e{QbXt{_@f9Snt2n`S)Lc+M9oM+b`Yrt&hLwbEkH`Z^!Nrzwtdk zVZS)&xntkMjZa*6<#jJO^RY8Ge1736ufG4!Zh2AtOV9ePpS|(>hX48Vzy9OTdey_< z_t#&UKYr#zA8|kR;^EuE=eWnp8+FP-#O^J2lAeVD5v|if#U$SSCXPz}nU1!3IAz_G=h|8lYVf%8VdW34`SfAvo`otjbf$~%H1$HR+* z{`cE^lb@zD(QRejru!vFUS}FFbXnF1q7srRz|gzPoJfaQRCcI}L=^k4*IJD z$*Wjh$Xb>6)bbJYPV$NyUayr~_A;wmybNLg^_SWB3Rbv+&H1Iq@})Pl&Y=;=S8#UD9y_G1Shs{YyP?Ts<1zI-%J zNo}Dd0n)Y;V&EE7CURTBd)c>wOW|C1S~}SA}LG2 z=y`-SE}9%xp3U#*aOqf4&JSf53vPml4NI8pf5X-^%aBBYE73IsSPYFpI#%d%Pclca zOQr=@@S-y>IvID0p#A}tTcT;Y@i@w^#m@7s^`2i*0yR5ew>$&8pcMfY(2|!-o89Sc z=aEF4l?f2a=(I_C)x{vaNo$quisyub&Roi!H~Nv*cgklUOb2h;Y73zJgp@^}s5X7u z8a-r%#_eX}mkuk2Z<%6*ALzKzy&3E;H8v_{iSY`esJ%u>D)q|DE@yHetUnCkhQ&dxWB}#g(_F7<8l@ zsw)O|5dpImRU7=&uwcKuy*?LcKzxblEN&ycm6uoTLtDxYw!^}J3grM1e&U?ck7sARr@ zdR%QwzcEPMq9h%=axy_wYew7Jv*kSJ4Q`#dBNSP0=v4TS%E43Q%Xv-#*Yq5Xvcs1b zHAxXFfNcoI3}#i)2w`C%Vhl0yE?$(k#Ru_m(IZE5I}zJUHP79_BmhVX(8}?nVn?3b zYr&vs7sKowRY9vjs`CFZ_a;DgBr>`uE8xyGx3CxL=) z-O?z;W)u*;P~HM@?888e!v;Q1TlH zZ>sLNfxC)?mH3i9z#JVb#(*+LPRt^zaF~^Vj-fB3pITLE?V5>nFhoY0%#rZQV#Fee zf`)=vLA^@Q)KyEt2-(mtW#QPQu0Cw7sLNont1%ZyuKJ5 ze{u@keN)c7IhzHp&64LVu2zVk5>%0-3v7!pUnFoZ00fBrBq7CwoLpOxW@Mz9AV3RQ z32U5KITW><%K$9MT&(>Nk3$Pr3QYapYs=W!G8}0f7HJ9UMW`&Cip@zm-Mw5`&f|IR zAh)|$FXeUj>cMMH@_Oj@E?tzJ78d3c|;>t5@ldoUZKpg-TCs9j6 zUt&z6#EpeSRPfNplf1Dnu|c9lk4@$jD9I)jr8P11Sj|1zv-Gza-~(EGf^X?yK~~K> z31&}DOS04x!}_B17ncjyy`zbiQB|D_j^m|6>O5gPF4fD85_B4->806IX$xl3z_>Rl z11dh7?5!fFQEmi@d@t+k?R%O8g_j!i9e6TB?A7^g@K2iUE&e6^lP8anLp5*yG?0FYvcFJFVzapBj^-(0Xi9Id&*H14ZIS>mvN*c=RJIcoFD01K9R zlwp9WQR(zB%XwaE1fv2&+(QczCK5QpR*jWa&e0dXlv@t5*p4B8>@7F=k~Vuab1Id|YrtO_8f z1o8-=f`y{0VL}b9M~KqEX`qY z`3XBBF5Pm=Bv$l;h0a9uC#N*_gurcfM)?_PI`y!Q@&0M`w2Md+6N%e3FDAx>=B{7V~65p>m3HNJLnExXBn66$X1@Q@qWY=PgGNXy~F1OgmPaBLepsr%_x1+>?hG z^JMt*vYo;pqqT(^h^QKPMw8O(w!kk&^$?m1)-A6SgB|H%eevRd&3P~Is1f(K{izp% z57xqOT`2!A?*HLdzQ+3SCpdoj4}L5DXz*)p{a{Xr zkodkwM7q{_eJlqoR``g)%|+mJ%+kQa>J-~B-+8HNzU*>>i_yRxO0*Ps)tE0h4%(c+ zurMUE2ar!Sc-Pi$kN^#w%sAT%wI!yTzF)qyiZwdaXLk@L57UuD9HBYg>6K-||=rzqJO5WVkxj!YF*X)KmBdN54nx-r`>mzKkEH^xALc8 zA%BYH=I;iT_#Xvdck91^s(;(X!ygbF$;@PYQqF)Af+~tPjHl>yb(~8xQx3!`B64AM9i{1o}jgyMh~MRX4)WtXVL_ z=Bw+OZ~O}{LrZE((s#c!7w#ApXHSp!oILu(;!Xz}{;1z=T6SJ9(e zxO)Bk)s;1^Yhn*`<-9d|b1_x5E6kWO52r(GHX7^v@(3aDdL6`hW-}Qw6N@62Qrm}G zKut2*U0OQLHQHTz;4*Mq)Rxlsi#NQIc?;y2KTVYY8!U#EW9p#^R?@)f6-dQ5aN;XV z*Kk&R-kcdeU({w|0o>?s|1CH83$&TOEfIf zw`~Zw4p*9`*ffUaJg$2|dd(q6>4ri|&nk8S?_bnW2hVCl6NyF6;vPL|CWtw?1n*eB zxVY{DAH2MPpYc`XP$gU3*Id&3VDtCl1J9Us4uL3MyBWN?xNxz!%4eQMGEJuRY}7Z$ zMO+2f@L#@$+mONmij$PR2lDvWdgrN{Ia!&U;&x%c_^~-vL62Qp!mt$F^DBil;jhwq zG$uOz&AylbRhC?ZYzWil0;L+FJ6Kt9wP5HF=1;OIU#RhDk=d3GEgMPejk&a&$`E{T za~whA9Yq+J-~lAx5pjC`E^L`b*bRai+|7nqVBz3Jo>wb%%SW&^SQ6wuoTHdn7&^m1 zc^U5qs84le5h^J1!0~R-G0U3n$%)VlC}QpnL1^wsAI$wWt#Lni-1dGm>HW(5-rGGz zVZs~6XbS{v^*(!eRD$};qgWrxg%XnU_Eo{65eZ`_&C{b2g>sXXqW3)_j*@vq9ESwK z6sxN&{QKRg%^kuHT=F2;j(lCI=oTr}x860PhC34+h*ZH*irY0Q$MEC8uyn$QI5bM2 zK@VE!g)wqi$q~cPFxC{0P=bOkW;NveCn%CBhZv);9PJj%S#(l5Ds=--3ZVvgXGXor z3QA49aN%Ugq!vEf`8}M ze*m+5KLVjXgc)FFKk`*>0o04T85#$Q!q0Xlume!gYm@8koLpCtrd*Z+Dv-<4m}7^b z??-v#?EN>xNKoTSC1mLcIl{Uc)pO_WT<4p@t>X({=wF-5prA)0%7y-vIp{ zS^C|q^6M_}-{{(YM{>QmZWnl1DRtiW+W-Hc&#}SZk@YyHrJ>N1e4@k77J2sHunSa? zd8~;*Rm3<8Ew8U%dth((6otrWlQ}>AeXBb(n;ms1tYU9ohIyO080}6HUYu8EK?b z@|p}IgcOFoOEy%Zf{=}uQwO1#QK4+OutXa|jfjjsrDtiDVw38#Op8Bk8ZymEt6X^% z)Fsgm;`p6LO&V=rGAAp*J>hVNk1Q=-xyTVb{cMFb(s8@RKdjg;!9TJ~X!`XIr=AFk zS)*&Dp?Y$&z>&_0x4XI{Sob_$yQH21aJ#hJ=w0eQElx{)eH|N2$QT2O6$T2%V9oE{ zW*zDGRbbr3t#(kb2Dz6~aG5@PaYR}vlWpnJ`NcPcq%aiT0OIMdj;32n>{rA?0ch|? zs?Ic22imuYnz3_%Xx)m#4BZpfup%-YG;o(KHd%r>C|RR}5|i(GBStssVWE@sC(E^$4PP-3a4!X)cF`Zoq02aMXti(XkXapqSh5FM~-Fky)cg zaosA2hJn?mgj$45q+!GJ22>?p$69T`U6)*O9Fu!#=lHyMni>aSw56f;(3|El@AEPD zhdmHj$r3znu($)$Ut56#W6e8aDjZRTBWZlLZ2`evEO005Hh1 zfg3k$$tHMdIj?^Z4I?W+idnreUHf$J}lD~^>gWPKBLiW zq*jHIgWXtKTq&o&sRnfid)((a0A80ZI53U3;E}(!XfU^&@T_r)vIJt3tEI1rRw~&W z8+di;T7Xl?6_g4WP0PJz`vhi&ANZFp^RJ1yBVB7VKJeLVep5m;RN%>o#~RK_>^cA@ zGVB|6>K&pTJ9B)u+5q|YTNh zdJP8wP$~Hhww=f?;~sKAqfo;%!`84}FZ7lp1D0pA;|SuwQeCS+2}>PtTJp|Ft-Fc) zJHeZV<_p6BDv62Ch~-5`3uDbQ)y!z{Yg{ejz%lYqm5fuR`6x73n}b~vwbe?G^38A@ z^};=BQk@#Uw#Kcv*<1>)&TqP{@QTOs)R7qJS0UB}johf=P;V}{j~>-IEOB(KHR3W( z5kaNd{@?&ID%+l6C*l4ojteTmv8YZ}{8`Qg@1jZ*{~g#`r)nHWQFT75*@ueeqd2V^ zgzvF#4Bj6#soJP{v%zImi9GY5Zq#6wUDc!NJk_3(Z-@L%Xs+1_F^6}CNGNgaX}G5C zGp)kJ8o|~TGlMzA6sH|c{pRJR<(13q!AQd;1N*m8#w@njT1*js)eU|NM8p5-e%3Aj zh4*8A<;$h%(myMms(rNFj6RP(&G(i+7u3JXteSuWmOzYdbhbEK;Y{+q(O)4OgskNZ z7&|zhLDz(~-I88DVi1;BVZs4F2|_jtldoT6jZn6nsb`iaBShj3JS(|u4(7s;$rLt4Ae)sT6s;h; zrxsqRKAgS9IKO&g@pkxxUUA)oPrr^JCoeK;5e=$TdBR;$G0USC>{a(^o>%|Aq{W zCwu-}#oH8&@;zaNlmjPqTa`j96=0!u6LX>G z5G<*JVRC8Pn(w;Mx;QT7%h^V1*K6WbPQ1*!mInD|OKSsmXIR&4+d`<)A9`Y&Z&_EU zq;19{a=19^&u zK?~Riwx78!VF)h>Mnjynilt|yf}Drd?8q8b;dU_+y`t9clgPMajNwc%DnfkHz0~fr z3`%utBFIVe+GT(0K>pIwi&>Uu5$VRt=u=XSMW6|%`kjv$8f9heZwv1guCB8#Yt&|rC;V~4>-r1hn$nnqp$;6ik-3+e4Gj>#G4?Ys4>96 ztm#NtG}0dIOXOvwf<+@_{2efZ$nIewuvKuRaANokoHzDrOOdG-!f`V(9y4&gNhDC5 zfLK|fJgNXJ*(0~rKn?Q?ZctC3plrd<_C`}b-IgtM8jJmoa|QuE=C>QtDYY~H(@@bCllKRCXN>#%j7V0B^V{-gSt zy}yZm+kM9m6De&P2bfo$*Z^#A$F`1JDbG)BaLNxS9jCHolJ;v4P@qzKXbWL5 zNUg9aCGB3^r&MI`iwu*lI@XetrmiETMWI%eeUJ%hPDmx(ac8-v6)~GkrK|*-U7;>} zWW?SG0TVSDB+YAXz`#f(SH^yoVw{R_Vxjy9V=6LJ1sTXhQ&*{q%O_OTC2&HrC?~7A;u=jA?xq^BhwYDja`Bt;+Zf9(p2_EK<>&haefdAYJX9u?aq@NOiHs}nbSXU3D{i$bU2Y31Q;>Bi` z{w!d05__aXpIzjFa!#}Wd1>j!RqC2M z<#JCDV3!u~9g(uo+L_-QF|aaUG<{;wqDh>7>SUESiHuGOWV6LuMozofXC_$|Br7r) zd4p%Eh<4Ehtg}_-$THDsu!QqXQ6`a2)9E9n%=wfbpP4ca0NP)yTA~9$g^xXY>=2`5 zc>U%|O0QtE*qDseT|)&cDQcomcgP@bE*>-S7>lMC=fP11I25SIDI}Y3Wg{# zK~u>_{+>JlrCZ>7KLu#dTBhRHvPbF5f35xVf<2YmK`5iz5%c*Vc->Kn+U85X-+oY@ zN6I0-UeDc8siCP#7L;U-Z8bY(tWXHxD?3_^YdlEr4uv3&t5|Su5r44iEJZH{lMOBQi&hI`AtYCMBv1 zG`?peXxlPg?bUV#9tXrBMl{`{@a(AI9EI`aCz~J=kqX}*puQxIWZh6*`wkf;Po&VM zY^mFjS-F#=u~tIFz1^nt4S+)#DzB0c&*or0oT_^dk7AsVVvRC8@s@Ch%0Ye^H(ln{ z(U7SvW@E~?3}!`h=FHG6Cygmpc!^x7FEcybmA|a^#;@PCoECU@&A5cO;^#5mumd z%x;sicc;wa)zSyG)Nw@sQ%&dNz7`{WDcH5S%PFOFEIi)>QC1{1+ z;fOOlJ~r9GJyWv1wY|B$zP+(M+1J|F+*jY%*vBr6rYt%cTm)A^1h{ny*v=h(M;co6&#O$Jk$oJyd3+eEI!R2KdX=V#miy)C$6voZbh7BF z`#Cx49$lSeT*~^7zw?QZDrYaIlC?D*NVOJB)8{|%I$SELyG6Z(70KcJg)6VDHMduD zFRbEReBsqam;)=zH*V(cYjF*J@z(V#vR?;7qbPxMF%d1uVzl_i^(ENWbYsOJq?+38 z3teHa&(Kjm3?c zp!}++W!!r>5;!P${6TajJx5{d<@80TY@&bqUA@*j3eC&zg z$#$)LvOO0JM;vtc%(&xoS?cCs+NRR2@EMjy`)BrCD$WH@po<&UDyQ05Zmw}I(M1W> zH{5bPn6@YSqqI4AByEUOOo8`ZfqXExHP0<!@J zQnjka_;VR(SiuLRG#AN9JtEhPN94&+T8@Tq1kZxKZQa_fw6^t%yt+2~pEI|m_eSY+ z%{f_6Sey%HWm*>_Fo>V@+k0!IFr0Ao0(N|B{%(>JXthnCmQ9(#lu9aF5q+Zad64sb;SgzR-Zf3^S=D6D~`-OZIX$_7N zLi!gss*#^vlKCTU zEyj32q)$0`6-y)3iQD&3iTeh@>+4mZT`9}gqeyM_-*S_E!0&a#w<(c5)2e%hJg){I z_?vF{B?{U{UftaHx)?jgxqF8X!*RCVR~YV|=3A`O0AB?HJY10;4gr88*WpD2YYc<& zIx6E*=P8A|Nx)vM8IG+G@B>00hz!cr{cfd$J$MYYj}8`K!IIn0y+5;hVwmYrtJTcQ zZU9K{i4Wrn=Z+Sneq%0pPdKGAtTv-UNoY+#skQ~r^FX~7mD*jNHwt;nXSRkf;7YL7 zjc0-<;I%ZOlUws?%ygnyaC$3-u7}#Dd@>RZb;1{DqTcG(J9#SuOV(&%1ej?2I@w@4pV|r^c`o=~vrvD(OM@{9TeWlsUv}%wsLq&&U*(;{Kg%C>B(8=pN6o0F zX&}}u^8v26UAEHClF=`>B55Pw?*;Ze`WvW|i+UCl6h%Nx#DD1WmE~8Ku35ugvSqS# z@oPaw*mG`yKi%@j4up>%FMrbgb+_`by%VL+vy1+q-;RIYf2saCP`n9~49F$E7@@}t z%7#WbF7<3UxrTx#P)~qLuqU)+11FR^Hq+tq&z`9)+`7252&=1QskrRXmoAPECiFx)xZ_*A zX_+xAE4LPJG_d@3{(_un881`KUeqEFmW`@aXoNT^DOI4D5)}LdiRj&Wtrwsva)&<# z@Q!~4qDd^lidSbtH%v?vFl8*w9{eO-5HDQkL9^K{#EZ6y~@{v z6SZFg#Qcv!Fa9UtpJG+S9YNKq!x@o#YQxl76{f$43jD-9w{0MY<26AJO;sLkNXm3bqzNm-(rYy}0{9WI-o0ipOxPNch}E-AQt97BOIgLNd+`!IsoTqMhp4DKv{ zkO82HL{#$4nIG|Pg;$hZ*CyxOcJ%or3O1-l&UQpOa!EW7V_g9zLx4EiYMIEl4}@5L z=M7m0F$Ti_lEH}mXP`BwK*kttS@>RtAH2S}J_>PN&>?51LBDYZWVqIG$1*zCf)uWW zAiU#826y5v`Mhj*7>cQ;53VY2+YU@UvGG_kYoK4cyiOQ<#!L}j$I=I40J2Q9EyTZ2XP@N@K1#R#93d+NZ>0b;(z)#we5RPW+npx(|ZekYA-^q6rtGnCs zC%VD2Uikcx@~7SZ(XIS0o67g&#pGxGDE?)Css0rJ|3hplsK`O;7q%4vJJ@P8p!q(M z^z9>TY8V00X5!V5bNEfLj}?w&Mt0b@z4%R519YguCVL8kM1J9?$SQ?_R!FgyuZ#hB z4uspaSNgXV`J}enig`fG9cT}1BPMIZT8ZoLF9p0owuxZF2M5{qJigX3yfe6|nES5x zF77gG?>ej3vJeRTlx-#st9RaA;D8!|yfxs2c-)(p(Hq2G_$7h1u7!Y>SfNpqN+b-! z*c4NcRhsvm@y?S{B6wK3&Ad?9TYE3)ZjYEsN)UG4V@VMbcPpB|nVx0IyH)95DJ@p~ zZgtk;R;|G|`(~P1kJt9iv?c!BH&f5@^uC#zJvQRqYWu*G{8J0B+^t+LM||$q_3Kw| z-)r$qpnA9RvdblFw0A34u*;Qt`M6t++}c;_1>~N~<38jYD7y-TGEmpc`+-A}fxN+} zmvUSy&n^NigPAy3U?85AFP&Pxabx-Qf!x8Uhud2~UEyhkgP8+y&%br!ivPlmD+B3+ zQ4bd9h4Tn2Q|a_z{y^NbrhKfap@F=?sMoU`nH#ViEHe-cMgYaZgxIz>&AhXYB7l{$MmO-$={f>sv{0BXUf* z%onc9YT(7e<_F^5+J}<#NHkD*FzS_4-}WIh=(WYc?16Y*x_$1(;`N0aix&;ycpHKe zNX;%V7_823P#!Dw11I(O^F1u#jntuF#llbfwnG1#qvxVIj+ zXMj&NP;D?O8NeO??wX$!Fb1>k*pv}NsYFzkgTs#92#>rxx3xRydg|*?edO!r%+~m$T{Jh zb{;`-3^pzQB)3MOg!xJOS5y>$3yWW4baye?4E_V($7uS-0TMd#7*Q>Q8B0gq@o`)x zxtqr@WA4q35c(4^?H&Ix>SK=o1RQ(^w+r|}DxI801U8ANyE2jtIn@yq0&1gZlSTDe zq!>Ffm>wMh85&PdPC4;xy#ctbQVu)8s6Sf8surR!K{gBq3^_m<_a+7}1ttzi5q$ zFW<;cu$fT?Xxiq&G&rr3hV>pq;%ZGIjX=Je)*Ob zljq>_UIaSbV?v5&Sj}%)F=#K&Cd1jqdE|w%alU+GrHM+KT3!Ui&SJR|rEw{mMGIG# zZ(YOJC^yR(QezNgh-^`1yK%G699{->znZG4oyQ65VrubpELGTEG0%3Mvq(BT;MZ0z z+yD>G?fQ`Jc|@K;Es`yHw&4e-G_m%^|H?!-x`y0VcTo>0~~N54K*Z}7mfI*d2R9L>&rJT z%XGb*b`53m)HM%s+XAaS1}&Jpbb# zYK7Jr3#Hv$zOZ~n?!h>$o#zg%JPj7T3MD}Am#!m(Ha=$6uc;KhuiMz}pI@*pXIT+L z&d^IY>*lHCUsjWIvM+WXw`5|IESgt6+Mjpn7D^17I6L~&8LOL13s>-O+nd1W?K~T3 z9SPym^2PI47O%Z>^VRYGR){hE%!KHe9IG(Vdn-CmM*Pc0u%>G}{YHA1dT3_;(lxXm za~^t;t)IW;>HG>c($~|$dgTUsq~~ovH>_3Q=Bo_zt61u-_j2Ld&Gurvo7;sJ^9rdI zfrh3L?pe}_l%&z#TfP@11p_F&!+x0Uo9h)pz>;P4lo%YZi9-4qwyA60-kNtj+=yAE z{WWi2&D&A)9ScOQ?3P0l0pS@+@2u+KwbD-CPEp(MX(EBF`^FN4j(OlDM(S(d0&^OFSJ zWKaW|6_t`&DD^m0Fe3MnPBwsOij)j6bOtQx{K;C}GpAr|E&w6wgqVh@d8cdnsH2

zgq1_W7AVo2>so4?fuZ2gmBpr(9qbqweGGlGpJ@yjkxt z?*rZky&K*qT;~<%JMp{)jaBELOu(RI=~lsV7;HztRfvcetK@VEO2w%{pGDATaSV_H z{eU`#=jyWUh8iYX$GqqxqVXmIrff^WP>JAOQV4i=nXC6oE8trqu3l2`9u+ySU_(ss zT%BMq?p~bzjh5qFZX{?AG=@+$P_VOslZ)pS98&ZN1?Loe-c|TwqwP2^C^)R(dt8Ob z8@#g<3Qj6`Si$MWh~u16@Q{K>6r61UDSfap<~Y|CTvV{E;JaOg3+m#Nx_U~%Rt4h< zo*tiYoL3u@j>CO^vZ_GK)eVciUOS3y2@7gIS zU4QJ%DZjaF-FuwUXWjk4amv$2LcmKqPEA5d zzVp5i8|E_y#+>N=2NEZ~e|DEsPv%3XKB-{pIKkdId`%qLebT8vIXC9ipHlGj2{cTe zJw)zv4-vfUV8yAw_h`+jU$~##MFm$V*Essfq|^AE``|I>x!LEoy>s8Y4!@`Jt_R+I z^3b+Jvj?{woPFlV(?_1)_WbO#+sbQ59ta@RYix>>y#1k z@N1BTtH36m7i6+RRf@$VpX?njk{7e1M0T?28W~z0hDv@2>Ewryv~B3vH0Ogy!qw>kBt7u5_}vx&=vVQ;P?biN#BoAi{5nWTen509HBV8F^%?ek+BWa8Y9! zV-)1Z0tSCEY=?m=MK0X54zJ8}tUj%@by_J730=4Zz^r#>kr9#n%u<(e5vdiS5Oj5M zv@d2X;IWG88>6@WkQCT zEy2$rtnXdZxjZYMG$Vz?!h;o?$(|{ttvB`8Fy}Yu?EG=$m$%Ry9BHIj@&* zdpDG37no3Vt#cgzxvIzX#>eNsaW*m zOmF_Yk;~zWHSciE`yLJnFVwsPmD0vaX=f#Vz7ij-l%A-R&Q(gEN9FspIYFE<;X@{T z#DvEPE2R@AJZZv*O*qE^%d73La5VPMR;pY5S1QeM7*`L%%pwn8Iq#@cKkbH>!Wmdx zoSU-f1%JhV#($r2MYH8hy4!)H*RYUNu zMqm(iqS0s!Hqm4>B^N?WlDRR#-JVj99B%q)ory#xwR#DA$+%@m2I_pd1x+pzjVypa?{vUlRZ%x(@35|I_sfc@hO9hohUo{iv*+mrhzQ`hMfw zG?Wgtivz%>CpBn1rOH{3adEo$s2FVMQ1nzCH)DDPtLnyRNwlrz&nMqf%04zcaHh_h zLbzsQGUW9WVkHAYC5@WG!3@)Q;>FL#`_W7Dp z-o3_1o2Sk#t~9{l(;0kO$>*wUz|1F&tWFKCvzkb4`VBTQ6;AWi#+B$R zYH>U7G{itnJ#3bvZPq~s+A*33#ypBVs+l!GMX@z9Fw=+xMfLva$1TA~s(T0wP85Y}=lU(rj^dc8MOtaV=WchouYh0S_ z)3x4=Ug@H@Kg$wJ`~Z>DUql1Syf*aw_4atz6$~6OpY-}rr!_xVPrz)^R3)y5TTPA> zZOMt-Gl(1N!LCX?79KV^U<{N~Cub}g3yvaj56_sKmd$CAGaiixXDjgp<`s0GA)7Nq z&O|g3ysHvVhA$U$hRK-A6f7(NyqSCEgIeg?7G4Gg)eGL$o3I zIE=E5;b&}~t%VyJHnEMt_ZZdTAFx>+Q>!Dh?I;lcC|N=H6E^HmA*&MpUNK7z%B=Ap zl2r}=#Aeyf$LLvCqiXQyWYxm2+brApI$21Ig8xib6n>OdgyFEAM<*OZ$WidNS3!sR zb2iI%eu%6X@W%I%)d+v6m^Dlm7I=fNkkt%-%4XTlE2|kbgP$X-75@EVmKxM{{Ex^= z!e5njj}mWRol)ntFMo}!q3}0tmND4TQs=cn|2A2};qTilng1xY)OqdHe@s?8{4<+1 zVX|~DZQ1`9S)K4d6|>Ys?cIMt)=2mrsPxm+q^YGWZQ`Hd8#M}~@o4x3nZBTaS-I5g zXf)~_WXoz|1-y%lDKj)8OWWA>j9l|sc!wzRFrA?YY-QYKU``CXnS3xZS4#VsWC+J> z4b<R`&B?|2Af;)d`^{RLOq z0}RQ4gY;&lgM)y(6{>dFlPQ#})zbU8RjFC)c=z9>2zv8JRRlQCn*HfQgV0hNc^@W9 z&Uz21FecjjcBrgw6SFMrdD*j=nvxWnnh)N?Z8}-uW8dg)uqn@vV1El|%2bWDLw6u<==HAm6{jjX_kDKJfe$4r6Iv_LZ} zFlq|SbdvzOh!)1Wwl6J?Ha39*q>rcRNtQltD$Y^ywka@?78uG3Oqc?jDeztPX(rPW z!;~;z$5H5gLc=2}!||SE5qb=73alcKxPJ07+mU{+N}IVy zJ7dl#LKxRe!14$L^@|v6M{JCpUxaN&rcHAp^~#|CsyrbFK;*?TLE&R-eh6*UHtxL| zkct>UK>oeWx$1nE>wL!Lpy?FgDu1-GAPwK>ZG4A+y?j= zyj1$*kt4M)jaK3>kG@r|e|clj{4pG;-S5so&Ww0t-Za|W4+2p<4n*-joIpM2JdX=a z2ZL=43052tc>BhFF=8tyz&WfsF)Ge6j!9x5jP({?n3^2o zMXv3!BYxOD|lML!wTMkQE2Ckf^&*KqTmSyCl$;}?w(*d z!5L;EIvZOE+Bdb3p>H7Y$G1aH2166bO@p9w;D~-UA2`TQWo|Pz_bZ2|kRnzO%+5J+ zGR!%1cwddf=E$fMPr75EEB7{?c*}0UmRq-!op{?;f*laZ@lF9Scj;<3#B;m{(m0-n zSdRB%#Vg*gw1Y}Jq_hWyY3d=haspa4KB?f8g3}7lK(fXU>*^5&k1BXf!C3{5D|kY| zlM0?v@D9~@T36>3Jfq;*(L>J8@%hO;Id?f$P{A)LmHuBqHX~>-nSer1k#iW(c5h48oL0 zx6WEME^4q6^;*_Q&><4kU~eS2dBCvM@x`iFWdif?Y2PYX5=osfAdTla84Mu2r>v>s z0i(<7OOX5UsXcLW1Cg=?t3D?b-Ee=@Ap1LOq`i1x6~xslruM^EH@$EzW1zm6NtmC` z#DmC_dlKjgE7dHB5mOc`B1*363m0FLjcF@<%4Ej{2WAGingUh(wc`8mr5UPpDn*Q^ zXhO*;kO$LOVJwl?j$K(^kS{YJg?Jdc{W5UV>i`8WE+!@;g}|CxomD9UsrK7a9yao{ zK1tfjpu8%zrHpH`>llaZ4a90W)emVM)PF2zz_ z$~|Acv8mWto{H!;6}pUN`KgWPyvXc&8NIrBZ;X7aU*MbfMtQl!3kOYBXxA@On_fHl zeEw{0B!YT_eO&rf_O{L3J$s(slMhPaX4Wg0_pZ;(Wk0C4Po5^hSz+^EM^@G@&Z2DY zStaka#A6+>xXZu^`e(<^RHT!?ufp?X!XMtZfWkZOp{SJ;ZM{Rug23ob_a2ZO-MMI= z2!WvCI{g` zIkg&QKA{9)CSy*BqfEuPXT)w8=lw+7>q4~%&k7Vnbd-uZ?#FTyMd4hkXz~Ddn~8$K zRdhbViFRN7C{~$z`S9+K0p9IPIH{ume1`1a^T~tQH0w_}f%S1x=)8Kz;R%9gKG}T+O`3vG&G_bGc698=yG1-5vsJnYJI7WNWI9^@bx?AePws(N2T^T}~|>Xc0% zjE*0Muilq%8tys?*+@JDd%Z9JDOl`%2~WUg?@M?RR(oH<3$WY!65avJy)WSmZ1=u| zbFkj~5*~s5-k0zUEcm{JM`6SFB|HZ!zAxb{?06FLcfzQq` zI4t_Ugr{NC_a!`RSoQfJ9x}{&my>a_HQDEFVWzh?F!6*hh9Q^HE?%xZe(d{{cp9#ic0C1Ehu^T2Y z;z};F7)81)Ca0NjYY??%CGp`SF5xK-`7Zpy9pN4^2jwkEwbAx-`gY)tY$%+!+g|&S z|JcuZ!PmXn@QePx^2+~P__a#qXB#h-KGXb~Tl?+SsrZY@XQTR$Pfay{Wh!vaxO-%{ z&)e?p_8ta#e%5;&Cqig5aMA@ko*lO_+GjB4=CI8{+UNno`80=Zd?PzMb(rRx1*I)4 z_R~5kKjCvVf>xe$6eTw2eoTzPF(o)BFnLtxYFNReI@uo7$@X-Mt5XW*1R0-EaAKH7 zPs%vlwl=|ZhqNP{Tb+Fth)&@#LA@Ut=jxDx%_9_f@95qKxHNXK&DFXS5WrC4?j$ZNwW!ghHi5W-P-PiwWkpmqDLnB<~ z=ocV1|In^5ag)ZSj3ye_K-TLIhe2cI9&6z=N@WuRHB6JXcWFp_+|Xp3fc2PPI!`Cd zX}iWCBd2(b>}RT%{OU$V^O4rgP0e{#eYOqQR0G$x-J`Hf_M3M8QV_^%9s1VB23t$| z1-zP$bwwa>3IawoQ3mo;m>EZ~*A{LpEns}4f&-qLXf3X+hXo$WF~XeT>_IX0$uo!& zwyKDY{_|KL*w+}@Zx}Fdj-YEPYB7-(QfCr$P=TB@E`T^Vb~4qtzQ)IU$les;mVs*{ za+a?3AibPu7W;6%og(NrhhJuzje+zQn+ohf&>;R&ll@EAq@P$sjiw;c$qbTNKzU~8 zt$LOxDBj{X0U@2p8c0EZ3}4oEhAvGC_q8KAR7gqR{M{j_Rft^+Ib9oTx^YdcY2KTA zf}*ZIe6MptjZ>((L8Ye$PlML98kjAkhgo<>>wq{{<2L)Ywftadhph<6T>s#h+`1-A zTjAzw(%YPVE#_@BdtVmqosV)WPfwrquS;$5U;e-Y?C}7~6zZ=>rT=w<+vL(vfACZ7 z1y}5*=FhL7{*_*_ZKqf4y7teDHde~GFLJ>m5j^S}tXk!;KW4X)z5W4z-XBMw#q%EV z51~^TPyy82DgQpRoFj5@CgE}eB11xw?Cau`vavz8}AV*Y8jwDsY{UvEKj-bRSiY}@MgJb+q@+KFGzIzhb0~mYuD^2r9kIX%dHV)}@pgoRwuMj1RNiOfKdU zz2pScEf0C-L|LWV=0H27!|Z)nE!MH}AOMYqYu|jwI5J}pz%IBMjT7hdGNSNHMf=rY zK^uO1cW|N-K6bYJdG{CH%CCASY9GVY^!NCe>;J_MnjgWl0!$dbY+0B$f?aOK>5~XH z>*;7+9uQ&z7YAqD6!9Kmqo(VooS&6TxSuJNN(R=$@y03XFvJOkg)s}{dQ=A>s>^MM zbRx^Ki0cH|y}q z>z0~KX~QWSTQ+9Dplj?n-&lMZ5x|NiV*WB{6Fgpca`4A7(uCT8!$4?+bO&{`KA;m zUf^kPK8^kMJA=zXxcmvX{B;Z*Ren5psr2#ik=p-&qm$nY|7)rKyK&I`eXOdF2!%_h zHG|Um3TlB{;PHS3Q@jb|ZpOd}5{dEy3Hn>I;ecCcE$2*XSB?!FkIg}r(FyRV;`ntS ztA4Y=)d)cqcP26~#L!{x2Y(9qjbQ$8DHn1?i0VhjSQ5FCLpP!fGP`<3CsPwwicZ=( z1mi!B64cXamp&?qlRDWl1B7s&Qn5)?Kpj=Dd-W%2yZ1`bGku5s5gf6hr7^-1txvMLq3pu<0^%!Q3+Dbhhl{UhEZC@Z`6Px z4f#p{MgbIO)L7+cPUU-%XgoU6Bk2t-IGZ z##7IqtkZdjwK@EA$;JnMULjIX)n&*8-(~I1&;*m#XBK8ea#CRg?5}8AEnC`(*w3Hu zHLRlp4VQ3Eg?Bex(G?I?YH-Ll_&E0qPuqc^>?mq6kdU>i=>GBuBW4VlnE0r9cgm4U zMOD~;6LGf$$HJ35o_xykF6C~_cwN^t!W&YkUP901eVYlM37^9gt8vH{fza%_(?McP zs?>*p{xByr7fvJENLaU2*?lB`(Wae|*YnmyWcAOAD#R8uVJ^fT8xGNG*pk8YOkQC# z6sAs1<L1&~-74i5-WGgrwR|%?hzW!2v*I{or`~e-pnd=N zJTp^Jta_$l8Q)?}kkaZ1NE*VDobPQ8-lvzd6{|VGT8(R6xb}0w2gJbN5?l&Dkf!z< zdAV<3VnMyK%+eV`2mYkF0PrTr8vr=7US;N`ey&v5reZH1@n^RLj|SlrkC(roUFz5T z6Qz#_Q?;)I?f931zvtHfG^{oMBQ7|Gcw8J>xgB@Z5nTh+PGCF)3w$3T1Q7%mcjokm zOBD7j-2LG#D5*Etl*TwA%T-4RK$BT65Di-a@E)u~@Ywt_Ng1_c((y}b%NdJuTtfXA z_NuO6?wX$hmsE65KZ@r0H%`NoM4;PG0`+kopt3R8h9w zXJX4(E?d0O$EwUAEqz=+OI`K9m;uXNeRjVF@~kzi)N0oq$g+Z!ElYNp&vE@AUG3K` z;03L0m&nTWxIT~+tf9QD$glcsf0%WHcMcIVT*LA26I&p6YuKHVP^f_POEe#-^+|HphiUq$!qp9l5$UkBIgzY+$`UxQmS#In(F@t`I#mB`03-)g`#=4LH@ zHn{52k&Y$2qN7Tw&X)nDhF`U)QPO%SLy?%{GM`8Z>?=YJz;2Xc$aMmdSVhS)_GHqb z&?HVUW>m0Y@{J+C*0wJ)B}cH!H`>gw!DZi!F^UwS zwF-u1J=(5w4p|GBGu`V)KmI6FJnoTY{qMREgDAU;s&V!`tn+)rz@~?l zP4i({NVT+0OS>;lVS0!i43$?M{2>L57DzAzFA&N+>n0qKggh`1P-}25 zp~B>`9!{ZncqOBf1y_q1%k->+sU9wV5Dd%DJy+OHj(A6I`akXd?NadD;Ln!t>&LS8#;f>buYU=$LZdi@gDG& zyw|+%b-gdT-j`hOM_lhmUGHaH?`K`-bFTAM*ZFbR`DxerJM3zFC{23U;$OnQAKmlUpvCLIOaRfCj`yFESEAjNR{y` zu2dF}Kn^?mu@vk4psVnw+=TEaO!$*{_La@CCiv)UZS0A?q7pA8SP^?yiQt{11baqF zd$*#y$GCb_X2KrA%B*u@1Hls;VVyiVP1>0mf~RK*o+=Z(L&3SpIRM+62_8uZ&MNo? zcgt4Cd3~GjI3L z6n?r===mG&MhZ?e_c+e`)$MQT5{x$wIPSq)&+|5pKd110&!RNxzUtp1{IvT#Ki;kv zNb=tEF;_otE>gCnS1iR`q}m9h<+|U@SuL)eDW!NYO^ah z-Jmx2#%;p;A2>nyp6h^2YA;_0&QiPi8TWa9zU>@9g6La+hz`Z^(I;9?{I>hVai_j( zVT8F;zu!NjpVJH9qMyfJey@I>yNIhdr~aN#x~9y+C*4o+;X;djpL;>SZ@z4PKIMKA zm(rNa+x2M%z;8VN1>WPJniB+D=WuDfSJ8bx z%*<^aQQ}D@K0deXv|hY1;P_diK<_N1roJC`lZtgr%xSwqV;&|$<{lMJ$LNsQxCO{w;pIcf9&jA?#b3; zA9vsPw!8SYd*N;OrMKNzj(_0z((xCMz3bRxpLO4VY2nh#Z@U-Yc3(Yy>C*cyoxk+R zix0o}p6Zzw&%f=SIR4mI;8j-IWtkfjNZv8m25LZ>5@1F&1Rp3>+7^J&5cmKw1_*jn zZOBv`qL`ey0;eGxO-N;@VH1W6j2Lk?s39JnuA($qjgVyNTHQ&&0r+*XClkly7$iGU zzXA(KJejuoQol%98q;#MY zMs!*1oYVvum8`qmsR1)B?Y!wAb|lmGH3;g3_(yFlS^DVh@IQV;o8khQP>+~ZM`a*T zdGf^i_Nz9c%VL$1nzK!1W10?siRQDcQr0R5RNbT^r3pKXyu>yK4F*)*$47ID^l0-| z%4y7CKNwV~Axv+NdZGSthG?c`;l-NqMx#J4N5xgt&Ye-rY-sQ`S$Yj(Tay>q_HkRQ zcgFi-yv!{)ouKW+o5(;V7CfRB#Sw!fWB!m=CWZONA)7dFLQ}G>$0$`eYXMe~>(Fv# z)Ff*THhB1K95jqvXG2*tGf;^ftjcie+VqER@i_JwMF{H6>d`Dw2+$-;P$CRJI!=Q% zaFF8Rfhi-@=$iKcnnr3Ibe;=Bl~Sapuxx&KF=m*Cc{q)=sw>U*!=&imn#IPnpT>wD z2i%J*{YyuXNk_3#Wkd%4OoqTec`3LFW}pZu)N%Qr?}%@Rs2;)(`N&Y?fWOYLaO~hX z&o^qnRQi^_b&4`PE&5^yC%vM8u6QQ(nDY!X%{&QjhnGNaH6|LmbnpWuaTCo}-KPeP zmW`}y+lNPL=6ym*ra%9yj`<6CVP*#CHN$k)WMCLHIhdgONn`e!fzz}z#jq}^Cb+XE z9|0sk*e+%^X{I|xJBHS;#oeqK_Pfql-?pJe7UjModZ&wf*C`o&G1EDDST_B{`!yyK z$Ja@J@xV}?^Ei+iQ+PN}dUA2$wMCP*_TeSYOFp-Fvz13ur%Q@OVN$qC%AYSfW}$u%KrOu0%)8BK(nHu z7p~xRo=+%$fR(v*%4e!}6umys0eTPnWH>___ByLRPgJ(r;g2f4cYeNf2^6saEd-bg-fD*M{<6?|df zl);Fn13hoa`X-n%YJ9zFeOiCwJ5|MZGKzF-aiEG8M0<^xb)$&DT+eRjc=q6fW3A8dA?$;6gb4J>4g}K(UD$-;xmo+Cu_LU z*ErS7DG+Yy1X#}iCMtLr`0AyKXMGj(%X_~Iv$|%sqTAYt{#ro>vb7GE8p+;Ta-{zz zam0%$r@RI;5?%`co$3#|{sc$8E1VB2?W;Ba^Wr&hZhzMNR7QVHaX1W{I3kArF%(z4 z+EM>+y5WcY&$#$xx$b|D>wmD4V9zW3e$KpJ)XV)lYZTzrZ3=uhyraMi^6T?PymtS= zhLg<0?nhpR`w{2lvDnYq`$jD7&jp*pRzu?GV>l`aYNh1En0I^=Hh;fZ8Nd(=N z0V~)3VNMFz6aND23%bc^1O?@K8HYaO0L+D|JiL!B!(=M|6noKJs1*_Jv1`c+M@yz*d_eP1Rw#izrZ)rS_P7 z9LT2;cszbs0+06^tTUqdZprAx!DWDXQdfO}BPz)!5TZ4FTAwi13zdsWM?d+n1dC0? z_uVRNdNuVe$#w38E+n!LSND+@HaO~Mxa)h77^7V+wOZX)54v@OOAV3vG^QDmjml{0 zb|7NTv<$Esz?j!PbZVea)u8g%4noLOTlriTaKV*zqzIzw%<%)R)AaJ^fUAed3 z+m&w8W&)t)l}rGu*h(_MjP6kO7I^8Hebi_Nm~G54nt0=GNgZQ>WH+eFcKBnGKo^V@N49{9!n*o>oFjH2Cj{L*ACWQhtTyMjvA;7V||XP;$UbFqiPI6B{`&c zf(j*iko0FyO;~kPpyoGPadjTUsfeL5ii+VGI6#$TJ29)A>0?+GkO=JcdFq$wr2VX% zM24*#=cNxQlh3(aE9W2d=HNnDjSL#le4wLdKH}DpPaA9>9~P>~%py=u*mv6+aJyEy z5qH~xz64!$To;?KfS|Sp@60n#q?y9|!{w4dOn)P_N+$wZTM8dlfhf*%x}*7}tOnNn z&B2R$06vT{L0>?<1Xa8#?T~@QG?~GRF#2bcF;FODQ84{59_l!kWI#_3+k*%pvqvvE zb$Ub-?&)L6jRD|`P(=oy{>p=bW^(76*1r?4n(h+}I#DufVUuE73o+Hg8(HB!e8laJ zpTS&f{!jpVKr6_3Qg@<=HY+j9l1~E2@4XV1Ua&lK!O?I(AJ!zEt@M6Dl$BW5LIarx z$pT*ALgsz?Y|LEF@tO>7zL4_m20UL#_^<)c!y)8z@(^-5*Q9NLqBqvXf?-`oT=I=k zBJ#^#04+^=)@~iGka8H+)04MPh1~Tjn4uJv#CRHN27W zl&xDq&^LNGXmT#`ItHFRdt3_uJC?jzb7y4umldPCiFeUA#~5C>J8i)FYHHwV`kLx) zxTmHB>VIv%=AJwr_q2qty=3gI31GOL7lF*^)N!dK6!(YmaFZQ(Z4vnH?9QBNt%Akb z7gO>!G8>*TV0?pA%r}|Hs43-}jArHOV+Pg#I$9ED`+n+RS&ff*|nWQWJ7Hv zQ&NE;d%9jCn=gkgFzC)Qa=`i6D`a@M_&%mJFPS2|lkWhr=%F?-L(*|%EZPLg@T#QE z4y08`n`5lLjjxE3lLi10XR0BLfY04c?Ni4*d|x@=ZjecIaMGA)re{FBL$(=U7TUIg z@p_pm_Jawjls^x~y?CC@GSs>q-o86S-5@wnuTWg8fwuS!npawGG{@$RVcu_JOGrOR zfivQa;OO;@Ts|mcy4MueN5c0|ES%&I!fiZmBXR$SQL0Hk=4wi)_W^VPCejVjvV~5f z=fMi5wS<1M!MNQy9;=+Z-Uw%c>2R}Xr(q#S07J-zz(?I_7WpvQO@G7g=4mjjSea{v z54Q|_iz&dSAZ#zM7c4280;`i*{W%Tnpn>mjkJhkB(Pz|KgdQ^i(ob6)1X>vajT@Ai z9BuH4+RaMZ%}6T&l4UiO0#`|9Axp_lBxR;@qSK0pMnas@ut}KjB*X zRdCzx)_~;^Eb2D3I^W%{7v29IVU=9p(5Y~uQLY82j*E_kirt}o6a69$VRvV6zargT zQDwfnTeci|zcd+k@md%j!&?=<{p+8G-1T-?P1Vo2%+UFhKuhx_hEa#yp9_wppkg7ePqo-B02oVRMJ@BU z86tF|fY?RhjOPl?r%icn?>oYs;eI7jEvFA2ZPD|-mD~V*Jy0OdC2Y;kuemm6ZVa^* z+#XmEwWBjRym?yqyt^^)p=J_|ttJm9%)a8XoeR4ZFb&oPUhh?}WKML=hT3L~L}grC zi^>4}g8#oWy$)5n`-+r7m1x*-H2oh4>vf0850z1?F?<|QKq2}Q=I~Xkl@4|Hqt#bB zBx(zBYq3NbNP4YSRtd;FN>tH^G++xbbT!a43zI&(Z^&EdoFfb@Xh*?S^C^#Q8yT?3C~ zyf_!s!il)rd;auO*eTPhd(%wC+Cq;ww(3w`fQme9y^iL6EQXibNv0D5^ z_lk(=(RieA&$w`nO<3;V#usO)dw4YNX;8+)wc%QOE_g*IVr#>;2k|(HVZ)l^qm5Ey zxO*hnXMXQ%V+DFNYK}6Z$7-Co#>Z+SbhJfcOXG}R2wXix(HU@Gwt=xm#We7O`K5sb z9}meW#;o55cDply73i^OXhfE*<9fXj)p^k2-nW5q=pl`795syXtWUyWq;Jhh-6F*-o<y(x%#gw;gI^$$f+_h|THRPR23ouf+f!Kk;E zh@c(U1NC>+JIw6)5%+m0j$_5?!Gc(;&IM;f^|r$23I3jY(IGmql?7bC54M?^rEnvY znNtKE2}wK_6dt}D?#PD}9St1FvYggwYK4Dqxdb0ku*_3dcuJkI_=#A@lQ*!3Q^UK% z@dy?Sozx$nob1$RgUzF`1sx&x^X^0pA`i69m%RJ4D4St-$e61Bsyi)mI}RC-0uu*5kyb2w z9GZgUfZ4-re=`p1eA{P^4=h(Ge@V>*v|A#I0oSwxX!hwZkd_wEA6VUIzn9nX}}mHKr{iOh-!=wNPvuxuq2v$#WwB* zY{xxui+i`@ZpU_P$0fzxF;1LZ2Pf`vi<8*@`<9bc(Y^zR$Va>3qR^Vz%qWvQ>^-%Qq`Ou6Tsk z-mN@2=y|WP!gt0Rs*UfTY(cF_9%G zupczcfgsdh0vZe58`+UJ6_6;KW4|v*zpv0yn-Efu;Z2N zcp5ui&5mcV<5heJ@tn?%*D#lz?07jlp307CVWgxe1#sP2bQqvR1)+>b(bY{nL62|d zBUp*w!lz)_T^+&dxR##P&|xMakRvHrc9v%QI6iO9R21j{109%LP*%z@{DAj(|#YC$t~bqOw#@ZG$0U zEqlr&z-6^UM)q^lF&%?< z^No|aP$Vyl3q|v4xKP@12Nz18k3()=Di<14ThE2Y(xI*nhj9}paiPhvTrM<~p3YpE z$%W<}qH&=G)d;b$5{K4W92QmKu$bJI9_r&l8}ib)&~^%OGe0rPg>L01;`$yQ5bvgf z2}Ki&rWUl6&nTE)Fspoa`P}k3lM%S(sat4}^fWtVa5C}Dd>kyv6QwZsSWqE?GrZT9sLMe8*0_2?) zILathM!!h-OO!PV2#fTS9^2KOO<)NVQ=gZV82I^8x%TLXaN=ph;H@4rw}} z4(N&nri39KbW#L5I*=oz11Cy46-ODvv!XaLF*D@T5K4k#S`O?y=wLl&*i3~1H=*%v zKs4>xOk@(cT?A&8=(9kaz>KGoXP06ft%^g$NHQ?v!69!XyV-yMBO}Ox+bCvJCYg*= zbe8FGRvRSyc&|xghcvKS)Umj8&7Ag4bXwRsa8Pj1AB5+$gEtu|y34{hn)B1BiCZ+; zr-7(33t~Sg#W)~w2s+WUnVkR?1`I9Mj?ISA@V1TpQNslw1)xy|x;A18+rAM{B481R z7}*X(nU;)fGct3FD&*i9+zD`K;rSR#UmOwA*1I-`;B!gfm0p8L#3T^O9`f9POPJ&% z|II)?6Q8Fse4d$v1AyqoadQ?Zjsf0Wj42Qhj?C`PwFA9a79cP!=d6}liFmsHrGFcVPm0W zY#5mC)>xphAvFBJge5btw{>mY)IAhCwjXl-|5>oIcxte8OyH6C0X7?!U*FA4!f)*D z=*)^=5ve}#+y>95$rD(Rcshs+DBcP?JJS-LELXEV4*O(lJK*Ie1n!3!*Z$9$1#tI6 z?7KS$dV36f-9ES-leO`~2575)^XmSt4xru`3@J3*2MgZ@AaBk3 z_P%wUNz_Vsn+>dIb|ZM3&9Sc&TsP>O305DE%BTW@^Zi@)43g=ayheXY82GiFo&Eb| zbFw4&a_q1*Q??R>3Y{IpVfTi&nvf3qo}soBSc|qSSjRzlZG+4xSQe~@dN6qWEn@?T z=Qi^Gp|KBQ>>tj(G_L=FhBab|3QORWVc!UvNe1;x)^Hx;jAhZp z(`0?Ge-ojclZclF!Z>!>f}HOc!JL-}jOpI?wnWC_=~m)iOqM`wkSC#=4+7R4Cqo7( z{XaoAi2dIE{(x#WvCVd_G%Lt|h6i~lY8pDq#bTE@No*8v;Kkc`@p@hyO@L!JIX4V> zAPD{ibshsW{U0e9AMP|*Vw87BNOPeq3QrXDIx0Hz(RavbAr@S zb|(=kV_Vz!;iJDJPzUl??AvqLl*1h4VeuA_Dls_PQ4S-5V1IAMY))R(- zwG`m>oLW$ez}z716oxpE?1TUs1#A^x)cyEwj(wK64GerO2CuB@hn+_A_4ot`sAwu6 zqM($+2l^D%hfk+oe0%iZ)32xof}~W9-SKqAR}q+!oI2#tW>7})!QKY2IKY?r0U14p zp?-;jg#pfvW7u8?vB%=uCXOJEz}Igw0Kz^tf~5?25m2S!0XVD)DND>G4gw5vf!;jfz8O45Ln6(x^XXr z$hN$=OfSnBR@VVc2gQOg$Kx|9l7!ErZ~!V1Z(0Um0-OLoBqw~I!aY#M#GB$XV73yV zw?-jA0~`pCqnHJFM+lgo`9Kp*E|i=MiA@M{*ypw01$3pCVg^VJurmQbYZJ$ed@?jF zWsF0r@m4}F`2`}Bap3zh%d+iBKp-?kLj}H$ZG>3R=hr+2#=;97#R_oF2JlCWNg0#{ zRVKJRFy45gJAle?cp*!mch;RTsT9yES$E+nnkn&+H`Lf-VE8jc>txYVVC$ucIHXKD zk6;3zV}m!igQ?k3p(|1)dm+9%%hmK*o8Y2GxdIQ(%6bXIfF>1x2g80QIjt5_D5SlT z)-d#El2f-3fv$bEgX{i4aup`vnPVm)h^`I8wZ!w+T>saGE7S73ZW2A3x7vQLPuCD z1f-h)Zcc5CmI(w(W+7nqE;X|dHq=vrUV+tg6T(S$R0#V63cdt~bAeRqfaO*8Lp;3C zNsx0O6SYx*5scCSh6e8uZlNEb7UPz&j4NTVH^w_#_)t;+_XdME+#Pmzm7x^@3Df|1 z^J{@ttxPFX>r%Bbq-3C#LIuIAWf9 zP&$-U$TqrG;w?e&(MVYdrEJVtZLE?tR?Ak)#;Vy-6+oUUD7I9M(S2H_TBSMxgwfKq z3Qwg{L2d*+_1SUloDX)W2|DNlzxTbo{1Ag9qTHZ@LJ5s|D{-trI`CZ?v7m!ci6@Tt zGXwFwKR~JmygwL*PQfNza+LD|7B+{4tyW`dPDIO9Yt-t9R!f+EU&ttHh9JKM9+6_TNGKvH}1dv(u*HDc>b%}B*yCKLh zCIk0EA* z-`7b@j&1l4Ci@C(x{FDhU-&r0mIm{Bd%F)R;PAF_4=Rd;-Q1CaB|tiX#TF66oX&yv zj`o4}0}q?P9UXX>kXb$p3`?qvu;jo)r!49*mtF@GK1s=QFyHn_FHZ_Ql$?8{;6q_g zLoiw+MPZXsY=&#`3d0AxgA2MZ#+ieQW#9ZX2N%yUY&HiM!EoF+BgK%k?QH*$J5s>p z?eQd{-I0Rs0~|Kw+wTvIXP8gMKQN}@K4$;GsD`)7{R1N#_S5-DaoN5I46-6VQb>!k zI6A$vr?bDSKi=RDw1>1JNHrsvvL1%v5}))B^!;hAy&F1vrh*uuuWR+@0ho7*h7-#_sOkHSCkuNZ|;~xWC^>yFkQ}dLX}%-UIxa@xydvq@Q>o6-{b7`}>h* zJNu24GFv2qyawgEL7qa#g!@RZa!egqD4&D(9n7mj#5{~IUtOq25CmZi9Ow)-+TAd$ z4%p;kq*OS6NQNDKppfX@=vcr`i7d}ZTn>BXTJV>Qo37hXk@oe?w|?@#!oo*(T=tv! ze}40e^PJ~D`J0`$Zr$-i665aZ?7=oFT8C>Yu4dyR@$wN;B#962=^DsAdDgk1KmPv4 zH@{6gcG`Q|BTo5yX;kthjV_+D|=$^@ajW(4(*A&Yl=MY%^2m~IWrwI z-Z9)*ZUw)N$J9Sls1PcJNy0**Q`im|@JS#vg%K|=h<$-Mh3>I&*32_OZ$sFp^8uqw z&poC)hzsbRhY=MCLI?!AOt4_dMKS^FL4;e!y;A^uQKTn|6Sy+43ac;>tENhv765NB z78b-L>o}i{{V6&h1+8|Rj@H=eFprn+ zmK&#KK6cmb$2-91ONe8R+YnBnQ&$Lh+e0HP&WTZWDobOht?5ukEzM-t1zGIWnw>*o zqS-Mz&B)E8)AalTI!!Z9O@*Kvo>^23C%(A30=XH*j-wBqi0j5?7-;4vH&4ZJ&H}og zHv!bR{NWQ|wU}SYjvdW(-NTMs4lM=1I#q`8ew1^`1l+2)r zJynY+j;4}HrL)CDOUp|q)f`$=UQ<YD4kF#KC`4QG;exyUUSKmk|J&tm&=W>Dc~w=%4+n|VooWaSgMv!tf{D} zD4kJ~$IU8fXq`WOM&8`%v+_!6N^8c|l$LtR8Ks69WL3tOgNql<&XUq>gBpi_#KsuphO~6ienRq38#5v;;7%#WI2mA8Y zixa+R@oc13bjO1lKwh8m040DmI4`)su`0>zh;?~9?-U#fgf(Ow1GHopf%60tixnH5 z$324*7aFe6Wn$%sYv?{Peo!KVH5?IxWL*=K8LF*V_CvFt-hjyC&nPtU`O{1BI{eWk z0}C^xCuP>SB{(h8#0m{bQSktnQYu1}B?^!n%u)bdZ@iF~#01d3&Rx)c+!nlf@%D=QQO^5HP= zDQAqC*2n}%w22g!vSOXt4JwO@h1|$?7NepixG>d_af=w)+tP_aJ2?|ZbY>osa{#aO z)JT8E@IgtSjO_0xtRbu{E_Oi5ta9g^W^z~w0?KVv5Q!0cfUM$wA}3%0hSfdF74l@| z7SF9qH?OiBB@SjL5^tv(LSUmzv5{?v4K|=CR@D+dqo$#Hg_?B*En!DZx}a!gJ|y5X z398{I?hVAYh5*OCkyJ={o`}lnaLH&5C_JOw(9&2PG7?+dP%8j)EvRuVFYGzmkJ&b? z07~N;#@sgryjPRkyW7FoX-q;Huvwkb*EO)ZYoN8Sv(r6gGeA@wFi;KNTXl9<&xX#9 zR$>ixv}~gFgxt~x?oSUpPN8_ZJ3CZ%H?6B1*Txg$dU1{MWL`JE!<$cx>%}z^?RKej zn9|pYd1wa_=UTYst~DEG14`SS&{uP{Zr{{77r0(=(afo7?M80e+PAcKbyMOjV6D%3 zVgka%_Kpszy?;Z$#0dXPS7mj3KX~Fm$u^LgxCDhBta5h^5XqO3uk_&_u4UOA_R%Jm z&#d7>fwK+^I(+|+*q#Cy%!n`vGv;OPYplW#!t&pm^=3-iz&zX8C#~)RK+s^X4I3w7 zqg_iBbIc@KaF8RoAZ;wSQS&MBbS4E07};&ZXMf{G{hH}wyq2b>#=0dLeeE@2*JhEc z<}GovY3!PemLqGiLf@gan3?>X7E9p4H7gIu?M=P?po66X^H?{G z{t$PJdbW*G!iJLWkY@-K19yiQn=H#*ys85>H=~9PEVsXN+orC*&IHQa&zNFSCgAfM zAbL?ZbD$DULUqe9=+ZNgO2}*W=9;$Ey}cXQGHi1{18fgT|Er-)ZInox-75?WAwzZ%yMJLXFI?& zikBy0u*tAH!(eOOtd}szh5fX=BC9bymM~HES(WGN?gdgEbdG_IFvi{oDo3(#*#=~N zr^oKn@7~Dd9|dAw;q6(58x;a!RSe3@c&laBqFe;@rqsI#$xRpH#?9RX-IjU)#1GJc zm9kM*T^)uCMQOQ!6%~Fcs0q(jZJ9Cc&ATkmnt8!WNABI))7ckby#Q93@lp*ME@pkP z5Ei|uXH#G2mag8-j8}TC%<3{h{pqrLB$bJ`(PKqz*KPJ5#AiH+w=$CqG;DB!vK+;` z3K)0a2(Y}7X#?X+kl3{TtuU+JxsCJ{Sbww@I|nRU`xE0m^oaZzq?Ues!a#ijE4{I+ zzaK5%YdE0&v)XlIAA)52s7}qUFku2`t$fSG+}~%HNyD_4H-UJfp}-TrrEDc0vg{rJAZpJCSSRl9 zXRY@4<;SLrOkc8$AEpHHD;O_jKd7_CugD}f{a|syxWE;e$flo&@2|!W7AoUcP^D!* zyTx~`YE3uJHkAi-g08yqF{A!u42jsR;G<^|(CJ2;Lydg3v3jrCAOpJ%VqRD^$p4PwDKq zMqDj|M-gVkjmfN)hI}?r8=xa!i%Fa-oX6&N{cJ)9n?{MZQwpJ7nQ(|FhljegNIWdu zC?GjP*y14C0V)P&9S~aF)&&{3<-wq1GgqHyVPybCASn*BNIla3M3eMB+wEgHUSuiy?}5yBJ7b9k-~`Hkr(FOjZGMi z8&f@8?9k1GxjCsAWM-f$D1v2ZfXx`k2ApMZKk72kFQJqvD&lg`6@b8;U~7`W2Lgf; zz!Mx1W>0Z!)o^cM^bzz1L}hz};Y{TKgc0CkfkTinus8x)#+a|bV*xXWun$foTV8BO zk%*6b0$<+ig}@y8#LsKl_^1pkRT{8bbt19(Re6sN&b7@*jCn9r|H*8-$8P!@|`|;Q(MM(k3;NDv52wo zKVa$Vc*O4vd(yxf=}Fbob+97TIn^{hg9jywSI-1Ugw=}T2fzjtF%dPaXQ>&|UL@Zk ze~5Chx-ZaxVeKxRD<~kdF$*>ux;_p}O=UEyg3*BaGz7&C8DpAU=61}#%n zjey69q%9K#NsnS+gDOCsGEplvL~S?xD47toEG5elwG?&LDGOlHj@U+Lj){2Ok6DS(15WCLN=n( z!;lT9{CKi#K>Jrhvq=G!kx>%a)N)8Re#5J*Wv0ll;3`Qr!Xz76kc}|O#>cefMYkwN zn{3!kU;d4kQP}k=)j^7M7Ai%}Hsk?Pg4$CvnUp9>He)A+lt9Am5$s~}qVV!=0kp7G z_cYkfxzZ*tkPy9?$%@Cz5I#Z!Er-y%kbGwwev8F(O)3z((|?`G7w%KY(k-T13g_eWCc|Y+_9>I{jOdCjW9bUdKNra z0HdpOgIWrM7p6)eVbEz9Qw`5*67aB&fRgBBUFa^sZ;QQOP+|ooG$d^I8UtfEz>ra3SV8<2W7A%X*jM(d%Q7cnmRk8`Ym7Kv*=p0lx1Qlm>6MqcMIaRKfRJp~# zAKT-AC+aKhT0=G?v$@Knk6CiCZha7G0b9c zLqsqfqI^^xLxtw`>4(N+P~$Q9FjbJ9zFg^ch8;zbIEm~nkf6vD@Z6s(J)L;2;FDjj z^eB(90od)Kz5}_^pFn43Yf~@r@@t9kj4a!j`jT1%M)^q^sTl-Nv9P%A^yW$*@G{!F zQT(VsmC9Z#kWNM0q<|Rw*j#sTSS!GwG=UljUq;b>g82*t&xeR19RdT7Z&XjVwDrq^3+rwV|~5M)h{ zv4P%%+D~Q#;y?o9GZtTrCd3;6`zc{m98ad!vC~Wq7LyYhEZy}Yb^K_=MVf-%p#j|e zW>A+j4KHwGYB3XMktP?=+&~int6T`n0GG7N>qeq+V1o_K52>D=dWpOa*BmrS=(&y| zkTprKTaOB~1YJ>wNJJXIA)T6Bf%U~yOo2qbN%F}kN0xFt!kh^z0vO{(9TPgQlg2SA z7i703RE){JXafWR`(;5dYJ1c_R{g!JNo`Ikg} zfW+h#bF-i{H3Rwx%zHD6$U~zD#1!jrQHUUwtI$nAK%qWRPo%lCp^AlGsU9+}&mnCq zYZ)#?;W>|%a&CPT48OyVs0VmR9JU~YG(2oy%pSsxePPW9LpQXytN`fH6=wlBw}IOr zH@tPO@f4ypR}&hn4I)l++pe*MkIPRklxdK3Y)}y8Dh*j5{YCZ@u{%LiVh!I2r{rMk}Lre`?Y`+VcVU-pu`0$!}d*o)cQJ95#b7FM;f!`9dwi)Z9#0xv{7lek?fH(QMJzM4fyZ zQ4=Tqa-|>!E2F1?hx9QBj`+Os_>yL`ps=RV6AGa4Bw%LCUW#r9I1MY>0`zc(F9;pk zSuqm__G?NU8B}p(4oaMp1JAKET)V`=q3_J`{2e2AvHNGnctoDvo)F@%pmAv>2f2LFVwd>Ek{DAUbe&-b#@20r^ zS+&aXyXqZ+@?Pz;f_f9w;U2HMVS?w+eGT3l`pbO3?H2)XoXkz(`uOAd+j;&$UdR>3 z3iUz@FpkFxSM$Pkyl^Wo+{SZ3d>rwc5Jn5~D46${BccQ0!HI923ChTG>Fj929XSsq z9^geTpc#Hb8lX0D2&9uiATPa&c}3j>No=K_E4^lhe{9&I8z^H^qQJ z`XlJE?uU1rxChYt(fK|Y`J(e9yv&rRr-Rp*Yj)!>PQixKM2#UL5B0FqcrQCOXg&%u z$pb=Gj+@|Rr$fBVafZP(J2S|9KFmXv41OL3vnidg;ZsuS{9`_a5(8G&D7(LwkFxvT zv~+UrF-{vZ*xea?W)``g$!BKMc_*I}qw`7wE4hO=P?D$f`Gw?s8qbiD+=)D}joh43 zEFp7C*r|FnJ6+0`mT_EFIlC#TV5gc&c3N9iO_5|))zE3VSWBm6W5&{Hsc~9TmxpCg zy-rS>#*L@bk%vs6Q;v8jokkm{n|YCeIX4)mpPIL@EykRW0|zK~0&0dmZ#_ml>!|FXQ;Kqxvw|l(S=HPcN<)97Xufg-311vF%i3T;f-60CJY^ z-_VNV06T6~mg4&8qsZkPo}Dh?ds=XJ1-}77gp8e3WJ2anz7?me6%Cv)^%QF)uUpbFu5KK!JaBewK{A|I@zd~9=g;^Bbca1R+_`Sy*XMEW z)){z_dsP@N?v4{FIqs|YlX!Cc8GjhOE3;c%uv8w~bR;02YH9yeI#q1nMkl?Wz@X}^ z*)BRQGQ6zb#2mLOv&nH=KMQw0zXO!0xWAa+-h{I@ZKjLUwkk6@?GAp?Votl0U(~>P zvrlKfyromRhHr8JCC31;x*spJksUzU&haAfHjWEC-9ReHLh(s zqG_O6X*`8@p33{(jT`1U8`sTS)3Tr z(A3)0+t#~kSxfKgWld>~8I5h7sg3Jej%b3I-qt#=WnR)-?fq6BUc{$4r%YZR0+%#o#7ntVrMS2y9DeU$e@nzAUsay$!zN z&k!v{z)jS-M)I%;%W53LY+SgJbY5m?Nw`sX1i&998HB_#^KFVfQk2H-4LmJG0lA@S z#)_6s0y|P+yCQ@@Km;rjToNP{5vSvVSY%mS2)H6L$bB_e#1+5`nY_3Ne+!Qh`~gUg z)v|}YoA0Gh4)ks&D)2$AgIr`IFqUfM7e_|MOKigIu_Xf7O9gV-CN(gekdR-ydt9h7 zZ6}1SP2w2i0 zjJS2dY9L<14s=g&Vi*vmN~2s;GHFkQL3-&mR4oQ5+A4m-578bcL4H{YnwV&+1eOfc zY`m@w477y|MqndyheB_J*#*??D28hO!C6nr`SAIsl4XTVxgP!Rzm5RcMnMn%KC7G) zFJ<3J&1U0bDK-wQKTXJyy%21!@w!9FT0JlUvgsN%nNIj^YJRACLr|z%NW_^t{E#=Z zC9&3I!nfgQRg|0e+g~29OP0(exwNG#aS7T0mf6Gs{f)zDU@mkMh6K~{mEP|m7y1E` zPrV{`w8)v^zKu?gbw*${$5X2ZMBTaEES!;91V>;~YOR)lHfI)ui2lJ0OFb`sMi*rT zMprnf5wV7X5~Rc#FU@4_&T4rS$8cfb*(&@*P+-dyq?bBSlq+&-%m|PY{Flhd{+9$b zH$s@bDEewv7G`)G(mVhy(iTE4_fN3^)k9pSfLs+g(w>0>hNWv$4g7B-VfF@L)WdTK zHQL$1-fF4d9w2bt4pkolPS*&i-a_zBAm|SQsqR?hZPj=s+R{uBeYKd2e~=i&XS4E%4k+csD_V9YZa!#e{kzP6X`bL`yzKotRrlpqdPPe3)429DeeQcoYd?oa5U^!9bF>*{Imj_Wn-ORFGd zh@Js5J&-fhmGux-c@3kBkUCU7fntgc|rDUa##2r(~BXOt6 zo1p^p-=qRU+t&2^?@<9E2p|3I${>~@Q>daE5AoFc{x7;LLPS`Fia)hJT?*KH-Vdqo z2?=9+qU-9yA{JhBt@eLFL3Xf98lU!{qy9Y=szWELfd4gM$_L2tQ(5^x2WBQnYfA%_%@07jUhCE;Lr)Vv%hB1MQyHlF1?kmBO| zK6W!mZ3kc~h+1GiYCEb!A*NnL4)H!X0e_e-_z*XX1Qy-*5eYbZxjNXzWszh8v7Z!) zpl1tyN??`^4>Uw&35XPWF%=$OEb2Kx*An$FRdq}V9AtP&&qa8~LQ5z>28di!Z%ck0 zmt^r`U#s!g(x_SBvoM}yELEN4h-ykIJv{Q z(0K7l7vqap8EZM@#zFzk9s~6&kcu&o`2yP*^Mms|h?6@E%w){(2}4tb#h-$cH>{}aIIXrkypzTNO{ok=BwqY zP*$o`@(QH;c_=Vtf~Gc0$SyEyu-z3}ftsUaM6?{WQpt>HAoN8q!pb5;uhFtSrBFu^ zwb86!AZO2Lr9jC_g}MM@z)&$-ujM^aHCrD;C_~orfeNHDMuPB~h*AJdzEiqg@PN?R z>>B7Xn0GVPtcY5m*G(O-mPz*r5IOk)6j;1}%O&IE(%XVE3P^XOYU{!MTcqnT>3Kmd zV)ZY-fGdUyRVjV?IHg!0&nq=P2e_IK;pO)QHO6is7!$xcoTKIv!E=@LfgnR>8dMUc zO6erXmRhSR6@bDY%4@Z_tJWt5WJp}Crd9wTJxQzP5$sI#;JnJ@SUyWcmy)hG#H5Qv zwM0!H=g=F~D9DCOfn#+_SBh$(K3UV`t3~L<(3k^Ax7PYmVd?gxhJ5knl(!fZKwgcxeBoGDkmBacIc&D5K7=-En%VNU^N_SF=#l{ zV$g7?#h~F(i$TMo7K4VFiW3?R*$fC+%|?x&;iP*&$c%<_qM(dcatfr&p{WP4xRO^! zlQ2MVGg(lyCqvAc4Cf^zzLZ)|sZHWbA>R<}9Cb9ENRUnW&SdgEN-2eWQx_m1V#s%y zk{1{5G7|1IQ@AY;qtqMOO(x{1gtpE)Na(=mh7J`1R2s(~xL6V#olAKpS&I?J-A7FBhThyaX&POL& z6Eq%4*KwjsGEgbK2BAV~ia+#@Inm(Gqfuv4gz|nydRCJ1!8sQH=JbkgMz>=_fqtm&w;bxM?h8 z{XI*-;m|F9h~z>-gzkh!mEOeKCl6luS{|*&?C$f}B^!mxq!)O#%+^%Opc9OtfOLY5 z$e`NRq-A-slrprD^0>37+}1|Qsf|#Zz+0$oQ6fB1B}1R4LzmRvMljAO)Vjt(hvI@k zZ6pW7L7CY`MoSm7u>i`D*$LreV*%8*sf`%57H=cB#@op4@is!qz*xZANTtDAr8LkD-)0(MkvA zqfX369eO6-H3PO{0?;d$RcOZOqx6~R$=y02@2UsQrdts)agG#9Q{}lyG0MdVAtJ;q z42Ud$&NLRQMtU52Gpx_}p-c{qBqLVP%lj!_eYWaF=R(tUp8&(^+{ygWiYqW5Dl0Nn z5^2%0T!U2>tQB0UPoIOFqaBi}1X%o0VC)Bj0hUUl%2W~s*e;Otx$uPni#!jfl1OT? z(4(ToAl@TGD+p^@fUU7og!&SjF-+2+&O4Xx@z1Td1QmTA3=OEzrYnQAVc6H_BX%|< zcu=W`1S>NnT6KZCHmDPEn6NwAmzeIPG#yijkI`>3KwlgbNGhBBkR2z_W4$;OcC zN3Pvix#%EX%qIB=Se0h2ama&`+SF@IR(`C*Tx*c0q4GHs`XF{sco9r+2f7NRW%8P&C%$4R zWX;D+Ld2v(#zY>4D1kCy3DZ@VphumdFC|@UTtTe^MU7}+^g)5-1~gC|c)jRd{c46c zO;A10e*Wb7AQcf;5sWoFmA) zSqH`v=fvDwRQv_fZE%ANNE*<7{gHjfVN~BRxr#yJT%98Zn`J zLn9^>Q)tA5#u1H}&{m-l(}@!XXBfSr5tBBGpb}_wi6qtIT>1(DkX_Kojz@$}MWmQu zA_*O=fc`yK2_-(7xsYObfUWO{ z_yjBcac=!^As@}`T)vm~SD5NErzkp?q?}1E;~+Amgeg{F|6=7nlr!5%r;ZQAhA3ys zOgWQgV(R{sGl{MR{Vw_&UQq%V1+Z9dhw`P9lru3&IUMkHCe>KW>r-XuMPf@vVIK5q zbCUuer^>6T

+ 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/iota/vendor/wormhole_movement_m2_devnet/devnet/127.0.0.1-36219.yaml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/127.0.0.1-36219.yaml new file mode 100644 index 0000000000..e489d5c3dc --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/devnet/127.0.0.1-36853.yaml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/127.0.0.1-36853.yaml new file mode 100644 index 0000000000..ec936e990a --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/devnet/127.0.0.1-39101.yaml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/127.0.0.1-39101.yaml new file mode 100644 index 0000000000..9f5954a4d3 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/devnet/127.0.0.1-39187.yaml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/127.0.0.1-39187.yaml new file mode 100644 index 0000000000..59b82dc21c --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/devnet/client.yaml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/client.yaml new file mode 100644 index 0000000000..c9302468b5 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/devnet/fullnode.yaml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/devnet/fullnode.yaml new file mode 100644 index 0000000000..c89b027b28 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/AAIDGAMw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AQAAGQ8NAQAAIQwDAQAAKw8DAQAACAYQAQAACwYQAQAAEhESAQABIB8QAQABJyANAQABLBwQAQABLgMNAQACHB4KAQADGhQDAAMkABQAAyoVBQAEDSEDAgcEBBAZGwIHBAQRIiMCBwQEFRkaAgcEBCQAHQIHBBsYGRgSFxwYCBcUFw8XEBcRFwkXChcHFxMXGBgaGAEHCAcBCAECBggBBwgHAAEGCAEBBQIGCwIBCQAFAgMDAQsCAQkABAcLAgEJAAMGCAEHCAcBCwQBCQADBwsCAQkABQsDAQkAAwcLAgEJAAYIAQMBCwMBCQADBwsCAQkABggBCwMBCQADBwsCAQkABQMBAwIHCwIBCQAFAQcLAAEJAAIIBQUBCAUBBggFAwYLAAEJAAMDAQkAAgULAAEJAAIGCwYCCQAJAQkAAQEBBgkBAQYLAwEJAAELBgIJAAkBAgsDAQkABwgHAgcLAwEJAAsDAQkAAgcLAwEJAAMDBwsGAgkACQEJAAkBAgcLBgIJAAkBCQABBwkBB0FjY291bnQKQWNjb3VudENhcAdCYWxhbmNlBENvaW4JQ3VzdG9kaWFuBVRhYmxlCVR4Q29udGV4dANVSUQZYWNjb3VudF9hdmFpbGFibGVfYmFsYW5jZQ9hY2NvdW50X2JhbGFuY2UQYWNjb3VudF9iYWxhbmNlcxZhY2NvdW50X2xvY2tlZF9iYWxhbmNlDWFjY291bnRfb3duZXIDYWRkEWF2YWlsYWJsZV9iYWxhbmNlB2JhbGFuY2UGYm9ycm93CmJvcnJvd19tdXQaYm9ycm93X211dF9hY2NvdW50X2JhbGFuY2UHY2xvYl92MgRjb2luCGNvbnRhaW5zGGNyZWF0ZV9jaGlsZF9hY2NvdW50X2NhcAxjdXN0b2RpYW5fdjIfZGVjcmVhc2VfdXNlcl9hdmFpbGFibGVfYmFsYW5jZRxkZWNyZWFzZV91c2VyX2xvY2tlZF9iYWxhbmNlBmRlbGV0ZRJkZWxldGVfYWNjb3VudF9jYXAMZnJvbV9iYWxhbmNlAmlkH2luY3JlYXNlX3VzZXJfYXZhaWxhYmxlX2JhbGFuY2UcaW5jcmVhc2VfdXNlcl9sb2NrZWRfYmFsYW5jZQRqb2luDGxvY2tfYmFsYW5jZQ5sb2NrZWRfYmFsYW5jZRBtaW50X2FjY291bnRf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YXRhD0N1cnJlbmN5Q3JlYXRlZAdEZW55Q2FwCERlbnlMaXN0AklEBk9wdGlvbhVSZWd1bGF0ZWRDb2luTWV0YWRhdGEGU3RyaW5nBlN1cHBseQtUcmVhc3VyeUNhcAlUeENvbnRleHQIVHlwZU5hbWUDVUlEA1VybANhZGQFYXNjaWkHYmFsYW5jZQtiYWxhbmNlX211dARidXJuBGNvaW4UY29pbl9tZXRhZGF0YV9vYmplY3QIY29udGFpbnMPY3JlYXRlX2N1cnJlbmN5GWNyZWF0ZV9yZWd1bGF0ZWRfY3VycmVuY3kNY3JlYXRlX3N1cHBseQhkZWNpbWFscw9kZWNyZWFzZV9zdXBwbHkGZGVsZXRlD2RlbnlfY2FwX29iamVjdAlkZW55X2xpc3QNZGVueV9saXN0X2FkZBJkZW55X2xpc3RfY29udGFpbnMQZGVueV9saXN0X3JlbW92ZQtkZXNjcmlwdGlvbgxkZXN0cm95X3plcm8NZGl2aWRlX2ludG9fbg1mcmVlemVfb2JqZWN0DGZyb21fYmFsYW5jZQxnZXRfZGVjaW1hbHMPZ2V0X2Rlc2NyaXB0aW9uDGdldF9pY29uX3VybAhnZXRfbmFtZQpnZXRfc3ltYm9sFWdldF93aXRoX29yaWdpbmFsX2lkcwhpY29uX3VybAJpZA9pbmNyZWFzZV9zdXBwbHkMaW50b19iYWxhbmNlCmludG9fYnl0ZXMLaW50b19zdHJpbmcTaXNfb25lX3RpbWVfd2l0bmVzcwxpc19wcmltaXRpdmUEam9pbgRtaW50EW1pbnRfYW5kX3RyYW5zZmVyDG1pbnRfYmFsYW5jZQRuYW1lA25ldwpuZXdfdW5zYWZlBm9iamVjdAZvcHRpb24PcHVibGljX3RyYW5zZmVyA3B1dAZyZW1vdmUEc29tZQVzcGxpdAZzdHJpbmcGc3VwcGx5DHN1cHBseV9pbW11dApzdXBwbHlfbXV0DHN1cHBseV92YWx1ZQZzeW1ib2wEdGFrZQx0b3RhbF9zdXBwbHkIdHJhbnNmZXIUdHJlYXN1cnlfaW50b19zdXBwbHkKdHhfY29udGV4dAl0eXBlX25hbWUFdHlwZXMSdXBkYXRlX2Rlc2NyaXB0aW9uD3VwZGF0ZV9pY29uX3VybAt1cGRhdGVfbmFtZQ11cGRh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CQABCQECCA0IDQMHCA4JAAkBAQsHAQkAAQsJAQkAAgkAAwEGCwwBCQACCAYBAgcLCwEJAAsMAQkAAQsIAQkAAwgNAwgNAQsRAQkABggNCA0DCA0IDggNBQgNCA0IDQMDAwgOCA0IDQMDAwMBBgsKAQkAAQsKAQkAAQYLCwEJAAMHCwsBCQADBwgSAQgEAgYIDgkAAQgGAQgFAQYJAQEHCQEHQmFsYW5jZQZCb3Jyb3cEQ29pbgJJRARJdGVtDEl0ZW1EZWxpc3RlZApJdGVtTGlzdGVkDUl0ZW1QdXJjaGFzZWQFS2lvc2sNS2lvc2tPd25lckNhcAdMaXN0aW5nBExvY2sGT3B0aW9uC1B1cmNoYXNlQ2FwA1NVSQ5UcmFuc2ZlclBvbGljeQ9UcmFuc2ZlclJlcXVlc3QJVHhDb250ZXh0A1VJRANhZGQQYWxsb3dfZXh0ZW5zaW9ucwdiYWxhbmNlBmJvcnJvdwpib3Jyb3dfbXV0CmJvcnJvd192YWwSY2xvc2VfYW5kX3dpdGhkcmF3BGNvaW4HZGVmYXVsdAZkZWxldGUGZGVsaXN0DGRlc3Ryb3lfc29tZQ1keW5hbWljX2ZpZWxkFGR5bmFtaWNfb2JqZWN0X2ZpZWxkBGVtaXQFZXZlbnQHZXhpc3RzXxBleGlzdHNfd2l0aF90eXBlA2Zvcgxmcm9tX2JhbGFuY2UKaGFzX2FjY2VzcwhoYXNfaXRlbRJoYXNfaXRlbV93aXRoX3R5cGUCaWQMaXNfZXhjbHVzaXZlCWlzX2xpc3RlZBVpc19saXN0ZWRfZXhjbHVzaXZlbHkJaXNfbG9ja2VkB2lzX3NvbWUKaXRlbV9jb3VudAdpdGVtX2lkBWtpb3NrD2tpb3NrX2V4dGVuc2lvbghraW9za19pZBNraW9za19vd25lcl9jYXBfZm9yBGxpc3QWbGlzdF93aXRoX3B1cmNoYXNlX2NhcARsb2NrDWxvY2tfaW50ZXJuYWwJbWluX3ByaWNlA25ldwtuZXdfcmVxdWVzdAZvYmplY3QGb3B0aW9uBW93bmVyBXBsYWNlDnBsYWNlX2FuZF9saXN0DnBsYWNlX2ludGVybmFsBXByaWNlB3Byb2ZpdHMOcHJvZml0c19hbW91bnQLcHJvZml0c19tdXQIcHVyY2hhc2URcHVyY2hhc2VfY2Fw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CwIBCQABCA4BCQABCwkBCQACCAcLEQEICAELEAIJAAkBAQYJAAEIDQELBQEJAAEGCwkBCQACCQAFAQsGAQkACQgHAwsGAQULBgELCQEJAAcIDwsLAQkAAwsJAQkACA4CCwkBCQAHCA8BCwsBCQAHCAcDCwYBBQsGAQsJAQkABwgPCwABCQADAQYLCwEJAAILCQEJAAgOAgcLCQEJAAsJAQkAAgcLCQEJAAMBBggPBggHAwsGAQULBgELCQEJAAULEQEICAEICAELEQEJAAsKCAgDCxEBCAgDCAcLBgEFBggIBgoICAMFCwYBCwkBCQABBgsGAQkAAgYLEAIJAAkBBgkAAQYJAQEKCQACBgsRAQkABgkAAgsDAQkABwgPAQcLBgEJAAUDCAcLBgEFBQsGAQsJAQkAAQcLDAEJAAEHCwoBCQACBwsKAQkACwkBCQABCQECBwsRAQkACQACCwQBCQEJAgMHCA4JAAkBAwkACQEJAgIGCA4JAAIHCA4JAAEHCQEBCwQBCQEDBwsQAgkACQEJAAkBAgcLEAIJAAkBBgkAAgkACQECCAgHCxEBCAgCBwsRAQkABgkAAgcLCgEJAAMCAwsJAQkAAQoCDUFjdGlvblJlcXVlc3QHQmFsYW5jZQRDb2luAklEBk9wdGlvbgdSdWxlS2V5BlN0cmluZwZTdXBwbHkFVG9rZW4LVG9rZW5Qb2xpY3kOVG9rZW5Qb2xpY3lDYXASVG9rZW5Qb2xpY3lDcmVhdGVkC1RyZWFzdXJ5Q2FwCVR4Q29udGV4dAhUeXBlTmFtZQNVSUQGVmVjTWFwBlZlY1NldAZhY3Rpb24DYWRkDGFkZF9hcHByb3ZhbA9hZGRfcnVsZV9jb25maWcTYWRkX3J1bGVfZm9yX2FjdGlvbgVhbGxvdwZhbW91bnQJYXBwcm92YWxzB2JhbGFuY2UGYm9ycm93CmJvcnJvd19tdXQEYnVybgRjb2luD2NvbmZpcm1fcmVxdWVzdBNjb25maXJtX3JlcXVlc3RfbXV0F2NvbmZpcm1fd2l0aF9wb2xpY3lfY2FwGWNvbmZpcm1fd2l0aF90cmVhc3VyeV9jYXAIY29udGFpbnMPZGVjcmVhc2Vfc3VwcGx5BmRlbGV0ZQxkZXN0cm95X25vbmUMZGVzdHJveV9zb21lDGRlc3Ryb3lfemVybwhkaXNhbGxvdw1keW5hbWljX2ZpZWxkBGVtaXQFZW1wdHkFZXZlbnQHZXhpc3RzXxBleGlzdHNfd2l0aF90eXBlB2V4dHJhY3QFZmx1c2gDZm9yDGZyb21fYmFsYW5jZQlmcm9tX2NvaW4QZnJvbV9jb2luX2FjdGlvbgNnZXQHZ2V0X211dA9oYXNfcnVsZV9jb25maWcZaGFzX3J1bGVfY29uZmlnX3dpdGhfdHlwZQJpZA9pbmNyZWFzZV9zdXBwbHkGaW5zZXJ0DGludG9fYmFsYW5jZQlpbnRvX2tleXMKaXNfYWxsb3dlZAppc19tdXRhYmxlB2lzX25vbmUMaXNfcHJvdGVjdGVkB2lzX3NvbWUEam9pbgRrZWVwA2tleQRtaW50BG5hbWUDbmV3Cm5ld19wb2xpY3kLbmV3X3JlcXVlc3QEbm9uZQZvYmplY3QGb3B0aW9uCXJlY2lwaWVudAZyZW1vdmUScmVtb3ZlX3J1bGVfY29uZmlnFnJlbW92ZV9ydWxlX2Zvcl9hY3Rpb24LcnVsZV9jb25maWcPcnVsZV9jb25maWdfbXV0BXJ1bGVzBnNlbmRlcgxzaGFyZV9vYmplY3QMc2hhcmVfcG9saWN5BHNvbWUFc3BlbmQMc3BlbmRfYWN0aW9uBXNwZW50DXNwZW50X2JhbGFuY2UFc3BsaXQGc3RyaW5nCnN1cHBseV9tdXQHdG9fY29pbg50b19jb2luX2FjdGlvbgV0b2tlbgh0cmFuc2Zlcg90cmFuc2Zlcl9hY3Rpb24KdHhfY29udGV4dAl0eXBlX25hbWUEdXRmOAV2YWx1ZQd2ZWNfbWFwB3ZlY19zZXQEemVybwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDCAAAAAAAAAAAAwgBAAAAAAAAAAMIAgAAAAAAAAADCAMAAAAAAAAAAwgEAAAAAAAAAAMIBQAAAAAAAAADCAYAAAAAAAAAAwgHAAAAAAAAAAoCBgVzcGVuZAoCCQh0cmFuc2ZlcgoCCAd0b19jb2luCgIKCWZyb21fY29pbgACAjoIDhoLCQEJAAECAjoIDjIIDQICAzoIDl0LCQEJAFULEAIIBwsRAQgIAwIGSAgHGANWBU8LBgEFXQsGAQsJAQkA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DAMHQmFsYW5jZQRDb2luFkdlbmVzaXNDaGFpblBhcmFtZXRlcnMYR2VuZXNpc1ZhbGlkYXRvck1ldGFkYXRhBk9wdGlvbgNTVUkMU3Rha2VTdWJzaWR5EFN5c3RlbVBhcmFtZXRlcnMPVG9rZW5BbGxvY2F0aW9uGVRva2VuRGlzdHJpYnV0aW9uU2NoZWR1bGUJVHhDb250ZXh0A1VJRAlWYWxpZGF0b3IIYWN0aXZhdGUTYWN0aXZhdGVfdmFsaWRhdG9ycw9hbGxvY2F0ZV90b2tlbnMLYWxsb2NhdGlvbnMLYW1vdW50X21pc3QHYmFsYW5jZRhjaGFpbl9zdGFydF90aW1lc3RhbXBfbXMEY29pbg9jb21taXNzaW9uX3JhdGUGY3JlYXRlGGNyZWF0ZV9zeXN0ZW1fcGFyYW1ldGVycwtkZXNjcmlwdGlvbgxkZXN0cm95X3NvbWUMZGVzdHJveV96ZXJvBWVwb2NoEWVwb2NoX2R1cmF0aW9uX21zDGZyb21fYmFsYW5jZQlnYXNfcHJpY2UHZ2VuZXNpcxFnZXRfdmFsaWRhdG9yX211dAlpbWFnZV91cmwWaXNfZHVwbGljYXRlX3ZhbGlkYXRvcghpc19lbXB0eQdpc19zb21lE21heF92YWxpZGF0b3JfY291bnQbbWluX3ZhbGlkYXRvcl9qb2luaW5nX3N0YWtlBG5hbWUPbmV0d29ya19hZGRyZXNzEm5ldHdvcmtfcHVibGljX2tleQNuZXcGb2JqZWN0Bm9wdGlvbgtwMnBfYWRkcmVzcw9wcmltYXJ5X2FkZHJlc3MLcHJvamVjdF91cmwTcHJvb2Zfb2ZfcG9zc2Vzc2lvbhNwcm90b2NvbF9wdWJsaWNfa2V5EHByb3RvY29sX3ZlcnNpb24RcmVjaXBpZW50X2FkZHJlc3MccmVxdWVzdF9hZGRfc3Rha2VfYXRfZ2VuZXNpcwVzcGxpdA1zdGFrZV9zdWJzaWR5G3N0YWtlX3N1YnNpZHlfZGVjcmVhc2VfcmF0ZRdzdGFrZV9zdWJzaWR5X2Z1bmRfbWlzdClzdGFrZV9zdWJzaWR5X2luaXRpYWxfZGlzdHJpYnV0aW9uX2Ftb3VudBtzdGFrZV9zdWJzaWR5X3BlcmlvZF9sZW5ndGgZc3Rha2Vfc3Vic2lkeV9zdGFydF9l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ABAFCgIKAgoCCgIKAgoCCgIKAgoCCgIKAgoCAwMHCAsBCAECBwgBAwABBwgBBAcIAQsIAQgKBQcICwEIDgMHCAEIDgYICwELCAEICgMHCAEIEAMCBwgBCwgBCAoCBwgBBggLAQYIAQEBAQYIAAEFAQYIBgEGCAwBBgoCAQYLBQEIBgEGCwUBCgIBBggJAQMCBggBAwEIDQEICQIGCAEGCAECBgsFAQkABgkAAgYLBQEJAAYLBQEJAAIHCAEHCAsCBwgBCgIDBwgBCgIKAgEKAgEGCA8ECAADAwcICxYFCAYIBggGCAYLBQEKAgsFAQoCCwUBCgILBQEKAgsFAQgGCwUBCAYKAgsFAQgGCwUBCAYIBwoCCgIKAggGCAYIDAgMAQsFAQkAAQgGCAEBAQEBAQEIAAECAQgEAQgMAQcICwEIBwIHCA8DAwMDCA4BCAoBBgsIAQkAAQYICwQHCA8LCAEICgMHCAsBBwgPAQgCAQkAAgkABQUDAwMDCwgBCAoBBggOAwcIDwgOBggLAQgDAQYIEAEGBQIHCA8LCAEICgIHCA8GCAsCBggPAwEIDwEGCQAdAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBBgsFAQkAAgEBAgUHCAsBBwsFAQkAAwgJCA8FA0JhZwdCYWxhbmNlAklEBk9wdGlvbhVQb29sVG9rZW5FeGNoYW5nZVJhdGUDU1VJCVN0YWtlZFN1aQtTdGFraW5nUG9vbBNTdGFraW5nUmVxdWVzdEV2ZW50BlN0cmluZwlUeENvbnRleHQVVW5zdGFraW5nUmVxdWVzdEV2ZW50A1VybAlWYWxpZGF0b3IRVmFsaWRhdG9yTWV0YWRhdGEVVmFsaWRhdG9yT3BlcmF0aW9uQ2FwCGFjdGl2YXRlFWFjdGl2YXRlX3N0YWtpbmdfcG9vbBphZGp1c3Rfc3Rha2VfYW5kX2dhc19wcmljZQZhbW91bnQFYXNjaWkDYmFnB2JhbGFuY2UDYmNzBmJvcnJvdw9jb21taXNzaW9uX3JhdGUKZGVhY3RpdmF0ZRdkZWFjdGl2YXRlX3N0YWtpbmdfcG9vbA9kZXBvc2l0X3Jld2FyZHMVZGVwb3NpdF9zdGFrZV9yZXdhcmRzC2Rlc2NyaXB0aW9uGmVmZmVjdHVhdGVfc3RhZ2VkX21ldGFkYXRhBGVtaXQFZXBvY2gFZXZlbnQMZXh0cmFfZmllbGRzB2V4dHJhY3QKZnJvbV9hc2NpaQlnYXNfcHJpY2UHZ2VuZXNpcxRnZXRfc3Rha2luZ19wb29sX3JlZgJpZAlpbWFnZV91cmwMaXNfZHVwbGljYXRlDWlzX2VxdWFsX3NvbWUXaXNfZXF1YWxfc29tZV9hbmRfdmFsdWUHaXNfbm9uZQxpc19wcmVhY3RpdmUHaXNfc29tZQhtZXRhZGF0YQRuYW1lC25ldF9hZGRyZXNzD25ldHdvcmtfYWRkcmVzcxRuZXR3b3JrX3B1YmtleV9ieXRlcwNuZXcRbmV3X2Zyb21fbWV0YWRhdGEMbmV3X21ldGFkYXRhFW5ld191bnNhZmVfZnJvbV9ieXRlczNuZXdfdW52ZXJpZmllZF92YWxpZGF0b3Jfb3BlcmF0aW9uX2NhcF9hbmRfdHJhbnNmZXIabmV4dF9lcG9jaF9jb21taXNzaW9uX3JhdGUUbmV4dF9lcG9jaF9nYXNfcHJpY2UWbmV4dF9lcG9jaF9uZXRfYWRkcmVzcxpuZXh0X2Vwb2NoX25ldHdvcmtfYWRkcmVzcx9uZXh0X2Vwb2NoX25ldHdvcmtfcHVia2V5X2J5dGVzFm5leHRfZXBvY2hfcDJwX2FkZHJlc3MabmV4dF9lcG9jaF9wcmltYXJ5X2FkZHJlc3MebmV4dF9lcG9jaF9wcm9vZl9vZl9wb3NzZXNzaW9uIG5leHRfZXBvY2hfcHJvdG9jb2xfcHVia2V5X2J5dGVzEG5leHRfZXBvY2hfc3Rha2UZbmV4dF9lcG9jaF93b3JrZXJfYWRkcmVzcx5uZXh0X2Vwb2NoX3dvcmtlcl9wdWJrZXlfYnl0ZXMEbm9uZQZvYmplY3QQb3BlcmF0aW9uX2NhcF9pZAZvcHRpb24LcDJwX2FkZHJlc3MUcGVuZGluZ19zdGFrZV9hbW91bnQdcGVuZGluZ19zdGFrZV93aXRoZHJhd19hbW91bnQHcG9vbF9pZCFwb29sX3Rva2VuX2V4Y2hhbmdlX3JhdGVfYXRfZXBvY2gPcHJpbWFyeV9hZGRyZXNzEHByaW5jaXBhbF9hbW91bnQVcHJvY2Vzc19wZW5kaW5nX3N0YWtlJHByb2Nlc3NfcGVuZGluZ19zdGFrZXNfYW5kX3dpdGhkcmF3cwtwcm9qZWN0X3VybBNwcm9vZl9vZl9wb3NzZXNzaW9uFXByb3RvY29sX3B1YmtleV9ieXRlcw9wdWJsaWNfdHJhbnNmZXIRcmVxdWVzdF9hZGRfc3Rha2UccmVxdWVzdF9hZGRfc3Rha2VfYXRfZ2VuZXNpcxtyZXF1ZXN0X3NldF9jb21taXNzaW9uX3JhdGUVcmVxdWVzdF9zZXRfZ2FzX3ByaWNlFnJlcXVlc3Rfd2l0aGRyYXdfc3Rha2UNcmV3YXJkX2Ftb3VudAZzZW5kZXIdc2V0X2NhbmRpZGF0ZV9jb21taXNzaW9uX3JhdGUXc2V0X2NhbmRpZGF0ZV9nYXNfcHJpY2UQc2V0X3ZvdGluZ19wb3dlcgRzb21lFnN0YWtlX2FjdGl2YXRpb25fZXBvY2gMc3Rha2VfYW1vdW50EXN0YWtlZF9zdWlfYW1vdW50DnN0YWtlcl9hZGRyZXNzDHN0YWtpbmdfcG9vbA9zdGFraW5nX3Bvb2xfaWQGc3RyaW5nA3N1aQtzdWlfYWRkcmVzcwtzdWlfYmFsYW5jZRZzdWlfc3lzdGVtX3N0YXRlX2lubmVyCHRvX2J5dGVzC3RvdGFsX3N0YWtlEnRvdGFsX3N0YWtlX2Ftb3VudAh0cmFuc2Zlcgp0eF9jb250ZXh0D3Vuc3Rha2luZ19lcG9jaCB1cGRhdGVfY2FuZGlkYXRlX25ldHdvcmtfYWRkcmVzcx91cGRhdGVfY2FuZGlkYXRlX25ldHdvcmtfcHVia2V5HHVwZGF0ZV9jYW5kaWRhdGVfcDJwX2FkZHJlc3MgdXBkYXRlX2NhbmRpZGF0ZV9wcmltYXJ5X2FkZHJlc3MgdXBkYXRlX2NhbmRpZGF0ZV9wcm90b2NvbF9wdWJrZXkfdXBkYXRlX2NhbmRpZGF0ZV93b3JrZXJfYWRkcmVzcx51cGRhdGVfY2FuZGlkYXRlX3dvcmtlcl9wdWJrZXkSdXBkYXRlX2Rlc2NyaXB0aW9uEHVwZGF0ZV9pbWFnZV91cmwLdXBkYXRlX25hbWUhdXBkYXRlX25leHRfZXBvY2hfbmV0d29ya19hZGRyZXNzIHVwZGF0ZV9uZXh0X2Vwb2NoX25ldHdvcmtfcHVia2V5HXVwZGF0ZV9uZXh0X2Vwb2NoX3AycF9hZGRyZXNzIXVwZGF0ZV9uZXh0X2Vwb2NoX3ByaW1hcnlfYWRkcmVzcyF1cGRhdGVfbmV4dF9lcG9jaF9wcm90b2NvbF9wdWJrZXkgdXBkYXRlX25leHRfZXBvY2hfd29ya2VyX2FkZHJlc3MfdXBkYXRlX25leHRfZXBvY2hfd29ya2VyX3B1YmtleRJ1cGRhdGVfcHJvamVjdF91cmwDdXJsEXZhbGlkYXRlX21ldGFkYXRhFXZhbGlkYXRlX21ldGFkYXRhX2Jjcwl2YWxpZGF0b3IRdmFsaWRhdG9yX2FkZHJlc3MNdmFsaWRhdG9yX2NhcA12YWxpZGF0b3Jfc2V0EXZhbGlkYXRvcl93cmFwcGVyBXZhbHVlHnZlcmlmaWVkX29wZXJhdGlvbl9jYXBfYWRkcmVzcwx2b3RpbmdfcG93ZXIOd29ya2VyX2FkZHJlc3MTd29ya2VyX3B1YmtleV9ieXRlcwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgMIAAAAAAAAAAADCAEAAAAAAAAAAwgCAAAAAAAAAAMIAwAAAAAAAAADCAQAAAAAAAAAAwgFAAAAAAAAAAMIBgAAAAAAAAADCAcAAAAAAAAAAwgIAAAAAAAAAAMICQAAAAAAAAADCAoAAAAAAAAAAwgLAAAAAAAAAAMIDAAAAAAAAAADCGQAAAAAAAAAAwhlAAAAAAAAAAMIZgAAAAAAAAADCNAHAAAAAAAAAwgAAQAAAAAAAAMIoIYBAAAAAAAAAhZrBVYKAjUKApIBCgJVCgIyCAYeCAYqCAxUCAwzCAZLCAZQCAaRAQgGQwsFAQoCQgsFAQoCPwsFAQoCRgsFAQoCPQsFAQgGQAsFAQgGQQsFAQgGRQsFAQgGIwgHAQIKMQgAkAEDSQgJJgNnCA8ZA0QDPAM7AyMIBwICBU4ICYoBBWYFIQMTAwMCB04ICYoBBWYFYwNzA1EDXQMAAwAAJUQLAAwOCwEMGQsCDB0LAwweCwQMHwsFDCALBgwhCwcMIgsIDCMLCQwPCwoMEAsLDBELDAwSOAAMEzgADBQ4AAwVOAAMFjgBDBc4AQwYOAEMGjgBDBsLDQwcCw4LGQsdCx4LHwsgCyELIgsjCw8LEAsRCxILEwsWCxQLFQsXCxgLGgsbCxwSAAIBAwAAKIkBDglBKQcRJQQLDgpBKQcRJQwQBQ0JDBALEAQVDgtBKQcRJQwRBRcJDBELEQQfDgxBKQcRJQwSBSEJDBILEgQpDgVBKQcRJQwTBSsJDBMLEwQzDgZBKQcRJQwUBTUJDBQLFAQ9DgdBKQcRJQwVBT8JDBULFQRHDghBKQcRJQwWBUkJDBYLFgRMBVALDwEHCScKDgcQJQRVBVkLDwEHCCcKDQcSIwReBWILDwEHDycLAAsBCwILAwsECwURTRFVCwYRTRFVCwcRXQsIEV0LCRFNEVULChFNEVULCxFNEVULDBFNEVUKDxFWEQAMFw4XEUkLFwsNCw4LDxFMAgIDAAAFBQsADwALARFfAgMDAAAFBQsADwALARFeAgQDAAAFDQoAEAEUCgAPAhUKABADFAsADwQVAgUDAAAvOg4BOAIMBAoEBgAAAAAAAAAAJAQIBQ4LAAELAwEHCycKAy4RWwYBAAAAAAAAABYMBQoADwALAQsFCgMRaAwGCgAQABFhBCIKAA8AEWYKABAFFAoEFgoADwUVCgAuETELABAGEAcUCwILAy4RWwsEEgI4AwsGAgYDAAAYLwoDLhFbBgAAAAAAAAAAIQQHBQ0LAAELAwEHDCcOATgCDAQKBAYAAAAAAAAAACQEFQUbCwABCwMBBwsnCgAPAAsBBgAAAAAAAAAACwMRaAsCOAQKAA8AEWYKABAFFAsEFgsADwUVAgcDAAA4LQ4BEWsMAw4BEWoMBQoADwALAQoCEWkMBw4HOAIMBgoGCgMXDAQKABAFFAsGFwoADwUVCgAuETELABAGEAcUCgIRXAsFCwIRWwsDCwQSAzgFCwcCCAMAAAUcCgIHEiMEBQUJCwABBw8nDgERbhQKABAGEAcUIQQTBRcLAAEHDicLAgsADwEVAgkDAAAFKQoALhEOBAUFCQsAAQcKJwoCBxIjBA4FEgsAAQcPJw4BEW4UCgAQBhAHFCEEHAUgCwABBw4nCgIKAA8BFQsCCwAPAhUCCgMAAAUOCgEHECUEBQUJCwABBwgnCwELAA8DFQILAwAABRcKAC4RDgQFBQkLAAEHCicKAQcQJQQOBRILAAEHCCcLAQsADwQVAgwDAAAFDgoAEAUUDgE4AhYKAA8FFQsADwALARFgAg0DAAAFEAoADwALARFnCgAuESgLABAFFCEEDQUPBwsnAg4BAAAFBAsAEAARYQIPAQAABQMLABAGAhABAAAFBQsAEAYQBxQCEQEAAAUECwAQBhAIAhIBAAAFBAsAEAYQCQITAQAABQQLABAGEAoCFAEAAAUECwAQBhALAhUBAAAFBAsAEAYQDAIWAQAABQQLABAGEA0CFwEAAAUECwAQBhAOAhgBAAAFBAsAEAYQDwIZAQAABQQLABAGEBACGgEAAAUECwAQBhARAhsBAAAFBAsAEAYQEgIcAQAABQQLABAGEBMCHQEAAAUECwAQBhAUAh4BAAAFBAsAEAYQFQIfAQAABQQLABAGEBYCIAEAAAUECwAQBhAXAiEBAAAFBAsAEAYQGAIiAQAABQQLABAGEBkCIwEAAAUECwAQBhAaAiQBAAAFBAsAEAYQGwIlAQAABQMLABAcAiYBAAAFBAsAEAEUAicBAAAFBAsAEAARbAIoAQAABQQLABAAEWwCKQEAAAUDCwARKAIqAQAABQQLABAdFAIrAwAABQULAQsADx0VAiwBAAAFBAsAEAARYwItAQAABQQLABAAEWQCLgEAAAUECwAQAhQCLwEAAAUECwAQBBQCMAEAAAUFCwAQAAsBEWUCMQEAAAUECwAQADgGAjIBAABDlwMKABAGEAcUCgEQBhAHFCEEDQgMAgUXCgAQBhAIFAoBEAYQCBQhDAILAgQcCAwNBSYKABAGEAwUCgEQBhAMFCEMDQsNBCsIDBgFNQoAEAYQDRQKARAGEA0UIQwYCxgEOggMGQVECgAQBhAQFAoBEAYQEBQhDBkLGQRJCAwaBVMKABAGEBIUCgEQBhASFCEMGgsaBFgIDBsFYgoAEAYQEhQKARAGEBMUIQwbCxsEZwgMHAVxCgAQBhATFAoBEAYQExQhDBwLHAR2CAwdBYABCgAQBhATFAoBEAYQEhQhDB0LHQSFAQgMHgWNAQoAEAYQFAoBEAYQFDgHDB4LHgSSAQgMAwWaAQoAEAYQFQoBEAYQFTgHDAMLAwSfAQgMBAWnAQoAEAYQGAoBEAYQGDgIDAQLBASsAQgMBQW0AQoAEAYQGgoBEAYQGjgIDAULBQS5AQgMBgXBAQoAEAYQGgoBEAYQGzgIDAYLBgTGAQgMBwXOAQoAEAYQGwoBEAYQGzgIDAcLBwTTAQgMCAXbAQoAEAYQGwoBEAYQGjgIDAgLCATgAQgMCQXoAQoAEAYQFAoBEAYQDDgJDAkLCQTtAQgMCgX1AQoAEAYQFQoBEAYQDTgJDAoLCgT6AQgMCwWCAgoAEAYQGAoBEAYQEDgKDAsLCwSHAggMDAWPAgoAEAYQGgoBEAYQEjgKDAwLDASUAggMDgWcAgoAEAYQGgoBEAYQEzgKDA4LDgShAggMDwWpAgoAEAYQGwoBEAYQEzgKDA8LDwSuAggMEAW2AgoAEAYQGwoBEAYQEjgKDBALEAS7AggMEQXDAgoBEAYQFAoAEAYQDDgJDBELEQTIAggMEgXQAgoBEAYQFQoAEAYQDTgJDBILEgTVAggMEwXdAgoBEAYQGAoAEAYQEDgKDBMLEwTiAggMFAXqAgoBEAYQGgoAEAYQEjgKDBQLFATvAggMFQX3AgoBEAYQGgoAEAYQEzgKDBULFQT8AggMFgWEAwoBEAYQGwoAEAYQEzgKDBYLFgSNAwsAAQsBAQgMFwWVAwsBEAYQGwsAEAYQEjgKDBcLFwIzAAAADxEKADgLBAoLAQELAAEJDAIFDwsAOAwLASEMAgsCAjQAAABFGgoAOAsEBggMAgUJCgE4CwwCCwIEEgsBAQsAAQkMAwUYCwA4DAsBOAwhDAMLAwI1AwAAERkKAS4RXAwCCgIKABAGEAcUIQQMBRILAAELAQEHDScLAgsBEW0LAA8cFQI2AwAABRIOAUEpBxElBAYFCgsAAQcJJwsBEU0RVQsADwYPCBUCNwMAAAUSDgFBKQcRJQQGBQoLAAEHCScLARFNEVULAA8GDwkVAjgDAAAFEQ4BQSkHESUEBgUKCwABBwknCwERXQsADwYPChUCOQMAAAURDgFBKQcRJQQGBQoLAAEHCScLARFdCwAPBg8LFQI6AwAABRYOAUEpBxElBAYFCgsAAQcJJwsBEU0RVTgNCgAPBg8UFQsAEAYRSQI7AwAABR4KAC4RDgQFBQkLAAEHCicOAUEpBxElBA8FEwsAAQcJJwsBEU0RVQoADwYPDBULABAGEUkCPAMAAAUWDgFBKQcRJQQGBQoLAAEHCScLARFNEVU4DQoADwYPFRULABAGEUkCPQMAAAUeCgAuEQ4EBQUJCwABBwonDgFBKQcRJQQPBRMLAAEHCScLARFNEVUKAA8GDw0VCwAQBhFJAj4DAAAFFg4BQSkHESUEBgUKCwABBwknCwERTRFVOA0KAA8GDxYVCwAQBhFJAj8DAAAFHgoALhEOBAUFCQsAAQcKJw4BQSkHESUEDwUTCwABBwknCwERTRFVCgAPBg8OFQsAEAYRSQJAAwAABRYOAUEpBxElBAYFCgsAAQcJJwsBEU0RVTgNCgAPBg8XFQsAEAYRSQJBAwAABR4KAC4RDgQFBQkLAAEHCicOAUEpBxElBA8FEwsAAQcJJwsBEU0RVQoADwYPDxULABAGEUkCQgMAAAUQCwE4DgoADwYPGBULAjgOCgAPBg8ZFQsAEAYRSQJDAwAABRcKAC4RDgQFBQkLAAEHCicLAQoADwYPEBULAgoADwYPERULABAGEUkCRAMAAAUKCwE4DgoADwYPGhULABAGEUkCRQMAAAUSCgAuEQ4EBQUJCwABBwonCwEKAA8GDxIVCwAQBhFJAkYDAAAFCgsBOA4KAA8GDxsVCwAQBhFJAkcDAAAFEgoALhEOBAUFCQsAAQcKJwsBCgAPBg8TFQsAEAYRSQJIAwAABY8BCgAuER04DwQSCgAPBg8UOBAKAA8GDwwVOAEKAA8GDxQVCgAuER44DwQkCgAPBg8VOBAKAA8GDw0VOAEKAA8GDxUVCgAuER84DwQ2CgAPBg8WOBAKAA8GDw4VOAEKAA8GDxYVCgAuESA4DwRICgAPBg8XOBAKAA8GDw8VOAEKAA8GDxcVCgAuESE4EQRnCgAPBg8YOBIKAA8GDxAVOAAKAA8GDxgVCgAPBg8ZOBIKAA8GDxEVOAAKAA8GDxkVCgAuESM4EQR5CgAPBg8aOBIKAA8GDxIVOAAKAA8GDxoVCgAuESQ4EQSMAQoADwYPGzgSCgAPBg8TFTgACwAPBg8bFQWOAQsAAQJJAQAABQQLADgTEUoCSgECAEsDAAAFAwsAEAACTAAAAEgYDgAQBxQMBgoDEWIMBQsGCgMRbQwECwAGAAAAAAAAAAALBAoBCwUKAgYAAAAAAAAAAAsBCwILAxFWEgECAQQBBwEDAQgBBQEGAQAAAAAFAAYABwAIAAkACgALAAwAAQAEAAIAAwARABIAEwAUAA0ADgAPABABAgEBACcAbQCMAQCNAQCQAQAKc3VpX3N5c3RlbeEeoRzrCwYAAAAMAQAeAh5OA2y8AwSoBBAFuATdAweVCO0NCIIWYAbiFjYKmBcIDKAXgAcNoB4ED6QeAgA5ASECFAIWAhgCIAI4AjwCPQI+ADYANwA6AFMAVAAICAABAwcBAAACAAQBAAEDAQwBAAEFAgcABQ4EAAYFAgAHDAwCBwEEAQkNAgAKBgQACwQHAAsHDAAMCQQADAoEAAwLBAANEAQADg8MAAAXAAEAACoCAQAALAMBAAApAwEAACsDAQAALgQBAAA0BAEAAC0FAQAAMwUBAAAmBgEAACgGBwAAJwgBAAAvCQEAADAJCgAAJQsBAAA/CwEAADEDAQAASQwBAABHDAEAAEgMAQAAUQwBAABKDAEAAEAMAQAATAwBAABCDAEAAE0MAQAAQwwBAABPDAEAAEUMAQAATg0BAABEDQEAAFAMAQAARgwBAABLDAEAAEEMAQAAIg4PAAAREBEAABMSCgAAHhATAAAfEBQAAB0QFAADGSgpAQAEEhoBAgcEBBUyNgIHBAQkMjMCBwQIIyQBAQwINRwBAQgJMiIjAAwRExEADBMwCgAMFxYXAAwbARgADCIvDwAMJSwBAAwmJQcADCcmBwAMKR8BAAwqHQEADCsfAQAMLB4BAAwtIQEADC4gAQAMLysKAAwxHgEADDMhAQAMNCABAAw7ExgADD8sAQAMQC0BAAxBLQEADEItAQAMQy0BAAxELgEADEUtAQAMRi0BAAxHLQEADEgtAQAMSS0BAAxKLQEADEstAQAMTC0BAAxNLQEADE4uAQAMTy0BAAxQLQEADFEtAQAMUhc0ACoZLhstByknLSosGSo1KzUICAUKCA8LAgEIBgMDCA4ICQcICAAQBwgACgIKAgoCCgIKAgoCCgIKAgoCCgIKAgoCAwMHCAgCBwgABwgIAwcIAAYIEAMDBwgAAwcICAQHCAALAwEIBgUHCAgBCAsFBwgACgsDAQgGCwEBAwUHCAgDBwgACAsHCAgBCwIBCAYDBwgABggQBQMHCAAKAgYICAQHCAAKAgoCBggIAgcIAAYIBAEGCwcCAwgKAQcIAAEKBQsLAgEIBgsCAQgGBwgAAwMDAwMDAwcICAEGCA0BBwgNAwgACAwDBwoIDwsCAQgGAwMIDggJBwgIAQgMAQMCAwgMAwcIBQkACQEBCAABCQAQBwgNCgIKAgoCCgIKAgoCCgIKAgoCCgIKAgoCAwMHCAgCBwgNBwgIAgcIDQYICAMHCA0GCBADAwcIDQMGCAgBBggIAQUCCQAFBAcIDQsDAQgGBQcICAUHCA0KCwMBCAYLAQEDBQcICAEIBgILAgEJAAcICAELAwEJAAELAwEIBgMHCA0ICwYICAMHCA0GCBAFAwcIDQoCBggIBAcIDQoCCgIGCAgCBwgNBggECwcIDQMDCwIBCAYLAgEIBgMDAwMDBwgIAgcIDQgNAgcIBQkAAQkBAQgNAgMIDQEHCQEHQmFsYW5jZQRDb2luAklEBk9wdGlvbhVQb29sVG9rZW5FeGNoYW5nZVJhdGUDU1VJDFN0YWtlU3Vic2lkeQlTdGFrZWRTdWkOU3VpU3lzdGVtU3RhdGUTU3VpU3lzdGVtU3RhdGVJbm5lchVTdWlTeXN0ZW1TdGF0ZUlubmVyVjIQU3lzdGVtUGFyYW1ldGVycwVUYWJsZQlUeENvbnRleHQDVUlEH1VudmVyaWZpZWRWYWxpZGF0b3JPcGVyYXRpb25DYXAJVmFsaWRhdG9yGmFjdGl2ZV92YWxpZGF0b3JfYWRkcmVzc2VzA2FkZA1hZHZhbmNlX2Vwb2NoB2JhbGFuY2UKYm9ycm93X211dARjb2luBmNyZWF0ZQ1keW5hbWljX2ZpZWxkDGZyb21fYmFsYW5jZQdnZW5lc2lzHGdlbmVzaXNfc3lzdGVtX3N0YXRlX3ZlcnNpb24CaWQYbG9hZF9pbm5lcl9tYXliZV91cGdyYWRlEWxvYWRfc3lzdGVtX3N0YXRlFWxvYWRfc3lzdGVtX3N0YXRlX211dAZvYmplY3QGb3B0aW9uE3Bvb2xfZXhjaGFuZ2VfcmF0ZXMPcHVibGljX3RyYW5zZmVyBnJlbW92ZRByZXBvcnRfdmFsaWRhdG9yEXJlcXVlc3RfYWRkX3N0YWtlGnJlcXVlc3RfYWRkX3N0YWtlX211bF9jb2luG3JlcXVlc3RfYWRkX3N0YWtlX25vbl9lbnRyeRVyZXF1ZXN0X2FkZF92YWxpZGF0b3IfcmVxdWVzdF9hZGRfdmFsaWRhdG9yX2NhbmRpZGF0ZRhyZXF1ZXN0X3JlbW92ZV92YWxpZGF0b3IicmVxdWVzdF9yZW1vdmVfdmFsaWRhdG9yX2NhbmRpZGF0ZRtyZXF1ZXN0X3NldF9jb21taXNzaW9uX3JhdGUVcmVxdWVzdF9zZXRfZ2FzX3ByaWNlFnJlcXVlc3Rfd2l0aGRyYXdfc3Rha2UgcmVxdWVzdF93aXRoZHJhd19zdGFrZV9ub25fZW50cnkUcm90YXRlX29wZXJhdGlvbl9jYXAGc2VuZGVyJ3NldF9jYW5kaWRhdGVfdmFsaWRhdG9yX2NvbW1pc3Npb25fcmF0ZSFzZXRfY2FuZGlkYXRlX3ZhbGlkYXRvcl9nYXNfcHJpY2UMc2hhcmVfb2JqZWN0DXN0YWtlX3N1YnNpZHkMc3Rha2luZ19wb29sA3N1aQpzdWlfc3lzdGVtFnN1aV9zeXN0ZW1fc3RhdGVfaW5uZXIUc3lzdGVtX3N0YXRlX3ZlcnNpb24FdGFibGUIdHJhbnNmZXIKdHhfY29udGV4dBV1bmRvX3JlcG9ydF92YWxpZGF0b3IqdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3JfbmV0d29ya19hZGRyZXNzKXVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX25ldHdvcmtfcHVia2V5JnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3AycF9hZGRyZXNzKnVwZGF0ZV9jYW5kaWRhdGVfdmFsaWRhdG9yX3ByaW1hcnlfYWRkcmVzcyp1cGRhdGVfY2FuZGlkYXRlX3ZhbGlkYXRvcl9wcm90b2NvbF9wdWJrZXkpdXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3Jfd29ya2VyX2FkZHJlc3ModXBkYXRlX2NhbmRpZGF0ZV92YWxpZGF0b3Jfd29ya2VyX3B1YmtleRx1cGRhdGVfdmFsaWRhdG9yX2Rlc2NyaXB0aW9uGnVwZGF0ZV92YWxpZGF0b3JfaW1hZ2VfdXJsFXVwZGF0ZV92YWxpZGF0b3JfbmFtZSt1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfbmV0d29ya19hZGRyZXNzKnVwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF9uZXR3b3JrX3B1YmtleSd1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfcDJwX2FkZHJlc3MrdXBkYXRlX3ZhbGlkYXRvcl9uZXh0X2Vwb2NoX3ByaW1hcnlfYWRkcmVzcyt1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfcHJvdG9jb2xfcHVia2V5KnVwZGF0ZV92YWxpZGF0b3JfbmV4dF9lcG9jaF93b3JrZXJfYWRkcmVzcyl1cGRhdGVfdmFsaWRhdG9yX25leHRfZXBvY2hfd29ya2VyX3B1YmtleRx1cGRhdGVfdmFsaWRhdG9yX3Byb2plY3RfdXJsCHYxX3RvX3YyCXZhbGlkYXRvcg12YWxpZGF0b3JfY2FwB3ZlcnNpb24AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDCAAAAAAAAAAAAwgBAAAAAAAAAAUgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgIcCAVVAwADAAAVFwsBCwILAwsECwULBgsHETIMCREzDAoLAAoKEgAMCA0IDwALCgsJOAALCDgBAgEBBAABEwsAEScLAQsCCwMLBAsFCwYLBwsICwkLCgsLCwwLDQsOCw8ROQICAQQAAQULABEnCwEROwIDAQQAAQYLABEnCwEuETgCBAEEAAEGCwARJwsBLhE6AgUBBAABBgsAEScLAQsCET0CBgEEAAEGCwARJwsBCwIRQQIHAQQAAQcLABEnCwELAi4RPAIIAQQAAQcLABEnCwELAi4RQAIJAQQAAQoLAAsBCwIKAxEKCwMuES84AgIKAQAAAQcLABEnCwELAgsDETYCCwEEAAEMCwARJwsBCwILAwoEETcLBC4RLzgCAgwBBAABCwsACwEKAhENCgI4AwsCLhEvOAQCDQEAAAEHCwARJwsBCwIuET4CDgEEAAEGCwARJwsBCwIRNQIPAQQAAQYLABEnCwELAhFDAhABBAABBQsAEScLARE/AhEBBAABBgsAEScLAQsCEU0CEgEEAAEGCwARJwsBCwIRSwITAQQAAQYLABEnCwELAhFMAhQBBAABBgsAEScLAQsCEVUCFQEEAAEGCwARJwsBCwIRTgIWAQQAAQYLABEnCwELAhFEAhcBBAABBgsAEScLAQsCEVACGAEEAAEGCwARJwsBCwIRRgIZAQQAAQYLABEnCwELAhFRAhoBBAABBgsAEScLAQsCEUcCGwEEAAEGCwARJwsBCwIRUwIcAQQAAQYLABEnCwELAhFJAh0BBAABBwsAEScLAQsCCwMRUgIeAQQAAQcLABEnCwELAgsDEUgCHwEEAAEGCwARJwsBCwIRVAIgAQQAAQYLABEnCwELAhFKAiEBBAABBgsAEScLAQsCEU8CIgEEAAEGCwARJwsBCwIRRQIjAQAAAQULABEnCwERNAIkAQAAAQQLABEmETACJQAAABQdCwIRJwwLCgouES8HAiEECgUQCwsBCwoBBwAnCwsLAwsECwALAQsFCwYLBwsICwkLChExAiYAAAABBAsAESguAicAAAABAwsAESgCKAAAADEvCgAQARQGAQAAAAAAAAAhBBkKAA8ACgAQARQ4BRFWDAIGAgAAAAAAAAAKAA8BFQoADwAKABABFAsCOAYKAA8ACgAQARQ4BwwBCgEuEUILABABFCEEKQUtCwEBBwEnCwECAAAAAQAaAAxzdGFraW5nX3Bvb2yBHaEc6wsGAAAADAEAFAIUNANIrQIE9QIiBZcDzgIH5QXICAitDmAGjQ/IAQrVEEIMlxGOCw2lHBwPwRwEAEQBLQIOAg8CKAIsAkUCSAJJAkoABwwAAAQHAAAGDAABAwcBAAACAAwAAwEEAQABBQIHAAUKBAAGBQIABwgMAgcBBAEJCQIAACoAAQAAOwIDAAA8BAUAAE8GBwAASwMFAAAWCAkAADoKCQAAOQsJAAA4CwkAAFAMBQAACw0JAAATDQkAAEcODwAAMxARAABDEA8AAEIQDwAAIw4SAAAhDhIAAEATAwAAQRMJAAAnFAkAACAVEgAANhYXAAAwDg8AADEODwAAGA4YAABGGQ8AADQZDwAAJBYSAAAbGg8AABwaDwAAHwkXAAARFgkAARAwJQEAARouCQEAAR00LwEDASIwEgEAASUwEgEAASsJHwEAAT8vHwEAAioAIgADJigPAQADQC0hAQADTiQPAQADUQkhAQAEKSoPAAUVHgkABR4lEQEIBSoAHgAHDSsJAgcEBxA1NgIHBAcSNRICBwQHKgAdAgcECEkyCQEICRcnDwAJPicxADQcJg8sICsgLwEpIDEcKiAiDycPJA8lDzUDIw8hDzMcMhwBBwgKAQgABAcIAAsFAQgIAwcICgEIAgMHCAAIAgYICgELBQEICAIGCAAIAgIDCwUBCAgCBwgACwUBCAgAAgcIAAYICgEHCAAEBwgAAwMDAgcIAAMBBggAAQMBBggCAQgGAQEDBwgCAwcICgIHCAIIAgIGCAIGCAICBggAAwEIAQEGCwkCAwgBAQYIAQIGCAEDAQsJAgMIAQIDCAEBCwkCCQAJAQEIBwELAwEJAAEICAELBQEJAAEIBAIIAgMBBgsFAQkAAQYJAAYIAgMLBQEICAMLBQEICAMBBggKAgcLBQEJAAsFAQkAAggBCwUBCAgCAwMDBwsJAgkACQEJAAkBBQMDCAEDAwIHCwUBCQADAgcLAwEJAAkAAQkAAQYLAwEJAAEFAgkABQIGCAILBQEICAIGCwMBCQAJAAIGCwkCCQAJAQkAAQYJAQMDCAEDA0JhZwdCYWxhbmNlAklEBk9wdGlvbhVQb29sVG9rZW5FeGNoYW5nZVJhdGUDU1VJCVN0YWtlZFN1aQtTdGFraW5nUG9vbAVUYWJsZQlUeENvbnRleHQDVUlEFWFjdGl2YXRlX3N0YWtpbmdfcG9vbBBhY3RpdmF0aW9uX2Vwb2NoA2FkZANiYWcHYmFsYW5jZQZib3Jyb3cYY2hlY2tfYmFsYW5jZV9pbnZhcmlhbnRzCGNvbnRhaW5zF2RlYWN0aXZhdGVfc3Rha2luZ19wb29sEmRlYWN0aXZhdGlvbl9lcG9jaAZkZWxldGUPZGVwb3NpdF9yZXdhcmRzBWVwb2NoDmV4Y2hhbmdlX3JhdGVzDGV4dHJhX2ZpZWxkcwRmaWxsDmdldF9zdWlfYW1vdW50EGdldF90b2tlbl9hbW91bnQQZ2V0X3dpdGhfZGVmYXVsdAJpZBVpbml0aWFsX2V4Y2hhbmdlX3JhdGUZaXNfZXF1YWxfc3Rha2luZ19tZXRhZGF0YQtpc19pbmFjdGl2ZQdpc19ub25lDGlzX3ByZWFjdGl2ZRVpc19wcmVhY3RpdmVfYXRfZXBvY2gHaXNfc29tZQRqb2luD2pvaW5fc3Rha2VkX3N1aQRtYXRoA21pbgNuZXcEbm9uZQZvYmplY3QGb3B0aW9uG3BlbmRpbmdfcG9vbF90b2tlbl93aXRoZHJhdw1wZW5kaW5nX3N0YWtlFHBlbmRpbmdfc3Rha2VfYW1vdW50HXBlbmRpbmdfc3Rha2Vfd2l0aGRyYXdfYW1vdW50GnBlbmRpbmdfdG90YWxfc3VpX3dpdGhkcmF3B3Bvb2xfaWQRcG9vbF90b2tlbl9hbW91bnQScG9vbF90b2tlbl9iYWxhbmNlIXBvb2xfdG9rZW5fZXhjaGFuZ2VfcmF0ZV9hdF9lcG9jaAlwcmluY2lw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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/iota/vendor/wormhole_movement_m2_devnet/devnet/genesis.blob b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/examples/coins/sources/coin_10.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/coins/sources/coin_10.move new file mode 100644 index 0000000000..2c98b87936 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/examples/coins/sources/coin_8.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/coins/sources/coin_8.move new file mode 100644 index 0000000000..0edd761603 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/Makefile b/target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/Makefile new file mode 100644 index 0000000000..210a28de7a --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/Move.devnet.toml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/Move.devnet.toml new file mode 100644 index 0000000000..d9363b2554 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/Move.lock b/target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/Move.lock new file mode 100644 index 0000000000..9bddcd1268 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/Move.toml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/Move.toml new file mode 100644 index 0000000000..38872b17dc --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/sources/sender.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/core_messages/sources/sender.move new file mode 100644 index 0000000000..960c6355c3 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/examples/templates/README.md b/target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/templates/README.md new file mode 100644 index 0000000000..41a8dbd907 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/examples/templates/wrapped_coin/Move.toml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/templates/wrapped_coin/Move.toml new file mode 100644 index 0000000000..0c25220b1d --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/examples/templates/wrapped_coin/sources/coin.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/examples/templates/wrapped_coin/sources/coin.move new file mode 100644 index 0000000000..313b9ba919 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/scripts/deploy.sh b/target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/deploy.sh new file mode 100755 index 0000000000..d572f8cbf0 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/scripts/node_builder.sh b/target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/node_builder.sh new file mode 100755 index 0000000000..940017e330 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/scripts/register_devnet.sh b/target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/register_devnet.sh new file mode 100755 index 0000000000..79df935672 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/scripts/setup_rust.sh b/target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/setup_rust.sh new file mode 100755 index 0000000000..3738ba9548 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/scripts/start_node.sh b/target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/start_node.sh new file mode 100755 index 0000000000..2ffce35438 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/start_node.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +set -x + +sui start 2>&1 diff --git a/target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/switch.sh b/target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/switch.sh new file mode 100755 index 0000000000..caba0f4b70 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/scripts/wait_for_devnet.sh b/target_chains/iota/vendor/wormhole_movement_m2_devnet/scripts/wait_for_devnet.sh new file mode 100755 index 0000000000..ff0b00355e --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/.gitignore b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/.gitignore new file mode 100644 index 0000000000..b552b7394c --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/.gitignore @@ -0,0 +1,4 @@ +node_modules +sui.log.* +./token_bridge/ +./wormhole/ diff --git a/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/Makefile b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/Makefile new file mode 100644 index 0000000000..60c5246198 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/js/00_environment.ts b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/00_environment.ts new file mode 100644 index 0000000000..3ca09f236d --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/js/01_wormhole.ts b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/01_wormhole.ts new file mode 100644 index 0000000000..ae9a26f08b --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/build.ts b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/build.ts new file mode 100644 index 0000000000..374831ab8f --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/consts.ts b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/consts.ts new file mode 100644 index 0000000000..c4aa0787f4 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/error/moveAbort.ts b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/error/moveAbort.ts new file mode 100644 index 0000000000..04fdde7524 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/error/wormhole.ts b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/error/wormhole.ts new file mode 100644 index 0000000000..f011d606df --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/setup.ts b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/setup.ts new file mode 100644 index 0000000000..0399be84e7 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/upgrade.ts b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/upgrade.ts new file mode 100644 index 0000000000..4698e9a85b --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/utils.ts b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/utils.ts new file mode 100644 index 0000000000..58f71444e1 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/wormhole/testPublishMessage.ts b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/js/helpers/wormhole/testPublishMessage.ts new file mode 100644 index 0000000000..35a3876a51 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/package-lock.json b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/package-lock.json new file mode 100644 index 0000000000..3b6dd637e6 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/package.json b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/package.json new file mode 100644 index 0000000000..f29c59e15c --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/run_integration_test.sh b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/run_integration_test.sh new file mode 100755 index 0000000000..80da863836 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/scripts/upgrade-token-bridge.ts b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/scripts/upgrade-token-bridge.ts new file mode 100644 index 0000000000..605128dddb --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/scripts/upgrade-wormhole.ts b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/scripts/upgrade-wormhole.ts new file mode 100644 index 0000000000..82d26b0f72 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/client.yaml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/client.yaml new file mode 100644 index 0000000000..33371df956 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/fullnode.yaml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/fullnode.yaml new file mode 100644 index 0000000000..fc71e910e6 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/testing/sui_config/genesis.blob b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/complete_transfer.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/complete_transfer.move new file mode 100644 index 0000000000..c9086e7dbc --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/complete_transfer_with_payload.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/complete_transfer_with_payload.move new file mode 100644 index 0000000000..ba35a7a9e8 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/create_wrapped.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/create_wrapped.move new file mode 100644 index 0000000000..639d170373 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/datatypes/normalized_amount.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/datatypes/normalized_amount.move new file mode 100644 index 0000000000..e63d3d296b --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/governance/register_chain.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/governance/register_chain.move new file mode 100644 index 0000000000..90af5c1fbe --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/governance/upgrade_contract.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/governance/upgrade_contract.move new file mode 100644 index 0000000000..e03729b021 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/messages/asset_meta.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/messages/asset_meta.move new file mode 100644 index 0000000000..30f03f2cde --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/messages/transfer.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/messages/transfer.move new file mode 100644 index 0000000000..190afad9dc --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/messages/transfer_with_payload.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/messages/transfer_with_payload.move new file mode 100644 index 0000000000..3180616872 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/migrate.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/migrate.move new file mode 100644 index 0000000000..e3559de48a --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/resources/native_asset.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/resources/native_asset.move new file mode 100644 index 0000000000..dba6b91960 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/resources/token_registry.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/resources/token_registry.move new file mode 100644 index 0000000000..f03f1d3383 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/resources/wrapped_asset.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/resources/wrapped_asset.move new file mode 100644 index 0000000000..7df31c540e --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/setup.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/setup.move new file mode 100644 index 0000000000..89beaf418b --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/state.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/state.move new file mode 100644 index 0000000000..fbe8dab4a4 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/coin_native_10.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/coin_native_10.move new file mode 100644 index 0000000000..c87282a157 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/coin_native_4.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/coin_native_4.move new file mode 100644 index 0000000000..889d036200 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/coin_wrapped_12.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/coin_wrapped_12.move new file mode 100644 index 0000000000..74932765ef --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/coin_wrapped_7.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/coin_wrapped_7.move new file mode 100644 index 0000000000..fa09e1b444 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/dummy_message.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/dummy_message.move new file mode 100644 index 0000000000..d3784e5a97 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/token_bridge_scenario.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/test/token_bridge_scenario.move new file mode 100644 index 0000000000..d7921ee809 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/transfer_tokens.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/transfer_tokens.move new file mode 100644 index 0000000000..60f1aa2301 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/transfer_tokens_with_payload.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/transfer_tokens_with_payload.move new file mode 100644 index 0000000000..0ed204bfb7 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/utils/coin_utils.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/utils/coin_utils.move new file mode 100644 index 0000000000..80d351f933 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/utils/string_utils.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/utils/string_utils.move new file mode 100644 index 0000000000..868fa74927 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/vaa.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/vaa.move new file mode 100644 index 0000000000..c2e9a26096 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/version_control.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/token_bridge/sources/version_control.move new file mode 100644 index 0000000000..caee75794c --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/.gitignore b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/.gitignore new file mode 100644 index 0000000000..378eac25d3 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/.gitignore @@ -0,0 +1 @@ +build diff --git a/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/Makefile b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/Makefile new file mode 100644 index 0000000000..1f9d45f8af --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.devnet.toml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.devnet.toml new file mode 100644 index 0000000000..ba3f5e54aa --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.lock b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.lock new file mode 100644 index 0000000000..570d17a100 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.mainnet.toml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.mainnet.toml new file mode 100644 index 0000000000..7465390ebb --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.testnet.toml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.testnet.toml new file mode 100644 index 0000000000..ed34ccb472 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.toml b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/Move.toml new file mode 100644 index 0000000000..42fe952451 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/README.md b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/README.md new file mode 100644 index 0000000000..62a62a67b1 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/datatypes/bytes20.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/datatypes/bytes20.move new file mode 100644 index 0000000000..3c097dec69 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/datatypes/bytes32.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/datatypes/bytes32.move new file mode 100644 index 0000000000..ab713f6f25 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/datatypes/external_address.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/datatypes/external_address.move new file mode 100644 index 0000000000..b2134caf35 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/datatypes/guardian_signature.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/datatypes/guardian_signature.move new file mode 100644 index 0000000000..58698d51a6 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/emitter.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/emitter.move new file mode 100644 index 0000000000..5c5f2dbe73 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance/set_fee.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance/set_fee.move new file mode 100644 index 0000000000..ab3431b7ca --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance/transfer_fee.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance/transfer_fee.move new file mode 100644 index 0000000000..f31274b8e0 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance/update_guardian_set.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance/update_guardian_set.move new file mode 100644 index 0000000000..15bfb1953b --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance/upgrade_contract.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance/upgrade_contract.move new file mode 100644 index 0000000000..018511e784 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance_message.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/governance_message.move new file mode 100644 index 0000000000..20d4cfee5f --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/migrate.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/migrate.move new file mode 100644 index 0000000000..958a0b12f8 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/publish_message.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/publish_message.move new file mode 100644 index 0000000000..3255fee5ba --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/consumed_vaas.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/consumed_vaas.move new file mode 100644 index 0000000000..a09327cf7e --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/fee_collector.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/fee_collector.move new file mode 100644 index 0000000000..f75ab49719 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/guardian.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/guardian.move new file mode 100644 index 0000000000..84c6a48eb1 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/guardian_set.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/guardian_set.move new file mode 100644 index 0000000000..e55ccd3758 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/set.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/resources/set.move new file mode 100644 index 0000000000..bb52326489 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/setup.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/setup.move new file mode 100644 index 0000000000..c7f233e102 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/state.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/state.move new file mode 100644 index 0000000000..d3b1f0c94b --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/test/wormhole_scenario.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/test/wormhole_scenario.move new file mode 100644 index 0000000000..f587778e4c --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/utils/bytes.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/utils/bytes.move new file mode 100644 index 0000000000..0d181f6775 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/utils/cursor.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/utils/cursor.move new file mode 100644 index 0000000000..73a96ebea0 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/utils/package_utils.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/utils/package_utils.move new file mode 100644 index 0000000000..12dcfde91a --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/vaa.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/vaa.move new file mode 100644 index 0000000000..03528351d4 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/version_control.move b/target_chains/iota/vendor/wormhole_movement_m2_devnet/wormhole/sources/version_control.move new file mode 100644 index 0000000000..13ee6106df --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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 {} + } +} diff --git a/target_chains/sui/contracts/Move.lock b/target_chains/sui/contracts/Move.lock index 4d695c4552..c1788d4ba6 100644 --- a/target_chains/sui/contracts/Move.lock +++ b/target_chains/sui/contracts/Move.lock @@ -2,35 +2,35 @@ [move] version = 0 -manifest_digest = "915D6459031D76A0618631BDF08328171A3494876C5E17735BCBFF462BEDF464" +manifest_digest = "320300697C11C4D84BF6ED32C1DB48A4EE830B6B44F4DFDA4E89345875C5EA11" deps_digest = "3C4103934B1E040BB6B23F1D610B4EF9F2F1166A50A104EADCF77467C004C600" dependencies = [ - { name = "Iota" }, + { name = "Sui" }, { name = "Wormhole" }, ] [[move.package]] -name = "Iota" -source = { git = "https://github.com/iotaledger/iota.git", rev = "751c23caf24efd071463b9ffd07eabcb15f44f31", subdir = "crates/iota-framework/packages/iota-framework" } +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 = "MoveStdlib" -source = { git = "https://github.com/iotaledger/iota.git", rev = "751c23caf24efd071463b9ffd07eabcb15f44f31", subdir = "crates/iota-framework/packages/move-stdlib" } - [[move.package]] name = "Wormhole" -source = { local = "../vendor/wormhole_iota_testnet/wormhole" } +source = { git = "https://github.com/wormhole-foundation/wormhole.git", rev = "82d82bffd5a8566e4b5d94be4e4678ad55ab1f4f", subdir = "sui/wormhole" } dependencies = [ - { name = "Iota" }, + { name = "Sui" }, ] [move.toolchain-version] -compiler-version = "0.9.2-rc" -edition = "2024.beta" -flavor = "iota" +compiler-version = "1.27.2" +edition = "legacy" +flavor = "sui" diff --git a/target_chains/sui/contracts/Move.toml b/target_chains/sui/contracts/Move.toml index e21819c7b3..fabdea676e 100644 --- a/target_chains/sui/contracts/Move.toml +++ b/target_chains/sui/contracts/Move.toml @@ -1,15 +1,17 @@ [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.Sui] +git = "https://github.com/MystenLabs/sui.git" +subdir = "crates/sui-framework/packages/sui-framework" +rev = "041c5f2bae2fe52079e44b70514333532d69f4e6" [dependencies.Wormhole] -local = "../vendor/wormhole_iota_testnet/wormhole" +git = "https://github.com/wormhole-foundation/wormhole.git" +subdir = "sui/wormhole" +rev = "82d82bffd5a8566e4b5d94be4e4678ad55ab1f4f" [addresses] -pyth = "0x23994dd119480ea614f7623520337058dca913cb1bb6e5d8d51c7b067d3ca3bb" +pyth = "0x00b53b0f4174108627fbee72e2498b58d6a2714cded53fac537034c220d26302" +wormhole = "0x5306f64e312b581766351c07af79c72fcb1cd25147157fdc2f8ad76de9a3fb6a" diff --git a/target_chains/sui/contracts/sources/batch_price_attestation.move b/target_chains/sui/contracts/sources/batch_price_attestation.move index 461c8daac3..4d44703c09 100644 --- a/target_chains/sui/contracts/sources/batch_price_attestation.move +++ b/target_chains/sui/contracts/sources/batch_price_attestation.move @@ -1,6 +1,6 @@ module pyth::batch_price_attestation { use std::vector::{Self}; - use iota::clock::{Self, Clock}; + use sui::clock::{Self, Clock}; use pyth::price_feed::{Self}; use pyth::price_info::{Self, PriceInfo}; @@ -164,7 +164,7 @@ module pyth::batch_price_attestation { #[test] #[expected_failure] fun test_deserialize_batch_price_attestation_invalid_magic() { - use iota::test_scenario::{Self, ctx}; + use sui::test_scenario::{Self, ctx}; let test = test_scenario::begin(@0x1234); let test_clock = clock::create_for_testing(ctx(&mut test)); // A batch price attestation with a magic number of 0x50325749 @@ -176,7 +176,7 @@ module pyth::batch_price_attestation { #[test] fun test_deserialize_batch_price_attestation() { - use iota::test_scenario::{Self, ctx}; + use sui::test_scenario::{Self, ctx}; // Set the arrival time let test = test_scenario::begin(@0x1234); let test_clock = clock::create_for_testing(ctx(&mut test)); diff --git a/target_chains/sui/contracts/sources/data_source.move b/target_chains/sui/contracts/sources/data_source.move index 2b7e4a56e9..d4814cd554 100644 --- a/target_chains/sui/contracts/sources/data_source.move +++ b/target_chains/sui/contracts/sources/data_source.move @@ -1,7 +1,7 @@ module pyth::data_source { - use iota::dynamic_field::{Self}; - use iota::object::{UID}; - use iota::tx_context::{TxContext}; + use sui::dynamic_field::{Self}; + use sui::object::{UID}; + use sui::tx_context::{TxContext}; use pyth::set::{Self}; diff --git a/target_chains/sui/contracts/sources/event.move b/target_chains/sui/contracts/sources/event.move index ab23be344d..a40c5d218b 100644 --- a/target_chains/sui/contracts/sources/event.move +++ b/target_chains/sui/contracts/sources/event.move @@ -1,5 +1,5 @@ module pyth::event { - use iota::event::{Self}; + use sui::event::{Self}; use pyth::price_feed::{PriceFeed}; friend pyth::pyth; diff --git a/target_chains/sui/contracts/sources/governance/contract_upgrade.move b/target_chains/sui/contracts/sources/governance/contract_upgrade.move index 5f1918dff1..cc5aae0820 100644 --- a/target_chains/sui/contracts/sources/governance/contract_upgrade.move +++ b/target_chains/sui/contracts/sources/governance/contract_upgrade.move @@ -8,9 +8,9 @@ /// 3. Upgrade. /// 4. Commit upgrade. module pyth::contract_upgrade { - use iota::event::{Self}; - use iota::object::{ID}; - use iota::package::{UpgradeReceipt, UpgradeTicket}; + use sui::event::{Self}; + use sui::object::{ID}; + use sui::package::{UpgradeReceipt, UpgradeTicket}; use wormhole::bytes32::{Self, Bytes32}; use wormhole::cursor::{Self}; @@ -38,9 +38,9 @@ module pyth::contract_upgrade { } /// Redeem governance VAA to issue an `UpgradeTicket` for the upgrade given - /// a contract upgrade VAA. This governance message is only relevant for Iota + /// 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 Iota), whose build digest is encoded in this message. + /// (in this case Sui), whose build digest is encoded in this message. public fun authorize_upgrade( pyth_state: &mut State, receipt: WormholeVAAVerificationReceipt, @@ -86,7 +86,7 @@ module pyth::contract_upgrade { /// Finalize the upgrade that ran to produce the given `receipt`. This /// method invokes `state::commit_upgrade` which interacts with - /// `iota::package`. + /// `sui::package`. public fun commit_upgrade( self: &mut State, receipt: UpgradeReceipt, diff --git a/target_chains/sui/contracts/sources/governance/set_data_sources.move b/target_chains/sui/contracts/sources/governance/set_data_sources.move index e3fbe4287a..47148c30b5 100644 --- a/target_chains/sui/contracts/sources/governance/set_data_sources.move +++ b/target_chains/sui/contracts/sources/governance/set_data_sources.move @@ -49,8 +49,8 @@ module pyth::set_data_sources { #[test_only] module pyth::set_data_sources_tests { - use iota::test_scenario::{Self}; - use iota::coin::Self; + use sui::test_scenario::{Self}; + use sui::coin::Self; use wormhole::external_address::{Self}; use wormhole::bytes32::{Self}; diff --git a/target_chains/sui/contracts/sources/governance/set_stale_price_threshold.move b/target_chains/sui/contracts/sources/governance/set_stale_price_threshold.move index 69efd66312..c4a158dfd7 100644 --- a/target_chains/sui/contracts/sources/governance/set_stale_price_threshold.move +++ b/target_chains/sui/contracts/sources/governance/set_stale_price_threshold.move @@ -27,8 +27,8 @@ module pyth::set_stale_price_threshold { #[test_only] module pyth::set_stale_price_threshold_test { - use iota::test_scenario::{Self}; - use iota::coin::Self; + use sui::test_scenario::{Self}; + use sui::coin::Self; use pyth::pyth_tests::{Self, setup_test, take_wormhole_and_pyth_states}; use pyth::state::Self; diff --git a/target_chains/sui/contracts/sources/governance/set_update_fee.move b/target_chains/sui/contracts/sources/governance/set_update_fee.move index c5c7fa07f7..a0c2c11a10 100644 --- a/target_chains/sui/contracts/sources/governance/set_update_fee.move +++ b/target_chains/sui/contracts/sources/governance/set_update_fee.move @@ -1,5 +1,5 @@ module pyth::set_update_fee { - use std::u64; + use sui::math::{Self}; use wormhole::cursor; @@ -34,14 +34,14 @@ module pyth::set_update_fee { } fun apply_exponent(mantissa: u64, exponent: u8): u64 { - mantissa * u64::pow(10, exponent) + mantissa * math::pow(10, exponent) } } #[test_only] module pyth::set_update_fee_tests { - use iota::test_scenario::{Self}; - use iota::coin::Self; + use sui::test_scenario::{Self}; + use sui::coin::Self; use pyth::pyth_tests::{Self, setup_test, take_wormhole_and_pyth_states}; use pyth::state::Self; diff --git a/target_chains/sui/contracts/sources/merkle_tree.move b/target_chains/sui/contracts/sources/merkle_tree.move index eb96cde668..54545c6651 100644 --- a/target_chains/sui/contracts/sources/merkle_tree.move +++ b/target_chains/sui/contracts/sources/merkle_tree.move @@ -2,7 +2,7 @@ // with a given depth, as well as proving that a leaf node belongs to the tree. module pyth::merkle_tree { use std::vector::{Self}; - use iota::hash::{keccak256}; + use sui::hash::{keccak256}; use wormhole::bytes20::{Self, Bytes20, data}; use wormhole::cursor::Cursor; use pyth::deserialize::{Self}; @@ -89,9 +89,9 @@ module pyth::merkle_tree { false } - // The Iota Move stdlb insert function shifts v[i] and subsequent elements to the right. + // The Sui Move stdlb insert function shifts v[i] and subsequent elements to the right. // We don't want this behavior, so we define our own set_element function that instead replaces the ith element. - // Reference: https://github.com/MystenLabs/iota/blob/main/crates/iota-framework/packages/move-stdlib/sources/vector.move + // Reference: https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/move-stdlib/sources/vector.move fun set_element(a: &mut vector, value: T, index: u64){ vector::push_back(a, value); // push value to end vector::swap_remove(a, index); // swap value to correct position and pop last value diff --git a/target_chains/sui/contracts/sources/migrate.move b/target_chains/sui/contracts/sources/migrate.move index 49d53017b3..394691e6cd 100644 --- a/target_chains/sui/contracts/sources/migrate.move +++ b/target_chains/sui/contracts/sources/migrate.move @@ -11,7 +11,7 @@ /// any of Pyth's methods by enforcing the current build version as /// their required minimum version. module pyth::migrate { - use iota::object::{ID}; + use sui::object::{ID}; use pyth::state::{Self, State}; use pyth::contract_upgrade::{Self}; @@ -67,7 +67,7 @@ module pyth::migrate { // Finally emit an event reflecting a successful migrate. let package = state::current_package(&latest_only, pyth_state); - iota::event::emit(MigrateComplete { package }); + sui::event::emit(MigrateComplete { package }); } #[test_only] diff --git a/target_chains/sui/contracts/sources/price_info.move b/target_chains/sui/contracts/sources/price_info.move index 14b8acb5ee..8f5cea1346 100644 --- a/target_chains/sui/contracts/sources/price_info.move +++ b/target_chains/sui/contracts/sources/price_info.move @@ -1,10 +1,10 @@ module pyth::price_info { - use iota::object::{Self, UID, ID}; - use iota::tx_context::{TxContext}; - use iota::dynamic_object_field::{Self}; - use iota::table::{Self}; - use iota::coin::{Self, Coin}; - use iota::iota::IOTA; + use sui::object::{Self, UID, ID}; + use sui::tx_context::{TxContext}; + use sui::dynamic_object_field::{Self}; + use sui::table::{Self}; + use sui::coin::{Self, Coin}; + use sui::sui::SUI; use pyth::price_feed::{Self, PriceFeed}; use pyth::price_identifier::{PriceIdentifier}; @@ -18,7 +18,7 @@ module pyth::price_info { friend pyth::pyth; friend pyth::state; - /// Iota object version of PriceInfo. + /// Sui object version of PriceInfo. /// Has a key ability, is unique for each price identifier, and lives in global store. struct PriceInfoObject has key, store { id: UID, @@ -33,7 +33,7 @@ module pyth::price_info { } /// Creates a table which maps a PriceIdentifier to the - /// UID (in bytes) of the corresponding Iota PriceInfoObject. + /// UID (in bytes) of the corresponding Sui PriceInfoObject. public(friend) fun new_price_info_registry(parent_id: &mut UID, ctx: &mut TxContext) { assert!( !dynamic_object_field::exists_(parent_id, KEY), @@ -95,19 +95,19 @@ module pyth::price_info { } public fun get_balance(price_info_object: &PriceInfoObject): u64 { - if (!dynamic_object_field::exists_with_type, Coin>(&price_info_object.id, FEE_STORAGE_KEY)) { + if (!dynamic_object_field::exists_with_type, Coin>(&price_info_object.id, FEE_STORAGE_KEY)) { return 0 }; - let fee = dynamic_object_field::borrow, Coin>(&price_info_object.id, FEE_STORAGE_KEY); + let fee = dynamic_object_field::borrow, Coin>(&price_info_object.id, FEE_STORAGE_KEY); coin::value(fee) } - public fun deposit_fee_coins(price_info_object: &mut PriceInfoObject, fee_coins: Coin) { - if (!dynamic_object_field::exists_with_type, Coin>(&price_info_object.id, FEE_STORAGE_KEY)) { + public fun deposit_fee_coins(price_info_object: &mut PriceInfoObject, fee_coins: Coin) { + if (!dynamic_object_field::exists_with_type, Coin>(&price_info_object.id, FEE_STORAGE_KEY)) { dynamic_object_field::add(&mut price_info_object.id, FEE_STORAGE_KEY, fee_coins); } else { - let current_fee = dynamic_object_field::borrow_mut, Coin>( + let current_fee = dynamic_object_field::borrow_mut, Coin>( &mut price_info_object.id, FEE_STORAGE_KEY ); @@ -139,8 +139,8 @@ module pyth::price_info { #[test] public fun test_get_price_info_object_id_from_price_identifier(){ - use iota::object::{Self}; - use iota::test_scenario::{Self, ctx}; + use sui::object::{Self}; + use sui::test_scenario::{Self, ctx}; use pyth::price_identifier::{Self}; let scenario = test_scenario::begin(@pyth); let uid = object::new(ctx(&mut scenario)); diff --git a/target_chains/sui/contracts/sources/pyth.move b/target_chains/sui/contracts/sources/pyth.move index 710a94bb99..7d7ce52d4f 100644 --- a/target_chains/sui/contracts/sources/pyth.move +++ b/target_chains/sui/contracts/sources/pyth.move @@ -1,11 +1,11 @@ module pyth::pyth { use std::vector; - use iota::tx_context::{TxContext}; - use iota::coin::{Self, Coin}; - use iota::iota::{IOTA}; - use iota::transfer::{Self}; - use iota::clock::{Self, Clock}; - use iota::package::{UpgradeCap}; + use sui::tx_context::{TxContext}; + use sui::coin::{Self, Coin}; + use sui::sui::{SUI}; + use sui::transfer::{Self}; + use sui::clock::{Self, Clock}; + use sui::package::{UpgradeCap}; use pyth::event::{Self as pyth_event}; use pyth::data_source::{Self, DataSource}; @@ -166,7 +166,7 @@ module pyth::pyth { while (!vector::is_empty(&price_infos)){ let cur_price_info = vector::pop_back(&mut price_infos); - // Only create new Iota PriceInfoObject if not already + // Only create new Sui PriceInfoObject if not already // registered with the Pyth State object. if (!state::price_feed_object_exists( pyth_state, @@ -175,7 +175,7 @@ module pyth::pyth { ) ) ){ - // Create and share newly created Iota PriceInfoObject containing a price feed, + // Create and share newly created Sui PriceInfoObject containing a price feed, // and then register a copy of its ID with State. let new_price_info_object = price_info::new_price_info_object(cur_price_info, ctx); let price_identifier = price_info::get_price_identifier(&cur_price_info); @@ -263,12 +263,12 @@ module pyth::pyth { pyth_state: &PythState, price_updates: HotPotatoVector, price_info_object: &mut PriceInfoObject, - fee: Coin, + fee: Coin, clock: &Clock ): HotPotatoVector { let latest_only = state::assert_latest_only(pyth_state); - // On Iota, users get to choose which price feeds to update. They specify a single price feed to + // On Sui, users get to choose which price feeds to update. They specify a single price feed to // update at a time. We therefore charge the base fee for each such individual update. // This is a departure from Eth, where users don't get to necessarily choose. assert!(state::get_base_update_fee(pyth_state) <= coin::value(&fee), E_INSUFFICIENT_FEE); @@ -359,7 +359,7 @@ module pyth::pyth { /// get_price() is likely to abort unless you call update_price_feeds() to update the cached price /// beforehand, as the cached prices may be older than the stale price threshold. /// - /// The price_info_object is a Iota object with the key ability that uniquely + /// The price_info_object is a Sui object with the key ability that uniquely /// contains a price feed for a given price_identifier. /// public fun get_price(state: &PythState, price_info_object: &PriceInfoObject, clock: &Clock): Price { @@ -419,12 +419,12 @@ module pyth::pyth { module pyth::pyth_tests{ use std::vector::{Self}; - use iota::iota::IOTA; - use iota::coin::{Self, Coin}; - use iota::test_scenario::{Self, Scenario, ctx, take_shared, return_shared}; - use iota::package::Self; - use iota::object::{Self, ID}; - use iota::clock::{Self, Clock}; + use sui::sui::SUI; + use sui::coin::{Self, Coin}; + use sui::test_scenario::{Self, Scenario, ctx, take_shared, return_shared}; + use sui::package::Self; + use sui::object::{Self, ID}; + use sui::clock::{Self, Clock}; use pyth::state::{State as PythState}; use pyth::setup::{Self}; @@ -501,8 +501,8 @@ module pyth::pyth_tests{ #[test_only] /// Init Wormhole core bridge state. /// Init Pyth state. - /// Set initial Iota clock time. - /// Mint some IOTA fee coins. + /// Set initial Sui clock time. + /// Mint some SUI fee coins. public fun setup_test( stale_price_threshold: u64, governance_emitter_chain_id: u64, @@ -511,7 +511,7 @@ module pyth::pyth_tests{ initial_guardians: vector>, base_update_fee: u64, to_mint: u64 - ): (Scenario, Coin, Clock) { + ): (Scenario, Coin, Clock) { let scenario = test_scenario::begin(DEPLOYER); @@ -576,7 +576,7 @@ module pyth::pyth_tests{ ctx(&mut scenario) ); - let coins = coin::mint_for_testing(to_mint, ctx(&mut scenario)); + let coins = coin::mint_for_testing(to_mint, ctx(&mut scenario)); let clock = clock::create_for_testing(ctx(&mut scenario)); (scenario, coins, clock) } @@ -673,7 +673,7 @@ module pyth::pyth_tests{ assert!(pyth::get_total_update_fee(&pyth_state, vector::length>(&multiple_vaas)) == 5*DEFAULT_BASE_UPDATE_FEE, 1); return_shared(pyth_state); - coin::burn_for_testing(test_coins); + coin::burn_for_testing(test_coins); clock::destroy_for_testing(_clock); test_scenario::end(scenario); } @@ -699,7 +699,7 @@ module pyth::pyth_tests{ ); cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); - coin::burn_for_testing(test_coins); + coin::burn_for_testing(test_coins); test_scenario::end(scenario); } @@ -730,7 +730,7 @@ module pyth::pyth_tests{ ); cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); - coin::burn_for_testing(test_coins); + coin::burn_for_testing(test_coins); test_scenario::end(scenario); } @@ -779,7 +779,7 @@ module pyth::pyth_tests{ let price_info_object_3 = take_shared(&scenario); let price_info_object_4 = take_shared(&scenario); - // Create vector of price info objects (Iota objects with key ability and living in global store), + // Create vector of price info objects (Sui objects with key ability and living in global store), // which contain the price feeds we want to update. Note that these can be passed into // update_price_feeds in any order! //let price_info_object_vec = vector[price_info_object_1, price_info_object_2, price_info_object_3, price_info_object_4]; @@ -930,7 +930,7 @@ module pyth::pyth_tests{ // clean up test scenario test_scenario::next_tx(&mut scenario, DEPLOYER); - coin::burn_for_testing(coins); + coin::burn_for_testing(coins); cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); test_scenario::end(scenario); @@ -963,7 +963,7 @@ module pyth::pyth_tests{ // clean up test scenario test_scenario::next_tx(&mut scenario, DEPLOYER); - coin::burn_for_testing(coins); + coin::burn_for_testing(coins); cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); test_scenario::end(scenario); @@ -996,7 +996,7 @@ module pyth::pyth_tests{ // clean up test scenario test_scenario::next_tx(&mut scenario, DEPLOYER); - coin::burn_for_testing(coins); + coin::burn_for_testing(coins); cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); test_scenario::end(scenario); @@ -1029,7 +1029,7 @@ module pyth::pyth_tests{ // clean up test scenario test_scenario::next_tx(&mut scenario, DEPLOYER); - coin::burn_for_testing(coins); + coin::burn_for_testing(coins); cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); test_scenario::end(scenario); @@ -1072,7 +1072,7 @@ module pyth::pyth_tests{ // clean up test scenario test_scenario::next_tx(&mut scenario, DEPLOYER); - coin::burn_for_testing(coins); + coin::burn_for_testing(coins); cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); test_scenario::end(scenario); @@ -1110,7 +1110,7 @@ module pyth::pyth_tests{ #[test] fun test_create_and_update_multiple_price_feeds_with_accumulator_success() { - use iota::coin::Self; + use sui::coin::Self; let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, ACCUMULATOR_TESTS_DATA_SOURCE(), ACCUMULATOR_TESTS_INITIAL_GUARDIANS, DEFAULT_BASE_UPDATE_FEE, DEFAULT_COIN_TO_MINT); @@ -1165,7 +1165,7 @@ module pyth::pyth_tests{ return_shared(price_info_object); idx = idx + 1; }; - coin::burn_for_testing(coins); + coin::burn_for_testing(coins); // clean up test scenario test_scenario::next_tx(&mut scenario, DEPLOYER); @@ -1211,7 +1211,7 @@ module pyth::pyth_tests{ let price_info_object_3 = take_shared(&scenario); let price_info_object_4 = take_shared(&scenario); - // Create vector of price info objects (Iota objects with key ability and living in global store), + // Create vector of price info objects (Sui objects with key ability and living in global store), // which contain the price feeds we want to update. Note that these can be passed into // update_price_feeds in any order! //let price_info_object_vec = vector[price_info_object_1, price_info_object_2, price_info_object_3, price_info_object_4]; @@ -1300,7 +1300,7 @@ module pyth::pyth_tests{ return_shared(price_info_object_2); return_shared(price_info_object_3); return_shared(price_info_object_4); - coin::burn_for_testing(test_coins); + coin::burn_for_testing(test_coins); cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); test_scenario::end(scenario); @@ -1395,7 +1395,7 @@ module pyth::pyth_tests{ return_shared(price_info_object_3); return_shared(price_info_object_4); - coin::burn_for_testing(test_coins); + coin::burn_for_testing(test_coins); cleanup_worm_state_pyth_state_and_clock(worm_state, pyth_state, clock); test_scenario::end(scenario); } @@ -1403,8 +1403,8 @@ module pyth::pyth_tests{ // pyth accumulator tests (included in this file instead of pyth_accumulator.move to avoid dependency cycle - as we need pyth_tests::setup_test) #[test] fun test_parse_and_verify_accumulator_updates(){ - use iota::test_scenario::{Self, take_shared, return_shared}; - use iota::transfer::{Self}; + use sui::test_scenario::{Self, take_shared, return_shared}; + use sui::transfer::{Self}; let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, vector[], ACCUMULATOR_TESTS_INITIAL_GUARDIANS, 50, 0); let worm_state = take_shared(&scenario); @@ -1434,8 +1434,8 @@ module pyth::pyth_tests{ #[test] fun test_parse_and_verify_accumulator_updates_with_extra_bytes_at_end_of_message(){ - use iota::test_scenario::{Self, take_shared, return_shared}; - use iota::transfer::{Self}; + use sui::test_scenario::{Self, take_shared, return_shared}; + use sui::transfer::{Self}; let (scenario, coins, clock) = setup_test(500, 23, ACCUMULATOR_TESTS_EMITTER_ADDRESS, vector[], ACCUMULATOR_TESTS_INITIAL_GUARDIANS, 50, 0); let worm_state = take_shared(&scenario); diff --git a/target_chains/sui/contracts/sources/pyth_accumulator.move b/target_chains/sui/contracts/sources/pyth_accumulator.move index e00da563f0..46000b3609 100644 --- a/target_chains/sui/contracts/sources/pyth_accumulator.move +++ b/target_chains/sui/contracts/sources/pyth_accumulator.move @@ -1,6 +1,6 @@ module pyth::accumulator { use std::vector::{Self}; - use iota::clock::{Clock, Self}; + use sui::clock::{Clock, Self}; use wormhole::bytes20::{Self, Bytes20}; use wormhole::cursor::{Self, Cursor}; use pyth::deserialize::{Self}; diff --git a/target_chains/sui/contracts/sources/set.move b/target_chains/sui/contracts/sources/set.move index f6d76186be..b0f53549c8 100644 --- a/target_chains/sui/contracts/sources/set.move +++ b/target_chains/sui/contracts/sources/set.move @@ -1,7 +1,7 @@ /// A set data structure. module pyth::set { - use iota::table::{Self, Table}; - use iota::tx_context::{TxContext}; + use sui::table::{Self, Table}; + use sui::tx_context::{TxContext}; use std::vector; /// Empty struct. Used as the value type in mappings to encode a set diff --git a/target_chains/sui/contracts/sources/setup.move b/target_chains/sui/contracts/sources/setup.move index 04eaba1bfe..b92609bed8 100644 --- a/target_chains/sui/contracts/sources/setup.move +++ b/target_chains/sui/contracts/sources/setup.move @@ -1,8 +1,8 @@ module pyth::setup { - use iota::object::{Self, UID}; - use iota::package::{Self, UpgradeCap}; - use iota::transfer::{Self}; - use iota::tx_context::{Self, TxContext}; + use sui::object::{Self, UID}; + use sui::package::{Self, UpgradeCap}; + use sui::transfer::{Self}; + use sui::tx_context::{Self, TxContext}; use pyth::state::{Self}; use pyth::data_source::{DataSource}; @@ -34,7 +34,7 @@ module pyth::setup { // This will be created and sent to the transaction sender // automatically when the contract is published. transfer::public_transfer( - iota::package::test_publish(object::id_from_address(@pyth), ctx), + sui::package::test_publish(object::id_from_address(@pyth), ctx), tx_context::sender(ctx) ); } diff --git a/target_chains/sui/contracts/sources/state.move b/target_chains/sui/contracts/sources/state.move index 3dcf6e62b5..9381e6457f 100644 --- a/target_chains/sui/contracts/sources/state.move +++ b/target_chains/sui/contracts/sources/state.move @@ -1,8 +1,8 @@ module pyth::state { use std::vector; - use iota::object::{Self, UID, ID}; - use iota::tx_context::{Self, TxContext}; - use iota::package::{UpgradeCap, UpgradeTicket, UpgradeReceipt}; + use sui::object::{Self, UID, ID}; + use sui::tx_context::{Self, TxContext}; + use sui::package::{UpgradeCap, UpgradeTicket, UpgradeReceipt}; use pyth::data_source::{Self, DataSource}; use pyth::price_info::{Self}; @@ -280,7 +280,7 @@ module pyth::state { /// Issue an `UpgradeTicket` for the upgrade. /// - /// NOTE: The Iota VM performs a check that this method is executed from the + /// 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`. @@ -294,7 +294,7 @@ module pyth::state { /// Finalize the upgrade that ran to produce the given `receipt`. /// - /// NOTE: The Iota VM performs a check that this method is executed from the + /// 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`. @@ -362,7 +362,7 @@ module pyth::state { // Add back in old dynamic field(s)... // Add dummy hash since this is the first time the package is published. - iota::dynamic_field::add(&mut self.id, CurrentDigest {}, bytes32::from_bytes(b"new build")); + sui::dynamic_field::add(&mut self.id, CurrentDigest {}, bytes32::from_bytes(b"new build")); } #[test_only]

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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 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::{ + 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/iota/vendor/wormhole_iota_testnet/examples/coins/sources/coin_10.move b/target_chains/iota/vendor/wormhole_iota_testnet/examples/coins/sources/coin_10.move new file mode 100644 index 0000000000..02fa816d27 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_iota_testnet/examples/coins/sources/coin_10.move @@ -0,0 +1,72 @@ +module coins::coin_10 { + use std::option; + 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` + /// 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 iota::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/iota/vendor/wormhole_iota_testnet/examples/coins/sources/coin_8.move b/target_chains/iota/vendor/wormhole_iota_testnet/examples/coins/sources/coin_8.move new file mode 100644 index 0000000000..26bbb35819 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_iota_testnet/examples/coins/sources/coin_8.move @@ -0,0 +1,72 @@ +module coins::coin_8 { + use std::option::{Self}; + 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` + /// 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 iota::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/iota/vendor/wormhole_iota_testnet/examples/core_messages/Makefile b/target_chains/iota/vendor/wormhole_iota_testnet/examples/core_messages/Makefile new file mode 100644 index 0000000000..210a28de7a --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/examples/core_messages/Move.devnet.toml b/target_chains/iota/vendor/wormhole_iota_testnet/examples/core_messages/Move.devnet.toml new file mode 100644 index 0000000000..d9363b2554 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/examples/core_messages/Move.lock b/target_chains/iota/vendor/wormhole_iota_testnet/examples/core_messages/Move.lock new file mode 100644 index 0000000000..9bddcd1268 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/examples/core_messages/Move.toml b/target_chains/iota/vendor/wormhole_iota_testnet/examples/core_messages/Move.toml new file mode 100644 index 0000000000..38872b17dc --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/examples/core_messages/sources/sender.move b/target_chains/iota/vendor/wormhole_iota_testnet/examples/core_messages/sources/sender.move new file mode 100644 index 0000000000..2f26f009f4 --- /dev/null +++ b/target_chains/iota/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 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}; + + 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 iota::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/iota/vendor/wormhole_iota_testnet/examples/templates/README.md b/target_chains/iota/vendor/wormhole_iota_testnet/examples/templates/README.md new file mode 100644 index 0000000000..41a8dbd907 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/Move.toml b/target_chains/iota/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/Move.toml new file mode 100644 index 0000000000..0c25220b1d --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/sources/coin.move b/target_chains/iota/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/sources/coin.move new file mode 100644 index 0000000000..096432f03e --- /dev/null +++ b/target_chains/iota/vendor/wormhole_iota_testnet/examples/templates/wrapped_coin/sources/coin.move @@ -0,0 +1,21 @@ +module wrapped_coin::coin { + use iota::transfer::{Self}; + use iota::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/iota/vendor/wormhole_iota_testnet/scripts/deploy.sh b/target_chains/iota/vendor/wormhole_iota_testnet/scripts/deploy.sh new file mode 100755 index 0000000000..d572f8cbf0 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/scripts/node_builder.sh b/target_chains/iota/vendor/wormhole_iota_testnet/scripts/node_builder.sh new file mode 100755 index 0000000000..940017e330 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/scripts/register_devnet.sh b/target_chains/iota/vendor/wormhole_iota_testnet/scripts/register_devnet.sh new file mode 100755 index 0000000000..79df935672 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/scripts/setup_rust.sh b/target_chains/iota/vendor/wormhole_iota_testnet/scripts/setup_rust.sh new file mode 100755 index 0000000000..3738ba9548 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/scripts/start_node.sh b/target_chains/iota/vendor/wormhole_iota_testnet/scripts/start_node.sh new file mode 100755 index 0000000000..2ffce35438 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/scripts/switch.sh b/target_chains/iota/vendor/wormhole_iota_testnet/scripts/switch.sh new file mode 100755 index 0000000000..caba0f4b70 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/scripts/wait_for_devnet.sh b/target_chains/iota/vendor/wormhole_iota_testnet/scripts/wait_for_devnet.sh new file mode 100755 index 0000000000..ff0b00355e --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/.gitignore b/target_chains/iota/vendor/wormhole_iota_testnet/testing/.gitignore new file mode 100644 index 0000000000..b552b7394c --- /dev/null +++ b/target_chains/iota/vendor/wormhole_iota_testnet/testing/.gitignore @@ -0,0 +1,4 @@ +node_modules +sui.log.* +./token_bridge/ +./wormhole/ diff --git a/target_chains/iota/vendor/wormhole_iota_testnet/testing/Makefile b/target_chains/iota/vendor/wormhole_iota_testnet/testing/Makefile new file mode 100644 index 0000000000..60c5246198 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/js/00_environment.ts b/target_chains/iota/vendor/wormhole_iota_testnet/testing/js/00_environment.ts new file mode 100644 index 0000000000..3ca09f236d --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/js/01_wormhole.ts b/target_chains/iota/vendor/wormhole_iota_testnet/testing/js/01_wormhole.ts new file mode 100644 index 0000000000..ae9a26f08b --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/js/helpers/build.ts b/target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/build.ts new file mode 100644 index 0000000000..374831ab8f --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/js/helpers/consts.ts b/target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/consts.ts new file mode 100644 index 0000000000..c4aa0787f4 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/js/helpers/error/moveAbort.ts b/target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/error/moveAbort.ts new file mode 100644 index 0000000000..04fdde7524 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/js/helpers/error/wormhole.ts b/target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/error/wormhole.ts new file mode 100644 index 0000000000..f011d606df --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/js/helpers/setup.ts b/target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/setup.ts new file mode 100644 index 0000000000..0399be84e7 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/js/helpers/upgrade.ts b/target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/upgrade.ts new file mode 100644 index 0000000000..4698e9a85b --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/js/helpers/utils.ts b/target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/utils.ts new file mode 100644 index 0000000000..58f71444e1 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/js/helpers/wormhole/testPublishMessage.ts b/target_chains/iota/vendor/wormhole_iota_testnet/testing/js/helpers/wormhole/testPublishMessage.ts new file mode 100644 index 0000000000..35a3876a51 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/package-lock.json b/target_chains/iota/vendor/wormhole_iota_testnet/testing/package-lock.json new file mode 100644 index 0000000000..3b6dd637e6 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/package.json b/target_chains/iota/vendor/wormhole_iota_testnet/testing/package.json new file mode 100644 index 0000000000..f29c59e15c --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/run_integration_test.sh b/target_chains/iota/vendor/wormhole_iota_testnet/testing/run_integration_test.sh new file mode 100755 index 0000000000..80da863836 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/scripts/upgrade-token-bridge.ts b/target_chains/iota/vendor/wormhole_iota_testnet/testing/scripts/upgrade-token-bridge.ts new file mode 100644 index 0000000000..605128dddb --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/scripts/upgrade-wormhole.ts b/target_chains/iota/vendor/wormhole_iota_testnet/testing/scripts/upgrade-wormhole.ts new file mode 100644 index 0000000000..82d26b0f72 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/sui_config/client.yaml b/target_chains/iota/vendor/wormhole_iota_testnet/testing/sui_config/client.yaml new file mode 100644 index 0000000000..33371df956 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/sui_config/fullnode.yaml b/target_chains/iota/vendor/wormhole_iota_testnet/testing/sui_config/fullnode.yaml new file mode 100644 index 0000000000..fc71e910e6 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/testing/sui_config/genesis.blob b/target_chains/iota/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 iota::coin::{Self}; + use iota::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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer.move new file mode 100644 index 0000000000..ae58d044f6 --- /dev/null +++ b/target_chains/iota/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 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}; + 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 Iota. + 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 Iota, 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 Iota. + 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 Iota event with `TransferRedeemed`. + iota::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. + iota::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 iota::coin::{Self, Coin}; + use iota::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 (Iota). + 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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer_with_payload.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/complete_transfer_with_payload.move new file mode 100644 index 0000000000..4489e82945 --- /dev/null +++ b/target_chains/iota/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 iota::coin::{Self, Coin}; + use iota::object::{Self}; + use iota::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 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}; + + 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 Iota 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 Iota 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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/create_wrapped.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/create_wrapped.move new file mode 100644 index 0000000000..914fa3dfaa --- /dev/null +++ b/target_chains/iota/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.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 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}; + 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!(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). + 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 Iota'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 = + iota::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 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}; + 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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/datatypes/normalized_amount.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/datatypes/normalized_amount.move new file mode 100644 index 0000000000..c253b57ab4 --- /dev/null +++ b/target_chains/iota/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 iota::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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/governance/register_chain.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/governance/register_chain.move new file mode 100644 index 0000000000..2255842ae6 --- /dev/null +++ b/target_chains/iota/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 iota::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 iota::table::{Self}; + use iota::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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/governance/upgrade_contract.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/governance/upgrade_contract.move new file mode 100644 index 0000000000..9ac95e13de --- /dev/null +++ b/target_chains/iota/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 iota::object::{ID}; + use iota::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 Iota + /// because a contract upgrade is only relevant to one particular network + /// (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 Iota 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 + /// `iota::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. + iota::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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/messages/asset_meta.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/messages/asset_meta.move new file mode 100644 index 0000000000..df28384cea --- /dev/null +++ b/target_chains/iota/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 iota::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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer.move new file mode 100644 index 0000000000..190afad9dc --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer_with_payload.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/messages/transfer_with_payload.move new file mode 100644 index 0000000000..9c00a36385 --- /dev/null +++ b/target_chains/iota/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 iota::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 iota::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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/migrate.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/migrate.move new file mode 100644 index 0000000000..b8d62bcab8 --- /dev/null +++ b/target_chains/iota/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 iota::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); + iota::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 iota::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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/resources/native_asset.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/resources/native_asset.move new file mode 100644 index 0000000000..89898868da --- /dev/null +++ b/target_chains/iota/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 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 Iota. +/// +/// See `token_registry` module for more details. +module token_bridge::native_asset { + 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}; + + 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 (Iota'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 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}; + + 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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/resources/token_registry.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/resources/token_registry.move new file mode 100644 index 0000000000..1a299ec40f --- /dev/null +++ b/target_chains/iota/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 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}; + 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, + iota::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 `iota::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 iota::balance::{Self}; + use iota::coin::{CoinMetadata}; + use iota::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 = 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(); + 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 + // `iota::dynamic_field` error. + token_registry::add_new_native_test_only( + &mut registry, + &coin_meta + ); + + abort 42 + } + + #[test] + #[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. + 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 + // `iota::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 = 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. + 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 + // `iota::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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/resources/wrapped_asset.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/resources/wrapped_asset.move new file mode 100644 index 0000000000..4e60f21995 --- /dev/null +++ b/target_chains/iota/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 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}; + + 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 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; + /// 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 Iota'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 Iota'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; + iota::test_utils::destroy(treasury_cap); + + let ForeignInfo { + token_chain: _, + token_address: _, + native_decimals: _, + symbol: _ + } = info; + + iota::package::make_immutable(upgrade_cap); + } +} + +#[test_only] +module token_bridge::wrapped_asset_tests { + use std::string::{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}; + + 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); + + // Iota'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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/setup.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/setup.move new file mode 100644 index 0000000000..246dae0100 --- /dev/null +++ b/target_chains/iota/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 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}; + + /// 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 iota::package for proposed upgrades. + use iota::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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/state.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/state.move new file mode 100644 index 0000000000..f16ad7ffab --- /dev/null +++ b/target_chains/iota/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 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}; + 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 = iota::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 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`. + 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 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`. + 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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_10.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_10.move new file mode 100644 index 0000000000..f58a56d727 --- /dev/null +++ b/target_chains/iota/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 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}; + use token_bridge::token_registry::{Self}; + + struct COIN_NATIVE_10 has drop {} + + // 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. + 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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_4.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_native_4.move new file mode 100644 index 0000000000..b9c2621005 --- /dev/null +++ b/target_chains/iota/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 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}; + use token_bridge::token_registry::{Self}; + + struct COIN_NATIVE_4 has drop {} + + // 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. + 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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_12.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_12.move new file mode 100644 index 0000000000..242b4b0ccf --- /dev/null +++ b/target_chains/iota/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 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}; + 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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_7.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/coin_wrapped_7.move new file mode 100644 index 0000000000..9ac16322d7 --- /dev/null +++ b/target_chains/iota/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 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}; + 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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/dummy_message.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/dummy_message.move new file mode 100644 index 0000000000..d3784e5a97 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/token_bridge_scenario.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/test/token_bridge_scenario.move new file mode 100644 index 0000000000..fab2145773 --- /dev/null +++ b/target_chains/iota/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 iota::balance::{Self}; + use iota::package::{UpgradeCap}; + use iota::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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens.move new file mode 100644 index 0000000000..0fbdb5ddc6 --- /dev/null +++ b/target_chains/iota/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 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 iota::balance::{Self, Balance}; + use iota::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 Iota 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 + /// 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 + /// `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 = iota::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 iota::coin::{Self}; + use iota::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 + ); + iota::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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens_with_payload.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/transfer_tokens_with_payload.move new file mode 100644 index 0000000000..48fca22f47 --- /dev/null +++ b/target_chains/iota/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 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 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}; + 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 Iota 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 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}; + 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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/utils/coin_utils.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/utils/coin_utils.move new file mode 100644 index 0000000000..4eb94151e5 --- /dev/null +++ b/target_chains/iota/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 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. + 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 { + iota::pay::keep(the_coin, ctx) + } + } +} diff --git a/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/utils/string_utils.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/utils/string_utils.move new file mode 100644 index 0000000000..17a1fbdcdb --- /dev/null +++ b/target_chains/iota/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 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); + // 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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/vaa.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/vaa.move new file mode 100644 index 0000000000..726ffa449f --- /dev/null +++ b/target_chains/iota/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 iota::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 iota::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/iota/vendor/wormhole_iota_testnet/token_bridge/sources/version_control.move b/target_chains/iota/vendor/wormhole_iota_testnet/token_bridge/sources/version_control.move new file mode 100644 index 0000000000..d36f8f2fdc --- /dev/null +++ b/target_chains/iota/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 Iota 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/iota/vendor/wormhole_iota_testnet/wormhole/.gitignore b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/.gitignore new file mode 100644 index 0000000000..378eac25d3 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/.gitignore @@ -0,0 +1 @@ +build diff --git a/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Makefile b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Makefile new file mode 100644 index 0000000000..1f9d45f8af --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/wormhole/Move.devnet.toml b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Move.devnet.toml new file mode 100644 index 0000000000..ba3f5e54aa --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/wormhole/Move.lock b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Move.lock new file mode 100644 index 0000000000..540307f10f --- /dev/null +++ b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Move.lock @@ -0,0 +1,35 @@ +# @generated by Move, please check-in and do not edit manually. + +[move] +version = 0 +manifest_digest = "6E83C6D69B7F45D2ACF973DFF878405621DC0E67C0EE2D4A28748A7FDD907AA1" +deps_digest = "F8BBB0CCB2491CA29A3DF03D6F92277A4F3574266507ACD77214D37ECA3F3082" + +dependencies = [ + { name = "Iota" }, +] + +[[move.package]] +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 = "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/iota/vendor/wormhole_iota_testnet/wormhole/Move.mainnet.toml b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Move.mainnet.toml new file mode 100644 index 0000000000..7465390ebb --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/wormhole/Move.testnet.toml b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Move.testnet.toml new file mode 100644 index 0000000000..ed34ccb472 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/wormhole/Move.toml b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Move.toml new file mode 100644 index 0000000000..e0197a6e62 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/Move.toml @@ -0,0 +1,12 @@ +[package] +name = "Wormhole" +version = "0.2.0" +published-at = "0xfca58c557f09cddb7930588c4e2a4edbe3cdded1ac1ed2270aa2dfa8d2b9ae0d" + +[dependencies.Iota] +git = "https://github.com/iotaledger/iota.git" +subdir = "crates/iota-framework/packages/iota-framework" +rev = "751c23caf24efd071463b9ffd07eabcb15f44f31" + +[addresses] +wormhole = "0xfca58c557f09cddb7930588c4e2a4edbe3cdded1ac1ed2270aa2dfa8d2b9ae0d" diff --git a/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/README.md b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/README.md new file mode 100644 index 0000000000..62a62a67b1 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes20.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes20.move new file mode 100644 index 0000000000..3c097dec69 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes32.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/bytes32.move new file mode 100644 index 0000000000..5c0157b9e7 --- /dev/null +++ b/target_chains/iota/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 iota::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 { + iota::address::from_bytes(to_bytes(value)) + } + + /// Create `Bytes32` from `address`. + public fun from_address(addr: address): Bytes32 { + new(iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/external_address.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/external_address.move new file mode 100644 index 0000000000..7bf326c738 --- /dev/null +++ b/target_chains/iota/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 iota::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 { + iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/guardian_signature.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/datatypes/guardian_signature.move new file mode 100644 index 0000000000..58698d51a6 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/wormhole/sources/emitter.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/emitter.move new file mode 100644 index 0000000000..109f35f0e5 --- /dev/null +++ b/target_chains/iota/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 iota::object::{Self, ID, UID}; + use iota::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 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 { + 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 + }; + + iota::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); + + iota::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 iota::tx_context::dummy()), + sequence: 0 + } + } +} + +#[test_only] +module wormhole::emitter_tests { + use iota::object::{Self}; + use iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance/set_fee.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance/set_fee.move new file mode 100644 index 0000000000..68a7c8bfe1 --- /dev/null +++ b/target_chains/iota/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 IOTA + /// denomination. This governance 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. + 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 iota::balance::{Self}; + use iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance/transfer_fee.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance/transfer_fee.move new file mode 100644 index 0000000000..6ac812ee4e --- /dev/null +++ b/target_chains/iota/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 iota::coin::{Self}; + use iota::transfer::{Self}; + use iota::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 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. + 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 IOTA 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 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}; + 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 = iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance/update_guardian_set.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance/update_guardian_set.move new file mode 100644 index 0000000000..8eb5671236 --- /dev/null +++ b/target_chains/iota/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 iota::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) + ); + + iota::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 iota::clock::{Self}; + use iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance/upgrade_contract.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance/upgrade_contract.move new file mode 100644 index 0000000000..89ea8fa7c2 --- /dev/null +++ b/target_chains/iota/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 iota::object::{ID}; + use iota::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 Iota + /// because a contract upgrade is only relevant to one particular network + /// (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 Iota 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 + /// `iota::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. + iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance_message.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/governance_message.move new file mode 100644 index 0000000000..a28ac2c80f --- /dev/null +++ b/target_chains/iota/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 Iota + /// 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 (Iota'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 Iota. + 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 iota::test_scenario::{Self}; + use iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/migrate.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/migrate.move new file mode 100644 index 0000000000..5003e16c02 --- /dev/null +++ b/target_chains/iota/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 iota::clock::{Clock}; + use iota::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); + iota::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 iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/publish_message.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/publish_message.move new file mode 100644 index 0000000000..cd6cd2c6df --- /dev/null +++ b/target_chains/iota/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 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 `iota::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 Iota 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; + + // Iota is an instant finality chain, so we don't need confirmations. + let consistency_level = 0; + + // Emit Iota event with `WormholeMessage`. + iota::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 iota::coin::{Self}; + use iota::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); + iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/consumed_vaas.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/consumed_vaas.move new file mode 100644 index 0000000000..eeb16a8f38 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/consumed_vaas.move @@ -0,0 +1,29 @@ +module wormhole::consumed_vaas { + use iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/fee_collector.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/fee_collector.move new file mode 100644 index 0000000000..c9d91533cf --- /dev/null +++ b/target_chains/iota/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 IOTA denomination. +/// The `FeeCollector` requires that the fee deposited is exactly equal to the +/// `fee_amount` configured. +module wormhole::fee_collector { + 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 IOTA 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 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) { + 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 `iota::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 iota::coin::{Self}; + use iota::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 = iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian.move new file mode 100644 index 0000000000..73f82631e3 --- /dev/null +++ b/target_chains/iota/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 iota::hash::{Self}; + use iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian_set.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/guardian_set.move new file mode 100644 index 0000000000..fd2cc1057d --- /dev/null +++ b/target_chains/iota/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 iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/set.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/resources/set.move new file mode 100644 index 0000000000..21da087083 --- /dev/null +++ b/target_chains/iota/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 `iota::table` to store unique keys of the same type. +/// +/// NOTE: Items added to this data structure cannot be removed. +module wormhole::set { + 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; + /// 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 iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/setup.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/setup.move new file mode 100644 index 0000000000..55eccb3ce0 --- /dev/null +++ b/target_chains/iota/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 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}; + + /// 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( + iota::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 iota::package::{Self}; + use iota::object::{Self}; + use iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/state.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/state.move new file mode 100644 index 0000000000..c06bd47db8 --- /dev/null +++ b/target_chains/iota/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 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}; + 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; + + /// 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 + /// 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 Iota + /// 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 = iota::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 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`. + 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 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`. + 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/iota/vendor/wormhole_iota_testnet/wormhole/sources/test/wormhole_scenario.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/test/wormhole_scenario.move new file mode 100644 index 0000000000..cb4ba70403 --- /dev/null +++ b/target_chains/iota/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 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}; + 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/iota/vendor/wormhole_iota_testnet/wormhole/sources/utils/bytes.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/utils/bytes.move new file mode 100644 index 0000000000..0d181f6775 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/wormhole/sources/utils/cursor.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/utils/cursor.move new file mode 100644 index 0000000000..73a96ebea0 --- /dev/null +++ b/target_chains/iota/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/iota/vendor/wormhole_iota_testnet/wormhole/sources/utils/package_utils.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/utils/package_utils.move new file mode 100644 index 0000000000..5c0e47c72e --- /dev/null +++ b/target_chains/iota/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 `iota::package`. +module wormhole::package_utils { + use std::type_name::{Self, TypeName}; + 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}; + + /// `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 = + iota::address::from_bytes( + iota::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 `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. + 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 `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. + /// + /// 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 iota::object::{Self, UID}; + use iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/vaa.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/vaa.move new file mode 100644 index 0000000000..0242bbf4c4 --- /dev/null +++ b/target_chains/iota/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 iota::clock::{Clock}; + use iota::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 iota::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 iota::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(iota::hash::keccak256(&body_buf)); + assert!(vaa::compute_message_hash(&parsed) == expected_message_hash, 0); + + let expected_digest = + bytes32::new( + iota::hash::keccak256(&iota::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/iota/vendor/wormhole_iota_testnet/wormhole/sources/version_control.move b/target_chains/iota/vendor/wormhole_iota_testnet/wormhole/sources/version_control.move new file mode 100644 index 0000000000..6821d04072 --- /dev/null +++ b/target_chains/iota/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 Iota 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 {} + } +} diff --git a/target_chains/iota/vendor/wormhole_movement_m2_devnet/.gitignore b/target_chains/iota/vendor/wormhole_movement_m2_devnet/.gitignore new file mode 100644 index 0000000000..6ddb4b0ac3 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/.gitignore @@ -0,0 +1,2 @@ +deploy.out +sui.log.* diff --git a/target_chains/iota/vendor/wormhole_movement_m2_devnet/Docker.md b/target_chains/iota/vendor/wormhole_movement_m2_devnet/Docker.md new file mode 100755 index 0000000000..97ff842f6d --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/Dockerfile b/target_chains/iota/vendor/wormhole_movement_m2_devnet/Dockerfile new file mode 100644 index 0000000000..4bef3a3624 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/Dockerfile.base b/target_chains/iota/vendor/wormhole_movement_m2_devnet/Dockerfile.base new file mode 100644 index 0000000000..4c76dc016d --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/Makefile b/target_chains/iota/vendor/wormhole_movement_m2_devnet/Makefile new file mode 100644 index 0000000000..0ee0e5eb94 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/NOTES.md b/target_chains/iota/vendor/wormhole_movement_m2_devnet/NOTES.md new file mode 100644 index 0000000000..5677cfb924 --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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/iota/vendor/wormhole_movement_m2_devnet/README.md b/target_chains/iota/vendor/wormhole_movement_m2_devnet/README.md new file mode 100644 index 0000000000..729a8c406c --- /dev/null +++ b/target_chains/iota/vendor/wormhole_movement_m2_devnet/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). + +